实用脚本统计禁区内射门次数对比?

wen 实用脚本 1

本文目录导读:

实用脚本统计禁区内射门次数对比?

  1. 方案一:使用 statsbombpy(免费API,顶级赛事数据)
  2. 方案二:使用 mplsoccer(适用于 Wyscout/Understat 数据)
  3. 方案三:通用脚本(从 CSV / 自定义数据计算)
  4. 关键注意点(非常重要)

在足球数据分析中,“禁区内射门”通常指的是射门发生时,皮球所在位置处于对方罚球区(大禁区)内。

针对你的需求,我提供三个不同场景的实用脚本(Python + 开源数据),请根据你的数据来源选择最合适的。


使用 statsbombpy(免费API,顶级赛事数据)

这是目前获取最详细事件数据最简单的方式,数据中自带 shot_end_location(射门结束位置),但由于我们需要“禁区内”,应使用 location(射门起始位置)。

import pandas as pd
from statsbombpy import sb
# 1. 加载数据(以2018世界杯决赛法国 vs 克罗地亚为例)
# 免费API数据ID:比赛ID 8658
df_events = sb.events(match_id=8658)
# 2. 过滤出射门事件
shots = df_events[(df_events['type'] == 'Shot')].copy()
# 3. 提取射门位置的坐标(StatsBomb坐标系:x为0-120,y为0-80)
# 注意:坐标原点在防守方半场,进攻方向为从左向右
shots['x'] = shots['location'].apply(lambda loc: loc[0] if loc else None)
shots['y'] = shots['location'].apply(lambda loc: loc[1] if loc else None)
# 4. 判断是否在禁区内(对方球门为120,禁区线为102)
# 同时考虑左右两个半场(即进攻方向)
def is_in_box(row):
    if row['x'] <= 60:  # 攻右路
        return (row['x'] >= 18) and (18 <= row['y'] <= 62)
    else:  # 攻左路
        return (row['x'] >= 102) and (18 <= row['y'] <= 62)
shots['in_box'] = shots.apply(is_in_box, axis=1)
# 5. 统计对比
# 确定球队(home_team / away_team)
result = shots.groupby(['team', 'in_box']).size().unstack(fill_value=0)
result.columns = ['禁区外射门', '禁区内射门']
result['总射门'] = result.sum(axis=1)
print(result)

使用 mplsoccer(适用于 Wyscout/Understat 数据)

如果你使用的是 WyscoutUnderstat 的公开数据(通常坐标是0-100),坐标逻辑不同(x=100是对方球门)。mplsoccer 内置了画球场功能,可以直观验证。

import pandas as pd
from mplsoccer import Pitch
# 假设你已经有了 shots_df,包含 'x'(射门起点x坐标0-100), 'y',和'team'
# 数据示例:
# shots_df = pd.DataFrame({'x': [85, 50, 92], 'y': [40, 20, 35], 'team': ['A', 'B', 'A']})
def is_box_mpl(row):
    # 坐标0-100,球门在x=100
    # 大禁区范围(近似):x > 82.5 且 21 < y < 79
    return (row['x'] > 82.5) and (21 < row['y'] < 79)
# 假设主队列名为 'home_team',客队为 'away_team'
shots_df['is_box'] = shots_df.apply(is_box_mpl, axis=1)
# 统计
summary = shots_df.groupby(['team', 'is_box']).size().unstack(fill_value=0)
summary.columns = ['Out', 'In']
print(summary)
# 可视化(可选):画一个带射门点的球场
pitch = Pitch(pitch_type='statsbomb', line_color='black')
fig, ax = pitch.draw()
# 按是否在禁区内着色
for idx, row in shots_df.iterrows():
    color = 'red' if row['is_box'] else 'blue'
    pitch.scatter(row['x'], row['y'], ax=ax, color=color, s=50)

通用脚本(从 CSV / 自定义数据计算)

如果你有标准的事件数据(列:team, x_start, y_start),可以用最精简的纯Pandas脚本:

import pandas as pd
# 读取你的数据
df = pd.read_csv('your_events.csv')
# 标准足球场坐标(x: 0-100, y: 0-100, 球门在 x=100)
# 调整你的坐标轴如果数据是 0-120 的
df['in_box'] = (df['x_start'] > 82.5) & (df['y_start'] > 21) & (df['y_start'] < 79)
# 按球队和是否在禁区内统计
result = df.groupby(['team', 'in_box']).size().unstack(fill_value=0)
# 重命名
result.columns = ['Box_Shots', 'Out_Box_Shots']
print(result)
# 如果想输出百分比
result['Box_Percentage'] = result['Box_Shots'] / (result['Box_Shots'] + result['Out_Box_Shots']) * 100
print(result)

关键注意点(非常重要)

  1. 坐标方向:不同的数据公司(StatsBomb / Opta / Wyscout)坐标系不一样。
    • StatsBomb:x 范围 0~120,y 范围 0~80,本方球门在 (0, 40)
    • Wyscout / Understat:x 范围 0~100,y 范围 0~100,对方球门在 (100, 50)
  2. 判别逻辑:禁区是一个矩形区域,判断条件是 x 超过大禁区线,且 y在禁区宽度内,对于左右两个半场分别判断。
  3. “禁区内”通常指:射门发生在对方罚球区(18码区)内,不包括点球点附近的小禁区(通常点球不计入运动战射门)。

如果你能告诉我你的数据来源(是某网站爬下来的Excel,还是API),我可以帮你改成完全适配的版本。

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