Pandas根据其他列向列multiindex添加新的第二级列

时间:2017-05-08 15:58:57

标签: pandas dataframe multi-index

我有一个包含列多索引的DataFrame:

System   A                B
Trial    Exp1    Exp2     Exp1    Exp2
1        NaN     1        2       3
2        4       5        NaN     NaN
3        6       NaN      7       8

每个系统(A, B)和每个衡量指标(1, 2, 3都在索引中),Exp1的结果总是优于Exp2。所以我想为每个系统生成第3列,称之为Final,只要可用就应该Exp1,否则默认为Exp2。期望的结果是

System   A                       B
Trial    Exp1    Exp2    Final   Exp1    Exp2    Final
1        NaN     1       1       2       3       2
2        4       5       4       NaN     NaN     NaN
3        6       NaN     6       7       8       7

这样做的最佳方式是什么?

我尝试在列上使用groupby

grp = df.groupby(level=0, axis=1)

并考虑使用transformapply组合assign来实现它。但我无法找到工作或有效的方法。具体来说,出于效率原因,我避免使用本机python for循环(否则问题很简单)。

3 个答案:

答案 0 :(得分:2)

  • stack,您的第一级列索引stack(0)在列索引中留下['Exp1', 'Exp2']
  • 使用lambda函数,该函数会在assign调用中应用于整个数据框。
  • 最后,unstackswaplevelsort_index进行清理并将所有内容放在其所属的位置。
f = lambda x: x.Exp1.fillna(x.Exp2)
df.stack(0).assign(Final=f).unstack() \
    .swaplevel(0, 1, 1).sort_index(1)

     A               B           
  Exp1 Exp2 Final Exp1 Exp2 Final
1  NaN  1.0   1.0  2.0  3.0   2.0
2  4.0  5.0   4.0  NaN  NaN   NaN
3  6.0  NaN   6.0  7.0  8.0   7.0

使用xs

的另一个概念
d1 = df.xs('Exp1', 1, 1).fillna(df.xs('Exp2', 1, 1))
d1.columns = [d1.columns, ['Final'] * len(d1.columns)]
pd.concat([df, d1], axis=1).sort_index(1)


     A               B           
  Exp1 Exp2 Final Exp1 Exp2 Final
1  NaN  1.0   1.0  2.0  3.0   2.0
2  4.0  5.0   4.0  NaN  NaN   NaN
3  6.0  NaN   6.0  7.0  8.0   7.0

答案 1 :(得分:2)

使用stack进行重塑,使用fillna添加列,然后使用unstack + swaplevel重新整形sort_index

df = df.stack(level=0)
df['Final'] = df['Exp1'].fillna(df['Exp1'])
df = df.unstack().swaplevel(0,1,axis=1).sort_index(axis=1)
print (df)
System    A               B           
Trial  Exp1 Exp2 Final Exp1 Exp2 Final
1       NaN  1.0   NaN  2.0  3.0   2.0
2       4.0  5.0   4.0  NaN  NaN   NaN
3       6.0  NaN   6.0  7.0  8.0   7.0

xs用于选择DataFrames的另一个解决方案,combine_first创建新DataFrame,但缺少第二级 - 由MultiIndex.from_product添加concat两个DataFrames

a = df.xs('Exp1', axis=1, level=1)
b = df.xs('Exp2', axis=1, level=1)
df1 =  a.combine_first(b)
df1.columns = pd.MultiIndex.from_product([df1.columns, ['Final']])
df = pd.concat([df, df1], axis=1).sort_index(axis=1)
print (df)
System    A               B           
Trial  Exp1 Exp2 Final Exp1 Exp2 Final
1       NaN  1.0   1.0  2.0  3.0   2.0
2       4.0  5.0   4.0  NaN  NaN   NaN
3       6.0  NaN   6.0  7.0  8.0   7.0

rename类似的解决方案:

a = df.xs('Exp1', axis=1, level=1, drop_level=False)
b = df.xs('Exp2', axis=1, level=1, drop_level=False)
df1 = a.rename(columns={'Exp1':'Final'}).combine_first(b.rename(columns={'Exp2':'Final'}))
df = pd.concat([df, df1], axis=1).sort_index(axis=1)
print (df)
System    A               B           
Trial  Exp1 Exp2 Final Exp1 Exp2 Final
1       NaN  1.0   1.0  2.0  3.0   2.0
2       4.0  5.0   4.0  NaN  NaN   NaN
3       6.0  NaN   6.0  7.0  8.0   7.0

答案 2 :(得分:0)

并不觉得超级优秀,但试试这个:

for system in df.columns.levels[0]:
    df[(system, 'final')] = df[(system, 'Exp1')].fillna(df[(system, 'Exp2')])