任意球直接得分统计脚本
下面给你一个可直接运行的 Python 脚本,用于统计足球比赛数据中「任意球直接得分」的次数,核心判断逻辑是:进球方式为任意球 且 无助攻(即非间接任意球/配合进球)。

脚本代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
统计任意球直接得分次数
用法:
python fk_goal_stats.py events.csv
python fk_goal_stats.py events.csv --player "梅西"
CSV 需包含字段(列名可自定义,见 COLUMN_MAP):
match_id, minute, team, player, event_type, sub_type, assist_player
"""
import csv
import sys
import argparse
from collections import defaultdict
# ===== 列名映射:左边是脚本内部用名,右边是你 CSV 里的实际列名 =====
COLUMN_MAP = {
"match_id": "match_id",
"minute": "minute",
"team": "team",
"player": "player",
"event_type": "event_type", # Goal / Shot
"sub_type": "sub_type", # Free Kick / Penalty / Open Play
"assist_player": "assist_player" # 助攻者,空表示无助攻
}
# 判定「任意球」的关键词(大小写不敏感)
FREE_KICK_KEYWORDS = ["free kick", "free-kick", "freekick", "任意球"]
GOAL_KEYWORDS = ["goal", "进球"]
def is_free_kick(text: str) -> bool:
t = (text or "").strip().lower()
return any(k in t for k in FREE_KICK_KEYWORDS)
def is_goal(text: str) -> bool:
t = (text or "").strip().lower()
return any(k in t for k in GOAL_KEYWORDS)
def load_events(path):
"""读取 CSV,返回字典列表(键统一为内部名)"""
events = []
with open(path, newline="", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
for row in reader:
events.append({
key: (row.get(col_name, "") or "").strip()
for key, col_name in COLUMN_MAP.items()
})
return events
def count_direct_fk_goals(events, player_filter=None):
"""
返回:
total: int 任意球直接得分总数
detail: list 每一条命中的记录
"""
detail = []
for e in events:
if not is_goal(e["event_type"]):
continue
if not is_free_kick(e["sub_type"]):
continue
# 有助攻 => 视为间接任意球配合进球,不算「直接得分」
if e["assist_player"]:
continue
if player_filter and player_filter not in e["player"]:
continue
detail.append(e)
return len(detail), detail
def main():
parser = argparse.ArgumentParser(description="统计任意球直接得分次数")
parser.add_argument("csv_file", help="事件数据 CSV 路径")
parser.add_argument("--player", default=None, help="只统计某球员(模糊匹配)")
parser.add_argument("--by-team", action="store_true", help="按球队分组统计")
parser.add_argument("--by-player", action="store_true", help="按球员分组统计")
args = parser.parse_args()
events = load_events(args.csv_file)
total, detail = count_direct_fk_goals(events, args.player)
print(f"\n=== 任意球直接得分统计 ===")
print(f"总次数: {total}")
if detail:
print("\n明细:")
for e in detail:
print(f" 第{e['minute']}分钟 | {e['team']} | {e['player']} "
f"| match={e['match_id']}")
if args.by_team:
agg = defaultdict(int)
for e in detail:
agg[e["team"]] += 1
print("\n按球队:")
for team, n in sorted(agg.items(), key=lambda x: -x[1]):
print(f" {team}: {n}")
if args.by_player:
agg = defaultdict(int)
for e in detail:
agg[e["player"]] += 1
print("\n按球员:")
for p, n in sorted(agg.items(), key=lambda x: -x[1]):
print(f" {p}: {n}")
if __name__ == "__main__":
main()
示例 CSV 格式
events.csv:
match_id,minute,team,player,event_type,sub_type,assist_player M001,23,阿根廷,梅西,Goal,Free Kick, M001,45,法国,格列兹曼,Goal,Free Kick,姆巴佩 M002,67,巴西,内马尔,Goal,Penalty, M002,80,德国,克罗斯,Goal,Free Kick,
运行:
python fk_goal_stats.py events.csv python fk_goal_stats.py events.csv --by-player python fk_goal_stats.py events.csv --player 梅西
输出:
=== 任意球直接得分统计 ===
总次数: 2
明细:
第23分钟 | 阿根廷 | 梅西 | match=M001
第80分钟 | 德国 | 克罗斯 | match=M002
注意:第 45 分钟格列兹曼那球
sub_type=Free Kick但有助攻姆巴佩,属于任意球配合进球,不计入直接得分。
关键判定规则说明
| 规则 | 说明 |
|---|---|
event_type 含 Goal |
是进球 |
sub_type 含 Free Kick |
是任意球 |
assist_player 为空 |
无助攻 → 直接得分 |
| 有助攻 | 间接任意球 / 配合进球,排除 |
可根据你的数据源(Opta / StatsBomb / 懂球帝 / 自制表格)修改 COLUMN_MAP 和关键词即可适配。
如果你用的是 StatsBomb 开放数据
StatsBomb 的射门事件里,任意球直接射门特征是:
shot.type.name == "Free Kick"shot.outcome.name == "Goal"shot.freeze_frame存在(人墙)
对应核心判断:
def is_direct_fk_goal(ev):
if ev.get("type", {}).get("name") != "Shot":
return False
shot = ev.get("shot", {})
return (shot.get("type", {}).get("name") == "Free Kick"
and shot.get("outcome", {}).get("name") == "Goal"
and shot.get("freeze_frame") is not None) # 有人墙=直接射门
需要我帮你改写成适配 StatsBomb JSON 或 Opta F24 的版本,告诉我数据格式即可。