Python 词典列表的收集计数器

Python Collections Counter for a List of Dictionaries

我有一个动态增长的数组列表,我想将类似的值加在一起。这是一个例子:

{"something" : [{"one":"200"}, {"three":"400"}, {"one":"100"}, {"two":"800"} ... ]}

我希望能够将列表中的词典加在一起。因此,在这种情况下,对于键 "something",结果将是:

["one":400, "three": 400, "two": 800]

或类似的东西。我熟悉 Python 的收集计数器,但由于 "something" 列表包含字典,它不会工作(除非我遗漏了什么)。字典也是动态创建的,所以没有字典我无法构建列表。例如:

Counter({'b':3, 'c':4, 'd':5, 'b':2})

通常可以,但是一旦我尝试添加一个元素,之前的值就会被覆盖。我注意到其他问题,例如:

Is there any pythonic way to combine two dicts (adding values for keys that appear in both)?

Python count of items in a dictionary of lists

但同样,列表中的对象是字典。

一种方法如下:

from collections import defaultdict

d = {"something" :
     [{"one":"200"}, {"three":"400"}, {"one":"100"}, {"two":"800"}]}

dd = defaultdict(list)

# first get and group values from the original data structure
# and change strings to ints
for inner_dict in d['something']:
    for k,v in inner_dict.items():
        dd[k].append(int(v))


# second. create output dictionary by summing grouped elemetns
# from the first step.
out_dict =  {k:sum(v) for k,v in dd.items()}

print(out_dict)
# {'two': 800, 'one': 300, 'three': 400}

这里我没有使用counter,而是defaultdict。这是一个两步走的方法。

我认为这符合您的要求,但我不确定,因为我不知道 "The dict is also being dynamically created, so I can't build the list without the dicts" 是什么意思。仍然:

input = {
    "something" : [{"one":"200"}, {"three":"400"}, {"one":"100"}, {"two":"800"}], 
    "foo" : [{"a" : 100, "b" : 200}, {"a" : 300, "b": 400}],
}

def counterize(x):
    return Counter({k : int(v) for k, v in x.iteritems()})

counts = {
    k : sum((counterize(x) for x in v), Counter()) 
    for k, v in input.iteritems()
}

结果:

{
    'foo': Counter({'b': 600, 'a': 400}), 
    'something': Counter({'two': 800, 'three': 400, 'one': 300})
}

我预计将 sumCounter 一起使用是低效的(就像将 sum 与字符串一起使用一样低效以至于 Guido 禁止它),但我可能是错的。无论如何,如果您遇到性能问题,您可以编写一个函数来创建 Counter 并在其上重复调用 +=update

def makeints(x):
    return {k : int(v) for k, v in x.iteritems()}

def total(seq):
    result = Counter()
    for s in seq:
        result.update(s)
    return result

counts = {k : total(makeints(x) for x in v) for k, v in input.iteritems()}