统计禁区内射门次数对比的脚本
下面给你一个完整可用的 Python 脚本,用于统计并对比两支球队(或两名球员)在禁区内的射门次数,支持从 CSV 读取数据,也提供模拟数据示例。

数据格式(CSV)
假设你有一份射门事件数据 shots.csv,字段如下:
match_id,team,player,minute,x,y,body_part,is_goal 1,TeamA,Alice,12,88.5,34.2,right_foot,1 1,TeamA,Bob,25,92.1,40.0,left_foot,0 1,TeamB,Carol,33,45.0,30.0,head,0 1,TeamB,Dave,67,85.0,50.0,right_foot,1 ...
x, y为球场坐标(0–100 × 0–100,进攻方向向右)。- 禁区判定:以球场宽度方向为 y,长度方向为 x,标准禁区内区域为:
x ≥ 83(距底线 17 米内,约合 100 坐标制的 83)y ∈ [21, 79](禁区宽度约 58 米,居中)
脚本代码
import pandas as pd
from collections import defaultdict
# ================= 配置 =================
CSV_PATH = "shots.csv"
PITCH_LEN = 100
PITCH_WID = 100
# 禁区判定参数(可调)
BOX_X_MIN = 83 # 距底线 17/105≈16.2,取整数 83
BOX_Y_MIN = 21 # 禁区两侧边界
BOX_Y_MAX = 79
# ================= 工具函数 =================
def in_penalty_box(x, y):
"""判断射门点是否在禁区内(进攻方向向右)"""
return (x >= BOX_X_MIN) and (BOX_Y_MIN <= y <= BOX_Y_MAX)
# ================= 主逻辑 =================
def analyze_shots(df):
# 标记是否在禁区内
df["in_box"] = df.apply(lambda r: in_penalty_box(r["x"], r["y"]), axis=1)
stats = defaultdict(lambda: {"total": 0, "in_box": 0, "goals_in_box": 0})
for _, row in df.iterrows():
key = row["team"] # 也可改成 row["player"]
stats[key]["total"] += 1
if row["in_box"]:
stats[key]["in_box"] += 1
if row["is_goal"] == 1:
stats[key]["goals_in_box"] += 1
# 生成对比表
rows = []
for team, s in stats.items():
pct = (s["in_box"] / s["total"] * 100) if s["total"] else 0
rows.append({
"Team": team,
"Total Shots": s["total"],
"Shots in Box": s["in_box"],
"In-Box %": round(pct, 1),
"Goals in Box": s["goals_in_box"],
})
return pd.DataFrame(rows).sort_values("Shots in Box", ascending=False)
# ================= 入口 =================
if __name__ == "__main__":
try:
df = pd.read_csv(CSV_PATH)
except FileNotFoundError:
print("⚠️ 未找到 CSV,使用模拟数据演示")
df = pd.DataFrame({
"match_id": [1]*8,
"team": ["TeamA","TeamA","TeamA","TeamA","TeamB","TeamB","TeamB","TeamB"],
"player": ["A1","A2","A1","A3","B1","B2","B1","B3"],
"minute": [12,25,55,78,33,67,80,90],
"x": [88.5, 60.0, 92.1, 95.0, 45.0, 85.0, 90.0, 80.0],
"y": [34.2, 50.0, 40.0, 30.0, 30.0, 50.0, 60.0, 25.0],
"body_part": ["right_foot","left_foot","right_foot","head","head","right_foot","left_foot","head"],
"is_goal": [1,0,0,1,0,1,0,0],
})
result = analyze_shots(df)
print("\n=== 禁区内射门对比 ===")
print(result.to_string(index=False))
输出示例
=== 禁区内射门对比 ===
Team Total Shots Shots in Box In-Box % Goals in Box
TeamA 4 3 75.0 2
TeamB 4 2 50.0 1
进阶扩展建议
| 需求 | 修改方式 |
|---|---|
| 按球员对比 | 把 key = row["team"] 改为 row["player"] |
| 按比赛分组 | df.groupby("match_id").apply(analyze_shots) |
| 支持左右半场进攻 | 右半场用标准判定,左半场坐标镜像:x = 100 - x |
| 统计xG或射门距离 | 增加列并按禁区、非禁区聚合 |
| 输出图表 | 用 matplotlib 或 pandas.plot.bar 可视化 In-Box 对比 |
坐标判定说明(重要)
- 若你的数据是 Opta / StatsBomb 坐标系(0–120 × 0–80 或类似),需按比例调整:
- StatsBomb 标准禁区:
x ∈ [102, 120],y ∈ [18, 62] - 只需改
BOX_X_MIN / BOX_Y_MIN / BOX_Y_MAX三个常量即可。
- StatsBomb 标准禁区:
- 上方默认值适用于 0–100 归一化坐标,进攻方向统一为向右(x 增大方向)。
需要我把这个脚本改成可视化版本(柱状图),或适配 StatsBomb 数据的具体格式吗?