How to remove last decimal digit from float in python? - python

Say I have an arbitrary float x = 123.123456 and want to remove the last n decimal digits from the float i.e. n = 1 then x = 123.12345, n = 2 then x = 123.1234 and so on. How can this be achieved in python?

This will do the trick you are asking, but be mindful of the issues with floating point numbers.
# let's cut 2 digits
n = 2
# naively we can do this
f = 123.123456
short_f = float(str(f)[:-n])
# but watch out for floating point error
f = 1.2 - 1.0 # f should be 0.2 but is actually 0.19999999999999996
short_f = float(str(f)[:-n]) # so this gives 0.199999999999999
This sounds like an XY problem, maybe you are looking for round or string formatting.

Related

how can i stop showing the decimal .0 after the whole number? python

So, i entered code below to round a column to the nearest integer, though it still shows up with decimals.
Eg. I want to round 62.040 to just 62 though once the code is entered it shows 62.0 in the output
data['Final'] = np.ceil(data['Total'].round())
This should work pretty ok data['Final'] = round(data['Total'])
This converts the decimal number to it nearest integer and the returned number doesn't have a decimal point
Note: I'm assuming that data is just dictionary that contains float numbers, since no more information was provided
Take your decimal value and pass it to the int function for example:
z = 123.45678
x = 1.0
y = 1.6
a = int(z)#a will be 123
b = int(x)#b will be 1
c = int(y)#c will be 1
However, if the number is 1.6 this will be converted to a 1 chopping the decimal off (because int always rounds down). So you should round to 0dp first.
z = 123.45678
x = 1.0
y = 1.6
a = int(round(z,0))#a will be 123
b = int(round(x,0))#b will be 1
c = int(round(y,0))#c will be 2
So for you this will work:
data['final'] = int( round(np.ceil(data['Total']),0) )
after you round the column up, convert the column type to int
data=pd.DataFrame({
'Total':[
62.040, 63.040, 64.040,
],
})
data['Final'] = data['Total'].round()
data['Final'] = data['Final'].astype(int) # or add this line to your code
print(data)
Total Final
0 62.04 62
1 63.04 63
2 64.04 64

Splitting a column with float numbers and add both int and decimal parts in a new column

