Round off dict values to 2 decimals - python

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)

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

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'

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

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

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}

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.

[[round(d[key],2) for key in d] for d in y]
[{key:round(d[key], 2) for key in d} for d in y]

Round dictionary values one-liner
with d as the dict and here to 2 decimal places
d = {k: round(v, 2) for k, v in d.items()}
Given the subject line Round off dict values to 2 decimals, this is what most will be coming here to find.
dictionary comprehension might be used in a search

Related

Compare a dictionary with list of dictionaries and return index from the list which has higher value than the separate dictionary

I have a list of dictionaries and a separate dictionary having the same keys and only the values are different. For example the list of dictionaries look like this:
[{'A': 0.102, 'B': 0.568, 'C': 0.33}, {'A': 0.026, 'B': 0.590, 'C': 0.382}, {'A': 0.005, 'B': 0.857, 'C': 0.137}, {'A': 0.0, 'B': 0.962, 'C': 0.036}, {'A': 0.0, 'B': 0.991, 'C': 0.008}]
and the separate dictionary looks like this:
{'A': 0.005, 'B': 0.956, 'C': 0.038}
I want to compare the separate dictionary with the list of dictionaries and return the index from the list which has higher value than the separate dictionary. In this example, the indices would be 3, 4 as the dictionary in indices 3 and 4 has a higher value for key 'B' since 'B' has the highest value in the separate dictionary.
Any ideas on how I should I proceed the problem?
You can use enumerate for finding index of max value:
org = [
{'A': 0.102, 'B': 0.568, 'C': 0.33},
{'A': 0.026, 'B': 0.590, 'C': 0.382},
{'A': 0.005, 'B': 0.857, 'C': 0.137},
{'A': 0.0, 'B': 0.962, 'C': 0.036},
{'A': 0.0, 'B': 0.991, 'C': 0.008}
]
com = {'A': 0.005, 'B': 0.956, 'C': 0.038}
def fnd_index(org, com):
key_max, val_max = max(com.items(), key=lambda x: x[1])
print('key_max:', key_max)
print('val_max:', val_max)
res = []
for idx, dct in enumerate(org):
if dct[key_max] > val_max:
res.append(idx)
return res
res = fnd_index(org, com)
print('result:', res)
Output:
key_max: B
val_max: 0.956
result: [3, 4]
are you sure that it should be only index 4?
dict_list = [{'A': 0.102, 'B': 0.568, 'C': 0.33},
{'A': 0.026, 'B': 0.590, 'C': 0.382},
{'A': 0.005, 'B': 0.857, 'C': 0.137},
{'A': 0.0, 'B': 0.962, 'C': 0.036},
{'A': 0.0, 'B': 0.991, 'C': 0.008}]
d = {'A': 0.005, 'B': 0.956, 'C': 0.038}
max_val = max(d.values())
idxmax = [i for i,j in enumerate(dict_list) if max(j.values()) > max_val]
print(idxmax) # [3, 4]

Assign dict values as arguments in Python

I'm using a class
class Variable():
def __init__(self, value, ratio):
self.value = value
self.ratio = {'A': 0.0, 'B': 0.0, 'C': 0.0}
and I would like to assign a value and a ratio (to one of A, B or C) when initializing, i.e.
> var1 = Variable(3.2, 'A'=100.0)
so that the outcome would be
> var1.value
3.2
> var1.ratio
{'A': 100.0, 'B': 0.0, 'C': 0.0}
Is this possible with a function argument like this, or should I use some other method?
define default value for each parameter and use them directly into dict:
class Variable():
def __init__(self, value, A=0.0, B=0.0, C=0.0):
self.value = value
self.ratio = {'A': A, 'B': B, 'C': C}
and you are ready to go
var1 = Variable(3.2, A=100.0)
var1.ratio
{'A': 100.0, 'B': 0.0, 'C': 0.0}
You can do something like that:
class Variable:
def __init__(self, value, ratio):
self.value = value
self.ratio = {'A' : 0.0, 'B' : 0.0, 'C' : 0.0}
self.ratio.update(ratio)
var1 = Variable(3.2, {'A' : 100.0})
print(var1.value) # 3.2
print(var1.ratio) # {'A': 100.0, 'B': 0.0, 'C': 0.0}

Sum of values from nested dictionary [duplicate]

