usernumber
usernumber

Reputation: 2186

Average values in all dicts with matching values for a given key in python

I have a list of dictionnaries that all have the same keys.

in_list = [{'index':1, 'value':2.}, {'index':1, 'value':3.}, {'index':2, 'value':4.}]

I'd like to create a new dictionnary with the average on 'value' for each 'index'.

out_dict = {1:2.5, 2:4.}

What would be the most pythonic way to do this ?


The following code does what I want, but I feel like it is clumsy

tmp = {x:[] for x in range(1,3)}
for el in in_list:
    tmp[el['index']].append(el['value'])

for key, val in tmp.iteritems():
    out_dict[key] = sum(val)/len(val)

Upvotes: 4

Views: 663

Answers (4)

PM 2Ring
PM 2Ring

Reputation: 55469

Your code is fine, but you can make it a little more compact. As Transhuman's answer shows you can avoid initialising tmp by making it a defaultdict of lists. Another way to do that is to use the dict.setdefault method. And then use a dict comprehension to calculate the averages.

in_list = [
    {'index':1, 'value':2.}, 
    {'index':1, 'value':3.}, 
    {'index':2, 'value':4.}
]

out_dict = {}
for d in in_list:
    out_dict.setdefault(d['index'], []).append(d['value'])
out_dict = {k: sum(v) / len(v) for k, v in out_dict.items()}
print(out_dict)

output

{1: 2.5, 2: 4.0}

Upvotes: 2

U13-Forward
U13-Forward

Reputation: 71560

To do it without any packages to install (long one-liner :-) ):

import itertools,statistics
a = dict(zip(sorted(set([i['index'] for i in lod])),[statistics.mean(int(item['value']) for item in group) for key, group in itertools.groupby(lod, key=lambda x: x['index'])]))

Now:

print(a)

Returns:

{1: 2.5, 2: 4}

If python 2:

import itertools
a = dict(zip(sorted(set([i['index'] for i in lod]),key=[i['index'] for i in lod].index),[sum(int(item['value']) for item in group)/len(int(item['value']) for item in group) for key, group in itertools.groupby(lod, key=lambda x: x['index'])]))

Explanation:

  • get the ordered list of unique elements using set

  • use itertools.groupby for grouping then iterate by key a group, the get average using statistics or sum and len

  • the above two notes are all in a zip(dict(zip(...)))

Or to make the code little cleaner:

Python 3:

import itertools,statistics
unique_elements=sorted(set([i['index'] for i in lod]))
groups=statistics.mean(int(item['value']) for item in group) for key, group in itertools.groupby(lod, key=lambda x: x['index'])]
a = dict(zip(unique_elements,groups))

Python 2:

import itertools
unique=sorted(set([i['index'] for i in lod])
groups=[sum(int(item['value']) for item in group)/len(int(item['value']) for item in group) for key, group in itertools.groupby(lod, key=lambda x: x['index'])]
a = dict(unique,groups))

Upvotes: 2

Transhuman
Transhuman

Reputation: 3547

One way you can do is using collections.defaultdict

in_list = [{'index':1, 'value':2.}, {'index':1, 'value':3.}, {'index':2, 'value':4.}]

from collections import defaultdict
d_dict = defaultdict(list)
for k,v in [d.values() for d in in_list]:
    d_dict[k].append(v)

{k:sum(v)/len(v) for k,v in d_dict.items()}
#{1: 2.5, 2: 4.0}

Upvotes: 1

timgeb
timgeb

Reputation: 78650

I don't think your code is clumsy, but you could check out pandas.

>>> import pandas as pd
>>> in_list = [{'index':1, 'value':2.}, {'index':1, 'value':3.}, {'index':2, 'value':4.}]
>>> 
>>> df = pd.DataFrame(in_list)
>>> df.groupby(by='index').mean()
       value
index       
1        2.5
2        4.0

You can transform the result to a standard dictionary if you like.

>>> df.groupby(by='index').mean().to_dict()['value']
{1: 2.5, 2: 4.0}

Upvotes: 1

Related Questions