本文目录导读:

分析球员跑动热点图(Heatmap)通常涉及数据获取、坐标映射和可视化三个核心步骤,由于国际足联(FIFA)和主流联赛的官方追踪数据(如STATS、Opta)多为商业机密,普通开发者常用公开的赛事数据或计算机视觉(CV)来生成。
以下提供一个从零开始的实用脚本分析方案,涵盖基于公开数据(如StatsBomb)和基于视频(半自动)两种主流路径。
基于公开事件数据的静态热点图(最常用、最准确)
这是最推荐的方案,如果你能获取到包含X/Y坐标的事件数据(例如StatsBomb的公开数据集),脚本会非常简单。
适用场景:赛后分析、战术复盘。
核心库:pandas(数据处理)、matplotlib + mplsoccer(足球场绘图)。
环境准备
pip install pandas matplotlib mplsoccer
核心脚本逻辑
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mplsoccer import Pitch, VerticalPitch
# --- 假设你已加载数据:DataFrame包含 [player_name, x, y, type] ---
# 数据来源可以是 StatsBomb 开源数据 或 你自己抓取的坐标数据
# df = pd.read_csv('match_events.csv')
def analyze_heatmap(df, player_name='某球员'):
# 1. 数据过滤:筛选目标球员的所有触球/跑动事件(通常排除门将)
player_df = df[(df['player_name'] == player_name) & (df['type'] != 'Goal')]
if player_df.empty:
print(f"未找到球员 {player_name} 的数据")
return
# 2. 数据清洗:过滤异常的坐标点(通常在0-120,0-80之间)
player_df = player_df[(player_df['x'] >= 0) & (player_df['x'] <= 120) &
(player_df['y'] >= 0) & (player_df['y'] <= 80)]
# 3. 绘制球场和热力图
pitch = Pitch(pitch_type='statsbomb', pitch_color='grass', line_color='white',
stripe=True) # 使用 StatsBomb 标准球场
fig, ax = pitch.draw(figsize=(12, 8))
# 关键点:使用 KDE 核密度估计来生成平滑的热力图
# 注意:kdeplot 的 x, y 坐标需要转换比例(StatsBomb的x最大120,y最大80)
sns.kdeplot(
data=player_df[['x', 'y']],
x='x', y='y', # 或者使用 x='x', y='y'
ax=ax,
fill=True, # 填充颜色
cmap='hot', # 使用热度色系,如 'hot', 'inferno', 'plasma'
levels=50, # 等高线密度
thresh=0.05, # 过滤掉低于5%密度的区域,让图更干净
alpha=0.8,
bw_adjust=0.8 # 带宽调整,数值越小越尖锐,越大越平滑
)
# 4. 标注球员名字
ax.set_title(f'{player_name} Run Heatmap', fontsize=16, fontweight='bold')
# 5. 保存图片
plt.savefig(f'heatmap_{player_name}.png', dpi=300, bbox_inches='tight')
plt.show()
print(f"热点图已保存为 heatmap_{player_name}.png")
# 示例调用
# analyze_heatmap(df, 'Lionel Messi')
核心点解析:
Pitch对象:mplsoccer库自带了球场绘制,无需手动画线。kdeplot:这是生成热力图的关键函数,它将离散的跑动坐标点转换为连续的概率密度分布,红色表示高频活动区域。- 坐标系转换:StatsBomb数据用的是
x: 0-120,y: 0-80。
基于视频追踪的半自动脚本(进阶与实用场景)
如果你没有官方数据,只有比赛录像,就需要用目标跟踪(如YOLO)来完成。
适用场景:业余联赛分析、无法拿到转播数据的比赛。
核心库:opencv-python、ultralytics(YOLOv8)、numpy。
脚本思路:这比方案一复杂得多,这里给出简化的流程骨架:
import cv2
from ultralytics import YOLO
import numpy as np
# 1. 加载预训练模型(需训练或下载能识别足球球员的权重)
model = YOLO('yolov8x.pt')
def video_to_heatmap(video_path, target_player_id=1):
cap = cv2.VideoCapture(video_path)
positions = [] # 存储坐标
# 2. 需要在视频第一帧手动选定你要分析的球员
ret, frame = cap.read()
bbox = cv2.selectROI("Select Player", frame) # 手动框选目标球员
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 3. 将裁剪区域送入模型或使用光流法跟踪
# 简易方法:使用模板匹配或均值漂移(CamShift)
# 复杂方法:每帧检测所有球员,通过IoU匹配ID
# 伪代码:假设拿到该球员中心点坐标 (cx, cy)
# positions.append((cx, cy))
# 4. 透视变换(可选):将像素坐标映射到2D战术板的球场坐标
# 这一步需要利用球场四条边线或角球点建立单应性矩阵
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 5. 将收集的像素坐标转换为2D平面坐标后,使用相同的 kdeplot 绘制
# 注意:这里的坐标尺度可能不是0-120,需要归一化
cap.release()
cv2.destroyAllWindows()
关键点:如何将数据变成真正有用的“跑动方向”?
单纯的静态热力图只能显示活动范围,如果希望展示跑动方向,建议叠加箭头或轨迹线。
增强脚本建议:
- 增加时间戳:如果数据包含时间,可以按时间排序,用线连接坐标点,并在头部画箭头,即可生成“跑动轨迹图”。
- 区分攻防:判断当球在前场时和在后场时,球员的坐标分布,分别生成“防守热力图”和“进攻热力图”,对比分析。
- 速度指标:如果数据频率高(如25Hz),计算相邻两帧的距离差与时间差,得出速度,并过滤出冲刺时的坐标点来画图,用于分析高强跑动。
总结与实用建议
- 优先找数据:如果你是做实用分析,先想想哪里能拿到带坐标的数据。StatsBomb开源数据是目前最好用的免费资源,支持英超、西甲、世界杯等。
- 不建议视频识别:除非你有极强的CV背景,否则从视频还原坐标精度极低且耗时巨大,不适合日常分析。
- 可视化细节:
- 推荐使用
cmap='viridis'(色盲友好)或者cmap='plasma'。 - 建议将
thresh参数调高一些(0.1),可以去除噪音,让有意义的“热点”更突出。 - 如果分析守门员,记得单独处理球场禁区范围。
- 推荐使用
如果你能提供具体的数据格式示例(比如有哪些列),我可以帮你写出完全适配的、可以直接跑通的完整代码。