This question already has answers here:
How to sum values in multidimensional dictionary?
(6 answers)
Closed 1 year ago.
I have this nested dictionary:
{'rekless': {'C': 2.0, 'H': 4.0, 'J': 0.0}, 'bwipo': {'C': 3.0, 'D': 4.0, 'H': 0.0}, 'wunder': {'D': 10.0, 'G': 20.0, 'H': 0.50}, 'caps': {'D': 3.1, 'I': 2.0, '9J': 1.0, '10K': 2.0}, 'jankos': {'D': 3.2, 'I': 2.0, 'J': 1.0, 'K': 2.0} }
Want i want to do is to loop through it and sum all values of the "C, H, J,", etc...
So like, 'C' is going to be (2.0 + 3.0), 'D' is going to be (4.0 + 10.0 + 3.1 + 3.2).
So i can end up with something similar to this:
{ C: 'sum of values from c', D: 'sum of values from d', etc.. }
It doesnt need to be in a dictionary the final result.
Any ideias on how to start?
You need to loop through both dictionary and subdictionary:
d = {
'rekless': {'C': 2.0, 'H': 4.0, 'J': 0.0},
'bwipo': {'C': 3.0, 'D': 4.0, 'H': 0.0},
'wunder': {'D': 10.0, 'G': 20.0, 'H': 0.50},
'caps': {'D': 3.1, 'I': 2.0, '9J': 1.0, '10K': 2.0},
'jankos': {'D': 3.2, 'I': 2.0, 'J': 1.0, 'K': 2.0}
}
summary = dict()
for key, subdict in d.items():
for k, v in subdict.items():
summary[k] = summary.get(k, 0) + v
print(summary)
Assume d is:
d = {'rekless': {'C': 2.0, 'H': 4.0, 'J': 0.0}, 'bwipo': {'C': 3.0, 'D': 4.0, 'H': 0.0}, 'wunder': {'D': 10.0, 'G': 20.0, 'H': 0.50}, 'caps': {'D': 3.1, 'I': 2.0, '9J': 1.0, '10K': 2.0}, 'jankos': {'D': 3.2, 'I': 2.0, 'J': 1.0, 'K': 2.0} }
Try this:
vals ={}
for k in d:
sum_ = 0
for k_ in d[k]:
if k_ in vals:
vals[k_]+= int(d[k][k_])
else:
vals[k_]= int(d[k][k_])
for k__ in vals:
vals[k__]='sum of values from {} is: {}'.format(k__, vals[k__])
which results in vals to be:
{'C': 'sum of values from C is: 5',
'H': 'sum of values from H is: 4',
'J': 'sum of values from J is: 1',
'D': 'sum of values from D is: 20',
'G': 'sum of values from G is: 20',
'I': 'sum of values from I is: 4',
'9J': 'sum of values from 9J is: 1',
'10K': 'sum of values from 10K is: 2',
'K': 'sum of values from K is: 2'}

clustering a list of dictionaries according to specific key/value pairs

Say i have a list of dictionaries that all have the same keys. and i want to regroup these into several lists such that the values for certain attributes of my choosing are equal. here is an example:
Suppose i have the following list of dictionaries:
[ {'a': 0.0, 'b': 0.2, 'c': 0.1},
{'a': 0.1, 'b': 0.7, 'c': 0.2},
{'a': 0.0, 'b': 0.2, 'c': 0.3},
{'a': 0.1, 'b': 0.7, 'c': 0.4},
{'a': 0.0, 'b': 0.7, 'c': 0.5},
{'a': 0.0, 'b': 0.7, 'c': 0.6}]
and i want to cluster it according the the keys a and b. Then the output would be the following list of list of dictionaries:
[[{'a': 0.0, 'b': 0.2, 'c': 0.1},
{'a': 0.0, 'b': 0.2, 'c': 0.3}]
[{'a': 0.1, 'b': 0.7, 'c': 0.2},
{'a': 0.1, 'b': 0.7, 'c': 0.4}]
[{'a': 0.0, 'b': 0.7, 'c': 0.5},
{'a': 0.0, 'b': 0.7, 'c': 0.6}]]
What is the best way of achieving this?
Sort this firstly, then use itertools.groupby.You may could try this below:
from itertools import groupby
t = [{'a': 0.0, 'b': 0.2, 'c': 0.1},
{'a': 0.1, 'b': 0.7, 'c': 0.2},
{'a': 0.0, 'b': 0.2, 'c': 0.3},
{'a': 0.1, 'b': 0.7, 'c': 0.4},
{'a': 0.0, 'b': 0.7, 'c': 0.5},
{'a': 0.0, 'b': 0.7, 'c': 0.6}]
print([[*j] for i, j in groupby(sorted(t, key=lambda x: (x['a'], x['b'])), key=lambda x: (x['a'], x['b']))])
Result:
[[{'a': 0.0, 'b': 0.2, 'c': 0.1}, {'a': 0.0, 'b': 0.2, 'c': 0.3}], [{'a': 0.0, 'b': 0.7, 'c': 0.5}, {'a': 0.0, 'b': 0.7, 'c': 0.6}], [{'a': 0.1, 'b': 0.7, 'c': 0.2}, {'a': 0.1, 'b': 0.7, 'c': 0.4}]]
If you want to create a function to receive muitlple keys, you could try:
from itertools import groupby
def group_by(*args):
return [[*j] for i, j in groupby(sorted(t, key=itemgetter(*args)), key=itemgetter(*args))]
t = [{'a': 0.0, 'b': 0.2, 'c': 0.1},
{'a': 0.1, 'b': 0.7, 'c': 0.2},
{'a': 0.0, 'b': 0.2, 'c': 0.3},
{'a': 0.1, 'b': 0.7, 'c': 0.4},
{'a': 0.0, 'b': 0.7, 'c': 0.5},
{'a': 0.0, 'b': 0.7, 'c': 0.6}]
print(group_by('a', 'b'))

