python案例统计头球争顶成功率如何?

wen python案例 2

统计头球争顶成功率

下面用一个完整的 Python 案例,演示如何从比赛事件数据中统计球员/球队的头球争顶成功率。

python案例统计头球争顶成功率如何?

数据准备

假设我们有一份足球事件数据(类似 StatsBomb / Opta 格式),每条记录是一次"争顶"事件:

import pandas as pd
import numpy as np
# 模拟一份争顶事件数据
data = {
    "match_id":   [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2],
    "player":     ["张三","李四","王五","张三","李四","王五",
                   "张三","李四","王五","张三","李四","王五","张三","李四"],
    "team":       ["A","A","A","B","B","B",
                   "A","A","A","B","B","B","A","A"],
    "duel_type":  ["aerial"]*14,
    "won":        [1, 0, 1, 0, 1, 0,  1, 1, 0, 1, 0, 1, 1, 0],  # 1=争顶成功
}
df = pd.DataFrame(data)
print(df.head())

真实场景中可从 events.json(StatsBomb 开源数据)读取,字段一般叫 duel / aerial_won。

总体头球争顶成功率

aerial = df[df["duel_type"] == "aerial"]
overall_rate = aerial["won"].mean()
print(f"整体头球争顶成功率: {overall_rate:.2%}")

按球员统计

def aerial_stats(group_col):
    stats = (
        aerial.groupby(group_col)
        .agg(
            attempts=("won", "size"),      # 争顶次数
            wins=("won", "sum"),           # 成功次数
        )
    )
    stats["success_rate"] = stats["wins"] / stats["attempts"]
    return stats.sort_values("success_rate", ascending=False)
player_stats = aerial_stats("player")
print(player_stats)

输出示例:

      attempts  wins  success_rate
player
张三          4     3          0.75
王五          3     1          0.33
李四          4     1          0.25

按球队统计

team_stats = aerial_stats("team")
print(team_stats)

加入"样本量阈值"过滤(关键!)

小样本(如只争顶 1 次且成功)的成功率 100% 没有参考意义,实战中必须加最小次数阈值:

MIN_ATTEMPTS = 3
reliable = player_stats[player_stats["attempts"] >= MIN_ATTEMPTS]
print(reliable)

进阶:按比赛过滤 + 对手强度分层

# 只统计某场比赛
match1 = aerial[aerial["match_id"] == 1]
print("第1场球员成功率:")
print(aerial_stats_from(match1, "player"))
# 分位置/时间段(如果数据里有 position、minute 字段)
# 例如统计"下半场"的成功率
# aerial["half"] = np.where(aerial["minute"] <= 45, "上半场", "下半场")
# aerial.groupby(["team", "half"])["won"].mean().unstack()

可视化

import matplotlib.pyplot as plt
player_stats["success_rate"].plot(kind="barh", color="steelblue")
plt.xlabel("头球争顶成功率")"球员头球争顶成功率")
plt.xlim(0, 1)
plt.tight_layout()
plt.show()

一个可复用的完整函数

def aerial_success(df, by="player", min_attempts=1, team=None):
    """统计头球争顶成功率"""
    d = df[df["duel_type"] == "aerial"].copy()
    if team:
        d = d[d["team"] == team]
    out = (
        d.groupby(by)
        .agg(attempts=("won", "size"), wins=("won", "sum"))
        .assign(success_rate=lambda x: x["wins"] / x["attempts"])
        .query("attempts >= @min_attempts")
        .sort_values("success_rate", ascending=False)
    )
    out["success_rate"] = out["success_rate"].map(lambda v: f"{v:.1%}")
    return out
print(aerial_success(df, by="player", min_attempts=2))

常见注意事项 ⚠️

问题 说明
样本量 必须设 min_attempts,否则 1/1=100% 会误导
争顶定义 有些数据把"头球解围""头球传球"也算争顶,要看清字段
双方各记一次 StatsBomb 中一次争顶通常只在获胜方记 duel,失败方也可能有对应记录,统计时避免重复计数
位置差异 中后卫争顶次数天然多于边锋,直接比成功率不公平,可分位置比较
对手强度 强队球员面对弱队时争顶成功率会虚高

用上面数据:

  • 整体成功率:约 50%(7/14)
  • 球员排名:张三 75% > 王五 33% > 李四 25%
  • 张三成功率最高且样本量≥3,可认为其头球争顶能力较强

如果你有真实的 StatsBomb 或 Wyscout 数据,我可以帮你写对应的字段映射和数据清洗代码。

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

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