统计直塞球成功率的实用脚本
下面给你一个完整的 Python 脚本示例,用于统计足球比赛中直塞球(Through Ball)的成功率。

核心指标定义
直塞球成功率 = 成功直塞次数 / 直塞总次数 × 100%
- 成功直塞:球被队友接到,且形成有效进攻(射门/继续控球)
- 失败直塞:被对方拦截、出界、队友未接到
基础版脚本(CSV 数据)
假设你有一份事件数据 events.csv:
match_id,minute,player,team,event_type,outcome M001,12,德布劳内,曼城,through_ball,success M001,25,德布劳内,曼城,through_ball,fail M001,33,京多安,曼城,through_ball,success M001,47,B席,曼城,through_ball,fail M001,58,德布劳内,曼城,through_ball,success
脚本代码
import pandas as pd
# 1. 读取数据
df = pd.read_csv("events.csv")
# 2. 筛选直塞球事件
tb = df[df["event_type"] == "through_ball"].copy()
# 3. 统计成功 / 失败
tb["is_success"] = tb["outcome"] == "success"
total = len(tb)
success = tb["is_success"].sum()
fail = total - success
rate = success / total * 100 if total else 0
# 4. 输出总体结果
print("=" * 40)
print(f"直塞总次数: {total}")
print(f"成功次数: {success}")
print(f"失败次数: {fail}")
print(f"成功率: {rate:.2f}%")
print("=" * 40)
# 5. 按球员统计
player_stats = tb.groupby("player").agg(
直塞次数=("is_success", "size"),
成功次数=("is_success", "sum"),
)
player_stats["成功率(%)"] = (
player_stats["成功次数"] / player_stats["直塞次数"] * 100
).round(2)
player_stats = player_stats.sort_values("成功率(%)", ascending=False)
print("\n【按球员统计】")
print(player_stats)
# 6. 导出结果
player_stats.to_csv("through_ball_stats.csv", encoding="utf-8-sig")
进阶版:加入场地区域和比赛阶段
import pandas as pd
df = pd.read_csv("events.csv")
tb = df[df["event_type"] == "through_ball"].copy()
tb["is_success"] = tb["outcome"] == "success"
# 按球队统计
team_stats = tb.groupby("team").apply(
lambda g: pd.Series({
"直塞次数": len(g),
"成功次数": g["is_success"].sum(),
"成功率(%)": round(g["is_success"].mean() * 100, 2),
})
)
# 按比赛阶段统计(假设有 minute 字段)
def phase(m):
if m <= 30: return "上半场前段"
elif m <= 45: return "上半场后段"
elif m <= 75: return "下半场前段"
else: return "下半场后段"
tb["阶段"] = tb["minute"].apply(phase)
phase_stats = tb.groupby("阶段").agg(
直塞次数=("is_success", "size"),
成功次数=("is_success", "sum"),
)
phase_stats["成功率(%)"] = (phase_stats["成功次数"] / phase_stats["直塞次数"] * 100).round(2)
print("【按球队】\n", team_stats)
print("\n【按阶段】\n", phase_stats)
如果用 StatsBomb / Opta 数据
StatsBomb 示例
from statsbombpy import sb
events = sb.events(match_id=3788741)
# StatsBomb 中直塞球定义为 pass 且 pass_type = "Through Ball"
tb = events[
(events["type"] == "Pass") &
(events["pass_type"].astype(str).str.contains("Through Ball", na=False))
].copy()
tb["success"] = tb["pass_outcome"].isna() # 无 outcome 即为成功
rate = tb["success"].mean() * 100
print(f"直塞球成功率: {rate:.2f}% (样本 {len(tb)} 次)")
Opta 示例
Opta 中直塞球类型码通常是 pass_type = 4(Through Ball),
成功判定看 outcome = 1(成功):
tb = df[(df["type_id"] == 1) & (df["pass_type_id"] == 4)]
tb["success"] = tb["outcome"] == 1
print(f"成功率: {tb['success'].mean()*100:.2f}%")
常见坑与建议
| 问题 | 建议 |
|---|---|
| 样本太小(<10次) | 成功率不可信,需加置信区间 |
| 不同数据源定义不同 | 统一「直塞球」口径后再比较 |
| 中场直塞 vs 禁区直塞 | 分区统计更客观 |
| 领先/落后情况 | 比赛状态会影响冒险程度 |
推荐输出格式
一份完整的直塞球报告应包含:
全队总体成功率
2. 每名球员成功率 + 次数(次数少需标注)
3. 按场地三分区:后场/中场/前场
4. 按比赛阶段:0-15、15-30 ……
5. 成功直塞后续结果:射门/进球/丢球
需要的话,我可以帮你:
- 适配你具体的数据源(WyScout / 中超 / 自定义Excel)
- 加上可视化图表(matplotlib 柱状图、热力图)
- 写一个一键运行的完整工程目录
告诉我你的数据格式即可。