在 Pandas 数据帧的组内使用 np.cumsum

问题描述

我最近使用 groupby()np.cumsum() 来累计计算当前网球比赛的获胜者在给定锦标赛中赢得的所有比赛。我使用此代码获取一个表中的 winner_tourney_games_cumulative 列:

df.groupby(['tourney_name','year','winner_name'])['winner_games_played'].cumsum()

一场比赛的网球比赛数据集:

winner_name 失败者姓名 tourney_name 回合 winner_tourney_games_cumulative winner_games_played loser_games_played
罗杰·费德勒 苏米特纳加尔 美国公开赛 R128 2019 22 22 13
罗杰·费德勒 damir Dzumhur 美国公开赛 R64 2019 43 21 15
罗杰·费德勒 丹尼尔·埃文斯 美国公开赛 R32 2019 61 18 5
罗杰·费德勒 大卫·戈芬 美国公开赛 R16 2019 79 18 4
格里戈尔·迪米特洛夫 罗杰·费德勒 美国公开赛 QF 2019 87 24 22

这对赢家来说相对简单,但对输家来说不是那么简单。鉴于本场比赛的输家已经通过了比赛的第一轮,这意味着他们之前至少赢过一场比赛,因此是上一场比赛的赢家。这使事情变得复杂,因为赢家和输家的列在数据中是分开的。因此,我相信带有 np.cumsum()I 编写的短代码(如上所示)对失败者不起作用。

谁能帮助我如何为失败者创建类似于我为获胜者所做的东西(winner_tourney_games_cumulative 列)?我想要的是输家在本次锦标赛前几场比赛中获胜场数的累计总和。

我希望失败者看起来像这样(你可以忽略下面的 x):

winner_name 失败者姓名 loser_tourney_games_cumulative
罗杰·费德勒 苏米特纳加尔 X
罗杰·费德勒 damir Dzumhur X
罗杰·费德勒 丹尼尔·埃文斯 X
罗杰·费德勒 大卫·戈芬 X
格里戈尔·迪米特洛夫 罗杰·费德勒 101

对于罗杰·费德勒输掉的那场比赛来说,应该是 79(他在过去比赛中第一桌赢得的累计比赛)+ 22(他在这场比赛中赢得的比赛场数)= 101。

非常感谢!

解决方法

这需要一些转换。我们首先melt使用 df 逐行列出每场比赛的赢家和输家:

df2 = (df.melt(id_vars ='round',value_vars = ['winner_name','loser_name'],var_name = 'win_lose',value_name = 'name')
   .join(
 df.melt(id_vars ='round',value_vars = ['winner_games_played','loser_games_played'],value_name = 'games_won')['games_won'] 
   )
)
df2

我们得到

    round    win_lose     name               games_won
--  -------  -----------  ---------------  -----------
 0  R128     winner_name  Roger Federer             22
 1  R64      winner_name  Roger Federer             21
 2  R32      winner_name  Roger Federer             18
 3  R16      winner_name  Roger Federer             18
 4  QF       winner_name  Grigor Dimitrov           24
 5  R128     loser_name   Sumit Nagal               13
 6  R64      loser_name   Damir Dzumhur             15
 7  R32      loser_name   Daniel Evans               5
 8  R16      loser_name   David Goffin               4
 9  QF       loser_name   Roger Federer             22

现在我们可以计算cumsum按玩家姓名

df2['cum_games'] = df2.groupby('name').cumsum()
df2

所以我们得到

    round    win_lose     name               games_won    cum_games
--  -------  -----------  ---------------  -----------  -----------
 0  R128     winner_name  Roger Federer             22           22
 1  R64      winner_name  Roger Federer             21           43
 2  R32      winner_name  Roger Federer             18           61
 3  R16      winner_name  Roger Federer             18           79
 4  QF       winner_name  Grigor Dimitrov           24           24
 5  R128     loser_name   Sumit Nagal               13           13
 6  R64      loser_name   Damir Dzumhur             15           15
 7  R32      loser_name   Daniel Evans               5            5
 8  R16      loser_name   David Goffin               4            4
 9  QF       loser_name   Roger Federer             22          101

也许这足以满足您的目的。但如果不是,我们通过 groupbyunstack

将它恢复到(或多或少)原始形状
df2.groupby(['round','win_lose'],sort = False).first().unstack(level = 1)

得到

            name                          games_won                 cum_games
win_lose    winner_name     loser_name    winner_name   loser_name  winner_name loser_name
round                       
R128        Roger Federer   Sumit Nagal   22            13          22          13
R64         Roger Federer   Damir Dzumhur 21            15          43          15
R32         Roger Federer   Daniel Evans  18             5          61          5
R16         Roger Federer   David Goffin  18             4          79          4
QF          Grigor Dimitrov Roger Federer 24            22          24          101

除了列名有些不同,数据就是你想要的,即('cum_games','loser_name')列是每局输家累计赢局数