How does pandas / numpy round() work when decimal>=1? - python

I have a pandas series
In [1]: import pandas as pd
In [2]: s = pd.Series([1.3, 2.6, 1.24, 1.27, 1.45])
and I need to round the numbers.
In [4]: s.round(1)
Out[4]:
0 1.3
1 2.6
2 1.2
3 1.3
4 1.4
dtype: float64
it works for 1.27, however 1.45 is rounded to be 1.4, is it the problem of the precision loss of float type? If it is, how can I deal with this problem?

This isn't a bug but it is because, most decimal numbers cannot be represented exactly as a float.
https://www.programiz.com/python-programming/methods/built-in/round
another way of rounding is:
int(number*10^precission+0.5)
however, you might run in simular problems because who knows if 1.45 is closer to 1.4499999.. or 1.4500...1

In general, round() often fails due to floats being imprecise estimates.
In this case though, it's because of a convention by which half of all the numbers (evens) are rounded down, in order to balance out rounding error.
You can pretty easily disable this behavior:
round(x[, n])
x rounded to n digits, rounding half to even. If n is omitted, it defaults to 0.

Related

Python pandas add absolute one to positive/negative numbers in a series

I've got a series of positive and negative numbers and want to increase the absolute value of every number by one, while still keeping them positive/negative.
0.2 -> 1.2
-0.3 -> -1.3
How can I do that?
Let us try sign from numpy
s=pd.Series([0.2,-0.3])
(s.abs()+1)*np.sign(s)
0 1.2
1 -1.3
dtype: float64
Or np.select
np.select([s>0,s<0],[s+1,s-1],default=1)
array([ 1.2, -1.3])
Also we can do np.where
np.where(s>=0,s+1,s-1)
You can do a np.where:
s += np.where(s>=0, 1, -1)
#YOBEN_S' answer sufficiently covers it within the pandas/numpy space - if you are working within lists, and outside Pandas/Numpy, the below code may suffice, as it uses the math module within python :
from math import fabs, copysign
[(fabs(ent)+1)*copysign(1,ent) for ent in l]
[1.2, -1.3]

expected value float, decimal rounding error

I'm trying to round to two decimal places with error.
How could I round, eg:
7.1450, should be: 7.15
7.144, should be 7.14
>>> round(7.1450,2)
7.14
I will appreciate your help, I have been looking for a long time, without finding the solution.
I just found the answer of this problem in the Python docs.
Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.
If you need more accurate method, you can use decimal module.
>>> import decimal
>>> decimal.getcontext().rounding = decimal.ROUND_HALF_UP # change round setting of decimal.
>>> round(decimal.Decimal('7.1450'), 2)
Decimal('7.15')
The decimal module lets you control what rounding mode is used. You're looking for ROUND_HALF_UP.
I'm not too familiar with decimal myself, but this is the simplest way I found to do it:
import decimal
with decimal.localcontext() as ctx:
ctx.rounding = decimal.ROUND_HALF_UP
a = decimal.Decimal('7.145')
print(round(a, 2)) # -> 7.15
You can add a very small value to make it a bit more than .5.
Here I use sys.float_info.min, which is the minimum representable positive normalized float:
import sys
round(4.115 + sys.float_info.min, 2)
How about this
def round2(number, digit=0):
return int(number*10**digit+0.5)/10**digit
Python stores a decimal in base 2 format. For example,
0.5 is stored as (1/2).
0.75 is stored as (1/2) + (1/4).
You observed? All decimals cannot be stored in this way. So, It maintains a large precision. For example, (0.1) cannot be stored exactly in this way. It rounds to nearest possible decimal.
For example, 7.1450 is stored as 7.144999999999999573674358543939888477325439453125. So, Naturally, when round() is applied on this It gives 7.14.
So, How to resolve this?
Add a very low value (Say 0.000000001) to your decimals before rounding it.
It does the job.
try this
import numpy as np
x = 2.78
print(np.round(x,1))
It will give desire output such as
>>> print(np.round(x,1))
2.8
And also try this.
import math
def round_nearest(num, a):
return round(round(num / a) * a, -int(math.floor(math.log10(a))))
round_nearest(1.145, 0.05)
>>> round_nearest(1.145, 0.05)
1.15

