熊猫MultiIndex(超过2个级别)DataFrame到嵌套Dict / JSON

时间:2018-06-19 13:31:28

标签: python pandas dictionary multi-index

该问题与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之类的东西,但到目前为止还没有成功。

2 个答案:

答案 0 :(得分:5)

因此,您确实需要在这里做两件事:

  • 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 :(得分:0)

我从上一个答案中得到了这个想法,并对其进行了稍微的修改。

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

这是相同的想法,但是做的略有不同