0

0

计算网球选手历史交手战绩(Head-to-Head)的正确方法

聖光之護

聖光之護

发布时间:2025-12-30 12:25:22

|

373人浏览过

|

来源于php中文网

原创

计算网球选手历史交手战绩(Head-to-Head)的正确方法

本文介绍如何在 pandas dataframe 中准确计算每对球员在当前比赛前的历史胜负记录,确保无论哪位选手作为 player1 出现,其 h2h 统计均基于真实对阵关系和时间顺序,避免因 target 标签误判导致的逻辑错误。

在构建网球比赛预测模型时,头对头(Head-to-Head, H2H)记录是一项关键特征:它反映两位选手过往直接交锋中的胜负分布,对预测当前比赛结果具有强解释性。但实现时极易出错——尤其是当同一对选手(如 A vs B)在不同行中以不同顺序(A 在 player1_id 或 player2_id)出现,且胜负标签 target 含义固定(1 表示 player1 获胜,0 表示 player1 失败)时,若未统一按「对阵对」而非「行列顺序」聚合数据,就会导致胜场数错误累加到错误选手名下。

✅ 正确思路:先归一化对阵对,再分组累积统计

核心在于两点:

  1. 标准化对阵组合:将 (A, B) 和 (B, A) 视为同一对选手,统一排序为 (min, max),确保所有 A-B 交锋落入同一分组;
  2. 按时间序累积胜场:在每个分组内,按 tourney_date 升序处理,动态统计截至当前比赛前、每位选手的实际获胜次数。

以下为完整、健壮、可扩展的实现方案:

微信 WeLM
微信 WeLM

WeLM不是一个直接的对话机器人,而是一个补全用户输入信息的生成模型。

下载
import pandas as pd
import numpy as np

def calculate_h2h_per_pair(group):
    # 确保 group 按时间升序排列(关键!)
    group = group.sort_values('tourney_date').reset_index(drop=True)

    # 提取实际胜者:target==1 → player1_id 胜;target==0 → player2_id 胜
    winner = np.where(group['target'] == 1, group['player1_id'], group['player2_id'])

    # 初始化两列:当前行之前(不含当前行)player1_id 和 player2_id 的胜场数
    player1_h2h = np.zeros(len(group), dtype=int)
    player2_h2h = np.zeros(len(group), dtype=int)

    # 遍历每一场比赛(从第 0 场开始,当前场不计入自身)
    for i in range(len(group)):
        # 获取当前比赛的两位选手(保持原始列顺序)
        p1 = group.iloc[i]['player1_id']
        p2 = group.iloc[i]['player2_id']

        # 统计此前所有比赛中,p1 和 p2 各自作为胜者的次数
        prev_winners = winner[:i]  # 仅看前面的 i 场
        player1_h2h[i] = (prev_winners == p1).sum()
        player2_h2h[i] = (prev_winners == p2).sum()

    return group.assign(player1_h2h=player1_h2h, player2_h2h=player2_h2h)

# 步骤1:构造标准化对阵对(自动处理 A-B 和 B-A)
df_sorted_pairs = np.sort(df[['player1_id', 'player2_id']], axis=1)
grp = pd.MultiIndex.from_arrays([df_sorted_pairs[:, 0], df_sorted_pairs[:, 1]])

# 步骤2:按对阵对分组,应用累积统计逻辑
result = df.groupby(grp, group_keys=False).apply(calculate_h2h_per_pair).sort_index()
✅ 优势说明:逻辑清晰:胜者识别与 target 语义严格对齐(target=1 → player1_id 胜),无需交换变量或条件翻转;时间安全:sort_values('tourney_date') + winner[:i] 确保只统计「历史」交锋,杜绝未来信息泄露;鲁棒性强:支持任意多对选手(如 A-B、C-D、E-F),且每对独立计算,互不干扰;可读性高:避免嵌套布尔索引与 shift().cumsum() 等易错技巧,便于调试与维护。

⚠️ 注意事项与常见陷阱

  • 日期格式必须为 datetime:确保 tourney_date 是 pd.Timestamp 类型,否则 sort_values 可能按字符串字典序排序(如 "2012-10-01"
  • 重复日期需谨慎:若同日存在多场比赛(如双打+单打混排),应补充唯一排序键(如 match_id),避免 sort_values 稳定性问题;
  • 性能优化提示:对百万级数据,上述循环版可替换为向量化 cumsum(参考答案中 np.where + shift().cumsum() 方案),但需额外构建「标准化胜者序列」,复杂度略高;本文推荐版本优先保障正确性与可理解性。

✅ 验证示例输出(A vs B 对阵)

输入(已按日期排序): | tourney_date | player1_id | player2_id | target | |--------------|------------|------------|--------| | 2012-01-16 | A | B | 0 | | 2012-01-27 | A | B | 0 | | 2012-03-14 | B | A | 1 | | 2015-01-20 | A | B | 0 |

输出: | tourney_date | player1_id | player2_id | target | player1_h2h | player2_h2h | |--------------|------------|------------|--------|-------------|-------------| | 2012-01-16 | A | B | 0 | 0 | 0 | | 2012-01-27 | A | B | 0 | 0 | 1 | | 2012-03-14 | B | A | 1 | 2 | 0 | | 2015-01-20 | A | B | 0 | 0 | 3 |

✅ 完全匹配预期:第三场(B胜)前,A 已负2场 → player1_h2h=2(因该行 player1_id=B,B 已赢2次);第四场(A负)前,B 已胜3次 → player2_h2h=3(因该行 player2_id=B)。

掌握此方法,即可为任意双人竞技类时序数据(网球、围棋、电竞1v1等)稳定生成高质量 H2H 特征,夯实模型基础。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

49

2025.12.04

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

247

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

204

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1434

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

608

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

546

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

539

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

157

2025.07.29

excel制作动态图表教程
excel制作动态图表教程

本专题整合了excel制作动态图表相关教程,阅读专题下面的文章了解更多详细教程。

30

2025.12.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 39.3万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号