Округление столбца pandas с до целого сохранением суммы
Вопрос не совсем однозначный, но могу предложить просто раскидать по единичке всю разницу между округлением и исходной суммой. На примере 50 значений по 0.1:
import pandas as pd # Подготовка и проверка данных df = pd.DataFrame() sum_float = df['num'].sum() print(f'Сумма float: ') df['num'] = df['num'].astype(int) sum_int = df['num'].sum() print(f'Сумма int: ') # Здесь кидаем по единичке в столько элементов, сколько у нас разница ones_num = int(round(sum_float - sum_int)) df.iloc[:ones_num, 0] += 1 # Проверяем sum_int = df['num'].sum() print(f'Исправленная сумма int: ')
Сумма float: 5 Сумма int: 0 Исправленная сумма int: 5
Как округлить число на python до сотых?
Напомню на всякий случай, что не каждое десятичное число можно представить конечным количеством двоичных знаков. Это как 1\3 в троичной системе будет просто 0.1, а в десятичной 0.333(3).
Это значит, что округлять с помощью round не всегда хорошая идея.
К примеру, десятичное число 0.2 предствляется периодической бинарной дробью.
Смотрите:
In [1]: f'' Out[1]: '0.200000000000000011102230246252'
Если вам нужна точная арифметика с десятичными числами с правильным округлением, то используйте модуль decimal, где есть специальный тип данных, контроллирующий точность.
В простых же случаях можно округлять до нужного числа знаков при преобразовании в текст, как показано выше. Однако при этом на способ округления (а там все не так просто, как некоторые думают) воздействовать нельзя. Если нужно совсем правильное округление — decimal.
Как округлить до целых python
При работе с числами с плавающей точкой (то есть float) мы сталкиваемся с тем, что в результате вычислений мы получаем не совсем верный результат:
number = 0.1 + 0.1 + 0.1 print(number) # 0.30000000000000004
Проблему может решить использование функции round() , которая округлит число. Однако есть и другой способ, который заключается в использовании встроенного модуля decimal .
Ключевым компонентом для работы с числами в этом модуле является класс Decimal . Для его применения нам надо создать его объект с помощью конструктора. В конструктор передается строковое значение, которое представляет число:
from decimal import Decimal number = Decimal("0.1")
После этого объект Decimal можно использовать в арифметических операциях:
from decimal import Decimal number = Decimal("0.1") number = number + number + number print(number) # 0.3
В операциях с Decimal можно использовать целые числа:
number = Decimal("0.1") number = number + 2
Однако нельзя смешивать в операциях дробные числа float и Decimal:
number = Decimal("0.1") number = number + 0.1 # здесь возникнет ошибка
С помощью дополнительных знаков мы можем определить, сколько будет символов в дробной части числа:
number = Decimal("0.10") number = 3 * number print(number) # 0.30
Строка «0.10» определяет два знака в дробной части, даже если последние символы будут представлять ноль. Соответственно «0.100» представляет три знака в дробной части.
Округление чисел
Объекты Decimal имеют метод quantize() , который позволяет округлять числа. В этот метод в качестве первого аргумента передается также объект Decimal, который указывает формат округления числа:
from decimal import Decimal number = Decimal("0.444") number = number.quantize(Decimal("1.00")) print(number) # 0.44 number = Decimal("0.555678") print(number.quantize(Decimal("1.00"))) # 0.56 number = Decimal("0.999") print(number.quantize(Decimal("1.00"))) # 1.00
Используемая строка «1.00» указывает, что округление будет идти до двух знаков в дробной части.
По умолчанию округление описывается константой ROUND_HALF_EVEN , при котором округление происходит до ближайшего четного числа, если округляемая часть равна 5. Например:
from decimal import Decimal, ROUND_HALF_EVEN number = Decimal("10.025") # 2 - ближайшее четное число print(number.quantize(Decimal("1.00"), ROUND_HALF_EVEN)) # 10.02 number = Decimal("10.035") # 4 - ближайшее четное число print(number.quantize(Decimal("1.00"), ROUND_HALF_EVEN)) # 10.04
Стратегия округления передается в качестве второго параметра в quantize.
Строка «1.00» означает, что округление будет идти до двух чисел в дробной части. Но в первом случае «10.025» — вторым знаком идет 2 — четное число, поэтому, несмотря на то, что следующее число 5, двойка не округляется до тройки.
Во втором случае «10.035» — вторым знаком идет 3 — нечетное число, ближайшим четным числом будет 4, поэтому 35 округляется до 40.
Данное поведение при округлении, возможно, не всем покажется желательным, и в этом случае его можно переопределить, использовав одну из следующих констант:
- ROUND_HALF_UP : округляет число в сторону повышения, если после него идет число 5 или выше
- ROUND_HALF_DOWN : округляет число в сторону повышения, если после него идет число больше 5
number = Decimal("10.026") print(number.quantize(Decimal("1.00"), ROUND_HALF_DOWN)) # 10.03 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_HALF_DOWN)) # 10.02
number = Decimal("10.005") print(number.quantize(Decimal("1.00"), ROUND_05UP)) # 10.01 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_05UP)) # 10.02
number = Decimal("10.021") print(number.quantize(Decimal("1.00"), ROUND_CEILING)) # 10.03 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_CEILING)) # 10.03
number = Decimal("10.021") print(number.quantize(Decimal("1.00"), ROUND_FLOOR)) # 10.02 number = Decimal("10.025") print(number.quantize(Decimal("1.00"), ROUND_FLOOR)) # 10.02
Банковское округление в Python
Банковское округление — это один способов округления вещественных чисел, который используется в Python и некоторых других языках программирования.
Проблемы классического округления
Перед тем, как начать новую тему, давайте вспомним правила классического округления, которые вам должны быть знакомы еще со школы. Округлять будем до целого числа́ вот такие чи́сла, с одним знаком после десятичной точки:

