我目前有这样的字典(假设许多国家,州和城市):
'USA': {
'Texas': {
'Austin': {
'2017-01-01': 169,
'2017-02-01': 231
},
'Houston': {
'2017-01-01': 265,
'2017-02-01': 310
}
}
我想创建一个新的字典"按"分组只有国家和日期,过滤给定的州,所以结果将是:
'USA': {
'2017-01-01': 434,
'2017-02-01': 541
}
我可以通过循环遍历dict的每一层来做到这一点,但它很难阅读。有没有办法用lambda / map函数来做呢?
另外,由于其他原因,我们无法使用pandas数据帧,因此我无法使用该groupby功能。
答案 0 :(得分:1)
如果您只想提取嵌套字典的最低级别值,可以使用生成器来实现。
以下生成器是由@Richard编写的稍微修改过的版本。
然后,您可以将其与collections.defaultdict
结合使用,以获得所需的结果。
from collections import defaultdict
def NestedDictValues(d):
for k, v in d.items():
if isinstance(v, dict):
yield from NestedDictValues(v)
else:
yield (k, v)
def sumvals(lst):
c = defaultdict(int)
for i, j in lst:
c[i] += j
return dict(c)
d = {'USA': sumvals(NestedDictValues(s))}
# {'USA': {'2017-01-01': 434, '2017-02-01': 541}}
答案 1 :(得分:0)
我相信在这种情况下,使用递归比使用map
或reduce
函数要清晰得多:
import re
import itertools
s = {'USA': {
'Texas': {
'Austin': {
'2017-01-01': 169,
'2017-02-01': 231
},
'Houston': {
'2017-01-01': 265,
'2017-02-01': 310
}
}
}
}
def get_dates(d):
val = [(a, b) if isinstance(b, int) and re.findall('\d+-\d+-\d+', a) else get_dates(b) for a, b in d.items()]
return [i for b in val for i in b] if not all(isinstance(i, tuple) for i in val) else val
last_data = {a:{c:sum(g for _, g in h) for c, h in itertools.groupby(sorted(get_dates(b), key=lambda x:x[0]), key=lambda x:x[0])} for a, b in s.items()}
输出:
{'USA': {'2017-02-01': 541, '2017-01-01': 434}}
答案 2 :(得分:0)
以下是使用collections.Counter
简化现有代码的方法。假设您的源词典名为d
:
from collections import Counter
my_state='Texas'
mapped = {
country: [Counter(d[country][my_state][city]) for city in d[country][my_state]]
for country in d
}
print(mapped)
#{'USA': [Counter({'2017-01-01': 265, '2017-02-01': 310}),
# Counter({'2017-01-01': 169, '2017-02-01': 231})]}
这会将原始字典映射到{country: list_of_counters}
形式之一。
现在您可以使用operator.add()
来缩小此列表:
from operator import add
for country in mapped:
print("{country}: {sums}".format(country=country, sums=reduce(add, mapped[country])))
#USA: Counter({'2017-02-01': 541, '2017-01-01': 434})
或map/reduce
:
map(lambda country: {country: reduce(add, mapped[country])}, mapped)
[{'USA': Counter({'2017-01-01': 434, '2017-02-01': 541})}]
如果您希望dict
代替Counter
s:
map(lambda country: {country: dict(reduce(add, mapped[country]))}, mapped)
#[{'USA': {'2017-01-01': 434, '2017-02-01': 541}}]