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

时间:2019-09-20 11:43:42

标签: python pandas dataframe

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

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

我想做的是针对给定的比赛类型计算获胜者和输者的历史胜利百分比,并将其放在自己的栏中。


下面是填写上表的示例。游戏将被称为(赢家,输家,类型)。

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


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

2)第二局(Steve,John,B)。以前没有B型游戏。因此我们填充0。

3)第三局(John,Michael,A)。以前有A型游戏,所以我们可以获得信息。首先,约翰是赢家。他在表的第一行赢得了A类的一场比赛。因此,我们将获胜者获胜=1。约翰之前没有输过A类游戏,因此我们将获胜者损失=0。迈克尔没有任何比赛历史,因此我们将失败者获胜= 0,并将失败者损失= 0。 >

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)     |
+--------+---------+------------+-------------+------------+---------------+--------------+--------------+-------------

3 个答案:

答案 0 :(得分:1)

这应该产生预期的结果:

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

pandas 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除。

答案 1 :(得分:0)

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

您可以使用属性playerfirstNamelastNamenumberWins创建numberLosses类。在构造函数中,您可以将numberWinsnumberLosses设置为0。我还建议添加一个wins方法和loses方法,它们将分别增加{{1} }和numberWins乘以1。

每次比赛后,您都可以根据比赛的结果增加属性(例如,如果我输了,我的输球数增加了1,如果我赢了,我的输球数增加了1)。在任何时候,您都可以使用numberLosses获得历史获胜百分比。

如果要按锦标赛计算该比率,则可以将player.numberWins / (player.numberWins + player.numberLosses)numberWins属性定义为数据帧。其中每列代表一场比赛(例如,列标签为A,B,C ...),而每行则代表相应的获胜次数。然后,您还将在numberLosseswins方法中添加一个参数来指定当前的锦标赛。 (您甚至可以定义一个loses类以提高灵活性)

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

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

答案 2 :(得分:0)

这是我的尝试

说明

  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)