【问题标题】:Query dataframe of sports data by winner/loser name, and get tables of aggregate stats per-player?按赢家/输家名称查询体育数据的数据框,并获取每个球员的汇总统计数据表?
【发布时间】:2020-07-20 05:37:59
【问题描述】:

我有一个网球比赛结果的 CSV 数据集,格式与此类似:https://www.kaggle.com/jordangoblet/atp-tour-20002016

我想创建另一个表格来显示每个球员的胜负数、自上次比赛以来的天数、平均发球率等。

一位朋友建议尝试这样的事情:

games = [
    ["Elliot", 7, "Maya", 10],
    ["Elliot", 10, "Brendan", 8],
    ["Jamie", 7, "Omid", 10],
    ["Elliot", 6, "Jamie", 10],
    ["Brendan", 7, "Jamie", 10],
]

players = {}
K = 32.0
game_number = 1

for game in games:
    p1name, p1score, p2name, p2score = game
    if p1name not in players:
        players[p1name] = {
            'wins': 0,
            'losses': 0,
            'score': 1000.0,
        }
    if p2name not in players:
        players[p2name] = {
            'wins': 0,
            'losses': 0,
            'score': 1000.0,
        }

更多代码...

...

print('Rank         Player         Elo       Wins    Losses')
rank = 1
for player, p in sorted(players.items(), key=lambda item: (item[1]['score'], item[0]), reverse=True):
    print('%4s %20s %7.1f %5s %7s' % (rank, player, p['score'], p['wins'], p['losses']))
    rank += 1 

我试图通过使我的 df 成为列表列表来根据我的需要调整此代码:

lol = df.values.tolist()
players = {}
for game in lol:
    game_id, tournament_name, tournament_id, tournament_date, round_match, winner, winner_id, loser, loser_id, winner_elo, 
    loser_elo, winner_delta, loser_delta, winner_set_1, loser_set_1, winner_set_2, loser_set_2, winner_set_3, loser_set_3,
    winner_set_4, loser_set_4, winner_set_5, loser_set_5, winner_sets, loser_sets, winner_serve_points_won, 
    loser_serve_points_won, winner_serve_hold, loser_serve_hold, winner_points_won, loser_points_won, winner_true_serve_pct, 
    loser_true_serve_pct = game

以上都是我原来的df中的列名

但我收到此错误: NameError: name "game_id" is not defined

原始代码运行良好,但我不明白为什么 =game 不适用于我的情况。

我错过了什么吗?有没有更好的方法来解决这个问题?我的数据集有大约 60k 个实例和 33 列

编辑:找到解决方案,变量 = 游戏应该在一行中。但是,如果有更好的解决方案来实现我在 cmets 中描述的内容,欢迎您发表评论

编辑:列信息

game/tournament/winner/loser_id = unique id's

tournament_name = matches are played in daily tournaments

tournament_date = date of tournament/match

round_match = round of tournament the match takes place

winner/loser= winner/loser name

winner/loser_elo = current elo for the day

winner/loser_delta = delta change after match

winner/loser_set_1,2,3,4,5 = score for winner/loser per game, match ends when a player reaches 3 games, 11 points needed to win a game or 2 point differential (extra points if game is tied at 10-10)

winner/loser_sets= number of sets

winner/loser_points_won = total points per player in the match

winner/loser_true_serve_pct = % of points player wins when he is serving

winner_serve_points_won/loser_serve_points_won is obsolete

winner_serve_hold/loser_serve_hold will be replaced by winner/loser_true_serve_pct

