我的代码库依赖于管理当前在非常嵌套的字典中的数据。例如:
'USA': {
'Texas': {
'Austin': {
'2017-01-01': 169,
'2017-02-01': 231
},
'Houston': {
'2017-01-01': 265,
'2017-02-01': 310
}
}
这适用于多个国家/地区,州/地区,城市和日期。
我在尝试访问值时遇到问题,因为我需要有一个深度嵌套的for循环来迭代每个国家/地区,州,城市和日期以应用某种操作。我正在寻找某种替代方案。
假设嵌套的dict结构相同,是否有这么多循环的替代方案?也许使用map,reduce或lambda?
有没有更好的方法来存储所有这些数据而不使用嵌套的dicts?
答案 0 :(得分:2)
您可以使用Pandas DataFrame
对象(Pandas Dataframe Documentation),它可以以表格格式存储您的数据,类似于电子表格。在这种情况下,您的DataFrame
应该有一列来表示嵌套数据中的每个键(一列用于Country,另一列用于State,依此类推)。
Pandas DataFrames
还根据每列的记录(行)计算过滤,分组和其他有用的操作。假设您要过滤数据以仅返回德克萨斯州在2018-02-01之后发生的行(df
是您的DataFrame
)。这可以通过以下方式实现:
df[df['State'] == 'Texas' & df['Date'] > '2018-02-01']
要构建这些DataFrame
对象,您可以从格式化为记录集合的数据开始:
data = [['USA', 'Texas', 'Austin', '2017-01-01', 169],
['USA', 'Texas', 'Austin', '2017-02-01', 231],
['USA', 'Texas', 'Houston', '2017-01-01', 265],
['USA', 'Texas', 'Houston', '2017-02-01', 310]]
然后像这样构建它们:
df = DataFrame(data, columns=['Country', 'State', 'City', 'Date', 'Value'])
如果DataFrame
个对象不是一个选项,并且您不想使用嵌套循环,您还可以使用带有嵌套谓词和过滤器的列表推导来访问内部数据:
[
d[country][state][city][date]
for country in d.keys()
for state in d[country].keys()
for city in d[country][state].keys()
for date in d[country][state][city].keys()
if country == 'USA' and state == 'Texas' and city == 'Houston'
]
但是,我看不出这种方法在嵌套循环上有太大差异,并且代码可读性存在损失,imho。
使用前面指出的记录集合方法(data
)而不是嵌套结构,您可以使用以下方法过滤行:
[r for r in data if r[2] == 'Houston']
为了提高可读性,您可以使用namedtuple
个对象列表作为记录列表。您的数据将是:
from collections import namedtuple
record = namedtuple('Record', 'country state city date value')
data = [
record('USA', 'Texas', 'Austin', '2017-01-01', 169),
record('USA', 'Texas', 'Austin', '2017-02-01', 231),
record('USA', 'Texas', 'Houston', '2017-01-01', 265),
record('USA', 'Texas', 'Houston', '2017-02-01', 310)
]
并且您的过滤将得到改善,例如:
获取特定记录
[r for r in data if r.city == 'Houston']
返回
[
Record(country='USA', state='Texas', city='Houston', date='2017-01-01', value=265),
Record(country='USA', state='Texas', city='Houston', date='2017-02-01', value=310)
]
仅获取这些特定记录的值
[r.value for r in data if r.city == 'Houston']
返回
[265, 310]
考虑到namedtuple
对象可以轻松存储它们,最后一种方法也可以处理自定义对象实例。
答案 1 :(得分:0)
您可以创建一个类,实现重载方法,并使用递归:
d = {'USA': {
'Texas': {
'Austin': {
'2017-01-01': 169,
'2017-02-01': 231
},
'Houston': {
'2017-01-01': 265,
'2017-02-01': 310
}
}
}
}
class StateData:
def __init__(self, structure):
self.structure = structure
self.levels = {'country':0, 'state':1, 'city':2, 'date':3}
def get_level(self, d, target, current= 0):
total_listing = [((a, b) if target == 3 else a) if current == target else self.get_level(b, target, current + 1) for a, b in d.items()]
return [i for b in total_listing for i in b] if all(isinstance(i, list) for i in total_listing) else total_listing
def __getitem__(self, val):
return self.get_level(self.structure, self.levels[val])
s = StateData(d)
print(s['city'])
print(s['date'])
输出:
['Austin', 'Houston']
[('2017-01-01', 169), ('2017-02-01', 231), ('2017-01-01', 265), ('2017-02-01', 310)]
最好将数据存储为列表列表,这样您就可以根据每个操作的需要进行分组。例如:
state_data = [['USA', 'Texas', 'Austin', '2017-01-01', 169],
['USA', 'Texas', 'Austin', '2017-02-01', 231],
['USA', 'Houston', '2017-01-01', 265],
['USA', 'Houston', '2017-02-01', 310]]
答案 2 :(得分:-2)
还有this。
它可让您将字典拼写成这样的pandas数据框:
from pandas.io.json import json_normalize
d = json.load(f)
# Parent node of dict d is 'programs'
n = json_normalize(d['programs'])