Trying to round a string that has a comma in it [python] - python

I am making a price ticker for reddit. Everything is working fine, but the API that it derives the price from has 4 decimal places, and a comma in it dividing the thousands.
I am just trying to figure out how to round the number to either 2 decimals, or a whole number. So far I have tried the round() function, which does not work because of the comma. I was able to remove the comma, but it still won't let me use the round function.
def main(self):
req = requests.get('https://api.coindesk.com/v1/bpi/currentprice.json')
req = req.json()
dollar = '1 BTC = $' + req['bpi']['USD']['rate'].replace(',' , '')
Any ideas?

Maybe this could point you in the right direction!
# Original String
val_string = '19,9999'
# Replace Comma with Decimal
val_string_decimal = val_string.replace(',','.')
# Convert String to Float
val = float(val_string_decimal)
# Print Float after rounding to 2 decimal places
print(round(val, 2))

You should usually use the Decimal class in python when playing with numbers like this-
>>> from decimal import Decimal
>>> t = '4,700.3245'
>>> Decimal(t.replace(',', '')).quantize(Decimal('1.00'))
Decimal('4700.32')
quantize is the "round" function for Decimal objects- it will round to the same number of decimal places as the Decimal object passed as an argument.

You didn't specify whether you want to re-insert the comma after rounding. Try this:
# dummy number
str_num = "4,000.8675"
# first, remove the comma
str_num_no_comma = str_num.replace(",","")
# then, convert to float, and then round
strm_num_as_num = round(float((str_num_no_comma)))
print(strm_num_as_num)
>>> 4001.0
Of course you can cast as int if you want to ignore decimal points entirely.

Related

Python increment float by smallest step possible predetermined by its number of decimals

I've been searching around for hours and I can't find a simple way of accomplishing the following.
Value 1 = 0.00531
Value 2 = 0.051959
Value 3 = 0.0067123
I want to increment each value by its smallest decimal point (however, the number must maintain the exact number of decimal points as it started with and the number of decimals varies with each value, hence my trouble).
Value 1 should be: 0.00532
Value 2 should be: 0.051960
Value 3 should be: 0.0067124
Does anyone know of a simple way of accomplishing the above in a function that can still handle any number of decimals?
Thanks.
Have you looked at the standard module decimal?
It circumvents the floating point behaviour.
Just to illustrate what can be done.
import decimal
my_number = '0.00531'
mnd = decimal.Decimal(my_number)
print(mnd)
mnt = mnd.as_tuple()
print(mnt)
mnt_digit_new = mnt.digits[:-1] + (mnt.digits[-1]+1,)
dec_incr = decimal.DecimalTuple(mnt.sign, mnt_digit_new, mnt.exponent)
print(dec_incr)
incremented = decimal.Decimal(dec_incr)
print(incremented)
prints
0.00531
DecimalTuple(sign=0, digits=(5, 3, 1), exponent=-5)
DecimalTuple(sign=0, digits=(5, 3, 2), exponent=-5)
0.00532
or a full version (after edit also carries any digit, so it also works on '0.199')...
from decimal import Decimal, getcontext
def add_one_at_last_digit(input_string):
dec = Decimal(input_string)
getcontext().prec = len(dec.as_tuple().digits)
return dec.next_plus()
for i in ('0.00531', '0.051959', '0.0067123', '1', '0.05199'):
print(add_one_at_last_digit(i))
that prints
0.00532
0.051960
0.0067124
2
0.05200
As the other commenters have noted: You should not operate with floats because a given number 0.1234 is converted into an internal representation and you cannot further process it the way you want. This is deliberately vaguely formulated. Floating points is a subject for itself. This article explains the topic very well and is a good primer on the topic.
That said, what you could do instead is to have the input as strings (e.g. do not convert it to float when reading from input). Then you could do this:
from decimal import Decimal
def add_one(v):
after_comma = Decimal(v).as_tuple()[-1]*-1
add = Decimal(1) / Decimal(10**after_comma)
return Decimal(v) + add
if __name__ == '__main__':
print(add_one("0.00531"))
print(add_one("0.051959"))
print(add_one("0.0067123"))
print(add_one("1"))
This prints
0.00532
0.051960
0.0067124
2
Update:
If you need to operate on floats, you could try to use a fuzzy logic to come to a close presentation. decimal offers a normalize function which lets you downgrade the precision of the decimal representation so that it matches the original number:
from decimal import Decimal, Context
def add_one_float(v):
v_normalized = Decimal(v).normalize(Context(prec=16))
after_comma = v_normalized.as_tuple()[-1]*-1
add = Decimal(1) / Decimal(10**after_comma)
return Decimal(v_normalized) + add
But please note that the precision of 16 is purely experimental, you need to play with it to see if it yields the desired results. If you need correct results, you cannot take this path.

How to print floating point numbers as it is without any truncation in python?

