本文目录导读:

这是一个用于统计足球比赛中横传转移球次数的实用脚本思路和实现方案,由于你未指定具体的数据源(是视频分析、还是如 StatsBomb 或 Wyscout 的 XML/JSON 数据),我提供两种最常用的落地场景:
基于事件数据(如 StatsBomb / Wyscout JSON 数据)
这是数据分析师最常用的方法,假设你有一份包含所有传球事件的数据,横传转移球的定义通常是:跨越了较长的横向距离(X轴变化小,Y轴变化大)且最终进入进攻三区另一侧的传球。
以下是一个用 Python 写的核心过滤逻辑脚本(假设数据格式为 StatsBomb,location 为 [x, y],球场长 120,宽 80):
import pandas as pd
import numpy as np
def calculate_switch_of_play(df_events,
min_y_distance=20,
max_x_progression=15,
final_third_x=80):
"""
计算横传转移球次数(Switch of Play)
参数:
- df_events: 包含所有事件的DataFrame(需有 'type', 'location', 'pass' 列)
- min_y_distance: 触发判定所需的最小横向距离(Y轴,默认20码)
- max_x_progression: 允许的最大向前推进距离(X轴,默认15码)
- final_third_x: 进攻三区起始线(默认80码)
返回:
- switch_count: 总次数
- switch_df: 符合条件的传球事件子集
"""
# 过滤出传球事件
passes = df_events[(df_events['type']['name'] == 'Pass')].copy()
# 提取坐标
passes['start_x'] = passes['location'].apply(lambda loc: loc[0])
passes['start_y'] = passes['location'].apply(lambda loc: loc[1])
passes['end_x'] = passes['pass'].apply(lambda p: p['end_location'][0])
passes['end_y'] = passes['pass'].apply(lambda p: p['end_location'][1])
# 计算差值
passes['delta_x'] = passes['end_x'] - passes['start_x']
passes['delta_y'] = abs(passes['end_y'] - passes['start_y'])
# 过滤条件:
# 1. 横向移动距离足够大(横传)
# 2. 纵向推进距离较小(非长传冲吊)
# 3. 防守反击场景常要求起点在后场,终点在前场
# 4. 排除横传回传(如果只算向前转移,则要求 end_x > start_x)
switches = passes[
(passes['delta_y'] >= min_y_distance) & # 横向跨度大
(passes['delta_x'] <= max_x_progression) & # 纵向推进有限
(passes['start_x'] < 60) & # 起始位置在中场或本方半场
(passes['end_x'] >= final_third_x) # 最终到达进攻三区
]
return len(switches), switches
调用示例:
# 假设 events 是 json 导入后的 DataFrame
count, result_df = calculate_switch_of_play(events)
print(f"本场比赛有效的横传转移球次数: {count}")
# 显示每次转移球的具体事件
print(result_df[['player', 'team', 'minute', 'start_x', 'start_y', 'end_x', 'end_y']])
如果你是在看比赛录像,需要手动标记辅助
如果你在观看视频并需要一个脚本帮助你快速记录,可以使用 Python 的键盘监听来打点,这里用 pynput 库写一个简易计数器:
from pynput import keyboard
class SwitchCounter:
def __init__(self):
self.count = 0
self.last_event_time = None
def on_press(self, key):
try:
# 按 'S' 键记录一次成功转移
if key == keyboard.KeyCode.from_char('s'):
self.count += 1
print(f"✅ 已记录成功转移球,总次数: {self.count} | 按 'q' 结束")
# 按 'Q' 键保存退出
elif key == keyboard.KeyCode.from_char('q'):
print(f"\n📊 最终统计:横传转移球次数为 {self.count} 次")
return False # 停止监听
except Exception as e:
print(f"处理按键时出错: {e}")
def main():
print("⚽ 横传转移球计数器启动")
print("按 'S' 记录一次转移,按 'Q' 退出")
counter = SwitchCounter()
with keyboard.Listener(on_press=counter.on_press) as listener:
listener.join()
if __name__ == "__main__":
main()
进阶优化建议(针对真实战术分析)
在实际战术中,单纯的横穿不一定算“转移”,你可以调整脚本参数来识别:
- 球场宽度利用:将球场Y轴标准化为0-100,要求从一侧40码以外传到另一侧40码外。
- 节奏变化:要求传球前有两次传递,且传球速度为One-Touch,模拟“拉扯防线后的弱侧转移”。
- 如果数据包含冻结帧,可以判断接球队员是否处于“无人压迫”状态(该区域防御人数 < 2),这更符合真正撕开防线的标准。
你需要哪一种更详细的数据结构定义(statsbomb 的 json 格式还是 Wyscout 的 events)?告诉我你的数据长什么样,我可以把脚本调整得更精准。