Почему Pandas sum() и Pythons sum() в списке чисел с плавающей запятой дают немного разные результаты, что приводит к разнице при округлении результата
>>> import pandas as pd
>>> from decimal import Decimal
>>> numbers = [0.495,1.495,2.495,3.495,4.495,5.495,6.495, 7.495,8.495, 9.495, 10.495]
>>> Decimal(sum(numbers))
Decimal('60.44500000000000028421709430404007434844970703125')
>>> round(Decimal(sum(numbers)),2)
Decimal('60.45')
>>> Decimal(float(pd.DataFrame(numbers).sum()))
Decimal('60.44499999999999317878973670303821563720703125')
>>> round(Decimal(float(pd.DataFrame(numbers).sum())),2)
Decimal('60.44')
Таким образом, несмотря на использование одной и той же функции round(), небольшой разницы в числах sum() между Pandas и Python достаточно, чтобы получить другой результат.
Я также заметил, что Pandas дает другой результат, если порядок чисел обратный, в отличие от стандартного sum() в Python:
>>> Decimal(sum(reversed(numbers)))
Decimal('60.44500000000000028421709430404007434844970703125'). # the same as unreversed
>>> Decimal(float(pd.DataFrame(reversed(numbers)).sum()))
Decimal('60.44499999999998607336237910203635692596435546875'). # different from unreversed
Разница в результате суммы по почитаемому и необратимому списку незначительна. Но до сих пор я думал, что сложение с плавающей запятой должно быть коммутативным. Похоже, что это не относится к пандам.
Так почему же Pandas sum() дает отличные от Python sum() результаты для чисел с плавающей запятой? Почему при простом возврате чисел получается другой результат? Это ошибка или особенность сложения чисел с плавающей запятой в Pandas? (Или это связано с моим базовым оборудованием? Я использую Python 3.12 с Pandas 2.2.2 в Mac OS 14.1.1 с чипом Apple M3 Pro)






Мое предположение:
Pandas внутренне использует функции/методы из библиотеки NumPy таким образом, что они по-прежнему полагаются на NumPy.sum
Всегда обращаю внимание на это маленькое замечание о методах Pandas sum:
Это эквивалентно методу numpy.sum
Там вы можете найти небольшую заметку о числах с плавающей запятой, которая, мы надеемся, проясняет расхождение результатов между обоими методами sum.
Для чисел с плавающей запятой числовая точность sum (и np.add.reduce) обычно ограничивается непосредственным добавлением каждого числа по отдельности к результату, что приводит к ошибкам округления на каждом этапе. Однако часто numpy будет использовать более эффективный численный подход (частичное попарное суммирование), что приводит к повышению точности во многих случаях использования. Эта повышенная точность всегда обеспечивается, когда ось не указана. Когда задана ось, это будет зависеть от того, по какой оси суммируется. Технически, чтобы обеспечить максимально возможную скорость, повышенная точность используется только тогда, когда суммирование происходит по быстрой оси в памяти. Обратите внимание, что точная точность может варьироваться в зависимости от других параметров. В отличие от NumPy, функция Python math.fsum использует более медленный, но более точный подход к суммированию. Числовые ошибки могут стать значительными, особенно при суммировании большого количества чисел с плавающей запятой более низкой точности, таких как float32. В таких случаях рекомендуется использовать dtype="float64", чтобы обеспечить более высокую точность вывода.
Дайте нам знать, если результат Python math.fsum становится более точным, чем результат Pandas.
Я пробовал fsum, но Decimal(math.fsum((numbers))) дает точно такой же результат, как и использование стандартного sum() в Python. Причина может заключаться в том, что начиная с Python 3.12 алгоритм для sum() был улучшен: «sum() теперь использует суммирование Неймайера для повышения точности и коммутативности при суммировании чисел с плавающей запятой или смешанных целых и чисел с плавающей запятой.», см. docs.python.org/3/ Whatsnew/3.12.html
Просто для отслеживания: спасибо, что указали на это также в NumPy GitHub Repo
Сложение с плавающей запятой коммутативно, но не ассоциативно! Часто
(a + b) + cдает немного другой результат, чемa + (b + c). Таким образом, порядок имеет значение, иa + b + cможет не равнятьсяc + b + a.