简体   繁体   中英

Summing Attribute in a list of Python Objects

Similar to "What's the most concise way in Python to group and sum a list of objects by the same property" , I have a script in which I need to sum the attributes of list of objects. However, my issue differs slightly.

I have a class of objects with attributes V, W, X, Y, and Z. I need to sum attribute Z by iterating through and matching the attributes W, X, and Y with all other W, X, and Y attributes that are the same. Producing a new summed value that is indexed by W, X, and Y.

Here is the class for the objects:

class xb(object):
    def __init__(self, V, W, X, Y, Z):
       self.V = V
       self.W = W
       self.X = X
       self.Y = Y
       self.Z = Z

xbs = [xb()]

My initial thought was to do this through a series of nested if statements but this slows processing considerably and I'm sure my logic is all out of whack.

for xb in xbs:
    if xb.W == xb.W:
          if xb.X == xb.X:
              if xb.Y == xb.Y:
                  sum(xb.Z)

Any suggestions on this would be greatly appreciated!

You can do this using a defaultdict:

from collections import defaultdict
indexed_sums = defaultdict(int)
for o in xbs:
    indexed_sums[(o.W, o.X, o.Y)] += o.Z

For instance, if you start with (using your class definition of xb ):

xbs = [xb(1, 2, 3, 4, 5),
       xb(1, 2, 3, 4, 5),
       xb(1, 2, 3, 4, 5),
       xb(1, 4, 3, 4, 5),
       xb(1, 4, 3, 4, 3),
       xb(1, 2, 3, 9, 3)]

You end up with:

print dict(indexed_sums)
# {(4, 3, 4): 8, (2, 3, 4): 15, (2, 3, 9): 3}

Thus, you could get the sum for W, X, Y being 2, 3, 4 as:

indexed_sums[(2, 3, 4)]
# 15

Note that the defaultdict is doing very little work here (it's just a dictionary of counts that starts at 0 by default): the main thing is that you are indexing the (oW, oX, oY) tuples in a dictionary. You could have done the same thing without defaultdict as:

indexed_sums = {}
for o in xbs:
    if (o.W, o.X, o.Y) not in indexed_sums:
        indexed_sums[(o.W, o.X, o.Y)] = 0
    indexed_sums[(o.W, o.X, o.Y)] += o.Z

The defaultdict is just saving you two lines.

Here's a very dirty hack of a one-liner:

{key:sum(g.Z for g in group)
    for key, group in 
    itertools.groupby(
        sorted(L, key=lambda p:tuple((operator.attrgetter(a)(p) for a in 'VWXYZ'))),
        key=lambda p:tuple(
                      (operator.attrgetter(a)(p) for a in 'VWXYZ')
                     )
    )
}

I don't recommend doing this at all (it's a pain to debug), but I think it's an interesting solution nonetheless

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