Python 分析定位球战术多样性案例
下面我用一个完整的案例,演示如何用 Python 分析足球定位球的战术多样性,核心思路是:特征工程 + 聚类 + 多样性指标。

分析框架
定位球战术多样性可以从三个维度衡量:
- 战术类型多样性:短传、长传、战术配合、直接射门等分布
- 空间多样性:落点、传球路线的空间分布
- 执行多样性:每场比赛的战术选择熵值
数据构造与预处理
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from scipy.stats import entropy
from collections import Counter
# 假设数据来自事件数据(如 StatsBomb/OPTA)
# 每条记录 = 一次定位球
data = pd.DataFrame({
'match_id': np.random.choice([f'M{i:03d}' for i in range(1, 21)], 500),
'team': np.random.choice(['A队', 'B队', 'C队', 'D队'], 500),
'set_piece_type': np.random.choice(
['角球', '任意球', '边线球', '点球', '开球'], 500,
p=[0.40, 0.30, 0.20, 0.05, 0.05]),
'start_x': np.random.uniform(0, 100, 500), # 起球点
'start_y': np.random.uniform(0, 68, 500),
'end_x': np.random.uniform(0, 100, 500), # 落点
'end_y': np.random.uniform(0, 68, 500),
'pass_length': np.random.uniform(5, 60, 500),
'num_passes': np.random.randint(0, 4, 500), # 战术配合传球数
'aerial': np.random.choice([0, 1], 500), # 是否高空球
'shot_result': np.random.choice(['进球', '射正', '偏出', '被封堵', '无射门'],
500, p=[0.05, 0.10, 0.15, 0.15, 0.55]),
})
print(data.head())
print(data.shape)
战术分类:基于规则的标签
先把定位球归类成可解释的战术类型:
def classify_tactic(row):
# 战术配合:≥2 次传球
if row['num_passes'] >= 2:
return '战术配合'
# 短传:传球长度短
if row['pass_length'] < 20 and not row['aerial']:
return '短传'
# 长传冲吊
if row['pass_length'] > 35 and row['aerial']:
return '长传冲吊'
# 直接射门(大禁区前沿任意球)
if row['set_piece_type'] == '任意球' and row['num_passes'] == 0 \
and row['pass_length'] < 10:
return '直接射门'
# 常规传中
return '常规传中'
data['tactic'] = data.apply(classify_tactic, axis=1)
print(data['tactic'].value_counts())
多样性指标计算
1 香农熵(Shannon Entropy)
熵越高,战术选择越多样:
def shannon_entropy(series):
counts = series.value_counts(normalize=True)
return entropy(counts, base=2)
# 各队战术多样性
team_diversity = data.groupby('team')['tactic'].apply(shannon_entropy)
print("各队战术熵:\n", team_diversity.sort_values(ascending=False))
2 辛普森多样性指数
def simpson_index(series):
p = series.value_counts(normalize=True)
return 1 - (p ** 2).sum()
team_simpson = data.groupby('team')['tactic'].apply(simpson_index)
print("辛普森多样性:\n", team_simpson)
3 战术组合总量
team_combos = data.groupby('team').apply(
lambda d: d.groupby(['set_piece_type', 'tactic']).ngroups
)
print("战术组合数:\n", team_combos)
空间多样性:落点聚类
用 K-Means 对落点聚类,再看各队使用了几种落点区域:
coords = data[['end_x', 'end_y']].values
scaler = StandardScaler()
coords_scaled = scaler.fit_transform(coords)
kmeans = KMeans(n_clusters=6, random_state=42, n_init=10)
data['zone'] = kmeans.fit_predict(coords_scaled)
# 各队使用的落点区域数量
team_zones = data.groupby('team')['zone'].nunique()
print("各队使用的落点区域数:\n", team_zones)
# 可视化
plt.figure(figsize=(10, 6))
sns.scatterplot(data=data, x='end_x', y='end_y', hue='zone',
palette='tab10', alpha=0.6)'定位球落点空间聚类')
plt.xlabel('X (球场长度方向)')
plt.ylabel('Y (球场宽度方向)')
plt.show()
战术类型分布可视化
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 各队战术占比
pd.crosstab(data['team'], data['tactic'], normalize='index').plot(
kind='bar', stacked=True, ax=axes[0], colormap='Set2')
axes[0].set_title('各队定位球战术构成')
axes[0].set_ylabel('占比')
axes[0].legend(bbox_to_anchor=(1, 1))
# 熵值对比
team_diversity.plot(kind='bar', ax=axes[1], color='steelblue')
axes[1].set_title('各队战术多样性 (香农熵, bit)')
axes[1].set_ylabel('熵值')
plt.tight_layout()
plt.show()
进阶:按对手类型分析多样性
# 假设有对手强度标签
opponent_strength = {t: np.random.choice(['强', '中', '弱'])
for t in data['team'].unique()}
data['opp_strength'] = data['team'].map(opponent_strength)
# 按对手强度看战术多样性
result = data.groupby(['team', 'opp_strength'])['tactic'].apply(shannon_entropy)
print(result.unstack())
# 假设检验:面对强队时是否战术更保守(多样性下降)
from scipy.stats import mannwhitneyu
strong = data[data['opp_strength'] == '强'].groupby('match_id')['tactic'].apply(shannon_entropy)
weak = data[data['opp_strength'] == '弱'].groupby('match_id')['tactic'].apply(shannon_entropy)
stat, p = mannwhitneyu(strong, weak)
print(f'Mann-Whitney U: stat={stat:.2f}, p={p:.4f}')
结果解读建议
| 指标 | 含义 | 高值说明 |
|---|---|---|
| 香农熵 | 战术分布均匀度 | 战术选择丰富、难以预测 |
| 辛普森指数 | 随机两次战术不同的概率 | 战术变化多 |
| 落点区域数 | 空间利用广度 | 覆盖多个攻击区域 |
| 战术组合数 | 类型 × 空间的组合 | 战术体系丰富 |
典型结论示例:
- A 队熵值 2.15,善用角球短传 + 任意球直接射门,多样性高
- B 队熵值 0.98,80% 依赖角球长传冲吊,战术单一易被针对
- 面对强队时整体熵值下降 → 说明球队倾向于保守战术
数据来源建议
如果你要做真实分析,推荐数据源:
| 来源 | 说明 |
|---|---|
| StatsBomb Open Data | 免费,含 360 数据,可获取定位球细节 |
| FBref | 免费,可爬取 |
| Wyscout API | 商业,数据粒度细 |
| Opta / Second Spectrum | 职业级,需授权 |
加载 StatsBomb 数据示例:
from statsbombpy import sb
events = sb.events(match_id=3788741)
set_pieces = events[events['period'] == 1].query(
"pass_type in ['Corner', 'Free Kick'] or shot_type == 'Free Kick'")
扩展方向
- 网络分析:用
networkx构建传球网络,看战术配合结构 - 时间维度:比赛不同时段的多样性变化(体能下降后是否更依赖长传)
- 对手针对性:用对手维度做条件熵
- 机器学习:XGBoost 预测定位球进球概率,SHAP 解释战术价值
- 对比联赛:不同联赛的定位球多样性基线