从复杂的嵌套字典(各种深度)和字典列表创建 DataFrame

Creating a DataFrame from a complex nested dictionary (various depths) and lists of dictionaries

提问人:cobonthecorn 提问时间:8/17/2023 最后编辑:Andrej Keselycobonthecorn 更新时间:8/17/2023 访问量:40

问:

我有一个 API 调用,它使用不同级别的嵌套字典进行响应。对于任何误用的术语或任何事情,我提前道歉,因为虽然我在一般的数据整理方面有很好的经验,但复杂的词典并不是我太熟悉的东西。我正在尝试创建一个数据框,该数据框将每个级别和属性作为列。列“Level0”、“Level1”等的级别值是字典中每个级别的“@name”键的值。属性列名是“@name”键的值,这些列的值在“@value”中。

以下是字典的片段:

dct={'@id': '21',
 '@name': 'Top Level',
 '@hasChildren': 'true',
 'level': [{'@id': '22',
   '@name': 'Asset1',
   '@hasChildren': 'true',
   'level': [{'@id': '530',
     '@name': 'Sub Asset1-1',
     'attributes': {'attribute': [{'@attributeId': '1581',
        '@name': 'Grouping',
        '@value': 'Tall'},
       {'@attributeId': '1141',
        '@name': 'Asset Reporting',
        '@value': 'Public'},
       {'@attributeId': '981',
        '@name': 'Unit',
        '@value': 'Tons'}]}},
    {'@id': '101',
     '@name': 'Asset2',
     '@hasChildren': 'true',
     'level': [{'@id': '123',
       '@name': 'Sub Asset2-1',
       '@hasChildren': 'true',
       'level': [{'@id': '1061',
         '@name': 'Store 2-1-1',
         '@hasChildren': 'true',
         'level': [{'@id': '205',
           '@name': 'Shelf 23',
           'attributes': {'attribute': [{'@attributeId': '581',
              '@name': 'Type',
              '@value': 'Shirt'},
             {'@attributeId': '1161',
              '@name': 'Region',
              '@value': 'USA'},
             {'@attributeId': '1261',
              '@name': 'Area_Grouping',
              '@value': 'East Coast'},
             {'@attributeId': '1581',
              '@name': 'Grouping',
              '@value': 'Short'},
             {'@attributeId': '1141',
              '@name': 'Asset Reporting',
              '@value': 'Consolidated'},
             {'@attributeId': '981',
              '@name': 'Unit',
              '@value': 'Feet'}]}},
          {'@id': '213',
           '@name': 'Shelf 49',
           'attributes': {'attribute': [{'@attributeId': '581',
              '@name': 'Type',
              '@value': 'Pants'},
             {'@attributeId': '1161',
              '@name': 'Region',
              '@value': 'USA'},
             {'@attributeId': '1261',
              '@name': 'Area_Grouping',
              '@value': 'West Coast'},
             {'@attributeId': '1581',
              '@name': 'Grouping',
              '@value': 'Short'},
             {'@attributeId': '1141',
              '@name': 'Asset Reporting',
              '@value': 'Consolidated'},
             {'@attributeId': '981',
              '@name': 'Unit',
              '@value': 'Feet'}]}}
'attributes': {'attribute': {'@attributeId': '841',
   '@name': 'Consolidated Reporting',
   '@value': 'Consolidated'}}}

我试图最终得到一个如下所示的数据框(由于大小格式而截断属性):

Level0    |Level1   |Level2     |Level3     |Level4     
|Grouping|Asset Reporting
Top Level |     |       |       |       |    |       
Top Level |Asset1   |Sub Asset1-1   |       |       |Tall    |Public     
Top Level |Asset2   |Sub Asset2-1   |Store 2-1-1    |Shelf 23   |    |Consolidated   
Top Level |Asset2   |Sub Asset2-1   |Store 2-1-1    |Shelf 49   |Short   |Consolidated   

经过许多不同的努力,我现在无法重新创建,我开始从这里获得帮助(参见 1.6),但我仍然只是得到一个字典,我无法弄清楚如何将其工作到数据帧中。

def get_all_values(obj, level=0):
    """Walk through a dictionary of dicts and lists."""
    if type(obj) is dict:
        for key, value in obj.items():
            if type(value) in [dict, list]:
                print('\t' * level, key, sep='')
                level = level + 1
                get_all_values(value, level)
                level = level - 1
            else:
                print('\t' * (level), key, ': ', value, sep='')
    elif type(obj) is list:
        for i, element in enumerate(obj):
            if type(element) in [dict, list]:
                print('\t' * level, i, sep='')
                level = level + 1
                get_all_values(element, level)
                level = level - 1
            else:
                print('\t' * (level), element, sep='')
    else:
        raise ValueError



我还尝试过使用 .explode() 和 pd 的组合。系列后将其放入数据帧中,但觉得最好在数据帧之前做更多的工作。

python 字典 嵌套

评论


答:

0赞 Tranbi 8/17/2023 #1

示例数据的语法无效。我关闭了括号,但我作为一个孩子......无论如何,这与隐含的逻辑无关。Asset2Asset1

使用递归函数是正确的方法:

import pandas as pd

def get_all_values(obj):
    res = []
    
    def get_level(obj, level=0, r={}):
        if type(obj) is dict:
            if n:=obj.get('@value', obj.get('@name')):
                r[f'Level{level}'] = n
            if l:=obj.get('level', obj.get('attributes', obj.get('attribute'))):
                r.update(get_level(l, level+1, r.copy()))
        elif type(obj) is list:
            bottom = False
            for element in obj:
                if isinstance(element, dict):
                    if '@value' in element:
                        r[element['@name']] = element['@value']
                        bottom = True
                    else:
                        r[f'Level{level}'] = element.get('@name')
                        r.update(get_level(element, level, r.copy()))
            if bottom:
                res.append(r)
        return r

    get_level(obj)
    return res

print(pd.DataFrame(get_all_values(dct)))

输出:

      Level0  Level1        Level2 Grouping Asset Reporting  Unit        Level3       Level4    Level5   Type Region Area_Grouping
0  Top Level  Asset1  Sub Asset1-1     Tall          Public  Tons           NaN          NaN       NaN    NaN    NaN           NaN       
1  Top Level  Asset1        Asset2    Short    Consolidated  Feet  Sub Asset2-1  Store 2-1-1  Shelf 23  Shirt    USA    East Coast       
2  Top Level  Asset1        Asset2    Short    Consolidated  Feet  Sub Asset2-1  Store 2-1-1  Shelf 49  Pants    USA    West Coast