Python округлить все элементы массива

Округление чисел в Python

В нашей повседневной жизни мы используем десятичную систему, основанную на числе 10. Компьютер использует двоичную систему с основанием 2, а внутри он хранит и обрабатывает значения как последовательность единиц и нулей. Ценности, с которыми мы работаем, должны постоянно трансформироваться между двумя представлениями. Как объяснено в документации Python:

… большинство десятичных дробей не могут быть представлены точно как двоичные дроби. Следствием этого является то, что, как правило, вводимые вами десятичные числа с плавающей запятой только аппроксимируются двоичными числами, фактически сохраненными в машине.

Пример 1: неточности с числами с плавающей запятой.

>>> s = 0.3 + 0.3 + 0.3 >>> s 0.8999999999999999

Как вы можете видеть, результат неточный, так как он должен иметь значение 0.9.

В примере 2 показан аналогичный случай форматирования числа с плавающей запятой для 17 десятичных разрядов.

Пример 2: форматирование числа с плавающей запятой

>>> format(0.1, '.17f') '0.10000000000000001'

Как вы, возможно, узнали из приведенных выше примеров, работа с числами с плавающей запятой немного сложна и требует дополнительных мер для достижения правильного результата и минимизации вычислительных ошибок. Округление значения может решить по крайней мере некоторые проблемы. Одна из возможностей – встроенная функция round():

Пример 3: расчет с округленными значениями

>>> s = 0.3 + 0.3 + 0.3 >>> s 0.8999999999999999 >>> s == 0.9 False >>> round(0.9, 1) == 0.9 True

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

Чтобы сохранить такие значения, в игру вступают два модуля Python: десятичная и дробная. Но сначала давайте подробнее рассмотрим термин «округление».

Что такое округление?

В двух словах процесс округления означает:

… заменяя [значение] другим числом, которое примерно равно исходному, но имеет более короткое, простое или более явное представление.

По сути, он увеличивает неточность точно рассчитанного значения, сокращая его. В большинстве случаев это делается путем удаления цифр после десятичной точки, например от 3,73 до 3,7, от 16,67 до 16,7 или от 999,95 до 1000.

Такое сокращение выполняется по нескольким причинам – например, для экономии места при сохранении значения или просто для удаления неиспользуемых цифр. Кроме того, устройства вывода, такие как аналоговые дисплеи или часы, могут отображать вычисленное значение только с ограниченной точностью и требуют скорректированных входных данных.

Цифры от 0 до 4 ведут к округлению в меньшую сторону, а числа от 5 до 9 – к округлению в большую сторону. В таблице ниже показаны варианты использования.

| original value | rounded to | result | |----------------|--------------|--------| | 226 | the ten | 230 | | 226 | the hundred | 200 | | 274 | the hundred | 300 | | 946 | the thousand | 1,000 | | 1,024 | the thousand | 1,000 | | 10h45m50s | the minute | 10h45m |

Методы

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

Например, округление от нуля до половины применяется Европейской комиссией по экономическим и финансовым вопросам при конвертации валют в евро. Некоторые страны, такие как Швеция, Нидерланды, Новая Зеландия и Южная Африка, следуют правилу под названием «округление денежных средств», «округление пенни».

[Округление наличных] происходит, когда минимальная расчетная единица меньше наименьшего физического достоинства валюты. Сумма, подлежащая выплате за транзакцию с наличными, округляется до ближайшего кратного значения минимальной доступной денежной единицы, тогда как транзакции, оплаченные другими способами, не округляются.

В Южной Африке с 2002 года округление наличных денег производится до ближайших 5 центов. Обычно такое округление не применяется к электронным безналичным платежам.

Напротив, округление от половины до четного является стратегией по умолчанию для Python, Numpy и Pandas и используется встроенной функцией round(), о которой уже упоминалось ранее. Он принадлежит к категории методов округления до ближайшего и также известен как конвергентное округление, округление статистики, по голландскому языку, по Гауссу, по нечетному и четному. Этот метод определен в IEEE 754 и работает таким образом, что «если дробная часть x равна 0,5, то y является ближайшим к x четным целым числом».

