python案例认为边路传中是得分利器吗?

wen python案例 4

本文目录导读:

python案例认为边路传中是得分利器吗?

  1. 分析思路
  2. 环境准备
  3. 完整代码案例
  4. 识别边路传中
  5. 关联传中与射门
  6. 对比分析
  7. 可视化
  8. 用大数据集验证
  9. 典型分析结论
  10. 进阶方向

这是一个很有意思的足球数据分析问题,下面用 Python 来做一个案例分析,看看边路传中到底是不是得分利器。


分析思路

我们需要验证的核心假设是:

边路传中能显著提高进球概率吗?

分析框架:

维度 说明
数据来源 StatsBomb 开放数据(免费)
传中定义 从边路区域(wing)传入禁区的传球
对比对象 传中后的射门转化率 vs 非传中的射门转化率
关键指标 xG(预期进球)、进球率、射门次数

环境准备

pip install statsbombpy pandas numpy matplotlib seaborn

完整代码案例

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from statsbombpy import sb
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# ========== 1. 加载数据 ==========
# 以 2018 世界杯决赛为例(法国 vs 克罗地亚)
matches = sb.matches(competition_id=43, season_id=3)  # 2018世界杯
match_id = matches.iloc[0]['match_id']
events = sb.events(match_id=match_id)
print(f"比赛事件总数: {len(events)}")
print(events['type'].value_counts().head(10))

识别边路传中

# ========== 2. 提取传中事件 ==========
def classify_cross(row):
    """判断是否为边路传中"""
    if row['type'] != 'Pass':
        return False
    loc = row['location']
    if not isinstance(loc, list):
        return False
    x, y = loc[0], loc[1]
    # 边路区域定义:y < 20 或 y > 60(场地宽80)
    is_wing = (y < 20) or (y > 60)
    # 前场区域:x > 60
    is_advanced = x > 60
    # 传球类型为传中
    pass_type = row.get('pass_type', '')
    is_cross = (pass_type == 'Cross') if pd.notna(pass_type) else False
    return is_wing and is_advanced and is_cross
events['is_cross'] = events.apply(classify_cross, axis=1)
crosses = events[events['is_cross']]
print(f"边路传中次数: {len(crosses)}")

关联传中与射门

# ========== 3. 找到传中后的射门 ==========
def find_shot_after_cross(events, crosses):
    """追踪传中后是否形成射门"""
    results = []
    for _, cross in crosses.iterrows():
        cross_id = cross['id']
        cross_poss = cross['possession']
        # 同一控球回合内的射门
        same_poss = events[
            (events['possession'] == cross_poss) & 
            (events['type'] == 'Shot')
        ]
        if len(same_poss) > 0:
            shot = same_poss.iloc[0]
            results.append({
                'cross_id': cross_id,
                'shot_xg': shot.get('shot_statsbomb_xg', 0),
                'is_goal': shot['shot_outcome'] == 'Goal'
            })
    return pd.DataFrame(results)
cross_shots = find_shot_after_cross(events, crosses)
print(f"传中形成射门数: {len(cross_shots)}")
if len(cross_shots) > 0:
    print(f"传中转化进球数: {cross_shots['is_goal'].sum()}")

对比分析

# ========== 4. 对比所有射门来源 ==========
all_shots = events[events['type'] == 'Shot'].copy()
all_shots['xg'] = all_shots['shot_statsbomb_xg'].fillna(0)
all_shots['is_goal'] = all_shots['shot_outcome'] == 'Goal'
# 标记是否来自传中
cross_shot_ids = set(cross_shots['cross_id']) if len(cross_shots) > 0 else set()
def shot_from_cross(shot):
    poss = shot['possession']
    related = events[
        (events['possession'] == poss) & 
        (events['is_cross'] == True)
    ]
    return len(related) > 0
all_shots['from_cross'] = all_shots.apply(shot_from_cross, axis=1)
# 分组统计
summary = all_shots.groupby('from_cross').agg(
    射门数=('id', 'count'),
    进球数=('is_goal', 'sum'),
    平均xG=('xg', 'mean'),
    总xG=('xg', 'sum')
).reset_index()
summary['转化率'] = summary['进球数'] / summary['射门数']
print("\n===== 传中 vs 非传中 射门效率对比 =====")
print(summary.to_string(index=False))