【问题讨论】:

  • 我正在使用 pandas 数据框,您在最后一个代码中看到的所有变量(game_id、tournament_name 等 = 游戏)都是我在 df 中的列的标题。我试图获取每个玩家玩游戏的最近日期,以找出他们最近的 elo。我按“获胜者”分组,然后按日期排序并获得最大日期。我为“失败者”做了同样的事情,但我找不到一种方法来比较同一玩家之间的最大日期并从“elo”列中获取一个值(如果最近日期该玩家是一个“赢家”,那么我需要'winner_elo',如果失败者我需要'loser_elo')
  • 所以除了统计输赢之外,处理 pandas 似乎很难,而这个字典列表列表似乎是一个很好的解决方案
  • 嗯,如果您要同时生成多个输出列,请用伪代码写出 'winner'、max date (newest?) / 'loser'、'elo' / 'winner_elo 的规范' / 'loser_elo';我无法关注你的评论。如果您展示一个如何计算这些内容的简单示例,例如 6-10 条记录,将会有所帮助。 (此外,这是非常特定于数据集的,Kaggle competition's kernels 通常会展示一些很好的探索性数据分析,或者发布您自己的数据集以获取了解该数据集的人的反馈)。
  • 以前问过同样的问题,但细节较少Update value for every row based on either of two previous columns
  • 抱歉低估了你的问题——这比乍一看更难——我们不能只使用df.groupby()。您需要在数据框中查询出现在“赢家”或“输家”列中的玩家姓名。这是最难做对的部分。剩下的就简单了。

标签: python pandas list dictionary


【解决方案1】:

您需要查询出现在“赢家”或“输家”列中的球员姓名的数据框,以获取matches,这是一个包含该球员的所有比赛的数据框。这比乍一看更难 - 我们不能只使用简单的df.groupby()抱歉低估了您的问题。

  • 以下解决方案使用 df[['Winner','Loser']].isin([player]).any(axis=1)

    • 请注意,参数化名称可能出现的列列表,它不会对它们进行硬编码。比df['Winner'] == player or df['Loser'] == player更优雅的成语
    • 我还研究了df.query("..."),它接受任意 SQL 样式的查询字符串,但它的语法很烦人
  • 两列共有 1387 名独立玩家:df['Winner'] 中的 879 名独立玩家和 df['Loser'] 中的 1383 名。我们将它们组合成一组player_names。您可以使用列表,但使用 set 删除重复项更容易:set(df['Winner'].unique()) | set(df['Loser'].unique()) where '|'是 setwise-'or'/.union

  • 为了提高处理玩家姓名的效率,我们使用分类而不是字符串。请注意代码必须如何在“赢家”、“输家”列中拥有一个具有合并类别值的统一分类(参见代码)。
  • 我们将defaultdict(dict) 用于players,这很优雅,因为现在您可以直接定义字段players[player]['WhateverField'],无论是否定义了字典players[player],否则将自动创建。
  • 此处的代码显示了您要创建的聚合字段的示例。
    • 注意如何直接使用 pandas 矢量化 mean()sum() 等来处理简单的事情
    • 要创建其他更任意的聚合,您可以使用matches.agg(...) 和内部的自定义/lambda 函数。

代码:

import pandas as pd

# https://www.kaggle.com/jordangoblet/atp-tour-20002016
df = pd.read_csv('../input/Data.csv', encoding='latin1', usecols=range(12+1), na_values='NR',
    parse_dates=['Date'], dayfirst=True)
# make sure that the 'NR' in WRank/LRank column is recognized as NaN, so they read in as float columns
# Columns 0-12 (basic data), 13-25 (results by set), 26-53 (betting odds: float)

# Create table of number of wins, losses, days since last match, average serve percentage etc. per player.

pd.options.display.max_rows = 100
pd.options.display.precision = 3    

player_names = set(df['Winner'].unique()) | set(df['Loser'].unique())
player_names = sorted(player_names) # 1.0.x BUG: order seems totally unstable(!)
player_dtype = pd.CategoricalDtype(categories = player_names) # merge across multiple columns

# WRONG: categories only taken from each individual column, not merged
# df[['WinnerCat1','LoserCat1']] = df[['Winner','Loser']].astype('category')
# RIGHT:
df[['WinnerCat','LoserCat']] = df[['Winner','Loser']].astype(player_dtype)

from collections import defaultdict
players = defaultdict(dict)

for _,player in (player_names):
    # query df where player occurs in either column, can't use `df[['Winner','Loser']].groupby(player)`
    matches = df[ df[['Winner','Loser']].isin([player]).any(axis=1) ]

    print(f'\n[MATCHES FOR {_} {player}]')
    print(matches.iloc[:, 0:13])

    players[player]['Wins']   = matches['Winner'].eq(player).sum()
    players[player]['Losses'] = matches['Loser'].eq(player).sum()
    players[player]['Avg']    = matches['Winner'].eq(player).mean().round(4)
    # You can do any arbitrary calculation involving both players' columns, e.g....
    players[player]['RankDiff'] = (matches['WRank'] - matches['LRank']).mean()

    #if _ >= 2: break # for debugging

【讨论】:

  • 嘿 smci,感谢您的努力和编辑标题(有时我很难问出正确的问题)。首先,我是初学者到中级,这就是为什么我在这个项目上工作是为了“做和学习”,然后能够展示一个完整的项目。我的目标之一是将一些预测技术应用于此数据集(logit 回归、马尔可夫链、蒙特卡洛模拟),仅用于实验。另一个是能够显示每个球员的个人统计数据,因为这个联盟没有其他来源。这个目标的一部分是能够在我计划创建的网站上拥有所有内容
  • 我编辑了帖子并在我的专栏中添加了一些解释。我从一个关于乒乓球的网站上抓取了这些数据,并将其格式化为与我之前链接的数据集相似的结构。与我正在寻找的东西类似的唯一来源是:1)kaggle.com/sadz2201/tennis-exploration-atpboost,但它是用 R 编写的,我无法理解他在做什么 2)medium.com/@elliotchance/…,这是在 SQL 和结果有点不同,但想法就在那里
  • 不幸的是,我在周末之前没有太多时间,但我评论说我没有忽略你的回答。我试过你的代码,但没有太多时间来试验它。你能解释一下这是如何工作的吗? player_names = set(df.Winner.unique()) | set(df.Loser.unique()) 这到底是什么意思? #if _ >= 2: break # for debugging。我运行了代码并做了一些实验。如果我理解正确,for 循环开始后的第一行会为每个玩家创建一个 df(他处于“赢家”或“输家”的位置,对吧?
  • 我尝试做players[player]['avg_serve_win'] = matches['winner_true_serve_pct'].eq(player).mean(),但它总是返回 0,我怀疑它是否正确,因为如果上面我们为每个出现在“赢家”或“输家”中的玩家创建了一个 df,然后得到'winner_true_serve_pct' 平均值,我们只需要从玩家获胜的行中获取此值。我不确定我是否足够清楚,希望你明白我想说什么。我不确定这整件事对我来说是否太难了,如果你愿意,我可以给你或上传一个示例数据集以更好地理解问题
  • 关于#if _ >= 2: break # for debugging 的澄清:我确实得到了ValueError: too many values to unpack (expected 2),但由于'_'仅用于打印语句,我刚刚删除了它。但很好奇你的意思break # for debugging
