在Python中按相同属性对对象列表进行分组和求和的最简洁方法是什么
我有一个包含C类型对象的列表,C类型的对象有一些属性,比如X、Y、Z,像这样:c.X、c.Y、c.Z。
现在我想做以下几件事:
- 对那些Y属性值相同的对象的Z属性进行求和。
- 输出一个包含元组(Y, Z的总和)的列表。
有没有什么简单的方法可以做到这一点?
6 个回答
你可以使用 collections.defaultdict
这个工具来根据 y 值对列表进行分组,然后对它们的 z 值进行求和:
import collections
ymap = collections.defaultdict(list)
for c in listOfCs:
ymap[c.Y].append(c)
print ([(y, sum(c.Z for c in clist)) for y,clist in ymap.values()])
在编程中,有时候我们会遇到一些问题,比如代码运行不正常或者出现错误。这种情况下,我们需要找到问题的根源。通常,我们可以通过查看错误信息或者调试工具来帮助我们理解发生了什么。
调试工具就像是一个侦探,可以帮助我们追踪代码的执行过程,找出哪里出了问题。我们可以设置一些“断点”,让程序在特定的地方暂停,这样我们就可以检查那时的变量值和程序状态。
另外,了解代码的逻辑结构也很重要。我们需要清楚每一部分代码的作用,以及它们是如何相互连接的。这样一来,当出现问题时,我们就能更快地定位到错误的地方。
总之,解决编程问题需要耐心和细致的观察,利用好工具和理解代码的逻辑,才能更有效地找到并修复错误。
from collections import defaultdict
totals = defaultdict(int)
for c in cs:
totals[c.Y] += c.Z
tuples = totals.items()
使用 defaultdict
的方法可能更好,前提是 c.Y
是可以哈希的,但这里还有另一种方法:
from itertools import groupby
from operator import attrgetter
get_y = attrgetter('Y')
tuples = [(y, sum(c.Z for c in cs_with_y) for y, cs_with_y in
groupby(sorted(cs, key=get_y), get_y)]
为了更具体地说明这些方法的区别:
这种方法需要先对
cs
进行排序复制,这个过程需要 O(n log n) 的时间和 O(n) 的额外空间。或者,你可以使用cs.sort(key=get_y)
来就地排序cs
,这样不需要额外的空间,但会修改原来的列表cs
。需要注意的是,groupby
返回的是一个迭代器,所以在这方面没有额外的开销。不过,如果c.Y
的值不是 可哈希 的话,这种方法是有效的,而defaultdict
方法会抛出一个TypeError
错误。但是要小心——在最近的 Python 版本中,如果里面有任何复数,都会抛出
TypeError
,可能在其他情况下也会出现这个问题。可以尝试用合适的key
函数来解决这个问题——key=lambda e: (e.real, e.imag) if isinstance(e, complex) else e
目前对我尝试的所有情况都有效,当然,如果自定义类重写了__lt__
操作符并抛出异常,那就不行了。也许你可以定义一个更复杂的key
函数来进行测试,等等。当然,我们在这里关心的只是相等的东西要放在一起,并不一定要真正排序。如果你愿意,可以写一个 O(n^2) 的函数来实现这个,而不是排序。或者写一个 O(num_hashable + num_nonhashable^2) 的函数。又或者你可以写一个 O(n^2) / O(num_hashable + num_nonhashable^2) 的
groupby
版本,把这两者结合起来。sblom 的回答 适用于可哈希的
c.Y
属性,额外空间需求很小(因为它直接计算总和)。philhag 的回答 基本上和 sblom 的相同,但使用了更多的辅助内存,因为它为每个
c
创建了一个列表——实际上是在做groupby
的工作,但使用哈希而不是假设它是排序的,并且使用实际的列表而不是迭代器。
所以,如果你知道你的 c.Y
属性是可哈希的并且只需要总和,使用 sblom 的方法;如果你知道它是可哈希的但还想把它们分组用于其他目的,使用 philhag 的方法;如果它们可能不是可哈希的,使用这个方法(如果它们可能是复数或重写了 __lt__
的自定义类型,还需要额外注意)。