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