I have some number 0.0000002345E^-60. I want to print the floating point value as it is.
What is the way to do it?
print %f truncates it to 6 digits. Also %n.nf gives fixed numbers. What is the way to print without truncation.
Like this?
>>> print('{:.100f}'.format(0.0000002345E-60))
0.0000000000000000000000000000000000000000000000000000000000000000002344999999999999860343602938602754
As you might notice from the output, it’s not really that clear how you want to do it. Due to the float representation you lose precision and can’t really represent the number precisely. As such it’s not really clear where you want the number to stop displaying.
Also note that the exponential representation is often used to more explicitly show the number of significant digits the number has.
You could also use decimal to not lose the precision due to binary float truncation:
>>> from decimal import Decimal
>>> d = Decimal('0.0000002345E-60')
>>> p = abs(d.as_tuple().exponent)
>>> print(('{:.%df}' % p).format(d))
0.0000000000000000000000000000000000000000000000000000000000000000002345
You can use decimal.Decimal:
>>> from decimal import Decimal
>>> str(Decimal(0.0000002345e-60))
'2.344999999999999860343602938602754401109865640550232148836753621775217856801120686600683401464097113374472942165409862789978024748827516129306833728589548440037314681709534891496105046826414763927459716796875E-67'
This is the actual value of float created by literal 0.0000002345e-60. Its value is a number representable as python float which is closest to actual 0.0000002345 * 10**-60.
float should be generally used for approximate calculations. If you want accurate results you should use something else, like mentioned Decimal.
If I understand, you want to print a float?
The problem is, you cannot print a float.
You can only print a string representation of a float. So, in short, you cannot print a float, that is your answer.
If you accept that you need to print a string representation of a float, and your question is how specify your preferred format for the string representations of your floats, then judging by the comments you have been very unclear in your question.
If you would like to print the string representations of your floats in exponent notation, then the format specification language allows this:
{:g} or {:G}, depending whether or not you want the E in the output to be capitalized). This gets around the default precision for e and E types, which leads to unwanted trailing 0s in the part before the exponent symbol.
Assuming your value is my_float, "{:G}".format(my_float) would print the output the way that the Python interpreter prints it. You could probably just print the number without any formatting and get the same exact result.
If your goal is to print the string representation of the float with its current precision, in non-exponentiated form, User poke describes a good way to do this by casting the float to a Decimal object.
If, for some reason, you do not want to do this, you can do something like is mentioned in this answer. However, you should set 'max_digits' to sys.float_info.max_10_exp, instead of 14 used in the answer. This requires you to import sys at some point prior in the code.
A full example of this would be:
import math
import sys
def precision_and_scale(x):
max_digits = sys.float_info.max_10_exp
int_part = int(abs(x))
magnitude = 1 if int_part == 0 else int(math.log10(int_part)) + 1
if magnitude >= max_digits:
return (magnitude, 0)
frac_part = abs(x) - int_part
multiplier = 10 ** (max_digits - magnitude)
frac_digits = multiplier + int(multiplier * frac_part + 0.5)
while frac_digits % 10 == 0:
frac_digits /= 10
scale = int(math.log10(frac_digits))
return (magnitude + scale, scale)
f = 0.0000002345E^-60
p, s = precision_and_scale(f)
print "{:.{p}f}".format(f, p=p)
But I think the method involving casting to Decimal is probably better, overall.

Decimal zero padding

