这个python案例如何看半场结束前攻势?

wen python案例 4

如何分析足球比赛半场结束前的攻势

你提到的"看半场结束前攻势",在足球数据分析中通常指分析上半场最后5-10分钟(如35'-45'+)的进攻强度,下面我给你一套完整的 Python 分析思路和代码框架。

这个python案例如何看半场结束前攻势?

核心思路

半场结束前的攻势通常体现在几个维度:

  1. 射门频率 — 最后10分钟射门数 vs 前35分钟均值
  2. 控球位置 — 危险区域(前场1/3)触球/传球占比
  3. 角球/定位球 — 半场前角球数激增
  4. 进攻节奏 — 传球速度、推进距离
  5. xG(预期进球) — 半场前xG累积

数据准备

假设你用 StatsBomb 开放数据(免费)或 football-data.org:

import pandas as pd
import numpy as np
from statsbombpy import sb
# 加载一场比赛的事件数据
matches = sb.matches(competition_id=11, season_id=90)  # 举例
events = sb.events(match_id=matches.iloc[0]['match_id'])
# 关键字段: minute, second, type, team, location, pass_end_location
events['minute'] = events['minute'].astype(int)
events['period'] = events['period'].astype(int)

核心分析代码

划分时间窗口

# 上半场最后10分钟
last10 = events[(events['period']==1) & (events['minute']>=35)]
first35 = events[(events['period']==1) & (events['minute']<35)]

射门强度对比

def attack_intensity(df, team):
    shots = df[(df['type']=='Shot') & (df['team']==team)]
    xg = shots['shot_statsbomb_xg'].sum() if 'shot_statsbomb_xg' in shots else 0
    return {
        '射门数': len(shots),
        'xG': round(xg, 2),
        '射门/分钟': round(len(shots)/10, 2)
    }
for team in events['team'].unique():
    print(team, 
          '前35分钟:', attack_intensity(first35, team),
          '| 最后10分钟:', attack_intensity(last10, team))

危险区域触球(前场1/3)

def final_third_touches(df, team):
    # 球场坐标: x 0-120,前场1/3 是 x>=80
    df = df[(df['team']==team) & (df['location'].notna())]
    df = df.copy()
    df['x'] = df['location'].apply(lambda l: l[0] if isinstance(l, list) else np.nan)
    return (df['x']>=80).sum()
for team in events['team'].unique():
    print(f"{team}: 前场触球 前35min={final_third_touches(first35, team)}, "
          f"末10min={final_third_touches(last10, team)}")

可视化"攻势时间轴"

import matplotlib.pyplot as plt
# 按分钟统计每队射门数
shot_min = events[events['type']=='Shot'].groupby(['minute','team']).size().unstack(fill_value=0)
# 只取上半场
shot_min = shot_min[shot_min.index <= 45]
fig, ax = plt.subplots(figsize=(12,4))
shot_min.plot(kind='bar', stacked=True, ax=ax)
ax.axvspan(34, 45, alpha=0.2, color='red', label='半场末段')
ax.set_xlabel('分钟'); ax.set_ylabel('射门次数')
ax.set_title('上半场射门分布 - 红色区域为半场结束前攻势')
plt.legend(); plt.show()

传球推进分析(控球推进速度)

def pass_progression(df, team):
    """统计向前推进的传球次数"""
    passes = df[(df['type']=='Pass') & (df['team']==team)].copy()
    passes = passes[passes['location'].notna() & passes['pass_end_location'].notna()]
    def forward_dist(row):
        try:
            x1 = row['location'][0]; x2 = row['pass_end_location'][0]
            return x2 - x1  # 向前为正
        except: return 0
    passes['fwd'] = passes.apply(forward_dist, axis=1)
    return passes[passes['fwd']>0].shape[0]
for team in events['team'].unique():
    print(f"{team}: 向前传球 前35={pass_progression(first35,team)}, 末10={pass_progression(last10,team)}")

综合评分(攻势指数)

def attack_score(df, team, minutes):
    """归一化的攻势指数 = 射门*2 + xG*5 + 前场触球*0.1 + 角球"""
    shots = len(df[(df['type']=='Shot') & (df['team']==team)])
    xg = df[(df['type']=='Shot') & (df['team']==team)]['shot_statsbomb_xg'].sum()
    touches = final_third_touches(df, team)
    corners = len(df[(df['type']=='Pass') & (df['team']==team) & 
                     (df.get('pass_type', pd.Series()).astype(str).str.contains('Corner', na=False))])
    score = shots*2 + xg*5 + touches*0.1 + corners
    return round(score / minutes, 2)  # 每分钟攻势强度
for team in events['team'].unique():
    print(f"{team}:")
    print(f"  前35min 攻势强度: {attack_score(first35, team, 35)}")
    print(f"  末10min 攻势强度: {attack_score(last10, team, 10)}")

如果末10min 的攻势强度 > 前35min 的 1.3 倍,就可判定为"半场末段攻势明显"。

进阶技巧

  1. 考虑补时 — 用 period==1 & minute>=35 包含 45+ 分钟
  2. 控球率衰减 — 半场前往往体能下降,可以对比双方高强度跑动
  3. 领先/落后场景过滤 — 落后球队末段攻势更猛,应分组看
  4. 滚动平均 — 用 rolling(10) 做每队每分钟攻势热度图

如果你用的是其他数据源

  • Wyscout: data['events'],字段 startSecond, positions
  • FBref: 用 sportsipy 抓取,需要 match_report
  • 自采集: 至少需要 minute, team, event_type, x, y

需要我针对你手上具体的数据结构(字段名、格式)给出定制代码,或者做一个"半场攻势热力图"的完整模板吗?把你的数据样例发我,我可以直接帮你写适配版。

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