展开 DataFrame 的索引级别

a_g*_*est 9 python pandas

我有一个带有多索引的数据框,其中一个级别的值代表该级别的所有其他值。例如(下面的代码示例):

         D
A B   C   
x a   y  0
  b   y  1
  all z  2
Run Code Online (Sandbox Code Playgroud)

all是表示该级别的所有其他值的简写,以便数据框实际表示:

       D
A B C   
x a y  0
  b y  1
  a z  2
  b z  2
Run Code Online (Sandbox Code Playgroud)

这也是我想获得的形式。对于包含all在该索引级别中的每一行,该行针对索引级别中的每个其他值进行复制。如果它是一列,我可以用all其他值的列表替换每次出现的,然后使用DataFrame.explode.

因此,我考虑重置该索引级别,用all其他值列表替换所有出现的,然后explode将该列替换为索引:

level_values = sorted(set(df.index.unique('B')) - {'all'})
tmp = df.reset_index('B')
mask = df.index.get_level_values('B') == 'all'
col_index = list(tmp.columns).index('B')
for i in np.argwhere(mask).ravel():
    tmp.iat[i, col_index] = level_values
result = tmp.explode('B').set_index('B', append=True)
Run Code Online (Sandbox Code Playgroud)

然而,这似乎效率很低,代码也不是很清楚。此外,索引级别现在的顺序错误(我的实际数据框有三个以上的索引级别,因此我无法对其swaplevel进行重新排序)。

所以我想知道是否有更简洁的方法来分解这些all值?


生成样本数据帧的代码:

import numpy as np
import pandas as pd

df = pd.DataFrame(
    data=[[0], [1], [2]],
    index=pd.MultiIndex.from_arrays(
        [['x', 'x', 'x'], ['a', 'b', 'all'], ['y', 'y', 'z']],
        names=['A', 'B', 'C']
    ),
    columns=['D']
)

expected = pd.DataFrame(
    data=[[0], [1], [2], [2]],
    index=pd.MultiIndex.from_arrays(
        [['x', 'x', 'x', 'x'], ['a', 'b', 'a', 'b'], ['y', 'y', 'z', 'z']],
        names=['A', 'B', 'C']
    ),
    columns=['D']
)
Run Code Online (Sandbox Code Playgroud)

And*_*ely 1

def fn(x):
    l, rv = [], []
    for v in x:
        if v == 'all':
            rv.append(l[:])
            l = []
        else:
            l.append(v)
            rv.append(v)
    return rv


df2 = pd.DataFrame(zip(*df.index)).T.assign(D=df['D'].values)
df2 = df2.apply(fn).explode(1).rename(columns={0:'A', 1:'B', 2:'C'}).set_index(keys=['A', 'B', 'C'])

print(df2)
Run Code Online (Sandbox Code Playgroud)

印刷:

       D
A B C   
x a y  0
  b y  1
  a z  2
  b z  2
Run Code Online (Sandbox Code Playgroud)