假设我有一个学生成绩的 DataFrame,并且想要随时间跟踪他们的成绩。 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,但感觉必须有一种更优雅的方法。
最佳答案
使用GroupBy.pct_change
按 MultiIndex
的第一级:
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)
关于python - 如何根据multiIndex DataFrame的内部索引进行操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55002118/