Почему Pythons sum() и Pandas sum() дают разные результаты

Почему 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)

Сложение с плавающей запятой коммутативно, но не ассоциативно! Часто (a + b) + c дает немного другой результат, чем a + (b + c). Таким образом, порядок имеет значение, и a + b + c может не равняться c + b + a.

Steve Summit 02.05.2024 22:13
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
1
89
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Мое предположение:

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

asmaier 03.05.2024 10:35

Просто для отслеживания: спасибо, что указали на это также в NumPy GitHub Repo

fizzlebix 06.05.2024 16:29

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