本文目录导读:

- 核心思路
- 方案一:基于球员级(Pandas + Seaborn/Matplotlib)
- 方案二:基于事件流(Pandas + 条件筛选)
- 方案三:热力图可视化(Matplotlib + 散点图)
- 总结:如何选择方案?
这是一个非常具体的足球数据分析问题,在Python中,要对比两队的边路突破能力,核心在于找到能够量化“边路”和“突破”的数据指标,并进行可视化对比。
由于你大概率是在做数据分析项目或足球数据挖掘,下面提供三种不同数据来源和颗粒度的解决方案,你可以根据自己掌握的数据类型,选择最适合的一种。
核心思路
- 定义边路区域:通常将球场宽度三等分或按左右球员的活动热区划分(左路、中路、右路)。
- 定义突破:成功过人(Dribble / Take-on)、传中(Cross)、突破造犯规、进入禁区的次数等。
- 可视化对比:雷达图、条形图、气泡图或热力图是首选。
基于球员级(Pandas + Seaborn/Matplotlib)
这是最常见的场景,假设你有一份包含球员比赛数据的DataFrame(例如从StatsBomb、Wyscout或CSV文件获取)。
数据假设结构:
| team | player | passes_into_box | successful_dribbles | cross_accuracy | area |
|---|---|---|---|---|---|
| 曼城 | Foden | 12 | 8 | 35 | Left |
| 曼联 | Rashford | 10 | 7 | 40 | Left |
| 曼城 | Mahrez | 15 | 6 | 45 | Right |
| 曼联 | Antony | 8 | 5 | 30 | Right |
代码示例:雷达图对比两队边路核心
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from math import pi
# 1. 加载数据
df = pd.read_csv('match_data.csv') # 你的数据
# 2. 筛选边路球员 (假设有 area 列: Left/Right)
df_wide = df[df['area'].isin(['Left', 'Right'])]
# 3. 聚合到球队级别,计算边路突破相关指标
team_stats = df_wide.groupby('team').agg({
'successful_dribbles': 'sum', # 成功过人总数
'passes_into_box': 'sum', # 传入禁区次数
'cross_accuracy': 'mean', # 传中成功率
'fouls_won_wide': 'sum' # 边路造犯规
}).reset_index()
# 4. 雷达图:对比两队边路综合能力
categories = ['成功过人', '传入禁区', '传中成功率', '边路造犯规']
# 归一化 (Min-Max) 以便在同一张图显示
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
team_stats[categories] = scaler.fit_transform(team_stats[categories].values)
# 设置雷达图
N = len(categories)
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1] # 闭合
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))
colors = ['#FF6347', '#4682B4'] # 两队颜色
for idx, row in team_stats.iterrows():
values = row[categories].tolist()
values += values[:1] # 闭合
ax.plot(angles, values, 'o-', linewidth=2, label=row['team'], color=colors[idx])
ax.fill(angles, values, alpha=0.25, color=colors[idx])
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories, fontsize=11)
ax.set_ylim(0, 1.1)'两队边路突破能力雷达对比', size=15, pad=20)
plt.legend(loc='upper right', bbox_to_anchor=(1.2, 1.1))
plt.show()
解读: 雷达图覆盖面积越大,该项能力越强,如果球队A的“成功过人”和“传入禁区”数值远高于球队B,说明其边路突破威胁更大。
基于事件流(Pandas + 条件筛选)
如果你有逐秒事件数据(Event Data),例如WhoScored或Opta数据,可以精确统计发生在边路的、导致威胁的事件。
数据假设结构:
| event_id | team | event_type | start_x | start_y | end_x | end_y | outcome |
|---|---|---|---|---|---|---|---|
| 1 | 队A | Dribble | 15 | 5 | 30 | 4 | Successful |
| 2 | 队B | Cross | 20 | 3 | 50 | 6 | Accurate |
| ... |
代码示例:统计有效边路突破
# 定义边路区域 (假设球场坐标:x: 0-105, y: 0-68)
# 左路: y < 22.6; 右路: y > 45.4
left_wing = (df['start_y'] < 22.6)
right_wing = (df['start_y'] > 45.4)
wide_areas = left_wing | right_wing
# 定义“突破”事件
breakthrough_events = [
'Dribble_Successful', # 成功过人
'Cross_Accurate', # 成功传中
'Shot_After_Dribble', # 带球射门
'Foul_Won_Wide' # 边路造犯规
]
# 筛选:在边路且是突破事件的组合
df_breakthroughs = df[(df['event_type'].isin(breakthrough_events)) & wide_areas]
# 统计每个队在边路的突破次数
team_breakthrough_counts = df_breakthroughs.groupby('team').size().reset_index(name='突破次数')
# 统计传中成功率和过人成功率
team_cross_stats = df[df['event_type'] == 'Cross'].groupby('team').agg(
传中总数 = ('event_id', 'count'),
传中成功 = ('outcome', lambda x: (x == 'Accurate').sum())
).reset_index()
team_cross_stats['传中成功率'] = team_cross_stats['传中成功'] / team_cross_stats['传中总数']
# 条形图对比
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 左图:边路突破次数
axes[0].bar(team_breakthrough_counts['team'], team_breakthrough_counts['突破次数'], color=['#FF6347', '#4682B4'])
axes[0].set_title('边路总突破次数')
axes[0].set_ylabel('次数')
# 右图:传中成功率
axes[1].bar(team_cross_stats['team'], team_cross_stats['传中成功率'], color=['#FF6347', '#4682B4'])
axes[1].set_title('边路传中成功率')
axes[1].set_ylabel('成功率')
plt.tight_layout()
plt.show()
解读: 如果一队“边路总突破次数”高,但“传中成功率”低,说明他们可能只会“闷头带”,缺乏有效终结;反之亦然。
热力图可视化(Matplotlib + 散点图)
如果你有球员触球/活动坐标数据,可以直接画热力图,直观看到哪队更擅长在高危边路区域活动。
代码示例:基于坐标的边路活动密度
# 假设你有两队的坐标数据: df_teamA_events, df_teamB_events
# 过滤出边路区域的过人/传中事件
from scipy.stats import gaussian_kde
import matplotlib.pyplot as plt
def plot_wing_heatmap(df_team, team_name, ax):
# 筛选边路 (y坐标 < 20 或 > 48)
df_wide = df_team[(df_team['y'] < 20) | (df_team['y'] > 48)]
# 只保留进攻端事件 (x > 50, 半场进攻)
df_attack_wide = df_wide[df_wide['x'] > 50]
if len(df_attack_wide) > 10:
# 核密度估计
xy = np.vstack([df_attack_wide['x'], df_attack_wide['y']])
z = gaussian_kde(xy)(xy)
ax.scatter(df_attack_wide['x'], df_attack_wide['y'],
c=z, s=50, alpha=0.6, cmap='Reds')
ax.set_title(f'{team_name} 边路进攻热区')
else:
ax.text(0.5, 0.5, '数据不足', ha='center')
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
plot_wing_heatmap(df_teamA, '队A', axes[0])
plot_wing_heatmap(df_teamB, '队B', axes[1])
# 添加球场背景 (可选, 需要 matplotlib.patches)
# ...
plt.suptitle('两队边路突破空间对比')
plt.show()
如何选择方案?
| 你的数据类型 | 推荐方案 | 核心洞察 |
|---|---|---|
| 汇总统计表 (球员场均过人数等) | 方案一 雷达图 | 综合对比各维度强弱项 |
| 逐事件流 (带坐标和结果) | 方案二 条件聚合 | 精确统计有效突破次数和效率 |
| 原始坐标/轨迹数据 | 方案三 热力图 | 战术空间压迫感和核心区域 |
进阶建议:
- 你可以加入每90分钟数据(
per90)来消除出场时间差异。 - 使用 Mplsoccer 库(专门画足球场地的Python库)能让图更加专业。
- 如果对比的是总突破 vs 威胁突破,可以引入“突破后射门”、“突破后关键传球”等后续指标。
如果你能提供你具体的数据格式(列名和样本),我可以直接帮你写一段适配的代码。