Разрешение нулей в товаре товаров в списке
Учитывая, что мы можем легко конвертировать между продуктами элементов в списке с суммой логарифма элементов в списке, если в списке нет 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
часть.