Python 3:扁平化嵌套字典和字典中的列表

Posted

技术标签:

【中文标题】Python 3:扁平化嵌套字典和字典中的列表【英文标题】:Python 3: Flattening nested dictionaries and lists within dictionaries 【发布时间】:2019-02-04 11:38:07 【问题描述】:

我正在处理一个复杂的嵌套字典和列表数据结构。我需要展平数据并将所有嵌套项置于 0 级。请参阅下面的示例以获得更清晰的信息:

a:1,b:2,c:c1:[c11:1,c12:2,c13:3,c21:1,c22:2,c23:3],d1:[d11:1,d12:2,d13:3,d21:1,d22:2,d23:3],x:1,y:2

我需要将其展平为:

a:1,b:2,c_c1_c11:1, c_c1_c12:2,c_c1_c13:3,c_c1_c21:1,c_c1_c22:2,c_c1_c23:3, c_d1,d11:1...and so on

我参考了this post 中的第一个答案,但它只有在我有嵌套字典时才能工作,而不是当列表嵌套在字典中并且更多字典嵌套在这些列表中时。

我稍微修改了代码以适应我的用例,但是这段代码不起作用

def flattenDict(d):
node_map = 
node_path = []
def nodeRecursiveMap(d, node_path):
    for key, val in d.items():
        if ((type(val) is not dict)&(type(val) is not list)): 
            node_map['_'.join(node_path + [key])] = val
        if type(val) is list:
            def nodeListRecursion(val,node_path):
                for element in val:
                    if ((type(element) is not dict)&(type(element) is not list)) : node_map['_'.join(node_path + [key])] = element
                    if type(element) is list: nodeListRecursion(element,node_map)
                    if type(element) is dict: nodeRecursiveMap(element, node_path + [key])
            nodeListRecursion(val,node_path)
        if type(val) is dict: nodeRecursiveMap(val, node_path + [key])
nodeRecursiveMap(d, node_path)
return node_map

当我将代码粘贴到此处时,缩进变得混乱。但我真的很感激这里的任何帮助。

【问题讨论】:

【参考方案1】:

我认为你把事情复杂化了。你从一个字典开始,有键和值。它的值要么是字典,要么是你想要递归的字典列表,或者它们不是,在这种情况下你想不理会它。所以:

def flatten(d):
    out = 
    for key, val in d.items():
        if isinstance(val, dict):
            val = [val]
        if isinstance(val, list):
            for subdict in val:
                deeper = flatten(subdict).items()
                out.update(key + '_' + key2: val2 for key2, val2 in deeper)
        else:
            out[key] = val
    return out

给我

In [34]: nested = 'a': 1, 'b': 2, 'c': 'c1': ['c11': 1, 'c12': 2, 'c13': 3, 'c21': 1, 'c22': 2, 'c23': 3], 'd1': ['d11': 1, 'd12': 2, 'd13': 3, 'd21': 1, 'd22': 2, 'd23': 3], 'x': 1, 'y': 2

In [35]: flatten(nested)
Out[35]: 
'a': 1,
 'b': 2,
 'c_c1_c11': 1,
 'c_c1_c12': 2,
 'c_c1_c13': 3,
 'c_c1_c21': 1,
 'c_c1_c22': 2,
 'c_c1_c23': 3,
 'c_d1_d11': 1,
 'c_d1_d12': 2,
 'c_d1_d13': 3,
 'c_d1_d21': 1,
 'c_d1_d22': 2,
 'c_d1_d23': 3,
 'x': 1,
 'y': 2

【讨论】:

赞成。非常聪明地使用val = [val] 以与列表值相同的方式处理 dict 值。 谢谢,这行得通。我刚刚意识到,在我的实际数据中,键在列表中的不同子项中重复。因此,最终的展平输出只有最后一个 subdict(覆盖它上面的那些)。 不是一个好的解决方案。例如对于 nested = 'asd': ['a': 'hi', 'a': 'hi2'] 你将失去值 'hi'。并且对于非字典列表崩溃,例如:nested = 'asd' : ['a', 'b' ] @DanielBraun:你说得对,对于形状与 OP 不同的输入,你需要一个不同的解决方案。这……并不意外。 @DSM 我认为唯一的字段命名是传达想法的操作方式,而不是输入的已知属性【参考方案2】:

在我的项目中,我使用来自DSMs answer 的更新版本的函数来展平可能包含其他字典或列表或字典列表的字典。我希望它会有所帮助。

def flatten(input_dict, separator='_', prefix=''):
    output_dict = 
    for key, value in input_dict.items():
        if isinstance(value, dict) and value:
            deeper = flatten(value, separator, prefix+key+separator)
            output_dict.update(key2: val2 for key2, val2 in deeper.items())
        elif isinstance(value, list) and value:
            for index, sublist in enumerate(value, start=1):
                if isinstance(sublist, dict) and sublist:
                    deeper = flatten(sublist, separator, prefix+key+separator+str(index)+separator)
                    output_dict.update(key2: val2 for key2, val2 in deeper.items())
                else:
                    output_dict[prefix+key+separator+str(index)] = value
        else:
            output_dict[prefix+key] = value
    return output_dict

【讨论】:

以上是关于Python 3:扁平化嵌套字典和字典中的列表的主要内容,如果未能解决你的问题,请参考以下文章

从扁平字典创建嵌套字典

Pandas DataFrame 中的嵌套字典列表

Python [练习题] :字典扁平化

Python [习题] 字典扁平化

用嵌套列表和嵌套字典列表展平一个非常大的 Json

我怎样才能扁平化一个字典列表?