Python案例:分析球员之间的默契程度
分析球员默契度是体育数据分析的热门话题,下面我从方法论 → 数据构建 → 代码实现 → 可视化完整讲一遍。

默契度的量化维度
球员之间的默契可以从多个角度衡量:
| 维度 | 含义 | 数据来源 |
|---|---|---|
| 传球关联 | A传给B的频率、成功率 | 传球网络数据 |
| 共同出场 | 同时在场时间 | 换人记录 |
| 配合效率 | 同场时球队净胜分/进球 | 比赛事件 |
| 传跑连线 | 助攻/进球组合 | 事件流数据 |
| 空间互补 | 跑动热区重叠/互补 | 追踪数据 |
下面用传球网络 + 共同出场 + 配合效率做一个综合案例。
构造模拟数据
import pandas as pd
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import seaborn as sns
from itertools import combinations
np.random.seed(42)
players = [f"P{i}" for i in range(1, 12)] # 11名球员
# 1. 传球数据:from, to, count, success
passes = []
for a, b in combinations(players, 2):
if np.random.rand() < 0.7: # 70%的球员对有传球
cnt = np.random.randint(1, 30)
succ = np.random.randint(int(cnt*0.5), cnt+1)
passes.append([a, b, cnt, succ])
passes.append([b, a, np.random.randint(1, 30), None]) # 反向也加
df_pass = pd.DataFrame(passes, columns=["from", "to", "count", "success"])
df_pass["success"] = df_pass["success"].fillna(df_pass["count"]*0.8).astype(int)
df_pass["success_rate"] = df_pass["success"] / df_pass["count"]
# 2. 共同出场时间(分钟)
co_play = {}
for a, b in combinations(players, 2):
co_play[(a, b)] = np.random.randint(10, 90)
df_co = pd.DataFrame(
[(a, b, t) for (a, b), t in co_play.items()],
columns=["p1", "p2", "minutes"]
)
核心计算:默契度评分
def compute_chemistry(df_pass, df_co, players):
"""综合传球 + 共同出场 计算默契度"""
result = []
# 归一化参数
max_pass = df_pass["count"].max()
max_min = df_co["minutes"].max()
for a, b in combinations(players, 2):
# A→B 和 B→A 的传球
ab = df_pass[(df_pass["from"]==a) & (df_pass["to"]==b)]
ba = df_pass[(df_pass["from"]==b) & (df_pass["to"]==a)]
pass_cnt = ab["count"].sum() + ba["count"].sum()
pass_sr = (
(ab["success_rate"].mean() if len(ab) else 0) +
(ba["success_rate"].mean() if len(ba) else 0)
) / 2
# 共同出场
co = df_co[
((df_co["p1"]==a) & (df_co["p2"]==b)) |
((df_co["p1"]==b) & (df_co["p2"]==a))
]["minutes"].values
co_min = co[0] if len(co) else 0
# 加权公式(可调权重)
# 传球频次(40%) + 传球成功率(30%) + 共同出场(30%)
score = (
0.4 * (pass_cnt / max_pass) +
0.3 * pass_sr +
0.3 * (co_min / max_min)
) * 100
result.append({
"player1": a, "player2": b,
"pass_count": pass_cnt,
"pass_success_rate": round(pass_sr, 2),
"co_minutes": co_min,
"chemistry_score": round(score, 2)
})
return pd.DataFrame(result).sort_values("chemistry_score", ascending=False)
df_chem = compute_chemistry(df_pass, df_co, players)
print(df_chem.head(10))
输出示例:
player1 player2 pass_count pass_success_rate co_minutes chemistry_score
0 P1 P5 48 0.82 85 88.35
1 P3 P7 42 0.79 78 80.12
...
可视化
默契度热力图
# 构造对称矩阵
matrix = pd.DataFrame(0.0, index=players, columns=players)
for _, r in df_chem.iterrows():
matrix.loc[r.player1, r.player2] = r.chemistry_score
matrix.loc[r.player2, r.player1] = r.chemistry_score
plt.figure(figsize=(9, 7))
sns.heatmap(matrix, annot=True, fmt=".0f", cmap="YlOrRd", square=True,
cbar_kws={"label": "默契度"})"球员默契度热力图")
plt.tight_layout()
plt.show()
传球网络图(边的粗细=默契度)
G = nx.Graph()
for _, r in df_chem.iterrows():
if r.chemistry_score > 30: # 只画强连接
G.add_edge(r.player1, r.player2, weight=r.chemistry_score)
pos = nx.spring_layout(G, seed=42, k=0.8)
weights = [G[u][v]["weight"]/10 for u, v in G.edges()]
plt.figure(figsize=(9, 7))
nx.draw_networkx_nodes(G, pos, node_size=800, node_color="skyblue")
nx.draw_networkx_labels(G, pos, font_size=12, font_weight="bold")
nx.draw_networkx_edges(G, pos, width=weights, alpha=0.6, edge_color="gray")
edge_labels = {(u,v): f"{G[u][v]['weight']:.0f}" for u,v in G.edges()}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=8)"球队传球默契网络")
plt.axis("off")
plt.show()
Top 10 默契组合条形图
top10 = df_chem.head(10).copy()
top10["pair"] = top10["player1"] + " ↔ " + top10["player2"]
plt.figure(figsize=(10, 6))
sns.barplot(data=top10, x="chemistry_score", y="pair", palette="viridis")
plt.xlabel("默契度评分")"Top 10 最佳默契组合")
plt.tight_layout()
plt.show()
进阶方向
如果要用真实数据,可以接入:
-
StatsBomb / Opta 事件数据(JSON格式,含每次传球坐标)
from statsbombpy import sb events = sb.events(match_id=3788741) passes = events[events["type"] == "Pass"]
-
追踪数据(如 Metrica Sports 公开数据集)计算:
- 空间互补度:两人跑动热区重叠 = 协同防守强度
- 相对距离:场均保持 x 米内的时长
- 传球前后位置变化:跑位配合
-
图神经网络(GNN) 建模传球网络,用节点嵌入相似度衡量默契。
-
时间衰减加权:
# 近期传球权重更高 score = pass_cnt * np.exp(-days_ago / 30)
结果解读建议
- 高默契 + 高传球量 → 战术核心组合,不可拆分
- 低默契 + 高同场时间 → 位置冲突,考虑调整
- 高默契但同场少 → 阵容被埋没,可增加同时出场机会
如果你告诉我你手头的数据格式(StatsBomb、自定义CSV、还是实时追踪),我可以针对性地给出完整可跑的代码。