python案例如何分析球员之间的默契程度?

wen python案例 2

Python案例:分析球员之间的默契程度

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

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()

进阶方向

如果要用真实数据,可以接入:

  1. StatsBomb / Opta 事件数据(JSON格式,含每次传球坐标)

    from statsbombpy import sb
    events = sb.events(match_id=3788741)
    passes = events[events["type"] == "Pass"]
  2. 追踪数据(如 Metrica Sports 公开数据集)计算:

    • 空间互补度:两人跑动热区重叠 = 协同防守强度
    • 相对距离:场均保持 x 米内的时长
    • 传球前后位置变化:跑位配合
  3. 图神经网络(GNN) 建模传球网络,用节点嵌入相似度衡量默契。

  4. 时间衰减加权

    # 近期传球权重更高
    score = pass_cnt * np.exp(-days_ago / 30)

结果解读建议

  • 高默契 + 高传球量 → 战术核心组合,不可拆分
  • 低默契 + 高同场时间 → 位置冲突,考虑调整
  • 高默契但同场少 → 阵容被埋没,可增加同时出场机会

如果你告诉我你手头的数据格式(StatsBomb、自定义CSV、还是实时追踪),我可以针对性地给出完整可跑的代码

抱歉,评论功能暂时关闭!