假设你有一本这样的字典:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
你会如何把它平摊成这样:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
假设你有一本这样的字典:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
你会如何把它平摊成这样:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
当前回答
或者如果你已经在使用pandas,你可以像这样使用json_normalize():
import pandas as pd
d = {'a': 1,
'c': {'a': 2, 'b': {'x': 5, 'y' : 10}},
'd': [1, 2, 3]}
df = pd.json_normalize(d, sep='_')
print(df.to_dict(orient='records')[0])
输出:
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'c_b_y': 10, 'd': [1, 2, 3]}
其他回答
这里有一个优雅的、就地替换的算法。使用Python 2.7和Python 3.5进行测试。使用点字符作为分隔符。
def flatten_json(json):
if type(json) == dict:
for k, v in list(json.items()):
if type(v) == dict:
flatten_json(v)
json.pop(k)
for k2, v2 in v.items():
json[k+"."+k2] = v2
例子:
d = {'a': {'b': 'c'}}
flatten_json(d)
print(d)
unflatten_json(d)
print(d)
输出:
{'a.b': 'c'}
{'a': {'b': 'c'}}
我在这里发布了这段代码以及匹配的unflat_json函数。
如果你使用pandas,有一个函数隐藏在pandas.io.json中。_normalize1调用nested_to_record来完成这个操作。
from pandas.io.json._normalize import nested_to_record
flat = nested_to_record(my_dict, sep='_')
1在熊猫0.24版本。X及以上版本使用panda .io.json.normalize(不带_)
利用递归,保持简单和人类可读:
def flatten_dict(dictionary, accumulator=None, parent_key=None, separator="."):
if accumulator is None:
accumulator = {}
for k, v in dictionary.items():
k = f"{parent_key}{separator}{k}" if parent_key else k
if isinstance(v, dict):
flatten_dict(dictionary=v, accumulator=accumulator, parent_key=k)
continue
accumulator[k] = v
return accumulator
调用很简单:
new_dict = flatten_dict(dictionary)
or
new_dict = flatten_dict(dictionary, separator="_")
如果我们想改变默认分隔符。
稍微分解一下:
当函数第一次被调用时,它只被调用传递我们想要扁平化的字典。这里的累加器参数支持递归,稍后我们将看到。因此,我们将accumulator实例化到一个空字典中,我们将在其中放入原始字典中的所有嵌套值。
if accumulator is None:
accumulator = {}
当我们遍历字典的值时,我们为每个值构造一个键。对于第一次调用,parent_key参数将为None,而对于每个嵌套字典,它将包含指向它的键,因此我们将该键前置。
k = f"{parent_key}{separator}{k}" if parent_key else k
如果键k指向的值v是一个字典,函数调用自身,传递嵌套的字典、累加器(通过引用传递,因此对它的所有更改都是在同一个实例上完成的)和键k,这样我们就可以构造连接键。注意continue语句。我们想要跳过if语句块之外的下一行,这样嵌套的字典就不会在键k下的累加器中结束。
if isinstance(v, dict):
flatten_dict(dict=v, accumulator=accumulator, parent_key=k)
continue
那么,如果值v不是字典,我们该怎么办呢?把它原封不动地放在累加器里。
accumulator[k] = v
一旦完成,我们只返回累加器,原始的字典参数保持不变。
NOTE
这只适用于有字符串作为键的字典。它将与实现__repr__方法的哈希对象一起工作,但将产生不想要的结果。
这一变化扁平化嵌套字典,压缩键与max_level和自定义减速器。
def flatten(d, max_level=None, reducer='tuple'):
if reducer == 'tuple':
reducer_seed = tuple()
reducer_func = lambda x, y: (*x, y)
else:
raise ValueError(f'Unknown reducer: {reducer}')
def impl(d, pref, level):
return reduce(
lambda new_d, kv:
(max_level is None or level < max_level)
and isinstance(kv[1], dict)
and {**new_d, **impl(kv[1], reducer_func(pref, kv[0]), level + 1)}
or {**new_d, reducer_func(pref, kv[0]): kv[1]},
d.items(),
{}
)
return impl(d, reducer_seed, 0)
实际上,我最近写了一个名为cherrypicker的包来处理这种确切的事情,因为我必须经常这样做!
我认为下面的代码会给你你想要的东西:
from cherrypicker import CherryPicker
dct = {
'a': 1,
'c': {
'a': 2,
'b': {
'x': 5,
'y' : 10
}
},
'd': [1, 2, 3]
}
picker = CherryPicker(dct)
picker.flatten().get()
您可以使用以下方法安装软件包:
pip install cherrypicker
...在https://cherrypicker.readthedocs.io上有更多的文档和指导。
其他方法可能更快,但这个包的优先级是使这些任务变得容易。如果你确实有一个很大的对象列表要扁平化,你也可以告诉CherryPicker使用并行处理来加快速度。