这个实用脚本显示横传转移球次数?

wen 实用脚本 2

本文目录导读:

这个实用脚本显示横传转移球次数?

  1. 方案一:基于 StatsBomb 开源数据(带坐标,最精确)
  2. 方案二:基于泛用型数据(只有事件描述,无坐标)
  3. 方案三:绘图可视化(检验识别是否准确)
  4. 重要提示(关于参数调优)

在足球数据分析中,“横传转移球”通常指的是大范围转移(从一侧边路到另一侧边路的横向长传)或横向疏导球,不同的数据公司(如Opta、Wyscout)定义略有不同。

这里我为你提供两种不同场景下的实用 Python 脚本,你可以根据你的数据结构选择。

基于 StatsBomb 开源数据(带坐标,最精确)

如果你有详细的 xy 坐标数据(如 StatsBomb 免费数据集),这个脚本能精准识别“横传转移”。

核心逻辑:

  1. 传球起止点的 y 坐标(0-100)跨越了中线(y=50)或跨越了半个场地宽度(>40米)。
  2. 传球距离较长(> 35 码)。
  3. 传球方向几乎是横向的(dx 较小,dy 较大)。
import pandas as pd
import json
# 假设 df 是你的传球事件 DataFrame,包含以下列:
# 'team': 队伍名称
# 'player': 球员名称
# 'x': 起始X坐标 (0-120)
# 'y': 起始Y坐标 (0-80)
# 'end_x': 结束X坐标
# 'end_y': 结束Y坐标
def calculate_switch_of_play(df):
    """
    统计横传转移球(Switch of Play)
    规则:传球跨越了场地的中分线,且横向距离超过总宽度的40%(>32)
    """
    switches = []
    for _, row in df.iterrows():
        # 计算横向移动距离(Y轴变化)
        dy = abs(row['end_y'] - row['y'])
        # 计算纵向移动距离(X轴变化)
        dx = abs(row['end_x'] - row['x'])
        # 判定条件(以 StatsBomb 标准为例)
        # 1. 必须是向前传球或平行传球
        # 2. 横向移动距离超过 32(半场宽度80计)
        # 3. 纵向推进距离小于横向距离的一半(确保是"横"传,而非"斜"长传)
        if (dy >= 32) and (dx <= dy * 1.5) and (dx >= 5):  # 最后一项避免把短横敲算进去
            switches.append(row)
    if not switches:
        return pd.DataFrame()
    else:
        return pd.DataFrame(switches)
# 使用示例:
# switches_df = calculate_switch_of_play(passes_df)
# print(f"共检测到 {len(switches_df)} 次横传转移")
# 如果想按球队看:
# print(switches_df.groupby('team').size())

基于泛用型数据(只有事件描述,无坐标)

很多中文足球论坛的数据只有“传球”事件,没有精确坐标,这个脚本适用于:

  • 数据中包含起止区域(如 Zone A, Zone B)
  • 或者包含传球类型标签(如“左侧长传至右侧”)
def count_switch_from_zones(df):
    """
    规则:
    1. 传球起始区域在左路(1/3),结束区域在右路(1/3)
    2. 传球起始区域在右路,结束区域在左路
    假设数据有以下列:
    - 'pass_origin': 传球起始区域 (如 'Left', 'Centre', 'Right')
    - 'pass_end': 传球结束区域
    """
    # 定义左右路
    left_zones = ['Left', 'LW', 'LB', '左路', '左']
    right_zones = ['Right', 'RW', 'RB', '右路', '右']
    # 过滤条件:起止区域分别是左右路
    mask = (
        (df['pass_origin'].isin(left_zones) & df['pass_end'].isin(right_zones)) |
        (df['pass_origin'].isin(right_zones) & df['pass_end'].isin(left_zones))
    )
    switches = df[mask]
    print(f"找到横传转移次数: {len(switches)} 次")
    # 按球队统计
    if 'team' in switches.columns:
        print(switches.groupby('team').size())
    return switches
# 使用示例
# result = count_switch_from_zones(events_df)

绘图可视化(检验识别是否准确)

想直观地看到你统计出来的横传在哪里?这个片段能帮你画出传球线路图:

import matplotlib.pyplot as plt
import pandas as pd
# 假设 df 为包含识别出来的横传事件
def plot_switches(df):
    fig, ax = plt.subplots(figsize=(12, 8))
    # 画出足球场边框
    ax.plot([0, 0, 120, 120, 0], [0, 80, 80, 0, 0], color='black', lw=2)
    # 中线
    ax.plot([60, 60], [0, 80], color='black', lw=1)
    # 禁区
    ax.plot([18, 18], [18, 62], color='black')
    ax.plot([102, 102], [18, 62], color='black')
    # 画出每条转移球路线
    for _, row in df.iterrows():
        ax.plot(
            [row['x'], row['end_x']],
            [row['y'], row['end_y']],
            color='red', alpha=0.6, lw=1.5
        )
    ax.set_title(f"横传转移球分布图 (共{len(df)}次)")
    ax.set_xlim(0, 120)
    ax.set_ylim(0, 80)
    plt.show()
# 调用绘图
# plot_switches(switches_df)

重要提示(关于参数调优)

在实际使用中,你需要根据你的数据来源调整阈值

  1. 距离阈值:在方案一中,dy >= 32 这个数值可以调。
    • StatsBomb: 场地宽度 80,通常要求跨越超过40(即过中线)。
    • Wyscout: 场地宽度 100,通常要求跨越超过50。
  2. 角度要求:真正的“转移”一般是横向推进,而不是大纵深长传。dx 远大于 dy,那可能是对角长传,不算作经典意义的“横传转移”。

推荐初始参数(通用足球场坐标系):

# 中场宽度为 68米(Opta坐标为50)
# 半场宽度为 25,全场宽度为 50
# 建议阈值:dy > 20(穿过半场中线),dx < dy

如果你能告诉我你拥有的数据结构(比如列名是英文还是中文,有无坐标),我可以帮你把脚本改到直接可运行。

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