我有一个C类型的对象列表,其中类型C由属性X,Y,Z组成,例如c.X,c.Y,c.Z
现在我想执行以下任务:
什么是最简洁的方式?
答案 0 :(得分:8)
from collections import defaultdict
totals = defaultdict(int)
for c in cs:
totals[c.Y] += c.Z
tuples = totals.items()
答案 1 :(得分:8)
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
值不是hashable,那么这确实有效,而defaultdict
方法会抛出TypeError
。
但请注意 - 在最近的Pythons中,如果那里有任何复杂的数字,它可能会提升TypeError
,也许在其他情况下。有可能使用适当的key
函数进行此工作 - key=lambda e: (e.real, e.imag) if isinstance(e, complex) else e
似乎适用于我现在尝试过的任何操作,当然,自定义类会覆盖{{1 }}运算符引发异常仍然没有去。也许你可以定义一个更复杂的关键函数来测试它,等等。
当然,我们所关心的只是相同的东西是彼此相邻的,而不是它实际上是排序的,你可以写一个O(n ^ 2)函数来做到这一点而不是排序如果你这样做期望。或者是O(num_hashable + num_nonhashable ^ 2)的函数。或者你可以编写一个O(n ^ 2)/ O(num_hashable + num_nonhashable ^ 2)版本的__lt__
来完成这两个版本。
sblom's answer适用于可散列groupby
属性,具有最小的额外空间(因为它直接计算总和)。
philhag's answer与sblom基本相同,但通过列出每个c.Y
的列表来使用更多辅助内存 - 有效地执行c
所做的事情,但是使用散列而不是假设它已经排序并使用实际列表而不是迭代器。
因此,如果你知道你的groupby
属性是可以清除的并且只需要总和,那么使用sblom's;如果你知道它是可以清洗的,但是希望它们也能用其他东西分组,那就用philhag了;如果它们可能不是可以清洗的,请使用这个(如果它们可能很复杂或者自定义类型覆盖c.Y
,则需要额外担心)。
答案 2 :(得分: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()])
答案 3 :(得分:3)
使用pandas
可能类似于:
df.groupby('Y')['Z'].sum()
>>> import pandas
>>> df = pandas.DataFrame(dict(X=[1,2,3], Y=[1,-1,1], Z=[3,4,5]))
>>> df
X Y Z
0 1 1 3
1 2 -1 4
2 3 1 5
>>> df.groupby('Y')['Z'].sum()
Y
-1 4
1 8
>>>
答案 4 :(得分:0)
您可以使用Counter
from collections import Counter
cnt = Counter()
for c in cs:
cnt[c.Y] += c.Z
print cnt
答案 5 :(得分:0)
尝试下面的运行示例:
import collections
class Product():
def __init__(self, name, amount, group = None):
self.name = name
self.amount = amount
self.group = group
def __repr__(self):
return "%s%r" % (self.__class__, self.__dict__)
def __str__(self):
return "%r" % self.__dict__
def print_amount_by_group():
product_list = [Product('p1', 11, 'GRP3'), Product('p3', 11, 'GRP2'),
Product('p2', 11, 'GRP2'), Product('p25', 11)]
prod_dict = collections.defaultdict(list)
for c in product_list:
prod_dict[c.group].append(c)
print([(key, sum(p.amount for p in product)) for key, product in
prod_dict.items()])
print_ammount_by_group()
结果: [('GRP3',11),('GRP2',22),(None,11)]