I have a column (version number) with more than 200k occurences as float for instance 1.2, 0.2 ...
I need to sum both sides of the floating number into a new column (total version), so that it gives me in the example 3, 2. Just integer numbers
Any advice?
Here is a solution that should be very easy to understand. I can make a oneline also you want to have that.
mylist = [1.3, 2.6, 3.1]
number = 0
fractions = 0
for a in mylist:
(a,b)=str(a).split('.')
number = number + int(a)
fractions = fractions + int(b)
print ("Number: " + str(number))
print ("Fractions: " + str(fractions))
This gives:
Number: 6
Fractions: 10
Do not use str(x).split('.') !
The one comment and the two other answers are currently suggesting to get the integer and fractional parts of a number x using
i,f = (int(s) for s in str(x).split('.'))
While this does give a result, I believe it is a bad idea.
The problem is, if you expect a meaningful result, you need to specify the precision of the fractional part explicitly. "1.20" and "1.2" are two string representations of the same number, but 20 and 2 are two very different integers. In addition, floating-point numbers are subject to precision errors, and you could easily find yourself with a number like "1.19999999999999999999999", which is only a small rounding error away from "1.2", but results in a completely different result with this str(x).split('.') approach.
One way to avoid this chaotic behaviour is to set a precision, ie, a number of decimal places, and stick to it. For instance, when dealing with monetary values, we're used to talk about cents; although 1.5€ and 1.50€ are technically both valid, you'll always hear people say "one euro fifty" and never "one euro five". If you hear someone say "one euro oh five", it actually means 1.05€. We always add exactly two decimal places.
With this approach, there is no chaotic behaviour of 1.2 becoming (1,2) or (1,20) or (1,1999999999). If you fixed the number of decimal places to 2, then 1.2 will always map to (1,20) and that's that.
A more standard way
Here are two standard ways of getting the integer and fractional parts of a number in python:
x = 1.20
# method 1
i = int(x)
f = x - i
# i = 1 and f = 0.2; i is an int and f a float
# method 2
import math
f, i = math.modf(x)
# i = 1.0 and f = 0.2; i and f are both floats
(EDIT: There is also a third method, pandas' divmod function. See user2314737's answer.)
Once you've done that, you can turn the fractional part f into an integer by multiplying it with the chosen power of 10 and converting it to an integer:
f = int(f * 100)
# f = 20
Finally you can apply this method to a whole list:
data = [13.0, 14.20, 12.299, 4.414]
def intfrac_pair(x, decimal_places):
i = int(x)
f = int((10**decimal_places) * (x - i))
return (i, f)
data_as_pairs = [intfrac_pair(x, 2) for x in data]
# = [(13, 0), (14, 20), (12, 30), (4, 41)]
sum_of_integer_parts = sum(i for i,f in data_as_pairs) # = 43
sum_of_fractional_parts = sum(f for i,f in data_as_pairs) # = 91
The following should work:
df['total_number']=[sum([int(i) for i in str(k).split('.')]) for k in df.version_number]
You can use divmod on the column
df = pd.DataFrame([1.2, 2.3, 3.4, 4.5, 0.1])
df
# 0
# 0 1.2
# 1 2.3
# 2 3.4
# 3 4.5
# 4 0.1
df['i'], df['d'] = df[0].divmod(1)
df
# Out:
# 0 i d
# 0 1.2 1.0 0.2
# 1 2.3 2.0 0.3
# 2 3.4 3.0 0.4
# 3 4.5 4.0 0.5
# 4 0.1 0.0 0.1
To sum row-wise as integers (a precision is needed, here I use p=1 assuming the original floats contain only one decimal digit) :
p = 1
df['s'] = (df['i']+10**p*df['d'].round(decimals=p)).astype(np.int)
df
# Out:
# 0 i d s
# 0 1.2 1.0 0.2 3
# 1 2.3 2.0 0.3 5
# 2 3.4 3.0 0.4 7
# 3 4.5 4.0 0.5 9
# 4 0.1 0.0 0.1 1
Sum by columns:
df.sum()
# Out:
# 0 11.5
# i 10.0
# d 1.5
Note: this will only work for positive integers as for instance divmod(-3.4, 1) returns (-4.0, 0.6).
Thank you all guys. I finally managed in a quite stupid, but effictive way. Before splitting, I transformed it to a string:
Allfiles['Version'] = Allfiles['Version'].round(3).astype(str)
Note that I rounded to 3 digits because a number like 2.111 was transformed to 2.11099999999999999999
Then I just did the split, creating a new column for minor versions (and having the major in the original colum
Allfiles[['Version', 'minor']] = Allfiles['Version'].str.split('.', expand=True)
Then I converted again both files into integers and sum both in the first column.
Allfiles['Version'] = Allfiles['Version']+Allfiles['minor']
(My dataframe name is Allfiles and the column version, as you can imagine.

Python setting Decimal Place range without rounding?

How can I take a float variable, and control how far out the float goes without round()? For example.
w = float(1.678)
I want to take x and make the following variables out of it.
x = 1.67
y = 1.6
z = 1
If I use the respective round methods:
x = round(w, 2) # With round I get 1.68
y = round(y, 1) # With round I get 1.7
z = round(z, 0) # With round I get 2.0
It's going to round and alter the numbers to the point where there no use to me. I understand this is the point of round and its working properly. How would I go about getting the information that I need in the x,y,z variables and still be able to use them in other equations in a float format?
You can do:
def truncate(f, n):
return math.floor(f * 10 ** n) / 10 ** n
testing:
>>> f=1.923328437452
>>> [truncate(f, n) for n in range(7)]
[1.0, 1.9, 1.92, 1.923, 1.9233, 1.92332, 1.923328]
A super simple solution is to use strings
x = float (str (w)[:-1])
y = float (str (w)[:-2])
z = float (str (w)[:-3])
Any of the floating point library solutions would require you dodge some rounding, and using floor/powers of 10 to pick out the decimals can get a little hairy by comparison to the above.
Integers are faster to manipulate than floats/doubles which are faster than strings. In this case, I tried to get time with both approach :
timeit.timeit(stmt = "float(str(math.pi)[:12])", setup = "import math", number = 1000000)
~1.1929605630000424
for :
timeit.timeit(stmt = "math.floor(math.pi * 10 ** 10) / 10 ** 10", setup = "import math", number = 1000000)
~0.3455968870000561
So it's safe to use math.floor rather than string operation on it.
If you just need to control the precision in format
pi = 3.14159265
format(pi, '.3f') #print 3.142 # 3 precision after the decimal point
format(pi, '.1f') #print 3.1
format(pi, '.10f') #print 3.1415926500, more precision than the original
If you need to control the precision in floating point arithmetic
import decimal
decimal.getcontext().prec=4 #4 precision in total
pi = decimal.Decimal(3.14159265)
pi**2 #print Decimal('9.870') whereas '3.142 squared' would be off
--edit--
Without "rounding", thus truncating the number
import decimal
from decimal import ROUND_DOWN
decimal.getcontext().prec=4
pi*1 #print Decimal('3.142')
decimal.getcontext().rounding = ROUND_DOWN
pi*1 #print Decimal('3.141')
I think the easiest answer is :
from math import trunc
w = 1.678
x = trunc(w * 100) / 100
y = trunc(w * 10) / 10
z = trunc(w)
also this:
>>> f = 1.678
>>> n = 2
>>> int(f * 10 ** n) / 10 ** n
1.67
Easiest way to get integer:
series_col.round(2).apply(lambda x: float(str(x).split(".",1)[0]))

Round to whole numbers without using conditional statements in Python - Logic

I'm taking a Python course at Udacity, and I'm trying to work this out for myself without looking at the answer. Perhaps you can give me a hint for my logic?
Below are the instructions and what I have so far. We haven't learned conditional statements yet, so I can't use those. We've only learned how to assign/print a variable, strings, indexing strings, sub-sequences, and .find. They just introduced the str command in this final exercise.
# Given a variable, x, that stores the
# value of any decimal number, write Python
# code that prints out the nearest whole
# number to x.
# If x is exactly half way between two
# whole numbers, round up, so
# 3.5 rounds to 4 and 2.5 rounds to 3.
# You may assume x is not negative.
# Hint: The str function can convert any number into a string.
# eg str(89) converts the number 89 to the string '89'
# Along with the str function, this problem can be solved
# using just the information introduced in unit 1.
# x = 3.14159
# >>> 3 (not 3.0)
# x = 27.63
# >>> 28 (not 28.0)
# x = 3.5
# >>> 4 (not 4.0)
x = 3.54159
#ENTER CODE BELOW HERE
x = str(x)
dec = x.find('.')
tenth = dec + 1
print x[0:dec]
////
So this gets me to print the characters up to the decimal point, but I can't figure out how to have the computer check whether "tenth" is > 4 or < 5 and print out something according to the answer.
I figured I could probably get far enough for it to return a -1 if "tenth" wasn't > 4, but I don't know how I can get it to print x[0:dec] if it's < 5 and x[0:dec]+1 if it's > 4.
:/
Could someone please give me a nudge in the right direction?
This is a weird restriction, but you could do this:
x = str(x)
dec_index = x.find('.')
tenth_index = dec_index + 1
tenth_place = x[tenth_index] # will be a string of length 1
should_round_up = 5 + tenth_place.find('5') + tenth_place.find('6') + tenth_place.find('7') + tenth_place.find('8') + tenth_place.find('9')
print int(x[0:dec_index]) + should_round_up
What we do is look at the tenths place. Since .find() returns -1 if the argument isn't found, the sum of the .find() calls will be -4 if if the tenths place is 5, 6, 7, 8, or 9 (since one of the .find() calls will succeed and return 0), but will be -5 if the tenths place is 0, 1, 2, 3, or 4. We add 5 to that, so that should_round_up equals 1 if we should round up, and 0 otherwise. Add that to the whole number part, and we're done.
That said, if you weren't subject to this artificial restriction, you would do:
print round(x)
And move on with your life.
judging by the accepted answer you only expects floats so that is pretty trivial to solve:
x = 3.54159
# split on .
a, b = str(x).split(".")
# cast left side to int and add result of test for right side being greater or equal to 5
print(int(a) + (int(b) >= 5))
(int(b) > 5) will be either 1 or 0 i.e True/False so we either add 1 when right side is > .5 or flooring when it's < .5 and adding 0.
If you were doing it mathematically you just need to print(int(x+.5)), anything >= .5 will mean x will be rounded up and floored when it is < .5.
x = 3.54159
# split on .
a, b = str(x).split(".")
# cast left side to int and add result of test for right side being greater or equal to 5
print(int(a) + (int(b[0]) >= 5))
# above code will not work with 3.14567 and the number with having two or more digits after decimal
I think it's easier...
x = x + 0.5
intPart, decPart = str(x).split(".")
print intPart
Examples:
If x = 1, then it will become 1.5 and intPart will be 1.
If x = 1.1, then it will become 1.6 and intPart will be 1.
If x = 1.6, then it will become 2.1 and intPart will be 2.
Note: it will only work for positive numbers.
This code will round numbers to the nearest whole
without using conditionals
You can do it this way
x = 3.54159
x = x + 0.5 # This automatically takes care of the rounding
str_x = str(x) # Converting number x to string
dp = str_x.find('.') # Finding decimal point index
print str_x[:dp] # Printing upto but excluding decimal point
I did the same course at Udacity. solved it using the following code:
y = str(x)
decimal = y.find('.')
y_increment = y[decimal+1:]
print decimal
print y_increment
# Section below finds >5
check5 = y_increment.find('5',0,1)
check6 = y_increment.find('6',0,1)
check7 = y_increment.find('7',0,1)
check8 = y_increment.find('8',0,1)
check9 = y_increment.find('9',0,1)
yes_increment = (check5 + 1) + (check6 + 1) + (check7 + 1) + (check8 + 1) + (check9 + 1)
print check5, check6, check7, check8, check9
#Calculate rounding up
z = x + (yes_increment)
z = str(z)
final_decimal = z.find('.')
print z[:final_decimal]

Python Decimal - engineering notation for mili (10e-3) and micro (10e-6)

Here is the example which is bothering me:
>>> x = decimal.Decimal('0.0001')
>>> print x.normalize()
>>> print x.normalize().to_eng_string()
0.0001
0.0001
Is there a way to have engineering notation for representing mili (10e-3) and micro (10e-6)?
Here's a function that does things explicitly, and also has support for using SI suffixes for the exponent:
def eng_string( x, format='%s', si=False):
'''
Returns float/int value <x> formatted in a simplified engineering format -
using an exponent that is a multiple of 3.
format: printf-style string used to format the value before the exponent.
si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
e-9 etc.
E.g. with format='%.2f':
1.23e-08 => 12.30e-9
123 => 123.00
1230.0 => 1.23e3
-1230000.0 => -1.23e6
and with si=True:
1230.0 => 1.23k
-1230000.0 => -1.23M
'''
sign = ''
if x < 0:
x = -x
sign = '-'
exp = int( math.floor( math.log10( x)))
exp3 = exp - ( exp % 3)
x3 = x / ( 10 ** exp3)
if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
exp3_text = 'yzafpnum kMGTPEZY'[ ( exp3 - (-24)) / 3]
elif exp3 == 0:
exp3_text = ''
else:
exp3_text = 'e%s' % exp3
return ( '%s'+format+'%s') % ( sign, x3, exp3_text)
EDIT:
Matplotlib implemented the engineering formatter, so one option is to directly use Matplotlibs formatter, e.g.:
import matplotlib as mpl
formatter = mpl.ticker.EngFormatter()
formatter(10000)
result: '10 k'
Original answer:
Based on Julian Smith's excellent answer (and this answer), I changed the function to improve on the following points:
Python3 compatible (integer division)
Compatible for 0 input
Rounding to significant number of digits, by default 3, no trailing zeros printed
so here's the updated function:
import math
def eng_string( x, sig_figs=3, si=True):
"""
Returns float/int value <x> formatted in a simplified engineering format -
using an exponent that is a multiple of 3.
sig_figs: number of significant figures
si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
e-9 etc.
"""
x = float(x)
sign = ''
if x < 0:
x = -x
sign = '-'
if x == 0:
exp = 0
exp3 = 0
x3 = 0
else:
exp = int(math.floor(math.log10( x )))
exp3 = exp - ( exp % 3)
x3 = x / ( 10 ** exp3)
x3 = round( x3, -int( math.floor(math.log10( x3 )) - (sig_figs-1)) )
if x3 == int(x3): # prevent from displaying .0
x3 = int(x3)
if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
exp3_text = 'yzafpnum kMGTPEZY'[ exp3 // 3 + 8]
elif exp3 == 0:
exp3_text = ''
else:
exp3_text = 'e%s' % exp3
return ( '%s%s%s') % ( sign, x3, exp3_text)
The decimal module is following the Decimal Arithmetic Specification, which states:
This is outdated - see below
to-scientific-string – conversion to numeric string
[...]
The coefficient is first converted to a string in base ten using the characters 0 through 9 with no leading zeros (except if its value is zero, in which case a single 0 character is used).
Next, the adjusted exponent is calculated; this is the exponent, plus the number of characters in the converted coefficient, less one. That is, exponent+(clength-1), where clength is the length of the coefficient in decimal digits.
If the exponent is less than or equal to zero and the adjusted exponent is greater than or equal to -6, the number will be converted
to a character form without using exponential notation.
[...]
to-engineering-string – conversion to numeric string
This operation converts a number to a string, using engineering
notation if an exponent is needed.
The conversion exactly follows the rules for conversion to scientific
numeric string except in the case of finite numbers where exponential
notation is used. In this case, the converted exponent is adjusted to be a multiple of three (engineering notation) by positioning the decimal point with one, two, or three characters preceding it (that is, the part before the decimal point will range from 1 through 999).
This may require the addition of either one or two trailing zeros.
If after the adjustment the decimal point would not be followed by a digit then it is not added. If the final exponent is zero then no indicator letter and exponent is suffixed.
Examples:
For each abstract representation [sign, coefficient, exponent] on the left, the resulting string is shown on the right.
Representation
String
[0,123,1]
"1.23E+3"
[0,123,3]
"123E+3"
[0,123,-10]
"12.3E-9"
[1,123,-12]
"-123E-12"
[0,7,-7]
"700E-9"
[0,7,1]
"70"
Or, in other words:
>>> for n in (10 ** e for e in range(-1, -8, -1)):
... d = Decimal(str(n))
... print d.to_eng_string()
...
0.1
0.01
0.001
0.0001
0.00001
0.000001
100E-9
I realize that this is an old thread, but it does come near the top of a search for python engineering notation and it seems prudent to have this information located here.
I am an engineer who likes the "engineering 101" engineering units. I don't even like designations such as 0.1uF, I want that to read 100nF. I played with the Decimal class and didn't really like its behavior over the range of possible values, so I rolled a package called engineering_notation that is pip-installable.
pip install engineering_notation
From within Python:
>>> from engineering_notation import EngNumber
>>> EngNumber('1000000')
1M
>>> EngNumber(1000000)
1M
>>> EngNumber(1000000.0)
1M
>>> EngNumber('0.1u')
100n
>>> EngNumber('1000m')
1
This package also supports comparisons and other simple numerical operations.
https://github.com/slightlynybbled/engineering_notation
The «full» quote shows what is wrong!
The decimal module is indeed following the proprietary (IBM) Decimal Arithmetic Specification.
Quoting this IBM specification in its entirety clearly shows what is wrong with decimal.to_eng_string() (emphasis added):
to-engineering-string – conversion to numeric string
This operation converts a number to a string, using engineering
notation if an exponent is needed.
The conversion exactly follows the rules for conversion to scientific
numeric string except in the case of finite numbers where exponential
notation is used. In this case, the converted exponent is adjusted to be a multiple of three (engineering notation) by positioning the decimal point with one, two, or three characters preceding it (that is, the part before the decimal point will range from 1 through 999). This may require the addition of either one or two trailing zeros.
If after the adjustment the decimal point would not be followed by a digit then it is not added. If the final exponent is zero then no indicator letter and exponent is suffixed.
This proprietary IBM specification actually admits to not applying the engineering notation for numbers with an infinite decimal representation, for which ordinary scientific notation is used instead! This is obviously incorrect behaviour for which a Python bug report was opened.
Solution
from math import floor, log10
def powerise10(x):
""" Returns x as a*10**b with 0 <= a < 10
"""
if x == 0: return 0,0
Neg = x < 0
if Neg: x = -x
a = 1.0 * x / 10**(floor(log10(x)))
b = int(floor(log10(x)))
if Neg: a = -a
return a,b
def eng(x):
"""Return a string representing x in an engineer friendly notation"""
a,b = powerise10(x)
if -3 < b < 3: return "%.4g" % x
a = a * 10**(b % 3)
b = b - b % 3
return "%.4gE%s" % (a,b)
Source: https://code.activestate.com/recipes/578238-engineering-notation/
Test result
>>> eng(0.0001)
100E-6
Like the answers above, but a bit more compact:
from math import log10, floor
def eng_format(x,precision=3):
"""Returns string in engineering format, i.e. 100.1e-3"""
x = float(x) # inplace copy
if x == 0:
a,b = 0,0
else:
sgn = 1.0 if x > 0 else -1.0
x = abs(x)
a = sgn * x / 10**(floor(log10(x)))
b = int(floor(log10(x)))
if -3 < b < 3:
return ("%." + str(precision) + "g") % x
else:
a = a * 10**(b % 3)
b = b - b % 3
return ("%." + str(precision) + "gE%s") % (a,b)
Trial:
In [10]: eng_format(-1.2345e-4,precision=5)
Out[10]: '-123.45E-6'

Categories