Round off dict values to 2 decimals
Solution 1
Taking the best bits from a couple of other answers:
class LessPrecise(float):
def __repr__(self):
return str(self)
def roundingVals_toTwoDeci(y):
for d in y:
for k, v in d.items():
v = LessPrecise(round(v, 2))
print v
d[k] = v
>>> roundingVals_toTwoDeci(y)
80.0
10.0
0.08
10.67
80.73
10.78
0.0
10.0
80.72
10.0
0.78
10.0
80.78
10.0
0.0
10.98
>>> s=json.dumps(y)
>>> s
'[{"a": 80.0, "c": 10.0, "b": 0.08, "d": 10.67}, {"a": 80.73, "c": 10.78, "b": 0.0, "d": 10.0}, {"a": 80.72, "c": 10.0, "b": 0.78, "d": 10.0}, {"a": 80.78, "c": 10.0, "b": 0.0, "d": 10.98}]'
Solution 2
import json
y = [{'a': 80.0, 'b': 0.0786235, 'c': 10.0, 'd': 10.6742903}, {'a': 80.73246, 'b': 0.0, 'c':
10.780323, 'd': 10.0}, {'a': 80.7239, 'b': 0.7823640, 'c': 10.0, 'd': 10.0}, {'a':
80.7802313217234, 'b': 0.0, 'c': 10.0, 'd': 10.9762304}]
def roundingVals_toTwoDeci(y):
for d in y:
for k, v in d.items():
v = round(v,2) # <--- round() does exact that.
d[k] = v # <--- You need to put the rounded v back in d
print v
return
roundingVals_toTwoDeci(y)
s = json.dumps(y)
print s
Solution 3
JSONEncoder
uses repr
, and repr
prints floats with all their available precision. The only possible solutions are to inherit from JSONEncoder
and round while actually converting the values to a string (which implies to copy and adapt some code from the json.encoder
module), or else wrap the floats into your own type RoundedFloat
and register a serializer for that. Also note that repr's behaviour depends on the Python version used.
As often with non-obvious behaviour, the observation during debugging can trick you: print
uses str()
, and str()
rounds at a certain point, unlike repr()
which shows the naked ugliness of floating point maths.
The proof is in the code:
>>> class F(float):
... def __str__(self): return "str"
... def __repr__(self): return "repr"
...
...
>>> print F(1)
str
>>> F(1)
repr
>>> repr(1-1e-15)
'0.999999999999999'
>>> str(1-1e-15)
'1.0'
Solution 4
Answering the second part of your question
Try replacing line 5 of your code with:
v = round(v, 2)
This will round the number to two decimal places. Using round
, I get
[{'a': 80.0, 'c': 10.0, 'b': 0.08, 'd': 10.67}, {'a': 80.73, 'c': 10.78, 'b': 0.0, 'd': 10.0}, {'a': 80.72, 'c': 10.0, 'b': 0.78, 'd': 10.0}, {'a': 80.78, 'c': 10.0, 'b': 0.0, 'd': 10.98}]
I am using Python 2.7.2. Here's all the code:
from math import ceil
import json
y = [{'a': 80.0, 'b': 0.0786235, 'c': 10.0, 'd': 10.6742903},
{'a': 80.73246, 'b': 0.0, 'c': 10.780323, 'd': 10.0},
{'a': 80.7239, 'b': 0.7823640, 'c': 10.0, 'd': 10.0},
{'a': 80.7802313217234, 'b': 0.0, 'c': 10.0, 'd': 10.9762304}]
def roundingVals_toTwoDeci(y):
for d in y:
for k, v in d.items():
v = round(v, 2)
#print v
d[k] = v
return
roundingVals_toTwoDeci(y)
s = json.dumps(y)
print s
Solution 5
I don't understand what relates to json, but I can propose:
from math import ceil
y = [{'a': 80.0, 'b': 0.0786235, 'c': 10.0, 'd': 10.6742903},
{'a': 80.73246, 'b': 0.0, 'c': 10.780323, 'd': 10.0},
{'a': 80.7239, 'b': 0.7823640, 'c': 10.0, 'd': 10.0},
{'a': 80.7802313217234, 'b': 0.0, 'c': 10.0, 'd': 10.9762304}]
class TwoDec(float):
def __repr__(self):
return "%.2f" % self
def roundingVals_to_TwoDeci(y,ceil=ceil,TwoDec=TwoDec):
for d in y:
for k, v in d.iteritems():
d[k] = TwoDec(ceil(v*100)/100)
roundingVals_to_TwoDeci(y)
for el in y:
print el
result
{'a': 80.00, 'c': 10.00, 'b': 0.08, 'd': 10.68}
{'a': 80.74, 'c': 10.79, 'b': 0.00, 'd': 10.00}
{'a': 80.73, 'c': 10.00, 'b': 0.79, 'd': 10.00}
{'a': 80.79, 'c': 10.00, 'b': 0.00, 'd': 10.98}
askance
Updated on June 04, 2022Comments
-
askance almost 2 years
I'm having a hard time rounding off values in dicts. What I have is a list of dicts like this:
y = [{'a': 80.0, 'b': 0.0786235, 'c': 10.0, 'd': 10.6742903}, {'a': 80.73246, 'b': 0.0, 'c': 10.780323, 'd': 10.0}, {'a': 80.7239, 'b': 0.7823640, 'c': 10.0, 'd': 10.0}, {'a': 80.7802313217234, 'b': 0.0, 'c': 10.0, 'd': 10.9762304}]
I need to round off the values to just 2 decimal places.
When I try the following:
def roundingVals_toTwoDeci(y): for d in y: for k, v in d.items(): v = ceil(v*100)/100.0 print v d[k] = v return roundingVals_toTwoDeci(y) s = json.dumps(y) print s
I get:
0.0 0.0 18.2 0.0 27.3 54.5 0.0 0.0 0.0 [{"a": 0.0, "b": 0.0, "c": 27.300000000000001, "d": 0.0, "e": 54.5, "f": 0.0, "g": 18.199999999999999, "h": 0.0, "i": 0.0}]
I need to make this work with versions 2.4+ and so am not using dict comprehensions. First, I am having a hard time looping through all the key, values in all the dicts in the original. Second, this result has just 1 decimal point instead of 2 when it prints inside the function? Third, why is the 'json.dumps' and then 'print' not showing the values from inside the function?
EDIT:
Working with @Mark Ransom's answer below, I get the desired o/p. However, I have to urlencode the
json.dumps
value and send it to a URL. At the URL, it decodes the values into all the decimal places. So, for example, if,josn.dumps
gives {"a": 9.1}, the URL shows it (after urlencode) as9.10034254344365
. The modified code is as below:class LessPrecise(float): def __repr__(self): return str(self) def roundingVals_toTwoDeci(y): for d in y: for k, v in d.items(): v = LessPrecise(round(v, 2)) print v d[k] = v roundingVals_toTwoDeci(y) j = json.dumps(y) print j params = urllib.urlencode({'thekey': j})
print json.dumps
gives{"a": 9.1}
At the URL afterurlencode
, it gives9.1078667322034
instead of9.1
as in the following:Output:::
100.0 0.0 0.0 0.0 100.0 0.0 0.0 0.0 81.8 0.0 18.2 0.0 90.0 0.0 0.0 10.0 [{"a": 100.0, "b": 0.0, "c": 0.0, "d": 0.0}, {"a": 100.0, "b": 0.0, "c": 0.0, "d": 0.0}, {"a": 81.8, "b": 0.0, "c": 18.2, "d": 0.0}, {"a": 90.0, "b": 0.0, "c": 0.0, "d": 10.0}]
At the URL:
9.100000381469727
The JSON string after json.dumps()
[{"a": 80.0, "b": 0.0, "c": 10.0, "d": 10.0}, {"a": 100.0, "b": 0.0, "c": 0.0, "d": 0.0}, {"a": 80.0, "b": 0.0, "c": 10.0, "d": 10.0}, {"a": 90.0, "b": 0.0, "c": 0.0, "d": 10.0}]
The urlencode string - after decoding at http://meyerweb.com/eric/tools/dencoder/
thekey=[{"a": 80.0, "b": 0.0, "c": 10.0, "d": 10.0}, {"a": 100.0, "b": 0.0, "c": 0.0, "d": 0.0}, {"a": 80.0, "b": 0.0, "c": 10.0, "d": 10.0}, {"a": 90.0, "b": 0.0, "c": 0.0, "d": 10.0}]
At the URL, I get values like
18.200000762939453
(this value is from a later script run)-
jhermann over 10 years
JSONEncoder
usesrepr
, and repr prints floats with all their available precision. The only possible solutions are to inherit fromJSONEncoder
and round while actually converting the values to a string, or else wrapa the floats into your own typeRoundedFloat
and register a serializer for that. Also note that repr's behaviour depends on the Python version used. -
askance over 10 yearsWhen I do a
print y
after callingroundingVals_toTwoDeci(y)
but befores = json.dumps(y
, I get the same exact problem. So, could it be an issue with the function/calling the function/return statement instead? (or, in addition to it) -
Mark Ransom over 10 years
print y
does the exact samerepr
to convert the floating point to a string when that number is inside a list or dictionary. -
askance over 10 yearsOk. What I did was add
v = str(v)
afterv = round(v, 2)
inside the function and now it seems to be working fine. But wouldstr(v)
be an issue when json sends the data to the external URL (if the URL is not conf. to accept strings or is conf. to accept only a certain datatype other than string, viz., 'float'? My other question is why does this print only upto 1 decimal point - my requirement is for 2 decimal points. -
Mark Ransom over 10 years@jhermann that deserves to be an answer, then you can format it properly.
-
Mark Ransom over 10 yearsThe new information doesn't make any sense. You're passing a string to
urlencode
, it's not going to change the number inside the string. Can you tell us the exact string you get fromjson.dumps
and the exact complete string you get fromurlencode
? -
askance over 10 yearsEdited the question to include the two strings from
json.dumps()
and the decoded values fromurlencode
. -
Mark Ransom over 10 yearsThanks. As you can see, the encoded URL is correct so there's absolutely nothing you can do to fix anything on that end; the problem is on the page receiving the URL. Before you do anything else read stackoverflow.com/questions/2100490/…
-
askance over 10 yearsThanks. Was waiting to hear back from the guys behind the URL. The decoded params are fine as you point above and it's their app (URL) that needs to be dealt with.
-
-
askance over 10 yearsI am getting the same exact result as before. Could it be the way I am calling the function or using the 'return' statement?
-
mdml over 10 yearsI ran your code exactly as is besides replacing line 5 as in my answer, and commenting out line 6 where you print the rounded value. Doing that, I obtain the output in my answer.
-
askance over 10 yearsI am not sure why I am not getting it then. I am doing the exact same thing. I am using 2.6 (The values in the data, i.e., in 'y' - the list of dicts, keep changing whenever the script is called though I don't think it should matter.)
-
askance over 10 yearsI have all the imports (incl. JSON and 'from math import ceil').
-
mdml over 10 yearsDo you mean that your output still isn't rounded, or is the output somehow different?
-
askance over 10 yearsBy same as before I mean:
[{"a": 0.0, "b": 0.0, "c": 0.0, "d": 90.900000000000006, "e": 9.0999999999999996, "f": 0.0, "g": 0.0, "h": 0.0, "i": 0.0}]
When I do a print statement inside the loop, I get the earlier values- rounded off to 1 decimal place but the print statement outside the function gives what is above. -
mdml over 10 yearsI will add all my code to the answer. I would try running that completely separately just to see.
-
mdml over 10 years
-
jhermann over 10 yearsPS: The
json
module's author probably intendedjson.encoder.FLOAT_REPR = lambda f: "%.2f" % f
to be possible, but then that has a bug (FLOAT_REPR
is only used at import time, not at runtime). -
Mark Ransom over 10 yearsI wonder if it has to do with your platform? With 2.7.1 on Windows I get the same results as @user2480526.
-
askance over 10 yearsThe answer works fine - o/p is values without strings and rounded off to 1 decimal on my machine. When I send the data to the URL after
urlencode
, it appears there with all 9/10 decimal points instead of the 1 decimal place that it shows upon printing afterjson.dumps
which is what I want to avoid. -
Mark Ransom over 10 years@user2480526 in that case
urlencode
is converting the string back into numbers before converting to another string. The problem needs to be fixed there. I wonder ifurlencode
would work with strings instead? -
askance over 10 yearsTried it with string values. It is decoded at the URL with all the decimal places -
9.100000381469727
while running myprint json.dumps
returns {"a": "9.1"} -
askance over 10 yearsThis answer leads to the same decoding at the URL.
-
Mark Ransom over 10 years@user2480526 the second answer on that thread does something similar to what I did. What exactly are you passing to
urlencode
? -
askance over 10 yearsI am doing a
json.dumps()
and then passing the value tourlencode
. Right now, I am passing `[{"a": "88.9", "b": "0.0", "sc": "0.0", "d": "11.1"}, {"a": "100.0", "b": "0.0", "c": "0.0", "d": "0.0"}, {"a": "81.8", "b": "0.0", "c": "9.1", "d": "9.1"} and at the URL for "d" I get 9.1068906293. I tried your solution and got the dict with values as floats - 9.1 instead of "9.1" but at the URL I get the same 10-15 decimal places. -
askance over 10 yearsI am trying several solutions given here and here. I now get the 2 decimal places but when the URL decodes it, it shows e.g., 9.1087978601324 instead of 9.1. After
w=json.dumps
, I do aurlencode(w)
. -
Mark Ransom over 10 years@user2480526, you're not being specific enough - what is the exact code you're using for
urlencode
? Maybe you should edit it into the question. -
askance over 10 yearsOops! I edited the question with a solution from elsewhere. Edited it again to include your code.
-
askance over 10 yearsThanks. +1 for using iteritems() - forgot about that :) This gives the desired o/p but the JSON/urlencode thing is driving me nuts!
-
Admin over 2 yearsAs it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.