将类似的函数应用于多级pandas数据帧

时间:2016-02-25 22:35:55

标签: python pandas

我有一个以下形式的多级数据框:

df = pd.DataFrame([[1,'A',2,'B',1,'B'],[2,'B',2,'B',2,'A'],[1,'A',1,'A',1,'A'],[1,'B',2,'A',2,'B']],
                  columns=pd.MultiIndex.from_tuples([('S1','Num'),('S1','Let'),('S2','Num'),('S2','Let'),('S3','Num'),('S3','Let')]))

   S1      S2      S3    
  Num Let Num Let Num Let
0   1   A   2   B   1   B
1   2   B   2   B   2   A
2   1   A   1   A   1   A
3   1   B   2   A   2   B

如何创建一个新的数据框,以便选择每个级别的零列,如果设置为=='B',那么它设置Num = 3?基本上我想得到以下数据帧:

   S1      S2      S3    
  Num Let Num Let Num Let
0   1   A   3   B   3   B
1   3   B   3   B   2   A
2   1   A   1   A   1   A
3   3   B   2   A   3   B

1 个答案:

答案 0 :(得分:4)

一种方法是按位置工作并使用ilocwhere

>>> df.iloc[:,0::2] = df.iloc[:,0::2].where((df.iloc[:,1::2]!="B").values, 3)
>>> df
   S1      S2      S3    
  Num Let Num Let Num Let
0   1   A   3   B   3   B
1   3   B   3   B   2   A
2   1   A   1   A   1   A
3   3   B   2   A   3   B

这使用数组

>>> (df.iloc[:,1::2]!="B").values
array([[ True, False, False],
       [False, False,  True],
       [ True,  True,  True],
       [False,  True, False]], dtype=bool)

决定我们需要单独保留原始值的位置。我们必须坚持.values,因为否则pandas会尝试对齐框架,我们会手动完成。

如果你坚持不使用iloc,那么事情变得有点棘手。一种方法是使用xsupdate

>>> df.update(df.xs("Num", level=1, axis=1, drop_level=False).where(
              df.xs("Let", level=1, axis=1, drop_level=False).values != "B", 3))
>>> df
   S1      S2      S3    
  Num Let Num Let Num Let
0   1   A   3   B   3   B
1   3   B   3   B   2   A
2   1   A   1   A   1   A
3   3   B   2   A   3   B

或者如果您的列是lexsorted,则可以将locslice(None)一起使用:

>>> df = df.sort_index(axis=1)
>>> nummask = slice(None), "Num"
>>> letmask = slice(None), "Let"
>>> df.loc[:, nummask] = df.loc[:, nummask].where((df.loc[:, letmask] != "B").values, 3)
>>> df
   S1      S2      S3    
  Let Num Let Num Let Num
0   A   1   B   3   B   3
1   B   3   B   3   A   2
2   A   1   A   1   A   1
3   B   3   A   2   B   3

使用IndexSlice可以稍微简化一下:

>>> df = df.sort_index(axis=1)
>>> idx = pd.IndexSlice
>>> df.loc[:,idx[:,"Num"]] = df.loc[:,idx[:,"Num"]].where((df.loc[:,idx[:,"Let"]] != "B").values, 3)
>>> df
   S1      S2      S3    
  Let Num Let Num Let Num
0   A   1   B   3   B   3
1   B   3   B   3   A   2
2   A   1   A   1   A   1
3   B   3   A   2   B   3