本文目录导读:

这是一个非常实用且有趣的足球数据统计脚本思路,要回答“门将谁更忙”,核心在于量化“扑救动作”,而不仅仅是“丢球数”。
以下是几种不同维度的统计逻辑和对应的 Python 脚本示例,你可以根据手头的数据格式(是赛事 API 数据,还是手录的文本数据)选择使用。
基于“事件数据”的统计(最推荐,最精确)
如果你有一份包含射正时间点、扑救时间点和丢球时间点的比赛数据(例如从 Opta、Wyscout 或一些开源 API 获取),我们可以精确计算出每位门将的有效扑救次数。
核心逻辑:
- 统计该门将所在球队被射正(On Target)的总次数。
- 统计该门将的丢球数(Goals Conceded)。
- 有效扑救次数 = 被射正次数 - 丢球数(排除被门柱挡出和对方打飞的球)。
- “忙”的程度 = 扑救次数 + 丢球数(即总被考验次数),或者计算扑救成功率。
# 假设你已经从API获取了结构化数据,这里是模拟数据
matches_data = {
"Match1": {
"home_team": "TeamA",
"away_team": "TeamB",
"teamA_events": {
"shots_on_target": 7, # 对方射正次数
"goals_conceded": 2, # 对方进球数
"keeper_name": "门将A"
},
"teamB_events": {
"shots_on_target": 3,
"goals_conceded": 1,
"keeper_name": "门将B"
}
},
"Match2": { # 更多比赛数据... }
}
def evaluate_keepers(data):
keeper_stats = {}
for match, info in data.items():
for team_key in ["teamA_events", "teamB_events"]:
ev = info[team_key]
name = ev["keeper_name"]
sot = ev["shots_on_target"]
conceded = ev["goals_conceded"]
saves = sot - conceded # 扑救次数
if name not in keeper_stats:
keeper_stats[name] = {"saves": 0, "faced": 0, "conceded": 0}
keeper_stats[name]["saves"] += saves
keeper_stats[name]["faced"] += sot # 被考验总次数(忙的程度核心指标)
keeper_stats[name]["conceded"] += conceded
# 输出结果:按“忙碌程度”(faced 被考验次数)排序
sorted_keepers = sorted(keeper_stats.items(), key=lambda x: x[1]["faced"], reverse=True)
print("门将忙碌程度排行榜(按被考验次数):")
for name, stats in sorted_keepers:
print(f" {name}: 扑救{stats['saves']}次, 丢{stats['conceded']}球, 共被考验{stats['faced']}次")
return sorted_keepers
# 调用函数
evaluate_keepers(matches_data)
基于“实时事件流”的统计(模拟现场)
如果你有类似“比赛事件流”的文本文件(每行记录一个事件),这个脚本可以实时统计“忙碌指数”。
脚本逻辑:
- 读取事件流。
- 关键词匹配:
SHOT_ON_TARGET(射正)、GOAL(进球)、SAVE(扑救)。 - 如果事件是
SHOT_ON_TARGET且没有紧跟GOAL,则该门将记一次“扑救”。
# 模拟一段比赛事件流文本
event_stream = """
45:00 [SHOT] Player X - TeamA - 禁区内射门
45:02 [SAVE] 门将A - TeamB - 侧扑救险
63:12 [SHOT] Player Y - TeamB - 远射
63:15 [GOAL] Player Y - TeamB - 死角破门
71:30 [SHOT] Player Z - TeamA - 单刀
71:31 [SAVE] 门将A - TeamB - 出击封堵
88:00 [SHOT] Player W - TeamB - 头球攻门
"""
# 提取关键数据(简化版正则)
import re
def parse_events(stream):
save_count = 0
faced_count = 0
lines = stream.strip().split('\n')
for line in lines:
if '[SHOT]' in line or '[SHOT_ON_TARGET]' in line:
faced_count += 1 # 一次射正 = 一次考验
if '[GOAL]' in line:
# 射正但是进球了,不算扑救
pass
if '[SAVE]' in line:
save_count += 1
# 注意:这里的逻辑需要结合时间戳判断,为了简化,这里直接累加
print(f"总被考验次数: {faced_count}")
print(f"成功扑救次数: {save_count}")
print(f"忙碌指数: {faced_count} 次面临危险")
parse_events(event_stream)
纯手动数据录入(Excel/CSV 读取)
这是最实用的场景,通常我们只有技术统计表,没有精确事件,那“忙”的定义就是被射正次数。
输入格式(CSV):
比赛,门将,球队,被射正,失球 M1,库尔图瓦,皇马,5,1 M1,特尔施特根,巴萨,9,2 M2,库尔图瓦,皇马,3,0 M2,特尔施特根,巴萨,12,3
脚本:
import csv
def process_csv(file_path):
keeper_stats = {}
with open(file_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
name = row['门将']
faced = int(row['被射正'])
conceded = int(row['失球'])
saves = faced - conceded
if name not in keeper_stats:
keeper_stats[name] = {'total_faced': 0, 'total_saves': 0, 'total_conceded': 0}
keeper_stats[name]['total_faced'] += faced
keeper_stats[name]['total_saves'] += saves
keeper_stats[name]['total_conceded'] += conceded
# 排序并输出
for name, stats in sorted(keeper_stats.items(), key=lambda x: x[1]['total_faced'], reverse=True):
print(f"门将 {name}: 面对{stats['total_faced']}次射正,扑出{stats['total_saves']}次,失{stats['total_conceded']}球,扑救成功率 {stats['total_saves']/stats['total_faced']*100:.1f}%")
# 运行
process_csv('goalkeeper_data.csv')
核心结论(如何定义“谁更忙”)
在足球评论中,“忙”通常指:
- 被射正次数(Faced)——这是防守压力指标,次数越多,门将越“忙碌”。
- 成功扑救次数(Saves)——这是工作量指标,扑救多不一定好(说明防线差),但绝对是“体感上”最忙的。
- 高难度扑救次数——如果数据源有 xG(预期进球)数据,扑出高 xG 的射门才是真正的“神扑”。
建议脚本逻辑:
其实最直观的脚本是:统计“某个门将面对球门方向的防守动作次数”,如果数据中没有“扑救”这个动作,那就用 被射正 - 失球 = 扑救。谁被射正次数多,谁就是全场最忙碌的人。 如果一个门将一场比赛被射正 15 次,而另一个只有 2 次,即使前者比后者少丢球,前者显然“更忙”。