具有 MultiIndex 的 DataFrame 到嵌套字典

问题描述 投票:0回答:2

我有一个带有多索引的数据框。

             01.01  02.01  03.01  04.01
bar total1     40     52     18     11
    total2     36     85      5     92
baz total1     23     39     45     70
    total2     50     49     51     65
foo total1     23     97     17     97
    total2     64     56     94     45
qux total1     13     73     38      4
    total2     80      8     61     50

df.index.values
结果:

array([('bar', 'total1'), ('bar', 'total2'), ('baz', 'total1'),
       ('baz', 'total2'), ('foo', 'total1'), ('foo', 'total2'),
       ('qux', 'total1'), ('qux', 'total2')], dtype=object)

我最终希望将数据帧转换为字典字典,以便第一个字典键是

['bar','baz', 'foo','qux']
之一,值是日期,内部字典由“total1”和“totals2”作为键和值组成是 df 的整数。 另一种解释是,例如,如果
dict1
是字典,那么调用
dict1['bar']
将导致输出:

{'bar':{'01.01':{'total1':40,'total2':36},'02.01':{'total1':52,'total2':85},'03.01':{'total1':18,'total2':5},'04.01':{'total1':11,'total2':92} } }

为了实现这一目标,我需要如何改变以及改变什么?这是索引问题吗?

python pandas dataframe dictionary group-by
2个回答
35
投票

将整个数据帧转换为字典尝试:

df.groupby(level=0).apply(lambda df: df.xs(df.name).to_dict()).to_dict()

{'bar': {'01.01': {'total1': 40, 'total2': 36},
  '02.01': {'total1': 52, 'total2': 85},
  '03.01': {'total1': 18, 'total2': 5},
  '04.01': {'total1': 11, 'total2': 92}},
 'baz': {'01.01': {'total1': 23, 'total2': 50},
  '02.01': {'total1': 39, 'total2': 49},
  '03.01': {'total1': 45, 'total2': 51},
  '04.01': {'total1': 70, 'total2': 65}},
 'foo': {'01.01': {'total1': 23, 'total2': 64},
  '02.01': {'total1': 97, 'total2': 56},
  '03.01': {'total1': 17, 'total2': 94},
  '04.01': {'total1': 97, 'total2': 45}},
 'qux': {'01.01': {'total1': 13, 'total2': 80},
  '02.01': {'total1': 73, 'total2': 8},
  '03.01': {'total1': 38, 'total2': 61},
  '04.01': {'total1': 4, 'total2': 50}}}

要转换某一特定列,请在将其转换为字典之前进行选择,即

df.groupby(level=0).apply(lambda df: df.xs(df.name)[colname].to_dict()).to_dict()

0
投票
如果组数很少,

piRSquared 的答案很好,但如果组很多,则速度非常慢,因为

groupby.apply
必须多次调用 Python 函数。1

如果有很多组,将数据帧转换为嵌套字典的更快方法是使用

itertuples
简单地循环数据帧并动态构建字典。因为我们需要一个嵌套字典,所以
dict.setdefault()
collections.defaultdict
会很有帮助。

dict2 = {}
for (idx1, idx2), *vals in df.itertuples():
    for col, val in zip(df.columns, vals):
        dict2.setdefault(idx1, {}).setdefault(col, {})[idx2] = val

dict.setdefault()
这里帮助我们插入一个带有空字典的键作为值(如果它不存在)。它产生与以下更详细的代码相同的结果。

dict2 = {}
for (idx1, idx2), *vals in df.itertuples():
    for col, val in zip(df.columns, vals):
        if idx1 in dict2:
            if col in dict2[idx1]:
                dict2[idx1][col][idx2] = val
            else:
                dict2[idx1][col] = {idx2: val}
        else:
            dict2[idx1] = {col: {idx2: val}}

1 支持上述主张的基准(在 Python 3.11.5、pandas 2.2.0 上测试):

idx = pd.MultiIndex.from_product((range(1000), range(10)))
df = pd.DataFrame({c: range(10000) for c in 'ABCD'}, index=idx)

%%timeit
dict1 = df.groupby(level=0).apply(lambda df: df.xs(df.name).to_dict()).to_dict()
# 849 ms ± 11.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
dict2 = {}
for (idx1, idx2), *vals in df.itertuples():
    for col, val in zip(df.columns, vals):
        dict2.setdefault(idx1, {}).setdefault(col, {})[idx2] = val
# 50.3 ms ± 4.59 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

从上面的基准测试中可以看出,如果有 1000 个组,那么直接循环比

groupby.apply
解决方案快 16 倍。随着团体数量的增加,这种差距会扩大。

另一方面,如果组数很少(<10) then both approaches are very fast so performance probably isn't an issue anymore.

关于这一点的更多解释可以在将 Pandas Dataframe 转换为嵌套 JSON 找到。

© www.soinside.com 2019 - 2024. All rights reserved.