python案例怎么看两队边路突破能力对比?

wen python案例 3

两队边路突破能力对比(Python案例)

下面用 StatsBomb 开放数据 + mplsoccer 做一个完整的边路突破能力对比案例,涵盖数据获取、指标构建、可视化。

python案例怎么看两队边路突破能力对比?

核心思路

"边路突破能力"不是一个官方统计项,需要自己构造指标,常用组合:

指标 含义
边路区域触球/推进次数 活动量
边路 1v1 过人(dribble)成功次数 突破成功率
边路带球推进距离(carry progress) 推进威胁
边路传中/倒三角传球 突破后的产出
边路进入进攻三区次数 最终威胁区域

关键在于:先按 x 坐标把球场切成左/中/右三块,再统计边路区域的事件。

数据准备

pip install statsbombpy mplsoccer pandas numpy matplotlib
from statsbombpy import sb
import pandas as pd
# 以曼城 vs 皇马(2022欧冠)为例
matches = sb.matches(competition_id=16, season_id=44)  # 欧冠2021/22
match_id = matches.iloc[0]['match_id']
events = sb.events(match_id=match_id)

事件表关键字段:type、location、player、team、under_pressure、pass_end_location、carry_end_location 等。

构造边路突破指标

import numpy as np
def side_zone(x):
    """按 x 坐标划分左右边路(StatsBomb 球场 120x80,x 为纵向)"""
    if x < 20 or x > 100:      # 两端禁区前后区域
        return 'central'
    if x < 40 or x > 80:
        return 'wide'
    return 'central'
# 提取坐标
def get_xy(loc):
    if isinstance(loc, list) and len(loc) == 2:
        return loc[0], loc[1]
    return np.nan, np.nan
events[['x', 'y']] = events['location'].apply(
    lambda l: pd.Series(get_xy(l))
)
# 只看边路(y 靠近边线:y<18 或 y>62 视为边路通道)
def is_wing(y):
    return (y < 18) or (y > 62)
events['on_wing'] = events['y'].apply(lambda v: is_wing(v) if pd.notna(v) else False)
# 1) 边路带球(carry)
carries = events[(events['type'] == 'Carry') & events['on_wing']].copy()
carries['carry_dist'] = carries.apply(
    lambda r: np.hypot(
        r['carry_end_location'][0] - r['x'],
        r['carry_end_location'][1] - r['y']
    ) if isinstance(r['carry_end_location'], list) else 0, axis=1
)
# 2) 边路过人(dribble)
dribbles = events[(events['type'] == 'Dribble') & events['on_wing']].copy()
dribbles['success'] = dribbles['dribble_outcome'] == 'Complete'
# 3) 边路传中(cross,进入禁区)
crosses = events[(events['type'] == 'Pass') & events['on_wing']].copy()
def is_cross(r):
    if not isinstance(r['pass_end_location'], list): return False
    ex, ey = r['pass_end_location']
    return ex > 102 and 18 < ey < 62
crosses['is_cross'] = crosses.apply(is_cross, axis=1)

汇总对比表:

summary = pd.DataFrame({
    '边路带球次数': carries.groupby('team').size(),
    '边路带球总距离': carries.groupby('team')['carry_dist'].sum().round(0),
    '边路过人尝试': dribbles.groupby('team').size(),
    '边路过人成功': dribbles[dribbles['success']].groupby('team').size(),
    '边路传中': crosses[crosses['is_cross']].groupby('team').size(),
}).fillna(0)
summary['过人成功率%'] = (summary['边路过人成功'] / summary['边路过人尝试'] * 100).round(1)
print(summary.T)

输出大致形如:

team           A队    B队
边路带球次数     58    41
边路带球总距离   620   430
边路过人成功     12     6
过人成功率%     52.2   33.3
边路传中         9      4

可视化对比

1)雷达图(多指标综合对比)

import matplotlib.pyplot as plt
from math import pi
metrics = ['边路带球次数', '边路带球总距离', '边路过人成功', '边路传中']
teams = summary.index.tolist()
# 归一化到 0-1
norm = summary[metrics] / summary[metrics].max()
angles = [n / len(metrics) * 2 * pi for n in range(len(metrics))]
angles += angles[:1]
fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(polar=True))
for team in teams:
    vals = norm.loc[team].tolist()
    vals += vals[:1]
    ax.plot(angles, vals, label=team)
    ax.fill(angles, vals, alpha=0.15)
ax.set_xticks(angles[:-1])
ax.set_xticklabels(metrics)
ax.legend(loc='upper right', bbox_to_anchor=(1.3, 1.1))'两队边路突破能力雷达对比')
plt.show()

2)边路活动热力图(mplsoccer)

from mplsoccer import Pitch
import matplotlib.pyplot as plt
pitch = Pitch(pitch_type='statsbomb', line_zorder=2)
fig, axes = pitch.draw(nrows=1, ncols=2, figsize=(14, 6))
for ax, team in zip(axes, teams):
    team_carries = carries[carries['team'] == team]
    bin_stat = pitch.bin_statistic(
        team_carries['x'], team_carries['y'],
        statistic='count', bins=(12, 8)
    )
    pitch.heatmap(bin_stat, ax=ax, cmap='Reds')
    ax.set_title(f'{team} 边路带球分布')
plt.show()

3)边路推进折线(按15分钟区间)

events['minute_bin'] = (events['minute'] // 15) * 15
wing_carry_by_time = (
    carries.assign(bin=(carries['minute'] // 15) * 15)
           .groupby(['bin', 'team']).size().unstack(fill_value=0)
)
wing_carry_by_time.plot(kind='bar', figsize=(10, 5))
plt.xlabel('比赛分钟(每15分钟)')
plt.ylabel('边路带球次数')'边路突破活跃度的时段分布')
plt.legend(title='球队')
plt.show()

进阶建议(真实分析场景)

  1. 区分左右路:y<18 算右路(StatsBomb y 从下到上),y>62 算左路,可以拆成"左路突破 vs 右路突破"两栏对比。
  2. 考虑对手强度:同一球队打强队/弱队边路数据差异大,需样本累积(多场比赛合并)。
  3. 用 xT / VAEP 量化威胁:单纯计数会高估"原地无效带球",可引入 xT(expected threat) 或 VAEP 框架,把每次边路推进算成实际创造价值。
  4. 压迫下突破:加上 under_pressure == True 过滤,得到"高压下边路突破",这更能反映真实突破能力。
  5. 球员级下钻:用 player 字段 groupby,找出各队边路突破核心球员。

小结

步骤 关键代码 目的
1 events['type']=='Carry' + y 坐标过滤 锁定边路事件
2 计算 carry 距离 / 过人成功率 构造突破指标
3 groupby('team') 汇总 两队横向对比
4 雷达图 + 热力图 + 时序图 多维可视化
5 加入 xT / 压迫条件 提升分析质量

这个模板可以直接套用到任意 StatsBomb 比赛或你的自采数据(只需字段对齐:type、location、team、minute),如果你有具体球队/联赛数据,我可以帮你把代码改成对应格式。

上一篇python案例统计转身过人次数谁多?

下一篇当前分类已是最新一篇

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

友情链接攻略窝 · 礼包窝 · 配置窝 · 换算窝 · 算法窝 · 查文窝 · 手册窝 · 问答窝 · 全文搜 · 手册库
相关站点礼包窝 · 查文窝