Предполагается, что «вероятности связи в наборе данных при округлении в меньшую или большую сторону равны», что обычно и имеет место на практике. Хотя эта стратегия не полностью совершенна, она дает заметные результаты.

В таблице ниже приведены практические примеры округления в Python для этого метода:

| original value | rounded to | |----------------|------------| | 23.3 | 23 | | 23.5 | 24 | | 24.0 | 24 | | 24.5 | 24 | | 24.8 | 25 | | 25.5 | 26 |

Функции

Python имеет встроенную функцию round(), которая в нашем случае весьма полезна. Она принимает два параметра – исходное значение и количество цифр после десятичной точки. В примере ниже показано использование метода для одной, двух и четырех цифр после десятичной точки.

Пример 4: округление с указанным количеством цифр

>>> round(15.45625, 1) 15.5 >>> round(15.45625, 2) 15.46 >>> round(15.45625, 4) 15.4563

Если вы вызываете эту функцию без второго параметра, значение округляется до полного целого числа.

Пример 5: без указанного количества цифр

>>> round(0.85) 1 >>> round(0.25) 0 >>> round(1.5) 2

Округленные значения в Python подходят, если вам не требуются абсолютно точные результаты. Имейте в виду, что сравнение округленных значений также может быть кошмаром. Это станет более очевидным в следующем примере – сравнении округленных значений на основе предварительного и последующего округления.

Первый расчет в примере 6 содержит предварительно округленные значения и описывает округление перед сложением значений. Второй расчет содержит итоговую сумму после округления, что означает округление после суммирования. Вы заметите, что результат сравнения другой.

>>> round(0.3, 10) + round(0.3, 10) + round(0.3, 10) == round(0.9, 10) False >>> round(0.3 + 0.3 + 0.3, 10) == round(0.9, 10) True

Модули для вычислений с плавающей запятой

Есть четыре популярных модуля, которые помогут вам правильно работать с числами с плавающей запятой. Сюда входят модули math, Numpy, decimal и fraction.

Математический модуль (math) сосредоточен на математических константах, операциях с плавающей запятой и тригонометрических методах. Модуль Numpy описывает себя как «фундаментальный пакет для научных вычислений» и известен своим разнообразием методов работы с массивами. Модуль decimal охватывает десятичную арифметику с фиксированной и плавающей запятой, а модуль fraction имеет дело, в частности, с рациональными числами.

Во-первых, мы должны попытаться улучшить вычисления из примера 1. Как показано в примере 7, после импорта математического модуля мы можем получить доступ к методу fsum(), который принимает список чисел с плавающей запятой. Для первого вычисления нет разницы между встроенным методом sum() и методом fsum() из математического модуля, но для второго – он возвращает правильный результат, которого мы ожидали. Точность зависит от базового алгоритма IEEE 754.

Пример 7: вычисления с плавающей запятой с помощью модуля math