I need to output decimal numbers in a price format,
i.e.
10 = 10.00
11.1 = 11.10
How can I achieve this using decimal.Decimal class ?
pad_zero(Decimal('10.0'))
>>>Decimal('10.00')
*EDIT:*format method does not fit my need because I need to pass it on as decimal, I understand though, that i can convert it back to afterwards, but such to-and-fro seems somewhat unpythonic.
try this :
Decimal('10.0').quantize(Decimal('1.00'))
For currency calculations, I prefer this.
>>> penny=Decimal('0.01')
>>> Decimal('10').quantize(penny)
Decimal('10.00')
It's wordy but explicit.
For currency formatting, I use format().
There's a good example of how to format Decimal objects as a "money formatted string" in the Python documentation for the decimal module.
I'm a little surprised at how awkward it is -- usually formatting in Python is fairly straightforward.
I would follow the moneyfmt recipe in the Python Decimal documentation Recipes section.
This recipe creates a function that takes a decimal value and returns a string formatted as a currency.
>>> d = Decimal('10.0')
>>> moneyfmt(d, curr='$')
'$10.00'
Below is the actual code, copied sans examples from the Decimal Recipe documentation:
def moneyfmt(value, places=2, curr='', sep=',', dp='.',
pos='', neg='-', trailneg=''):
"""Convert Decimal to a money formatted string.
places: required number of places after the decimal point
curr: optional currency symbol before the sign (may be blank)
sep: optional grouping separator (comma, period, space, or blank)
dp: decimal point indicator (comma or period)
only specify as blank when places is zero
pos: optional sign for positive numbers: '+', space or blank
neg: optional sign for negative numbers: '-', '(', space or blank
trailneg:optional trailing minus indicator: '-', ')', space or blank
"""
q = Decimal(10) ** -places # 2 places --> '0.01'
sign, digits, exp = value.quantize(q).as_tuple()
result = []
digits = map(str, digits)
build, next = result.append, digits.pop
if sign:
build(trailneg)
for i in range(places):
build(next() if digits else '0')
build(dp)
if not digits:
build('0')
i = 0
while digits:
build(next())
i += 1
if i == 3 and digits:
i = 0
build(sep)
build(curr)
build(neg if sign else pos)
return ''.join(reversed(result))
It should be quite simple like this (if you don't use decimal.Decimal class as suggested by S. Lott) :
>>> decimal_fmt = "{:.2f}"
>>> x = 10
>>> print(decimal_fmt.format(x))
10.00
>>> x = 11.1
>>> print(decimal_fmt.format(x))
11.10
Set the precision for your context before you create your instance:
>>> getcontext().prec = 2
Use locale currency. It works flawlessly with the Decimal class.
import locale
locale.setlocale(locale.LC_ALL, '') # this sets locale to the current Operating System value
print(locale.currency(Decimal('1346896.67544'), grouping=True, symbol=True))
will output in my Windows 10 configured to Brazilian Portuguese:
R$ 1.346.896,68
It is somewhat verbose, so if you will use it a lot, maybe it is better to predefine some parameters and have a shorter name and use it inside a f-string:
fmt = lambda x: locale.currency(x, grouping=True, symbol=True)
print(f"Value: {fmt(1346896.67444)}"
It works with Decimal and float. You can configure to symbol to False if it isn't necessary.
Instead of using Decimal('10.0') you could use float('10.0') which will produce the effect you require.
Edit: Realised that you were looking to represent it with 2 decimal places. In this case, there's a good example in the Python docs for converting a Decimal() object to money: http://docs.python.org/library/decimal.html#recipes

Python: indexing floats?

I have two sets of data that I am reading via nested for loops in Python. I need to match lines of the two different text files using a common number (time). In the two files, time is written differently (ex. 21:53:28.339 vs. 121082008.3399). I only need the last four digits of the times to match them up, for example from 21:53:28.339, I only need '8.339'. For the most part, indexing the number as a string works just fine (ex: timeList[nid][7:]), except for situations such as the numbers listed above, where python rounds .3399 to .34.
Is there a way for me to keep the numbers in float form and to select unrounded digits from the data?
Thanks!
edit - using Decimal exclusively - with full example
import decimal
def simplify(text):
# might be a : separated value
text = text.split(':')[-1]
# turn into decimal
number = decimal.Decimal(text)
# remove everything but the ones place and forwards
number = number - (number/10).quantize(1, rounding=decimal.ROUND_FLOOR) * 10
# truncate to the thousandths
return number.quantize(decimal.Decimal('.001'), rounding=decimal.ROUND_FLOOR)
a = '121082008.3399'
b = '21:53:28.339'
assert simplify(a) == simplify(b)
print simplify(a), '=', simplify(b)
Scott if you compare the numbers using strings then you don't need any floats and there will be no 'rounding' going on.
'8.339' == '8.339'
or, if you have
a = '8.3399'
b = '8.339'
then
a[:-1] == b
however if you do decide to work with them as 'numbers', then as Ignacio pointed out, you can use decimals.
from decimal import Decimal
number_a = Decimal(a[:-1])
number_b = Decimal(b)
now
number_a == number_b
Hope that helps
It appears from your description that you want to compare using one digit before the decimal point and 3 digits after the decimal point, using truncation instead of rounding. So just do that:
>>> def extract(s):
... i = s.find('.')
... return s[i-1:i+4]
...
>>> map(extract, ['21:53:28.339', '121082008.3399'])
['8.339', '8.339']
>>>
Use decimal.Decimal instead of float.

keeping same formatting for floating point values

I have a python program that reads floating point values using the following regular expression
(-?\d+\.\d+)
once I extract the value using float(match.group(1)), I get the actual floating point number. However, I am not able to distinguish if the number was 1.2345678 or 1.234 or 1.2340000.
The problem I am facing is to print out the floating point value again, with the exact same formatting. An easy solution is to "split and count" the floating point value when still a string, eg splitting at the decimal point, and counting the integer part length and the fractional part length, then create the formatter as
print "%"+str(total_len)+"."+str(fractional_len)+"f" % value
but maybe you know a standard way to achieve the same result ?
If you want to keep a fixed precision, avoid using floats and use Decimal instead:
>>> from decimal import Decimal
>>> d = Decimal('-1.2345')
>>> str(d)
'-1.2345'
>>> float(d)
-1.2344999999999999
You method is basically correct.
String formatting has a less often used * operator you can put for the formatting sizes, here's some code:
import re
def parse_float(str):
re_float = re.compile(r'(-?)(\d+)\.(\d+)')
grps = re_float.search(str)
sign, decimal, fraction = grps.groups()
float_val = float('%s%s.%s' % (sign, decimal, fraction))
total_len = len(grps.group(0))
print '%*.*f' % (total_len, len(fraction), float_val)
parse_float('1.2345678')
parse_float('1.234')
parse_float('1.2340000')
and it outputs
1.2345678
1.234
1.2340000
>>> from decimal import Decimal as d
>>> d('1.13200000')
Decimal('1.13200000')
>>> print d('1.13200000')
1.13200000

Categories