【问题标题】:Pandas MultiIndex (more than 2 levels) DataFrame to Nested Dict/JSONPandas MultiIndex(超过 2 个级别)DataFrame 到嵌套 Dict/JSON
【发布时间】:2018-11-28 11:57:05
【问题描述】:

这个问题类似于this one,但我想更进一步。是否可以扩展解决方案以使用更多级别?多级数据框的.to_dict() 方法有一些有前途的选项,但它们中的大多数将返回由元组(即(A, 0, 0): 274.0)索引的条目,而不是将它们嵌套在字典中。

作为我想要完成的示例,请考虑这个多索引数据框:

data = {0: {
        ('A', 0, 0): 274.0, 
        ('A', 0, 1): 19.0, 
        ('A', 1, 0): 67.0, 
        ('A', 1, 1): 12.0, 
        ('B', 0, 0): 83.0, 
        ('B', 0, 1): 45.0
    },
    1: {
        ('A', 0, 0): 254.0, 
        ('A', 0, 1): 11.0, 
        ('A', 1, 0): 58.0, 
        ('A', 1, 1): 11.0, 
        ('B', 0, 0): 76.0, 
        ('B', 0, 1): 56.0
    }   
}
df = pd.DataFrame(data).T
df.index = ['entry1', 'entry2']
df
# output:

         A                              B
         0              1               0
         0      1       0       1       0       1
entry1   274.0  19.0    67.0    12.0    83.0    45.0
entry2   254.0  11.0    58.0    11.0    76.0    56.0

您可以想象我们这里有很多记录,而不仅仅是两条,而且索引名称可能是更长的字符串。你怎么能把它变成像这样的嵌套字典(或直接变成 JSON):

[
 {'entry1': {'A': {0: {0: 274.0, 1: 19.0}, 1: {0: 67.0, 1: 12.0}},
  'B': {0: {0: 83.0, 1: 45.0}}},
 'entry2': {'A': {0: {0: 254.0, 1: 11.0}, 1: {0: 58.0, 1: 11.0}},
  'B': {0: {0: 76.0, 1: 56.0}}}}
]

我认为一些递归可能会有所帮助,可能类似于 this,但到目前为止还没有成功。

【问题讨论】:

    标签: python pandas dictionary multi-index


    【解决方案1】:

    所以,你真的需要在这里做两件事:

    • df.to_dict()
    • 将此转换为嵌套字典。

    df.to_dict(orient='index') 给你一个以索引为键的字典;它看起来像这样:

    >>> df.to_dict(orient='index')
    {'entry1': {('A', 0, 0): 274.0,
      ('A', 0, 1): 19.0,
      ('A', 1, 0): 67.0,
      ('A', 1, 1): 12.0,
      ('B', 0, 0): 83.0,
      ('B', 0, 1): 45.0},
     'entry2': {('A', 0, 0): 254.0,
      ('A', 0, 1): 11.0,
      ('A', 1, 0): 58.0,
      ('A', 1, 1): 11.0,
      ('B', 0, 0): 76.0,
      ('B', 0, 1): 56.0}}
    

    现在你需要嵌套它。这是一个技巧from Martijn Pieters 做到这一点:

    def nest(d: dict) -> dict:
        result = {}
        for key, value in d.items():
            target = result
            for k in key[:-1]:  # traverse all keys but the last
                target = target.setdefault(k, {})
            target[key[-1]] = value
        return result
    

    把这一切放在一起:

    def df_to_nested_dict(df: pd.DataFrame) -> dict:
        d = df.to_dict(orient='index')
        return {k: nest(v) for k, v in d.items()}
    

    输出:

    >>> df_to_nested_dict(df)
    {'entry1': {'A': {0: {0: 274.0, 1: 19.0}, 1: {0: 67.0, 1: 12.0}},
      'B': {0: {0: 83.0, 1: 45.0}}},
     'entry2': {'A': {0: {0: 254.0, 1: 11.0}, 1: {0: 58.0, 1: 11.0}},
      'B': {0: {0: 76.0, 1: 56.0}}}}
    

    【讨论】:

    • 使用类型提示你会得到 +1 :)
    • 很好,很干净的答案!不幸的是,Pandas 没有一种不涉及手动迭代每一行的方法
    【解决方案2】:

    我从上一个答案中汲取了想法并稍微修改了它。

    1) 从stackoverflow 获取函数nested_dict,创建字典

    from collections import defaultdict
    def nested_dict(n, type):
        if n == 1:
            return defaultdict(type)
        else:
            return defaultdict(lambda: nested_dict(n-1, type))
    

    2 写了如下函数:

    def df_to_nested_dict(self, df, type):
    
        # Get the number of levels
        temp = df.index.names
        lvl = len(temp)
    
        # Create the target dictionary
        new_nested_dict=nested_dict(lvl, type)
        # Convert the dataframe to a dictionary
        temp_dict = df.to_dict(orient='index')
        for x, y in temp_dict.items():
            dict_keys = ''
            # Process the individual items from the key
            for item in x:
                dkey = '[%d]' % item
                dict_keys = dict_keys + dkey
    
            # Create a string and execute it
            dict_update = 'new_nested_dict%s = y' % dict_keys
            exec(dict_update)
    
        return new_nested_dict
    

    思路相同,但做起来略有不同

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-03
      • 2019-02-16
      • 1970-01-01
      • 2020-12-26
      • 2019-07-26
      • 1970-01-01
      • 2017-10-16
      • 2018-06-24
      相关资源
      最近更新 更多