Если число в десятичной части содержит четверку или меньше, то оно округляется вниз, а если пятерку или больше, то вверх. 4.4 становится просто 4, 4.5 будет округлено до 5. Уверен вам это хорошо знакомо.

Но давайте посмотрим на такое округление с другой стороны. Реально из 10 представленных чисел 5 будут округлены в большую сторону и только 4 в меньшую. На 4.0 округление никак не повлияет:

Из-за такого незначительного смещения, на практике при суммировании большого количества округленных чисел появляется погрешность. То есть проблема возникает, когда мы берем какой-то пул чисел, округляем их все, а затем складываем, чтобы получить финальный результат.
Давайте для примера возьмем множество из 1000 значений от нуля до 99.9 с шагом 0.1: 0.1, 0.2, 0.3 и так далее. То есть мы имеем некий набор чисел с равномерным заполнением. Назовем его идеальным:

Если мы сложим все числа без округления, то получим значение 49 950. А если при сложении мы будем округлять каждое значение, то итоговый результат будет равен 50 000.
Разница небольшая, всего в 50, тем не менее она даёт нам погрешность в одну десятую процента. И это в идеальном наборе чисел.
| Набор данных | Без округления | Обычное округление |
Банковское округление |
|---|---|---|---|
| Идеальный | 49 950 | 50 000 0.10% |
Теперь давайте также возьмем 1000 чисел, но на этот раз заполнение будет неидеальным. Мы также будем помещать между двумя целыми числами десять дробных значений, но выбирать их будем случайном порядке. Какие-то числа будут повторяться, а какие-то будут пропущены. Например, мы можем встретить два раза 0.1 и при этом не встретить 0.2 и так далее:

Назовём такое множество нормальным. Так как промежуточные значения будут заполняться случайным образом, то и итоговые результаты мы всегда будем получать разные. Дополню табличку данными одного из таких экспериментов:
| Набор данных | Без округления | Обычное округление |
Банковское округление |
|---|---|---|---|
| Идеальный | 49 950 | 50 000 0.10% |
|
| Нормальный | 49 960 | 50 018 0.14% |
Итак, без округления в сумме я получил 49 960, а с округлением 50 018, что в итоге дало погрешность в 0.14%.
Готовясь к этому видео, я провел несколько экспериментов с разным набором данных и получил разброс погрешности от 0.03 до 0.18%, что в среднем стремится примерно к 0.10% как в идеальном случае, но всё же в зависимости от данных мы можем получить и большие показатели.
При этом надо понимать, что погрешность при округлении — это нормально, ведь мы намеренно отсекаем часть данных, а значит точность будет страдать.
Тем не менее есть сферы, где желательно минимизировать такую погрешность и речь, разумеется, про финансовый и банковский сектор.
Банковское округление
И для того, чтобы снизить погрешность и увеличить точность используют банковское округление, которое также называют конвергентное округление, статистическое, голландское, округление по Гауссу или округление до четного.
И когда мы говорим «до четного» это не просто слова — в них заложен смысл. Давайте возьмем две шкалы: одну от 4 до 5, а вторую от 5 до 6, и применим к этим шкалам банковское округление:

Числа, которые находятся слева и справа от центра, округляются классическим способом. 4.2 до 4-х, 4.9 до 5-и, 5.3 также до 5-и, 5.7 до 6-и — тут всё стандартно.
А вот средние значения: 4.5 и 5.5 надо округлить до ближайшего четного. Так 4.5 будет округлено до 4-х, потому что 4 находится явно ближе, чем 6. А 5.5 будет округлено до 6-и. На этот раз 6 ближе, чем четыре.
И соответственно, если не считать 4.0 и 5.0, на которые округление не действует, то из оставшихся 18 значений, 9 будут округлены в меньшую сторону и 9 в большую. А это значит, что погрешность при суммировании должна уменьшиться. Ведь теперь у нас нет перекоса в какую-либо из сторон.
Давайте посмотрим на нашу таблицу, и я заполнил её значениями, полученными ровно на том же наборе данных, что и для обычного округления:
| Набор данных | Без округления | Обычное округление |
Банковское округление |
|---|---|---|---|
| Идеальный | 49 950 | 50 000 0.10% |
49 950 0.00% |
| Нормальный | 49 960 | 50 018 0.14% |
49 964 0.03% |
И обратите внимание, что при идеальном распределении мы имеем нулевую погрешность, а при нормальном, когда числа выбираются случайно, погрешность составляет всего 0.03% вместо 0.14%.
Я также провел несколько экспериментов с разным набором данных, и каждый раз получал погрешность от 0.00% до 0.05%, причем 0.00% или 0.01% встречались гораздо чаще, чем большие значения. Иными словами, точность финального результата оказывалась близка к значению без округления.
Именно из-за этого банковское округление часто используют в финансовой сфере и некоторых языках программирование. В частности, в Python 3 из коробки, а также на платформе .Net.
Что ж, теперь вы больше знаете о том, как устроено банковское округление в Python. Если вы хотите узнать еще больше о том, как работают языки программирования, то подписывайтесь на наш канал, а если хотите научиться программировать на питоне, то ждём нашем курсе.
