统计头球争顶成功率
下面用一个完整的 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 数据,我可以帮你写对应的字段映射和数据清洗代码。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播