本文目录导读:

要制作一个显示低平球传中次数的实用脚本,我需要先确认您使用的数据源(如 StatsBomb、Wyscout、Opta 或普通的比赛 CSV 数据),因为“低平球”在不同数据源中的字段定义不同。
我可以先给您一个通用且最常用的 Python 脚本框架(适用于 Wyscout 或 StatsBomb 格式),并提供一个基于现场观感(通过视频时间戳或手动输入)的极简脚本。
由于没有具体数据文件,我假设您使用的是Wyscout 事件数据(这是目前最流行的免费公开数据集格式),其中传球类型用 tags 表示,高度用 104(低平球)标识。
以下是两个实用脚本,您可以根据您手里的数据形态选择:
处理 Wyscout / StatsBomb 事件数据(JSON 格式)
此脚本会统计指定球队(或全部球队)在比赛中低平球传中的具体次数,并打印出每一次的详细位置。
import json
from collections import Counter
# ------------------ 配置区 ------------------
# 请将您的数据文件放在同目录下,并修改文件名
EVENT_FILE = "events.json"
# 球队名称(如果只想统计某队,填 None 则统计所有球队)
TARGET_TEAM = None # "Liverpool" 或 "FC Barcelona"
# 传中事件子类型(Wyscout 中传中是 8,Open Play 是 1)
CROSS_SUBTYPE = 8
# ------------------ 核心代码 ------------------
def load_events(path):
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def is_low_cross(event):
"""
判断是否为低平球传中:
1. 必须是传中 (subtype 或 eventName 为 Cross)
2. 传球高度 tag 必须为 104 (Low)
"""
# ---- 处理 Wyscout 格式 ----
if 'subEventId' in event:
if event['subEventId'] != CROSS_SUBTYPE:
return False
# 检查 tags 是否包含精准/低平球 (id=104)
tags = [tag['id'] for tag in event.get('tags', [])]
# 104 在 Wyscout 中代表 "Low" (贴地飞行)
return 104 in tags
# ---- 处理 StatsBomb 格式 ----
if event.get('type', {}).get('name') == 'Pass':
if event.get('pass', {}).get('height', {}).get('name') == 'Ground Pass':
pass_type = event.get('pass', {}).get('type', {}).get('name')
if pass_type == 'Cross':
return True
return False
def main():
events = load_events(EVENT_FILE)
# 提取比赛 ID (如果有)
match_ids = set(e.get('matchId', 'Unknown') for e in events)
low_crosses = []
for ev in events:
# 过滤球队
team_name = ev.get('team', {}).get('name', 'Unknown')
if TARGET_TEAM and team_name != TARGET_TEAM:
continue
if is_low_cross(ev):
# 提取关键信息
info = {
'match': ev.get('matchId', 'N/A'),
'team': team_name,
'player': ev.get('player', {}).get('name', 'Unknown'),
'minute': ev.get('minute', 0) + ev.get('second', 0)/60,
'x': ev.get('positions', [{}])[0].get('x', 0) if ev.get('positions') else None,
'y': ev.get('positions', [{}])[0].get('y', 0) if ev.get('positions') else None,
'outcome': 'Successful' if 1801 in [t['id'] for t in ev.get('tags', [])] else 'Unsuccessful'
}
low_crosses.append(info)
# ----- 输出结果 -----
print(f"\n📊 数据统计:共发现 {len(low_crosses)} 次低平球传中\n")
print("-" * 60)
for i, cross in enumerate(low_crosses, 1):
print(f"{i:2d}. 第{cross['minute']:.0f}分钟 | {cross['team']} | "
f"{cross['player']} | 位置({cross['x']}, {cross['y']}) | {cross['outcome']}")
# 按队伍统计
print("\n📈 球队低平球传中次数:")
team_counts = Counter(c['team'] for c in low_crosses)
for team, count in team_counts.most_common():
print(f" {team}: {count} 次")
if __name__ == "__main__":
main()
极简通用版(手动录入比赛事件,适用于任何数据)
如果您没有现成的 JSON 数据文件,或者您是在看视频录像时手动记录,可以用这个交互式脚本,每次按回车记录一次,自动计数:
import sys
def run_tracker():
"""交互式低平球传中计数器(适合边看比赛边按)"""
print("⚽ 低平球传中计数器")
print("按 [Y] 记录一次,按 [C] 查看统计,按 [Ctrl+C] 退出\n")
count = 0
details = []
while True:
try:
cmd = input(">>> ").strip().lower()
if cmd == 'y':
count += 1
minute = input(" 分钟数(如45.3): ")
details.append(f" {len(details)+1:2d}. 第{minute}分钟")
print(f" ✅ 已记录!当前总数: {count}")
elif cmd == 'c':
print(f"\n📊 总计: {count} 次")
for d in details:
print(d)
print()
except KeyboardInterrupt:
print(f"\n\n最终结果:共 {count} 次低平球传中")
break
if __name__ == "__main__":
run_tracker()
如何选择?
| 场景 | 推荐方案 |
|---|---|
| 已有 Wyscout 或 StatsBomb 的 JSON 事件文件 | 方案一 |
| 只有 普通 CSV 表格(列如 Time, Event, Height) | 把方案一稍作修改,将 JSON 解析改为 csv.DictReader(可留言我帮您改) |
| 纯人工观赛,没有数据文件 | 方案二 |
重点提醒(数据字段问题)
- Wyscout:低平球标签
104,传中事件 ID 为8。 - StatsBomb:
height=Ground Pass且pass.type=Cross。 - 如果您手里的数据是 Opta 格式,字段名通常会叫
Pass.Height(值包含Ground)和Pass.Type(值包含Cross),请告诉我具体字段名,我可以立刻调整。
如果有具体的数据文件样例(前几行),您可以直接贴到评论区,我帮您把脚本改到完全匹配、直接可跑!