本文目录导读:

在开源项目中分析守门员的出击范围,通常属于体育数据分析领域,尤其是足球数据科学,开源项目一般通过事件数据、追踪数据和计算机视觉来实现,下面从数据来源、核心算法、开源工具到具体实现思路,做一个系统梳理。
什么是“守门员出击范围”
守门员出击范围是指:守门员在比赛中离开球门线、主动出击拦截传中、单刀球或长传的区域范围。
常见量化指标:
| 指标 | 含义 |
|---|---|
| 平均出击距离 | 每次出击时离球门线的距离 |
| 出击热区图 | 出击位置的空间分布 |
| 出击成功率 | 出击后成功解围/控制球的比例 |
| 防守覆盖面积 | 守门员控制的空间范围 |
| Sweeper Keeper 指数 | 综合衡量清道夫门将倾向 |
数据来源
事件数据
- StatsBomb Open Data(免费开源)
- Wyscout Open Data
- FBref / Opta
- 包含事件类型如
Goalkeeper Save、Keeper Sweeper、Punch、Claim
追踪数据
- Metrica Sports Open Data
- DFL Bundesliga Open Data
- 提供每帧 25fps 的球员坐标(x, y)
视频数据
- 通过计算机视觉自行提取
- 工具:YOLO + DeepSORT + 单应性变换
核心分析方法
方法 1:基于事件数据的出击范围分析
import pandas as pd
from statsbombpy import sb
# 加载比赛事件
events = sb.events(match_id=3788741)
# 筛选守门员出击事件
gk_actions = events[
(events['player'] == 'Manuel Neuer') &
(events['type'].isin(['Goal Keeper', 'Pass', 'Clearance']))
]
# 提取出击位置
gk_sweeper = events[
(events['type'] == 'Goal Keeper') &
(events['goalkeeper_outcome'] == 'Success') &
(events['goalkeeper_type'].isin(['Collected', 'Keeper Sweeper']))
]
# 计算平均出击 x 坐标(StatsBomb 坐标:x=0 己方球门,x=120 对方球门)
avg_x = gk_sweeper['location'].apply(lambda loc: loc[0]).mean()
print(f"平均出击位置 x = {avg_x:.1f}")
输出热区图:
from mplsoccer import Pitch import matplotlib.pyplot as plt pitch = Pitch(pitch_type='statsbomb') fig, ax = pitch.draw() # 绘制出击点 x = [loc[0] for loc in gk_sweeper['location']] y = [loc[1] for loc in gk_sweeper['location']] pitch.scatter(x, y, ax=ax, s=100, color='red', alpha=0.6)'守门员出击位置分布')
方法 2:基于追踪数据的覆盖范围
# 追踪数据处理(以 Metrica 数据为例)
import numpy as np
# 假设 df 包含 frame, player_id, x, y
gk_id = 1 # 守门员 ID
gk_df = df[df['player_id'] == gk_id]
# 计算守门员每帧距球门线距离
goal_line_x = 0 # 己方球门线
gk_df['distance_from_goal'] = np.abs(gk_df['x'] - goal_line_x)
# 出击阈值:超过 5 米算作出击
gk_df['is_rush_out'] = gk_df['distance_from_goal'] > 5
# 计算出击范围(凸包)
from scipy.spatial import ConvexHull
rush_points = gk_df[gk_df['is_rush_out']][['x', 'y']].values
if len(rush_points) > 3:
hull = ConvexHull(rush_points)
area = hull.volume # 2D 中 volume 即面积
print(f"出击覆盖面积: {area:.1f} 平方米")
方法 3:计算机视觉提取出击
# 使用 YOLOv8 检测球员,判断守门员是否离开球门区
from ultralytics import YOLO
import cv2
model = YOLO('yolov8n.pt')
cap = cv2.VideoCapture('match.mp4')
# 单应性矩阵:将像素坐标转为球场坐标
H = cv2.findHomography(pixel_points, pitch_points)[0]
while cap.isOpened():
ret, frame = cap.read()
results = model(frame)
for box in results[0].boxes:
x1, y1, x2, y2 = box.xyxy[0]
foot = np.array([[(x1+x2)/2, y2, 1]]).T
pitch_coord = H @ foot
pitch_coord /= pitch_coord[2]
# 判断是否守门员 + 是否在禁区外
...
推荐开源项目 & 工具
| 项目 | 用途 |
|---|---|
| statsbombpy | 加载 StatsBomb 开放数据 |
| mplsoccer | 球场绘图 |
| socceraction | 事件数据转换为 SPADL |
| kloppy | 统一追踪数据接口 |
| metrica-sports | 开放追踪数据 |
| SoccerNet | 视频理解数据集 |
| roboflow/sports | CV 体育分析 |
| footballcs | 比赛结果数据 |
完整分析流程
数据获取
├─ 事件数据(StatsBomb/Wyscout)
├─ 追踪数据(Metrica/DFL)
└─ 视频数据(SoccerNet)
2. 数据预处理
├─ 坐标归一化到统一球场尺寸(105x68)
├─ 识别守门员 ID
└─ 标记出击事件
3. 出击判定
├─ 事件法:goalkeeper_type == 'Keeper Sweeper'
├─ 距离法:距球门线 > 阈值
└─ 区域法:离开禁区/球门区
4. 量化分析
├─ 平均出击距离
├─ 出击热区图
├─ 凸包面积
└─ 成功率对比
5. 可视化 & 对比
├─ 多守门员对比
├─ 时间序列变化
└─ 与球队战术关联
进阶方向
- xG 链分析:出击是否降低了对手的预期进球
- 空间控制模型:用 Voronoi 图划分守门员控制区域
- 深度学习:用 LSTM/Transformer 预测出击决策
- 对比分析:诺伊尔 vs 布冯 vs 埃德森 的出击风格
- 战术关联:高位防线球队的守门员出击范围更大
示例:对比两位守门员
import matplotlib.pyplot as plt
from mplsoccer import Pitch
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
for ax, (name, df) in zip(axes, gk_data.items()):
pitch = Pitch(pitch_type='statsbomb')
pitch.draw(ax=ax)
x = [loc[0] for loc in df['location']]
y = [loc[1] for loc in df['location']]
pitch.scatter(x, y, ax=ax, s=80, alpha=0.5)
ax.set_title(f'{name} 出击范围')