本文目录导读:

识别球队的战术风格,本质上是把比赛中的行为数据映射到几个关键维度上,下面给出一套可直接落地的脚本思路,从数据采集到风格标签输出。
先定义战术风格的维度
不要一上来就分"传控/防反"这种大标签,先用连续变量描述:
| 维度 | 指标示例 | 含义 |
|---|---|---|
| 控球倾向 | possession%、传球数/90 | 是否主导球权 |
| 推进方式 | 长传占比、场均推进距离、中后场传球占比 | 地面渗透 vs 长传冲吊 |
| 进攻节奏 | 每次进攻用时、场均射门、PPDA | 快攻 vs 阵地 |
| 防守强度 | PPDA、抢断位置、犯规数 | 高位逼抢 vs 低位退防 |
| 空间利用 | 边路进攻占比、传中数、禁区内触球 | 边中结合 vs 中路 |
| 转换倾向 | 由守转攻平均时长、反击射门占比 | 反击型 vs 控制型 |
数据来源
# 常用数据源 # - StatsBomb Open Data (免费, JSON事件流) # - FBref (球队/球员赛季聚合数据, 可爬) # - Understat (xG相关, 部分联赛) # - football-data.org API (基础赛程比分) # - WhoScored / SofaScore (需注意ToS)
以 StatsBomb 事件数据为例:
from statsbombpy import sb matches = sb.matches(competition_id=11, season_id=90) # 西甲某赛季 events = sb.events(match_id=matches.iloc[0]['match_id'])
特征工程脚本骨架
import pandas as pd
import numpy as np
def extract_team_features(events: pd.DataFrame, team: str) -> dict:
ev = events[events['team'] == team].copy()
opp = events[events['team'] != team].copy()
passes = ev[ev['type'] == 'Pass']
total_passes = len(passes)
# 1. 控球率
possession = len(ev) / (len(ev) + len(opp))
# 2. 长传占比 (传球距离 > 32m)
if 'pass_length' in passes:
long_ball_ratio = (passes['pass_length'] > 32).mean()
else:
long_ball_ratio = np.nan
# 3. 进攻方向偏好 (边路 vs 中路)
if 'pass_end_x' in passes and 'pass_end_y' in passes:
# y 从 0-80, 中间 18-62 为中路
central = passes['pass_end_y'].between(18, 62).mean()
wide = 1 - central
else:
central, wide = np.nan, np.nan
# 4. 推进性传球占比 (向前 > 10m)
if 'pass_end_x' in passes and 'x' in passes:
progressive = ((passes['pass_end_x'] - passes['x']) > 10).mean()
# 5. PPDA (对手每次防守动作允许的传球数, 越低越高位逼抢)
opp_def_actions = len(opp[opp['type'].isin(
['Pressure','Interception','Tackle','Foul Committed'])])
ppda = total_passes / opp_def_actions if opp_def_actions else np.nan
# 6. 平均进攻发起位置
carries = ev[ev['type'] == 'Carry']
avg_x = ev['x'].mean() if 'x' in ev else np.nan
return {
'team': team,
'possession': possession,
'long_ball_ratio': long_ball_ratio,
'central_ratio': central,
'wide_ratio': wide,
'progressive_pass_ratio': progressive,
'ppda': ppda,
'avg_x': avg_x,
}
风格分类
方案 A:规则阈值(可解释性强)
def classify_style(f):
if f['possession'] > 0.58 and f['ppda'] < 10 and f['long_ball_ratio'] < 0.15:
return '控球压迫型' # 类似曼城/巴萨
if f['possession'] < 0.45 and f['ppda'] > 15 and f['progressive_pass_ratio'] > 0.25:
return '低位防反型' # 类似穆里尼奥球队
if f['long_ball_ratio'] > 0.25 and f['wide_ratio'] > 0.55:
return '长传边路型'
if f['ppda'] < 8 and f['possession'] < 0.5:
return '高压反击型' # 类似克洛普早期
return '均衡型'
方案 B:无监督聚类(更客观)
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
def cluster_styles(features_df: pd.DataFrame, k=5):
cols = ['possession','long_ball_ratio','central_ratio',
'progressive_pass_ratio','ppda','avg_x']
X = StandardScaler().fit_transform(features_df[cols].fillna(0))
km = KMeans(n_clusters=k, n_init=10, random_state=42)
features_df['cluster'] = km.fit_predict(X)
# 查看每个簇的均值, 人工命名
print(features_df.groupby('cluster')[cols].mean())
return features_df
聚类后每个簇的均值就是风格"指纹",人工对照命名即可。
方案 C:多维度雷达打分
输出 0–100 的风格分数,更适合作图:
def style_radar(f):
return {
'控球': min(100, f['possession'] * 150),
'高位逼抢': max(0, 100 - f['ppda'] * 5),
'直接性': min(100, f['long_ball_ratio'] * 300),
'边路倾向': min(100, f['wide_ratio'] * 150),
'推进性': min(100, f['progressive_pass_ratio'] * 300),
}
进阶:时序 + 对手校正
单一赛季均值会失真,建议:
- 滚动窗口:每 5 场滚动计算,看风格漂移
- 对手强度校正:用对手 PPDA / 控球率做回归,提取残差
- 情境分档:领先 / 落后 / 平局分别统计——很多球队落后时风格突变
- xG 链:把进球序列连成 possession chain,看每次进攻的推进路径
# 示例: 按比分状态分组
def split_by_scoreline(events):
# 需先从 shot/goal 事件推导比分时间线
...
工程化建议
- 事件数据用
polars或duckdb处理,比 pandas 快数倍 - 特征表落盘 Parquet,方便回测
- 加
matplotlib/mplsoccer画热图和雷达图,一眼看出风格 - 定期跑批:每周更新,监控风格变化(换帅、转会窗后常变)
一句话总结
采集事件 → 抽 6 个维度特征 → 标准化 → 规则或聚类打标签 → 结合情境和时序做校正 → 可视化输出。
核心三件套:possession、PPDA、long_ball_ratio,能把 80% 的战术风格区分开。
需要我针对某个具体数据源(如 StatsBomb / FBref)写一份完整可运行的脚本吗?
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播