【解决方案2】:

例如,如果你有一个 csv 文件,就这样加载它

import pandas as pd
dataframe = pd.read.csv('file.csv')

现在,选择您感兴趣的列(我将使用 kaggle 数据库作为参考)。

请注意,我不会写下您提出的所有问题,因为我没有完全理解您的问题(kaggle 数据库上没有日期,以及每个玩家的平均时间?)

dataframe = dataframe['Winner', 'Loser']

现在,我们使用 groupby 函数和 count 函数来获取每个玩家的输赢数量(同时使其在“赢家”/“输家”列上的出现独一无二。

dataframe['Wins'] = dataframe.groupby(labels='Winner', axis=1).count()
dataframe['Losses'] = dataframe.groupby(labels='Loser', axis=1).count()

【讨论】:

  • 嘿,正如我所说,我的数据集与我链接的 kaggle 相似。您可以在最后一段代码中看到我的专栏。实际上 kaggle 数据集上也有一个日期列。我知道要计算输赢的数量有一个类似于你的解决方案,但我想要做的我无法通过我尝试过的任何 groupby、sortby 组合来实现它。例如,当您意识到同一玩家可以同时出现在获胜者和失败者列中时,
  • [p2 of comment] 我希望能够获得每个玩家的最新实例,这样我就可以从“elo”列中获得最新的 Elo 值。同样,对于每场比赛,每个球员都有一个发球百分比和持球百分比,我想要每个球员的平均值。或者一个玩家平均赢得一盘的分数……诸如此类的东西。我认为我朋友提供的示例代码是个好主意,但我不明白为什么会出现这个错误
【解决方案3】:

我运行以下代码没有任何错误,所以我认为 = 游戏没有问题。

df = pd.read_csv(r'Ten.csv')


lol = df.values.tolist()


players = {}
for game in lol:
    Player,Tournament,Wins,Losses = game

    if Player not in players:
        players[Player] = {
            'Tournament': 'foo',
            'wins': 1,
            'losses': 2,
        }

print(players)

但是如果 Player 不同,例如以下(小写“p”)

player,Tournament,Wins,Losses = game

或:([播放器]末尾的“s”)

        players[Players] = {
            'Tournament': 'foo',
            'wins': 1,
            'losses': 2,
        }

会弹出错误 NameError: name 'Player' is not defined。您是否 100% 确定 game_id 没有拼错?

【讨论】:

  • 我在那个 "=game" 中使用了我的 df 列名,但如果我理解正确,只要它们与列表匹配,你使用什么名称并不重要。例如,我输入的任何名称都会给我同样的错误——不管它是否首先是 game_id。在我使用的原始代码中; p1name、p1score、p2name、p2score 未在其他任何地方定义,如果您输入其他名称,只要它是与列表匹配的 4 个变量,它仍然有效。
  • 没错,是的,您可以随意命名它们。但我怀疑 =game 之后某处的名称不一致。例如,我可以做 A,B,C,D = 游戏。但是,如果我尝试调用玩家 [a] 而不是玩家 [A],我将得到 NameError:name 'a' is not defined。奇怪的是它适用于 p1name、p1score、p2name、p2score 而不是您尝试的新名称
猜你喜欢
  • 2015-03-01
  • 2012-05-20
  • 1970-01-01
  • 2015-07-18
  • 2020-05-05
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多