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

核心思路
半场结束前的攻势通常体现在几个维度:
- 射门频率 — 最后10分钟射门数 vs 前35分钟均值
- 控球位置 — 危险区域(前场1/3)触球/传球占比
- 角球/定位球 — 半场前角球数激增
- 进攻节奏 — 传球速度、推进距离
- 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 倍,就可判定为"半场末段攻势明显"。
进阶技巧
- 考虑补时 — 用
period==1 & minute>=35包含 45+ 分钟 - 控球率衰减 — 半场前往往体能下降,可以对比双方高强度跑动
- 领先/落后场景过滤 — 落后球队末段攻势更猛,应分组看
- 滚动平均 — 用
rolling(10)做每队每分钟攻势热度图
如果你用的是其他数据源
- Wyscout:
data['events'],字段startSecond,positions - FBref: 用
sportsipy抓取,需要match_report - 自采集: 至少需要
minute, team, event_type, x, y
需要我针对你手上具体的数据结构(字段名、格式)给出定制代码,或者做一个"半场攻势热力图"的完整模板吗?把你的数据样例发我,我可以直接帮你写适配版。