ProvoWallis
ProvoWallis

Reputation: 41

Sum values in a Python list of dictionaries using a common key/value

Apologies if this question has already been asked. I'm a newbie at this but I've looked at a couple of other questions/answers that appear similar to mine but can't find one that does the trick. I've tried Counter but can't seem to figure out how to keep the ID key/value.

I'm trying to sum the values in a Python dictionary using a common key/value. E.g.,

list = [
    {'ID':1, 'T2':10, 'T3':20},
    {'ID':2, 'T2':5, 'T3':0},
    {'ID':1, 'T2':5, 'T3':10},
    {'ID':2, 'T2':10, 'T3':30},
    {'ID':3, 'T2':5, 'T3':0}
]

but I need this:

newlist = [
    {'ID':1, 'T2':15, 'T3':30}, 
    {'ID':2, 'T2':15, 'T3':30}, 
    {'ID':3, 'T2':5, 'T3':0}
]

Any help appreciated.

UPDATE:

I have borrowed from another answer and tried this:

superdict = {}
for d in rows:
  for k, v in d.items():
      if superdict.get(k) is None:
          superdict[k] = []
      if superdict.get(k) is not None:
          superdict[k].append(v)    

But instead of keeping getting a new list of combined/added values, I get something like this:

'ID': ['3903', '3997', '3997', '3997', '3947', '4097', '3445', 
       '3997', '4107', '3997', '3445', '3997', '3997', '3997', 
       '3997', '3445', '3997', etc. etc.

UPDATE 2:

Sorry for the confusion in regard to my examples. What I'm looking for is a way to keep the ID value static but add the other values in the dictionary. So all of the values in corresponding to IDs with a value of 1 would be added together.

E.g. this:

list = [{'ID':1, 'T2':10, 'T3':20}, {'ID':1, 'T2':5, 'T3':10}]

Becomes this:

newlist = [{'ID':1, 'T2':15, 'T3':30}]

Hope this helps.

Upvotes: 4

Views: 5374

Answers (4)

Iliyan Bobev
Iliyan Bobev

Reputation: 3108

If interpret this properly, the goal is to group by the value for key 'ID' then to sum the values of the corresponding keys within the group. If the keys are consistent i.e. all keys from the first dict are guaranteed to show in the later dicts, here is a a solution:

my_list = [{'ID':1, 'T2':10, 'T3':20},
           {'ID':2, 'T2':5, 'T3':0}, 
           {'ID':1, 'T2':5, 'T3':10}, 
           {'ID':2, 'T2':10, 'T3':30}, 
           {'ID':3, 'T2':5, 'T3':0}]


# prep keys
id_keys = set([ z['ID'] for z in my_list ])
sum_keys = [ z for z in my_list[0].keys() if z != 'ID' ]
print('ids to group by', id_keys)
print('keys to sum over', sum_keys)

# restructure the data
big = [ [z.pop('ID'), z] for z in my_list ]
print('[[group_key, {remaining dict],] :\n', big)

# initiate results accumulator
rez = {idd: dict.fromkeys(sum_keys, 0) for idd in id_keys }
print('empty accumulator', rez)

# two loops in list comprehension
[ rez[g[0]].update({k: rez[g[0]][k] + g[1][k]}) for k in sum_keys for g in big ]
print('result', rez)

Output:

ids to group by {1, 2, 3}
keys to sum over ['T2', 'T3']
[[group_key, {remaining dict],] :
 [[1, {'T2': 10, 'T3': 20}], [2, {'T2': 5, 'T3': 0}], [1, {'T2': 5, 'T3': 10}], [2, {'T2': 10, 'T3': 30}], [3, {'T2': 5, 'T3': 0}]]
empty accumulator {1: {'T2': 0, 'T3': 0}, 2: {'T2': 0, 'T3': 0}, 3: {'T2': 0, 'T3': 0}}
result {1: {'T2': 15, 'T3': 30}, 2: {'T2': 15, 'T3': 30}, 3: {'T2': 5, 'T3': 0}}
[Finished in 0.2s]

If the goal is to sum over keys, and the keys are not guaranteed to match from one dict to the next, use the following code:

my_list = [{'T1':1, 'T2':10, 'T3':20},
           {'T1':2, 'T2':5 , 'T3':0}, 
           {        'T2':5 ,       }, 
           {        'T2':10, 'T3':30, 'T4': 7}, 
           {'T1':3, 'T2':5 , 'T3':0 , 'T4': 3}]

rez = {}
for dic in my_list:
    for key in dic.keys():
        rez[key]=rez.setdefault(key, 0) + dic[key]

print(rez)

Output:

{'T1': 6, 'T2': 35, 'T3': 50, 'T4': 10}

Upvotes: 0

Irshad Bhat
Irshad Bhat

Reputation: 8709

I hope this will help you:

>>> from collections import Counter
>>> old_list=[{'ID':1, 'T2':10, 'T3':20}, {'ID':2, 'T2':5, 'T3':0}, {'ID':1, 'T2':5, 'T3':10}, {'ID':2, 'T2':10, 'T3':30}, {'ID':3, 'T2':5, 'T':0}]
>>> new_list=[]
>>> for i,a in enumerate(old_list):
...     z=0
...     for b in old_list[i+1:]:
...        if a['ID']==b['ID']:
...           new_list.append(dict(Counter(a)+Counter(b)))
...           new_list[-1]['ID']=a['ID']
...           temp=old_list.pop(old_list.index(b))
...           z=1
...     if not z:
...        new_list.append(a)
... 
>>> new_list
[{'T2': 15, 'ID': 1, 'T3': 30}, {'T2': 15, 'ID': 2, 'T3': 30}, {'T2': 5, 'ID': 3, 'T3': 0}]

Upvotes: 1

OnStrike
OnStrike

Reputation: 748

It's hard to understand what your desired result is supposed to be with your given inputs. Give a concrete example of the expected/correct output.

I suspect this is what you are going for?

my_list = [{'ID':1, 'T2':10, 'T3':20},
           {'ID':2, 'T2':5, 'T3':0}, 
           {'ID':1, 'T2':5, 'T3':10}, 
           {'ID':2, 'T2':10, 'T3':30}, 
           {'ID':3, 'T2':5, 'T3':0}]

new_dictionary = {}
for dictionary in my_list:
    for key, value in dictionary.items():
        if new_dictionary.has_key(key):
            new_dictionary[key] = value + new_dictionary[key]
        else:
            new_dictionary[key] = value

#Output:
>>>new_dictionary
{'T2': 35, 'ID': 9, 'T3': 60}

Upvotes: 4

ZZY
ZZY

Reputation: 3937

If I understood your needs correctly, here is a solution:

def sum_by_common_key(input_list, index_key='ID'):
    output_dict = {}
    for d in input_list:
        index = d[index_key]
        if index not in output_dict:
            output_dict[index] = {}
        for k, v in d.items():
            if k not in output_dict[index]:
                output_dict[index][k] = v
            elif k != index_key:
                output_dict[index][k] += v
    return output_dict.values()

l = [{'ID':1, 'T2':10, 'T3':20}, {'ID':2, 'T2':5, 'T3':0}, {'ID':1, 'T2':5, 'T3':10}, {'ID':2, 'T2':10, 'T3':30}, {'ID':3, 'T2':5, 'T3':0}]
print sum_by_common_key(l)

>>> [{'T2': 15, 'ID': 1, 'T3': 30}, {'T2': 15, 'ID': 2, 'T3': 30}, {'T2': 5, 'ID': 3, 'T3': 0}]

Upvotes: 1

Related Questions