Round off dict values to 2 decimals

孤街醉人 提交于 2019-12-30 11:48:29

问题


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) as 9.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 after urlencode, it gives 9.1078667322034 instead of 9.1as 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)


回答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}]'



回答2:


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'



回答3:


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



回答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



回答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}



回答6:


I know this question is old, but here is a quick one-liner solution that works at least here in Linux Python 2.7.6 and might be interesting to someone else:

y = [{ x : round(z, 2) for x,z in yi.items()} for yi in y ]

However, this might be inefficient for larger data sets, as it re-generates the list/dict structure.



来源:https://stackoverflow.com/questions/19145101/round-off-dict-values-to-2-decimals

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!