如何根据multiIndex DataFrame的内部索引执行操作?

时间:2019-03-05 11:45:55

标签: python pandas

假设我有一个学生成绩的数据框,并且想随时间跟踪他们的成绩。 DataFrame可能看起来像这样:

data = [ { "Name": "John", "Period": 1, "Grade": 60 }, { "Name": "John", "Period": 2, "Grade": 80 }, { "Name": "John", "Period": 3, "Grade": 90 }, { "Name": "Bill", "Period": 1, "Grade": 80 }, { "Name": "Bill", "Period": 2, "Grade": 70 }, { "Name": "Bill", "Period": 3, "Grade": 80 }, { "Name": "Tom", "Period": 1, "Grade": 50 }, { "Name": "Tom", "Period": 2, "Grade": 75 }, { "Name": "Tom", "Period": 3, "Grade": 50 } ]

df = pd.DataFrame(data)
df.set_index(["Name", "Period"], inplace=True)

             Grade
Name Period       
John 1          60
     2          80
     3          90
Bill 1          80
     2          70
     3          80
Tom  1          50
     2          75
     3          50

现在,我想添加一个“更改”列,该列显示每次检查的变化百分比。这些有点像堆叠的DataFrame。如果是一个,我会尝试

df["change"] = (df["Grade"] - df["Grade"].shift(1))/df["Grade"].shift(1)

这将正确地在第一行中返回NaN值,因为它没有先前的值。在上面的DataFrame上执行以下操作:

             Grade    change
Name Period                 
John 1          60       NaN
     2          80  0.333333
     3          90  0.125000
Bill 1          80 -0.111111
     2          70 -0.125000
     3          80  0.142857
Tom  1          50 -0.375000
     2          75  0.500000
     3          50 -0.333333

我希望每个外部索引值的第一行的“更改”值为NaN,如下所示:

             Grade    change
Name Period                 
John 1          60       NaN
     2          80  0.333333
     3          90  0.125000
Bill 1          80       NaN
     2          70 -0.125000
     3          80  0.142857
Tom  1          50       NaN
     2          75  0.500000
     3          50 -0.333333

稍后在汇总“更改”列时也是如此,因为一个学生的最终成绩会影响下一个学生的第一个成绩,所以不会出现任何疯狂的变化。我知道有些捷径可以简单地进行上述转换,然后将每个第一个“更改”值更改为np.nan,但感觉必须有一个更优雅的方法。

1 个答案:

答案 0 :(得分:2)

MultiIndex的第一级使用GroupBy.pct_change

df["change"] = df.groupby(level=0)['Grade'].pct_change()
print (df)
             Grade    change
Name Period                 
John 1          60       NaN
     2          80  0.333333
     3          90  0.125000
Bill 1          80       NaN
     2          70 -0.125000
     3          80  0.142857
Tom  1          50       NaN
     2          75  0.500000
     3          50 -0.333333

使用DataFrameGroupBy.shift的解决方案:

s = df.groupby(level=0)['Grade'].shift()
df["change"] = (df['Grade'] - s) / s

df["change"] =  df['Grade'].div(df.groupby(level=0)['Grade'].shift()).sub(1)

并使用GroupBy.apply

df["change"] = df.groupby(level=0)['Grade'].apply(lambda x: (x - x.shift())/ x.shift())

更好:

df["change"] = df.groupby(level=0)['Grade'].apply(lambda x: (x / x.shift()) - 1)