31

I have a list of (label, count) tuples like this:

[('grape', 100), ('grape', 3), ('apple', 15), ('apple', 10), ('apple', 4), ('banana', 3)]

From that I want to sum all values with the same label (same labels always adjacent) and return a list in the same label order:

[('grape', 103), ('apple', 29), ('banana', 3)]

I know I could solve it with something like:

def group(l):
    result = []
    if l:
        this_label = l[0][0]
        this_count = 0
        for label, count in l:
            if label != this_label:
                result.append((this_label, this_count))
                this_label = label
                this_count = 0
            this_count += count
        result.append((this_label, this_count))
    return result

But is there a more Pythonic / elegant / efficient way to do this?

hoju
  • 28,392
  • 37
  • 134
  • 178

8 Answers8

44

itertools.groupby can do what you want:

import itertools
import operator

L = [('grape', 100), ('grape', 3), ('apple', 15), ('apple', 10),
     ('apple', 4), ('banana', 3)]

def accumulate(l):
    it = itertools.groupby(l, operator.itemgetter(0))
    for key, subiter in it:
       yield key, sum(item[1] for item in subiter) 

print(list(accumulate(L)))
# [('grape', 103), ('apple', 29), ('banana', 3)]
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Thomas Wouters
  • 130,178
  • 23
  • 148
  • 122
8

using itertools and list comprehensions

import itertools

[(key, sum(num for _, num in value))
    for key, value in itertools.groupby(l, lambda x: x[0])]

Edit: as gnibbler pointed out: if l isn't already sorted replace it with sorted(l).

cobbal
  • 69,903
  • 20
  • 143
  • 156
6
import collections
d=collections.defaultdict(int)
a=[]
alist=[('grape', 100), ('banana', 3), ('apple', 10), ('apple', 4), ('grape', 3), ('apple', 15)]
for fruit,number in alist:
    if not fruit in a: a.append(fruit)
    d[fruit]+=number
for f in a:
    print (f,d[f])

output

$ ./python.py
('grape', 103)
('banana', 3)
('apple', 29)
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
5
>>> from itertools import groupby
>>> from operator import itemgetter
>>> L=[('grape', 100), ('grape', 3), ('apple', 15), ('apple', 10), ('apple', 4), ('banana', 3)]
>>> [(x,sum(map(itemgetter(1),y))) for x,y in groupby(L, itemgetter(0))]
[('grape', 103), ('apple', 29), ('banana', 3)]
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
4

my version without itertools
[(k, sum([y for (x,y) in l if x == k])) for k in dict(l).keys()]

1

Method

def group_by(my_list):
    result = {}
    for k, v in my_list:
        result[k] = v if k not in result else result[k] + v
    return result 

Usage

my_list = [
    ('grape', 100), ('grape', 3), ('apple', 15),
    ('apple', 10), ('apple', 4), ('banana', 3)
]

group_by(my_list) 

# Output: {'grape': 103, 'apple': 29, 'banana': 3}

You Convert to List of tuples like list(group_by(my_list).items()).

Shameem
  • 2,664
  • 17
  • 21
0

Or a simpler more readable answer ( without itertools ):

pairs = [('foo',1),('bar',2),('foo',2),('bar',3)]

def sum_pairs(pairs):
  sums = {}
  for pair in pairs:
    sums.setdefault(pair[0], 0)
    sums[pair[0]] += pair[1]
  return sums.items()

print sum_pairs(pairs)
Paul Kenjora
  • 1,914
  • 18
  • 20
0

Simpler answer without any third-party libraries:

dct={}

for key,value in alist:
    if key not in dct:
        dct[key]=value
    else:
        dct[key]+=value
ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
  • I don't see any third-party libraries here. [`itertools`](https://docs.python.org/library/itertools.html), [`operator`](https://docs.python.org/library/operator.html), and [`collections`](https://docs.python.org/library/collections.html) are all part of the standard library. They come with Python. – ChrisGPT was on strike Jul 08 '22 at 14:47