可视化

# ========== 5. 可视化 ==========
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 图1:射门数对比
axes[0].bar(summary['from_cross'].astype(str), summary['射门数'], 
            color=['#3498db', '#e74c3c'])
axes[0].set_title('射门次数对比')
axes[0].set_xlabel('是否来自传中')
axes[0].set_ylabel('射门数')
# 图2:平均xG对比
axes[1].bar(summary['from_cross'].astype(str), summary['平均xG'],
            color=['#3498db', '#e74c3c'])
axes[1].set_title('平均 xG 对比')
axes[1].set_xlabel('是否来自传中')
axes[1].set_ylabel('平均 xG')
# 图3:转化率对比
axes[2].bar(summary['from_cross'].astype(str), summary['转化率'],
            color=['#3498db', '#e74c3c'])
axes[2].set_title('射门转化率对比')
axes[2].set_xlabel('是否来自传中')
axes[2].set_ylabel('转化率')
plt.tight_layout()
plt.savefig('cross_analysis.png', dpi=150)
plt.show()

用大数据集验证

单场比赛样本太小,建议用整个赛季数据:

# ========== 6. 多场比赛批量分析 ==========
def analyze_season(competition_id, season_id):
    matches = sb.matches(competition_id=competition_id, season_id=season_id)
    all_results = []
    for match_id in matches['match_id'].head(50):  # 取前50场
        try:
            ev = sb.events(match_id=match_id)
            ev['is_cross'] = ev.apply(classify_cross, axis=1)
            shots = ev[ev['type'] == 'Shot'].copy()
            shots['xg'] = shots['shot_statsbomb_xg'].fillna(0)
            shots['is_goal'] = shots['shot_outcome'] == 'Goal'
            # 每场统计
            for _, shot in shots.iterrows():
                poss = shot['possession']
                has_cross = len(ev[(ev['possession']==poss) & (ev['is_cross'])]) > 0
                all_results.append({
                    'match_id': match_id,
                    'from_cross': has_cross,
                    'xg': shot['xg'],
                    'is_goal': shot['is_goal']
                })
        except Exception as e:
            continue
    return pd.DataFrame(all_results)
# 分析 2018 世界杯
df = analyze_season(43, 3)
result = df.groupby('from_cross').agg(
    射门数=('xg', 'count'),
    进球数=('is_goal', 'sum'),
    平均xG=('xg', 'mean'),
    总xG=('xg', 'sum')
)
result['转化率'] = result['进球数'] / result['射门数']
print("\n===== 2018世界杯 传中效果分析 =====")
print(result)

典型分析结论

根据 StatsBomb 等公开数据的研究,通常会发现:

指标 来自传中 非传中(阵地/反击)
平均 xG 06 ~ 0.09 10 ~ 0.15
转化率 ~8-10% ~11-15%
射门占比 25-35% 65-75%

核心结论:

  1. 边路传中数量多,但效率偏低 — 单次传中的期望进球值通常只有 0.05-0.10
  2. 传中依赖度高 ≠ 得分利器 — 很多球队传中占比 30%+,但只贡献 20% 左右的进球
  3. 高质量传中才有价值 — 倒三角、低平球、后点传中效率明显高于盲目高吊
  4. 顶级球队趋势 — 曼城、利物浦等更倾向于肋部渗透而非传统边路传中

一句话总结:

边路传中是战术手段而非得分利器,它是保底进攻方式,但单次效率远低于直塞、反击和定位球,真正的得分利器是高质量的机会创造,传中只是其中一种低效选项。


进阶方向

  • 加入 防守压力 因子(防守球员距离)
  • 区分 传中类型(高球、低平、倒三角)
  • 用 逻辑回归/贝叶斯 建模预测进球概率
  • 结合 球员能力(传中精度)

需要我针对某个具体联赛或球队做完整分析吗?

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