Разрешение нулей в товаре товаров в списке

Учитывая, что мы можем легко конвертировать между продуктами элементов в списке с суммой логарифма элементов в списке, если в списке нет 0, например:

>>> from operator import mul
>>> pn = [0.4, 0.3, 0.2, 0.1]
>>> math.pow(reduce(mul, pn, 1), 1./len(pn))
0.22133638394006433
>>> math.exp(sum(0.25 * math.log(p) for p in pn))
0.22133638394006436

Как мы должны обрабатывать случаи, когда в списке и в Python есть нули (программно и математически правильно)?

Более конкретно, как мы должны обрабатывать такие случаи, как:

>>> pn = [0.4, 0.3, 0, 0]
>>> math.pow(reduce(mul, pn, 1), 1./len(pn))
0.0
>>> math.exp(sum(1./len(pn) * math.log(p) for p in pn))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <genexpr>
ValueError: math domain error

Возвращение 0 действительно правильный способ справиться с этим? Что такое элегантное решение, которое учитывает 0 в списке, но не заканчивается 0?

Так как это своего рода среднее геометрическое (произведение списка), и оно не совсем полезно, когда мы возвращаем 0 только потому, что в списке есть один 0.

Перейдите от Math Stackexchange: https://math.stackexchange.com/questions/1727497/resolving-zeros-in-product-of-items-in-list, Нет ответа от математических людей, возможно, джедаи с Python/ кодом имеют лучшие идеи в решении этого.

4 ответа

Решение

TL; DR: Да, возвращать 0 - это единственный правильный путь. (Но см. Заключение.)

Математическое обоснование

В реальном анализе (т.е. не для комплексных чисел), когда рассматриваются логарифмы, мы традиционно принимаем область log реальные положительные числа. У нас есть личность:

x = exp(log(x)),   for x>0.

Это может быть естественно распространено на x=0 поскольку предел правого выражения хорошо определен в x->0+ и равно 0. Более того, это законно установить log(0)=-inf а также exp(-inf)=0 (опять же: только для реальных, а не сложных чисел). Формально мы расширяем набор действительных чисел, добавляя два элемента -inf, +inf и определение согласованной арифметики и т. д. (Для наших целей нам нужно иметь inf + x = inf, x * inf = inf для реального х, inf + inf = inf так далее.)

Другая личность x = log(exp(x)) менее хлопотно и справедливо для всех действительных чисел (и даже x=-inf или же +inf).

Среднее геометрическое

Среднее геометрическое можно определить для неотрицательных чисел (возможно, равных нулям). Для двух номеров a, b (это естественно обобщает на большее число, так что я буду использовать только два далее), это

gm(a,b) = sqrt(a*b),   for a,b >= 0.

Конечно, gm(0,b)=0, Принимая журнал, мы получаем:

log(gm(a,b)) = (log(a) + log(b))/2

и это хорошо определено, если a или же b это ноль. (Мы можем подключить log(0) = -inf и идентичность все еще остается верной благодаря расширенной арифметике, которую мы определили ранее.)

интерпретация

Неудивительно, что понятие геометрического среднего происходит от геометрии и первоначально (в древней Греции) использовалось для строго положительных чисел.

Предположим, у нас есть прямоугольник со сторонами длины a а также b, Найдите квадрат с площадью, равной площади прямоугольника. Легко видеть, что сторона квадрата является средним геометрическим a а также b,

Теперь, если мы возьмем a = 0тогда у нас на самом деле нет прямоугольника, и эта геометрическая интерпретация нарушается. Подобные проблемы могут возникнуть с другими интерпретациями. Мы можем смягчить это, рассматривая, например, вырожденные прямоугольники и квадраты, но это не всегда может быть правдоподобным подходом.

Заключение

Пользователь (математик, инженер, программист) понимает, как она понимает значение геометрического среднего, равного нулю. Если это вызывает серьезные проблемы с интерпретацией результатов или нарушает компьютерную программу, то, во-первых, возможно, выбор геометрического среднего не был оправдан в качестве математической модели.


питон

Как уже упоминалось в других ответах, в Python реализована бесконечность. Это вызывает предупреждение времени выполнения (деление на ноль) при выполнении np.exp(np.log(0)) но результат операции правильный.

Так или иначе 0 Правильный результат зависит от того, чего вы пытаетесь достичь. ptrj проделал большую работу с их ответом, поэтому я добавлю только одну вещь, которую нужно рассмотреть.

Вы можете рассмотреть возможность использования геометрического среднего с поправкой на эпсилон. В то время как стандартное геометрическое среднее имеет вид (a_1*a_2*...*a_n)^(1/n)геометрическое среднее с поправкой на эпсилон имеет вид ( (a_1+e)*(a_2+e)*...*(a_n+e) )^(1/n) - e, Подходящее значение для эпсилон (e) снова зависит от вашей задачи.

Скорректированные по эпсилону геометрические средние значения иногда используются при извлечении данных, когда 0 в наборе не должно приводить к полному исчезновению оценки записи, хотя все равно должно штрафовать оценку записи. См., Например, Методы агрегирования баллов в экспериментах поиска.

Например, с вашими данными и настройкой эпсилона 0.01

>>> from operator import mul
>>> pn=[0.4, 0.3, 0, 0]
>>> e=0.01
>>> pow(reduce(mul, [x+e for x in pn], 1), 1./len(pn)) - e
0.04970853116594962

Вы должны вернуться -math.inf в питоне 3.5 или -float('inf') в старых версиях. Это потому, что логарифм чисел, очень близких к 0, уходит в отрицательную бесконечность. Это значение с плавающей запятой сохраняет правильные неравенства между суммой логов между списками, например, можно ожидать, что

sumlog([5, 4, 1, 0, 2]) < sumlog([5, 1, 4, 0.0001, 1])

Это неравенство сохраняется, если вы возвращаете отрицательную бесконечность.

Вы можете попробовать использовать списочные выражения в Python. Они могут быть очень мощными для настройки способа обработки ваших данных. В этом примере используется понимание списка и номер ошибки -999,

>>> [math.log(i) if i > 0 else -999 for i in pn]
>>> [-0.916290731874155, -1.2039728043259361, -999, -999]

Если вы используете только if а не elseтогда if идет после for i in pn часть.

Другие вопросы по тегам