python案例统计高球传中争顶成功率?

wen python案例 9

Python案例:统计高球传中争顶成功率

下面我用一个完整的案例,模拟足球比赛中高球传中(Cross)后争顶(Aerial Duel)的数据统计,计算争顶成功率。

python案例统计高球传中争顶成功率?

需求分析

在足球数据分析中,常见指标:

  • 传中次数:球员/球队从边路起高球的次数
  • 争顶成功次数:接球方球员在对抗中赢下头球的次数
  • 争顶成功率 = 争顶成功次数 / 争顶总次数 × 100%

需要注意:

  1. 一次传中可能引发 0 次、1 次或多个人争顶(多人争顶只算一次"争顶事件",胜者唯一)
  2. 传中可能直接出界/被门将没收,不计入争顶
  3. 分球员、分球队统计

数据结构设计

用 pandas 模拟一份比赛事件数据:

import pandas as pd
import numpy as np
# 模拟一场比赛的所有传中事件
data = [
    # (球员, 球队, 传中是否形成争顶, 是否争顶成功)
    ("德布劳内", "曼城", True,  True),
    ("德布劳内", "曼城", True,  False),
    ("德布劳内", "曼城", True,  True),
    ("德布劳内", "曼城", False, None),   # 传中未形成争顶
    ("福登",     "曼城", True,  False),
    ("福登",     "曼城", True,  True),
    ("福登",     "曼城", False, None),
    ("B席",      "曼城", True,  True),
    ("B席",      "曼城", True,  True),
    ("萨卡",     "阿森纳", True, True),
    ("萨卡",     "阿森纳", True, False),
    ("萨卡",     "阿森纳", True, False),
    ("马丁内利", "阿森纳", True, True),
    ("马丁内利", "阿森纳", False, None),
    ("马丁内利", "阿森纳", True, False),
    ("厄德高",   "阿森纳", True, True),
]
df = pd.DataFrame(data, columns=["球员", "球队", "形成争顶", "争顶成功"])
print(df)

核心统计函数

def cross_aerial_stats(df, group_col):
    """
    统计传中后争顶成功率
    group_col: 分组字段,如 '球员' 或 '球队'
    """
    # 传中总次数
    total_cross = df.groupby(group_col).size().rename("传中次数")
    # 形成争顶的次数(过滤出有争顶的记录)
    duels = df[df["形成争顶"] == True]
    total_duel = duels.groupby(group_col).size().rename("争顶次数")
    # 争顶成功次数
    win_duel = duels[duels["争顶成功"] == True].groupby(group_col).size().rename("争顶成功")
    # 合并
    result = pd.concat([total_cross, total_duel, win_duel], axis=1).fillna(0)
    result = result.astype(int)
    # 争顶成功率 = 争顶成功 / 争顶次数 × 100%
    result["争顶成功率(%)"] = np.where(
        result["争顶次数"] > 0,
        (result["争顶成功"] / result["争顶次数"] * 100).round(1),
        0.0
    )
    # 传中形成争顶的转化率(可选指标)
    result["传中转化率(%)"] = (result["争顶次数"] / result["传中次数"] * 100).round(1)
    return result.sort_values("争顶成功率(%)", ascending=False)
# 按球员统计
print("=== 按球员统计 ===")
print(cross_aerial_stats(df, "球员"))
print("\n=== 按球队统计 ===")
print(cross_aerial_stats(df, "球队"))

输出示例:

=== 按球员统计 ===
           传中次数  争顶次数  争顶成功  争顶成功率(%)  传中转化率(%)
B席            2      2      2        100.0        100.0
德布劳内        4      3      2         66.7         75.0
厄德高         1      1      1        100.0        100.0
马丁内利        3      2      1         50.0         66.7
福登           3      2      1         50.0         66.7
萨卡           3      3      1         33.3        100.0
=== 按球队统计 ===
        传中次数  争顶次数  争顶成功  争顶成功率(%)  传中转化率(%)
曼城        9      7      5         71.4         77.8
阿森纳      7      6      3         50.0         85.7

多人争顶场景处理

现实中,一次传中可能有多名球员同时争顶,只有胜者算成功,推荐以"争顶事件ID"为单位记录:

# 更贴近实战的事件表:每条记录 = 一个球员参与一次争顶
events = [
    # 事件ID, 球员, 球队, 是否胜者
    (1, "哈兰德",  "曼城",  True),   # 事件1:哈兰德赢下
    (1, "萨利巴",  "阿森纳", False),
    (2, "哈兰德",  "曼城",  False),
    (2, "加布里埃尔","阿森纳", True),
    (3, "哈兰德",  "曼城",  True),
    (3, "萨利巴",  "阿森纳", False),
]
ev = pd.DataFrame(events, columns=["事件ID", "球员", "球队", "是否胜者"])
# 每个事件的胜者唯一,统计各球员参与次数与获胜次数
player_stats = ev.groupby("球员").agg(
    参与争顶=("事件ID", "count"),
    争顶成功=("是否胜者", "sum")
).reset_index()
player_stats["争顶成功率(%)"] = (
    player_stats["争顶成功"] / player_stats["参与争顶"] * 100
).round(1)
print(player_stats)

输出:

         球员  参与争顶  争顶成功  争顶成功率(%)
加布里埃尔     1      1        100.0
     哈兰德     3      2         66.7
    萨利巴     2      0          0.0

进阶:用真实数据集(可选)

如果想用真实数据,可以:

  • StatsBomb Open Data(免费):https://github.com/statsbomb/open-data 包含 Pass 事件中 cross=True 和 duel 类型事件
  • FBref / Understat:抓取球队层面传中与争顶数据

用 StatsBomb 的示例伪代码:

# 从 StatsBomb 加载事件后
crosses = events[events["type"] == "Pass"][
    events["pass"].apply(lambda x: x.get("cross", False))
]
# 再关联后续的 aerial duel 事件即可

小结

关键点 说明
传中次数 所有起高球传中的次数
争顶次数 真正形成空中对抗的次数
争顶成功率 争顶成功 / 争顶次数 × 100%
传中转化率 争顶次数 / 传中次数 × 100%(衡量传中质量)
多人争顶 用事件ID保证一次争顶只有一个胜者

延伸指标:

  • 前场传中争顶成功率(只统计进攻三区传中)
  • 按传中方向(左路/右路)分组
  • 结合 xG 分析传中后的射门威胁

需要我把它包装成一个可复用的类,或者接入 StatsBomb 真实数据吗?

抱歉,评论功能暂时关闭!