Lists in Python last element [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 6 years ago.
I am new to python and I tried this:
import numpy as np
x = np.arange(0.7,1.3,0.1)
print (x)
y = np.arange(0.6,1.3,0.1)
print (y)
The output was [ 0.7 0.8 0.9 1. 1.1 1.2 1.3] and [ 0.6 0.7 0.8 0.9 1. 1.1 1.2]. Why in the first case 1.3 appears in the list and in the second case it doesn't?
This is due to rounding errors. If you actually print the last element in x in it's full precision, you'll see that it is smaller than 1.3:
>>> import numpy as np
>>> x = np.arange(0.7,1.3,0.1)
>>> 1.3 > x[-1]
True
>>> x[-1]
1.2999999999999998
Note, as stated in the documentation
When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases.:
arange is not suitable for floating point numbers:
When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases.
I'm not familiar with the internals of numpy, but my guess is that this is a side effect of floating point numbers not being exact (meaning that they can't exactly represent some values).
See the numpy.arange documentation here:
specifically "When using a non-integer step, such as 0.1, the results will often not be consistent. It is better to use linspace for these cases"

Avoiding small numerical errors when using IPython

I have been switching from Matlab to IPython.
In IPython, if we multiply 3.1 by 2.1, the following is the result:
In [297]:
3.1 * 2.1
Out[297]:
6.510000000000001
There is a small round-off error. It is not a big problem, but it is a little bit annoying. I assume that it appeared while converting decimal numbers into binary numbers and vice versa, is it right?
However, in Numpy array, the result is correct:
>>> np.array([3.1 * 2.1])
array([ 6.51])
In Matlab command line prompt, also the result is correct:
>> 3.1 * 2.1
ans =
6.5100
The above round-off error in Python looks annoying. Are there some ways to avoid this error in the python interactive mode or in IPython?
The numpy result is no more precise than the pure Python one - the floating point imprecision is just hidden from you because, by default, numpy prints fewer decimal places of the result:
In [1]: float(np.array([3.1 * 2.1]))
Out[1]: 6.510000000000001
You can control how numpy displays floating point numbers using np.set_printoptions. For example, to print 16 decimal places rather than the usual 8:
In [2]: np.set_printoptions(precision=16)
In [3]: np.array([3.1 * 2.1])
Out[3]: array([ 6.5100000000000007])
In IPython you can also use the %precision magic to control the number of decimal places that are displayed when pretty-printing normal Python floats:
In [4]: %precision 8
Out[4]: u'%.8f'
In [5]: 3.1 * 2.1
Out[5]: 6.51000000
Note that this is purely cosmetic - the value of 3.1 * 2.1 will still be equal to 6.5100000000000006750155990... rather than 6.51.
In Octave, a MATLAB clone, I can display those distant decimals:
octave:12> printf("%24.20f\n", 3.1*2.1)
6.51000000000000067502
They are also present your numpy.array
In [6]: np.array([3.1*2.1]).item()
Out[6]: 6.510000000000001
even the component terms involve this sort of rounding:
octave:13> printf("%24.20f\n", 3.1)
3.10000000000000008882
octave:14> printf("%24.20f\n", 2.1)
2.10000000000000008882

numpy.rint not working as expected

I am trying to find the cause of this result:
import numpy
result1 = numpy.rint(1.5)
result2 = numpy.rint(6.5)
print result
The output:
result1-> 2
result2-> 6
This is odd: result1 is correct but I result2 is not (It has to be 7 because rint rounds any float to the nearest integer).
Any idea? (THANKS!)
From numpy's documentation on numpy.around, equivalent to numpy.round, which supposedly also is relevant for numpy.rint:
For values exactly halfway between rounded decimal values, Numpy
rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0, -0.5
and 0.5 round to 0.0, etc. Results may also be surprising due to the
inexact representation of decimal fractions in the IEEE floating point
standard [R9] and errors introduced when scaling by powers of ten.
Also relevant: While for large numbers there might be representation errors, for small values half integers are exactly representable in binary-base floating points, in particular 1.5 and 6.5 are exactly representable in standard single-precision floats. Without the preference for either odd, even, lower, upper integers or any other scheme one would have undefined behaviour here.
As #wim points out in the comments the behaviour of Python's build-in round is different. It rounds away from zero: It prefers upper integers for positive inputs and lower integers for negative inputs. (see http://docs.python.org/2/library/functions.html#round)
I think this is the rule of the thumb - when you have a float midway between two integers, like 1.5 lies midway between 1 and 2 and since both choices are equally good, we prefer rounding to the even number(which is 2 in this case) and for 6.5, which lies midway between 6 and 7, 6 is the closest even number.

Categories

Resources