简体   繁体   中英

Python - List of Tuples, sum of tuples having same first value in this case?

li=[('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

Assuming I have this case. Where it starts with the same name in the first position, I want to add the second with the second and third with the third.

Desired result:

[('name1', 11, 13), ('name2', 3, 2)]

does anyone know how I can do this in Python?

names = [('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

### Iterate over all members of the original list to perform the logic
new_names = dict()
for n,first,second in names:
    if n not in new_names:
        new_names[n] = (first, second)
    else:
        new_names[n] = (first+new_names[n][0], second+new_names[n][1])

### Get back list of tuples
new_names2 = [(k, *v) for k,v in new_names.items()]
print(new_names2)

Output:

[('name1', 11, 13), ('name2', 3, 2)]

You should define the tuple as a class. But if the data is given as a set of tuples... then define a function addTuple .

def addTuple(t1, t2):
    (ta,va1,va2) = t1

    try: (ta,va1,va2) = t1
    except: 

        # FIXME: default to None, value 0
        (ta,va1, va2) = (None, 0, 0)
    try:
        # FIXME: default to None, value 0
        (tb,vb1,vb2) = t2
    except: 
        # FIXME: default to None, value 0
        (tb,vb1, vb2) = (None, 0, 0)

    return (ta, va1+vb1, va2+vb2)

print([addTuple(arr[0],arr[2]), addTuple(arr[1],0)])
[('name1', 1

Here is a method using class defintion:

class mytuple(object):
    def __init__(self, tx, val1, val2):
        self.tx = tx
        self.val1 = val1
        self.val2 = val2

    def __add__(self, t2):
        if self.tx != t2.tx : 
            print("WARNING: BAD TUPLE, ignoring", self.tx, t2.tx)

        return mytuple(self.tx, 
                       self.val1 + t2.val1,
                       self.val2 + t2.val2)

    def __str__(self):
        self.__repr()

    def __repr__(self):
        tstr = ', '.join([self.tx, 
                        '%d'%self.val1,
                        '%d'%self.val2])
        return('(%s)'%tstr)

arr = [ mytuple('name1', 5, 10),
        mytuple('name2', 3, 2), 
        mytuple('name1', 6, 3)]

print([arr[0] + arr[2], arr[1]])
[(name1, 11, 13), (name2, 3, 2)]

Given:

LoT=[('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

You can first use a dict to accumulate the like tags:

di={}
for t in LoT:
    di.setdefault(t[0], []).append(t[1:])

>>> di
{'name1': [(5, 10), (6, 3)], 'name2': [(3, 2)]}

Then create a new list with the sum of those tags:

>>> [(k,)+tuple(map(sum, zip(*li))) for k,li in di.items()]
[('name1', 11, 13), ('name2', 3, 2)]
# the interesting thing here is the zip(*li) which rotates the list of tuples
# zip(*[(5, 10), (6, 3)]) => [(5, 6), (10, 3)]

Or, just keep a running total:

di={}
for t in LoT:
    try:
        di[t[0]]=tuple(map(sum, zip(di[t[0]], t[1:])))
    except KeyError:
        di[t[0]]=t[1:]

And then convert the running total dict to a list:

[(k,)+t for k,t in di.items()]
# same result

These solutions work regardless of the length of the tuples.

Initially tuple's values cant be changed but if you want to do it the way you want then you could do something like this.

li = [['name1', 5, 10], ['name2', 3, 2], ['name1', 6, 3]]
new_li = []
for tup in li:
    names = [tup[0] for tup in new_li]
    if tup[0] in names:
        index = names.index(tup[0])
        new_li[index][1] += tup[1]
        new_li[index][2] += tup[2]
    else:
        new_li.append(tup)

# [['name1', 11, 13], ['name2', 3, 2]]
# to make it tuple

new_li = [tuple(l) for l in new_li]
# [('name1', 11, 13), ('name2', 3, 2)]

Much simpler solution:

i = [('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]
d = {}
for name, fv, sv in i:
    if name not in d:
        d[name] = [name, fv, sv]
    else:
        d[name][1] += fv
        d[name][2] += sv
        
[tuple(v) for v in d.values()]

Yet another answer, but with some built in flexibility:

li=[('name1', 5, 10), ('name2', 3, 2), ('name1', 6, 3)]

# Get length of structure
li_len = len(li)

# Create empty collection
outdict = {}

# Iterate over all elements
for i in range(li_len):
    tmpname = li[i][0] # Temp name variable
    tmpvalues = li[i][1:] # Temp values variable

    # If not temp name in dictionary keys
    if not tmpname in outdict:
        # Create entry with values as first values
        outdict[tmpname] = list(tmpvalues)
    else:
        # Otherwise, iterate values and update entry by position
        for j in range(len(tmpvalues)):
            outdict[tmpname][j] += tmpvalues[j]

# Reformat for output
result = [tuple([k] + v) for k, v in outdict.items()]

# Print result
print(result)

Output:

[('name1', 11, 13), ('name2', 3, 2)]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM