熊猫 - 胜率计算;按两列分组并计数



我有一个包含以下列的数据帧:

| winner |  loser  | tournament |
+--------+---------+------------+
| John   | Steve   |      A     |
+--------+---------+------------+
| Steve  | John    |      B     |
+--------+---------+------------+
| John   | Michael |      A     |
+--------+---------+------------+
| Steve  | John    |      A     |
+--------+---------+------------+

我想做的是计算给定锦标赛类型的赢家和输家的历史胜率,并将其放在自己的列中。


填写上表的示例如下。游戏将被称为(赢家、输家、类型(。

我还添加了中间计算列以使其更清晰。


1(对于第一场比赛(约翰,史蒂夫,A(。以前没有A型游戏。所以我们用 0 填充。

2(对于第二场比赛(史蒂夫,约翰,B(。以前没有B型游戏。所以我们用 0 填充。

3(对于第三场比赛(约翰,迈克尔,A(。以前有A型游戏,所以我们可以得到信息。首先,约翰是赢家。他在表格的第一排赢得了 1 场 A 型比赛。所以我们把赢家的胜利 = 1。约翰以前没有输过A型比赛,所以我们把赢家输=0。迈克尔没有任何游戏历史,所以我们填写输家赢 = 0 和输家输 = 0。

4(对于第4场比赛,(史蒂夫,约翰,A(。我们看到史蒂夫以前没有赢得过任何A型比赛,所以我们把赢家的胜利=0。他输掉了1场A型比赛(第一排(。所以我们把赢家的损失 = 1。约翰赢了 2 场 A 型比赛,所以输家赢了 = 2。他输了

+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------+
| winner |  loser  | tournament | winner wins | loser wins | winner losses | loser losses | winner win % | loser win % |
+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------+
|  John  |  Steve  |      A     |      0      |      0     |       0       |       0      | 0/(0+0)      | 0/(0+0)     |
+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------+
|  Steve |   John  |      B     |      0      |      0     |       0       |       0      | 0/(0+0)      | 0/(0+0)     |
+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------+
|  John  | Michael |      A     |      1      |      0     |       0       |       0      | 1/(1+0)      | 0/(0+0)     |
+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------+
|  Steve |   John  |      A     |      0      |      2     |       1       |       0      | 0/(0+1)      | 2/(2+0)     |
+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------

这会产生预期的结果:

def win_los_percent(sdf):
sdf['winner wins'] = sdf.groupby('winner').cumcount()
sdf['winner losses'] = [(sdf.loc[0:i, 'loser'] == sdf.loc[i, 'winner']).sum() for i in sdf.index]
sdf['loser losses'] = sdf.groupby('loser').cumcount()
sdf['loser wins'] = [(sdf.loc[0:i, 'winner'] == sdf.loc[i, 'loser']).sum() for i in sdf.index]
sdf['winner win %'] = sdf['winner wins'] / (sdf['winner wins'] + sdf['winner losses'])
sdf['loser win %'] = sdf['loser wins'] / (sdf['loser wins'] + sdf['loser losses'])
return sdf
ddf = df.groupby('tournament').apply(win_los_percent)

使用提供的数据,ddf是:

winner    loser tournament  winner wins  winner losses  loser losses  loser wins  winner win %  loser win %
0   John    Steve          A            0              0             0           0           NaN          NaN
1  Steve     John          B            0              0             0           0           NaN          NaN
2   John  Michael          A            1              0             0           0           1.0          NaN
3  Steve     John          A            0              1             0           2           0.0          1.0

熊猫Groupby用于对同一锦标赛的数据进行分组,并将子数据帧传递给win_los_percent函数。返回此函数的返回值以生成最终数据帧。

对于每个子集,该函数计算几列:

  • sdf['winner wins']sdf['loser losses']是使用 cumcount:对于每一行,此方法计算分组列中以前出现的值(玩家名称(。
  • 获取sdf['winner losses']sdf['loser wins']更加复杂,因为我们需要在另一列中计算之前出现的值(玩家名称(。
    列表推导式循环访问数据帧索引以选择前几行,并检查列'winner'中的玩家名称是否等于第loser列中当前行的玩家名称,反之亦然。sum()允许计算 True 值:True 转换为 1,False 转换为 0,总和给出所需的结果:玩家名称在前几行中出现的次数。
  • 百分比列是通过矢量化获得的。结果NaN的地方是因为有一个除以 0。

我想到的第一个解决方案是使用面向对象编程。我在下面提出了一些实施指南。

您可以创建具有属性firstNamelastNamenumberWinsnumberLossesplayer类。在构造函数中,可以将numberWinsnumberLosses设置为 0。我还建议添加一个wins方法和loses方法,它们将分别将numberWinsnumberLosses递增 1。

每场比赛结束后,您可以根据比赛结果增加属性(例如,如果我输了,我的输球次数会增加 1,如果我赢了,我的赢次数会增加 1(。在任何时候,您都可以通过player.numberWins / (player.numberWins + player.numberLosses)获得历史胜率。

如果要按锦标赛计算此比率,则可以将numberWinsnumberLosses属性定义为数据框。 其中每列代表一个锦标赛(例如,列标签为 A、B、C...(,每行是相应的获胜次数。然后,您还将在winsloses方法中添加一个参数来指定手头的锦标赛。(您甚至可以定义一个score类以获得更大的灵活性(

在记录比赛的大数据框中,您可以实际存储两个玩家对象(而不是像现在这样标识玩家的字符串(,并随时更改其属性。

然后,可能还会有一些快速的数学巫术。但是,乍一看,面向对象的实现似乎特别适合您的情况(玩家最终是一个对象,具有自己的属性和方法......

这是我的尝试

解释

通过将"赢家"和"输家"列
  1. 合并到"玩家"列来创建新的数据框,并通过"赢家"列指示赢/输
  2. 按"玩家"和"锦标赛"对新数据帧进行分组,并使用每行的"calc_winning_percent"函数计算胜率
  3. 现在将new_df的胜率拆分为赢家胜率和输家胜率,并分配回DF
d = {
"winner": "John Steve John Steve".split(),
"loser": "Steve John Michael John".split(),
"tournament": "A B A A".split()
}
def calc_wining_percent (group):
group["wining_percent"] = group["won"].cumsum().shift()/np.arange(1, len(group)+1)
return group
df = pd.DataFrame(d)
new_df =  pd.DataFrame(np.ravel(df[["winner", "loser"]]), columns=["player"])
new_df["won"]= np.r_[np.ones(len(df)), np.zeros(len(df))]
new_df["tournament"] = np.tile(df["tournament"],2)
new_df = new_df.groupby(["player", "tournament"]).apply(calc_wining_percent)
df["winner win %"] = new_df["wining_percent"].values.reshape(-1,2)[:,0]
df["loser win %"] = new_df["wining_percent"].values.reshape(-1,2)[:,1]
display("result", df)

最新更新