Extracting dictionary items embedded in a list

Let's say I have the following list of dict
t = [{'a': 1.0, 'b': 2.0},
{'a': 3.0, 'b': 4.0},
{'a': 5.0, 'b': 6.0},
{'a': 7.0, 'b': 9.0},
{'a': 9.0, 'b': 0.0}]
Is there an efficient way to extract all the values contained in the dictionaries with a dictionary key value of a?
So far I have come up with the following solution
x = []
for j in t:
x.append(j['a'])
However, I don't like to loop over items, and was looking at a nicer way to achieve this goal.
You can use list comprehension:
t = [{'a': 1.0, 'b': 2.0},
{'a': 3.0, 'b': 4.0},
{'a': 5.0, 'b': 6.0},
{'a': 7.0, 'b': 9.0},
{'a': 9.0, 'b': 0.0}]
new_list = [i["a"] for i in t]
Output:
[1.0, 3.0, 5.0, 7.0, 9.0]
Since this solution uses a for-loop, you can use map instead:
x = list(map(lambda x: x["a"], t))
Output:
[1.0, 3.0, 5.0, 7.0, 9.0]
Performance-wise, you prefer to use list-comprehension solution rather the map one.
>>> timeit('new_list = [i["a"] for i in t]', setup='from __main__ import t', number=10000000)
4.318223718035199
>>> timeit('x = list(map(lambda x: x["a"], t))', setup='from __main__ import t', number=10000000)
16.243124993163093
def temp(p):
return p['a']
>>> timeit('x = list(map(temp, t))', setup='from __main__ import t, temp', number=10000000)
16.048683850689343
There is a slightly difference when using a lambda or a regular function; however, the comprehension execution takes 1/4 of the time.
You can use itemgetter:
from operator import itemgetter
t = [{'a': 1.0, 'b': 2.0},
{'a': 3.0, 'b': 4.0},
{'a': 5.0, 'b': 6.0},
{'a': 7.0, 'b': 9.0},
{'a': 9.0, 'b': 0.0}]
print map(itemgetter('a'), t)
result:
[1.0, 3.0, 5.0, 7.0, 9.0]
Use a list comprehension as suggested in Ajax1234'a answer, or even a generator expression if that would benefit your use case:
t = [{'a': 1.0, 'b': 2.0}, {'a': 3.0, 'b': 4.0}, {'a': 5.0, 'b': 6.0}, {'a': 7.0, 'b': 9.0}, {'a': 9.0, 'b': 0.0}]
x = (item["a"] for item in t)
print(x)
Output:
<generator object <genexpr> at 0x7f0027def550>
The generator has the advantage of not executing or consuming memory until a value is needed. Use next() to take the next item from the generator, or iterate over it with a for loop.
>>> next(x)
1.0
>>> next(x)
3.0
>>> for n in x:
... print(n)
5.0
7.0
9.0
An alternative, albeit a expensive one, is to use pandas:
import pandas as pd
x = pd.DataFrame(t)['a'].tolist()
print(x)
Output:
[1.0, 3.0, 5.0, 7.0, 9.0]

Categories

Resources