>>> import math >>> sum([0.1, 0.1, 0.1]) 0.30000000000000004 >>> math.fsum([0.1, 0.1, 0.1]) 0.30000000000000004 >>> sum([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) 0.9999999999999999 >>> math.fsum([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]) 1.0

Во-вторых, давайте посмотрим на модуль Numpy. Он поставляется с методом around(), который округляет значения, предоставленные в виде массива. Он обрабатывает отдельные значения так же, как метод round() по умолчанию.

Для сравнения значений Numpy предлагает метод equal(). Как и around(), он принимает отдельные значения, а также списки значений (так называемые векторы) для обработки. В примере 8 показано сравнение отдельных значений, а также округленных. Наблюдаемое поведение очень похоже на ранее показанные методы.

Пример 8: сравнение значений с использованием метода equal из модуля Numpy

>>> import numpy >>> print (numpy.equal(0.3, 0.3)) True >>> print (numpy.equal(0.3 + 0.3 + 0.3 , 0.9)) False >>> print (numpy.equal(round(0.3 + 0.3 + 0.3) , round(0.9))) True

Вариант третий – десятичный модуль (decimal). Он предлагает точное десятичное представление и сохраняет значащие цифры. По умолчанию точность составляет 28 цифр, и вы можете изменить это значение на число, которое будет настолько большим, насколько это необходимо для вашей проблемы. В примере 9 показано, как использовать точность до 8 цифр.

Пример 9: создание десятичных чисел с помощью модуля decimal

>>> import decimal >>> decimal.getcontext().prec = 8 >>> a = decimal.Decimal(1) >>> b = decimal.Decimal(7) >>> a / b Decimal('0.14285714')

Теперь сравнение значений с плавающей запятой стало намного проще и привело к желаемому результату.

Пример 10: сравнение с использованием модуля Decimal

>>> import decimal >>> decimal.getcontext().prec = 1 >>> a = decimal.Decimal(0.3) >>> b = decimal.Decimal(0.3) >>> c = decimal.Decimal(0.3) >>> a + b + c Decimal('0.9') >>> a + b + c == decimal.Decimal('0.9') True

Модуль decimal также имеет метод округления значений – quantize(). Стратегия округления по умолчанию – округление от половины до четного, и при необходимости ее также можно изменить на другой метод. В примере 11 показано использование метода quantize(). Обратите внимание, что количество цифр указывается с использованием десятичного значения в качестве параметра.

Пример 11: округление значения с помощью quantize()

>>> d = decimal.Decimal(4.6187) >>> d.quantize(decimal.Decimal("1.00")) Decimal('4.62')

И последнее, но не менее важное: мы рассмотрим модуль fractions. Этот модуль позволяет обрабатывать значения с плавающей запятой как дроби, например 0,3 как 3/10. Это упрощает сравнение значений с плавающей запятой и полностью исключает округление значений. В примере 12 показано, как использовать модуль fractions.

Пример 12: хранение и сравнение значений с плавающей запятой

>>> import fractions >>> fractions.Fraction(4, 10) Fraction(2, 5) >>> fractions.Fraction(6, 18) Fraction(1, 3) >>> fractions.Fraction(125) Fraction(125, 1) >>> a = fractions.Fraction(6, 18) >>> b = fractions.Fraction(1, 3) >>> a == b True

Кроме того, два модуля десятичной дроби и дроби можно комбинировать, как показано в следующем примере.

Пример 13: работа с десятичными знаками и дробями

>>> import fractions >>> import decimal >>> a = fractions.Fraction(1,10) >>> b = fractions.Fraction(decimal.Decimal(0.1)) >>> a,b (Fraction(1, 10), Fraction(3602879701896397, 36028797018963968)) >>> a == b False

Источник

How to round a numpy array?

the problem is not really a missing feature of NumPy, but rather that this sort of rounding is not a standard thing to do. You can make your own rounding function which achieves this like so:

def my_round(value, N): exponent = np.ceil(np.log10(value)) return 10**exponent*np.round(value*10**(-exponent), N) 

For a general solution handling 0 and negative values as well, you can do something like this:

def my_round(value, N): value = np.asarray(value).copy() zero_mask = (value == 0) value[zero_mask] = 1.0 sign_mask = (value < 0) value[sign_mask] *= -1 exponent = np.ceil(np.log10(value)) result = 10**exponent*np.round(value*10**(-exponent), N) result[sign_mask] *= -1 result[zero_mask] = 0.0 return result 

This was more or less what I wanted. Using np.around instead of round will make it work for multidimensional arrays as well.

It is worth noting that the accepted answer will round small floats down to zero as demonstrated below:

>>> import numpy as np >>> arr = np.asarray([2.92290007e+00, -1.57376965e-03, 4.82011728e-08, 1.92896977e-12]) >>> print(arr) [ 2.92290007e+00 -1.57376965e-03 4.82011728e-08 1.92896977e-12] >>> np.round(arr, 2) array([ 2.92, -0. , 0. , 0. ]) 

You can use set_printoptions and a custom formatter to fix this and get a more numpy-esque printout with fewer decimal places:

>>> np.set_printoptions(formatter=".format>) >>> print(arr) [2.92e+00 -1.57e-03 4.82e-08 1.93e-12] 

This way, you get the full versatility of format and maintain the precision of numpy's datatypes.

Also note that this only affects printing, not the actual precision of the stored values used for computation.

Источник

Читайте также:  Stack programming in java
Оцените статью