Python案例统计禁区内射门次数对比:从数据采集到可视化实战
目录导读
为什么要统计禁区内射门次数?
在现代足球数据分析中,禁区内射门次数是衡量一支球队进攻质量的核心指标之一,与禁区外远射相比,禁区内的射门距离球门更近,预期进球值(xG)显著更高,统计并对比不同球队、不同球员在禁区内的射门次数,能够帮助我们更客观地评估进攻效率、战术倾向以及锋线威胁程度。

传统的人工统计方式费时费力,且容易出错,借助 Python,我们可以自动化地完成数据采集、清洗、统计和可视化,将原本需要数小时的工作压缩到几分钟内完成,本文将通过一个完整的案例,演示如何用 Python 统计并对比禁区内射门次数,并给出可直接复用的代码框架。
数据来源与采集思路
要实现禁区内射门次数统计,首先需要获取包含射门位置信息的数据,常见的公开数据来源包括足球赛事数据平台的开放接口、赛事统计页面以及社区维护的数据集,采集时需要重点关注以下字段:
- 射门发生坐标(x, y)
- 射门所属球队与球员
- 比赛时间与赛事标识
- 射门结果(进球、被扑、偏出等)
需要说明的是,采集数据时应遵守目标网站的服务条款与robots协议,优先使用官方开放接口或已获授权的数据集,本文示例使用结构化程度较高的赛事数据接口返回的JSON数据,便于直接解析。
采集流程大致分为三步:请求数据 → 解析JSON → 存入DataFrame,对于分页接口,还需要循环翻页并做去重处理。
Python核心代码实现
下面给出一个精简但完整的实现框架,涵盖数据获取、禁区判定与分组统计。
import requests
import pandas as pd
# 1. 获取射门数据(示例接口,实际请替换为合规数据源)
def fetch_shots(match_id):
url = f"https://example-data-source.com/api/shots?match_id={match_id}"
resp = requests.get(url, timeout=10)
resp.raise_for_status()
return resp.json()["shots"]
# 2. 判定射门是否在禁区内
# 标准球场坐标:长105m,宽68m,禁区长16.5m,宽40.32m
def is_in_box(x, y):
return x >= 105 - 16.5 and 34 - 20.16 <= y <= 34 + 20.16
# 3. 统计每队禁区内射门次数
def count_box_shots(shots):
df = pd.DataFrame(shots)
df["in_box"] = df.apply(lambda r: is_in_box(r["x"], r["y"]), axis=1)
result = df[df["in_box"]].groupby("team").size().reset_index(name="box_shots")
return result.sort_values("box_shots", ascending=False)
if __name__ == "__main__":
all_shots = []
for mid in [1001, 1002, 1003]:
all_shots.extend(fetch_shots(mid))
print(count_box_shots(all_shots))
这段代码的关键在于坐标判定逻辑,不同数据源的坐标系原点可能不同(有的以球场中心为原点,有的以左下角为原点),实际使用时需要先确认坐标体系,再调整判定边界,建议在正式统计前,先抽样几条数据做人工核对,避免因坐标系错位导致统计失真。
禁区内射门次数对比分析
假设我们采集了某联赛三支球队近五场比赛的数据,统计结果如下(示例数据):
| 球队 | 总射门 | 禁区内射门 | 占比 |
|---|---|---|---|
| A队 | 68 | 41 | 3% |
| B队 | 72 | 33 | 8% |
| C队 | 55 | 38 | 1% |
从对比中可以读出几个有价值的结论:
第一,C队虽然总射门最少,但禁区内射门占比最高,说明其进攻更注重渗透和禁区内配合,射门选择更“经济”。第二,B队总射门最多但禁区占比偏低,反映其进攻可能依赖远射,效率存在提升空间。第三,A队数据较为均衡,属于典型的稳扎稳打型。
这种对比分析的价值在于,它把“射门多”和“射门好”区分开来,帮助教练组或分析师定位问题——是创造机会能力不足,还是机会质量不高。
可视化呈现与解读
统计结果用图表呈现会更直观,可以使用 matplotlib 绘制分组柱状图:
import matplotlib.pyplot as plt
data = {"A队": [68, 41], "B队": [72, 33], "C队": [55, 38]}
teams = list(data.keys())
total = [v[0] for v in data.values()]
inbox = [v[1] for v in data.values()]
x = range(len(teams))
plt.bar([i - 0.2 for i in x], total, width=0.4, label="总射门")
plt.bar([i + 0.2 for i in x], inbox, width=0.4, label="禁区内射门")
plt.xticks(list(x), teams)
plt.legend()"禁区内射门次数对比")
plt.show()
图表解读时,除了看绝对数量,更要关注占比变化,如果条件允许,还可以叠加期望进球值(xG)做二维对比,进一步区分“数量型”和“质量型”进攻。
常见问题问答(FAQ)
Q1:没有编程基础,能用Python做这类统计吗? 可以,本文的代码框架已经封装了核心逻辑,只需替换数据接口和坐标参数即可运行,建议先从单场比赛的小样本数据练手,熟悉 DataFrame 的基本操作后再扩展到批量统计。
Q2:禁区的判定边界为什么是16.5米? 这是国际足联标准场地的禁区深度,但要注意,不同数据源的坐标缩放比例可能不同,有的以米为单位,有的归一化到0-100,使用前务必核对数据文档,必要时先做坐标换算。
Q3:统计结果和官方数据对不上怎么办? 常见原因有三:坐标系原点不一致、射门事件定义不同(如是否包含被封堵射门)、数据去重逻辑有误,建议逐条比对原始事件,定位差异来源后再修正代码。
Q4:如何把统计扩展到球员级别?
只需在 groupby 中增加球员字段即可,df.groupby(["team", "player"]).size(),同时可以计算每名球员的禁区射门占比,用于评估锋线球员的跑位质量。
总结与进阶建议
本文通过一个完整的 Python 案例,演示了从数据采集、禁区判定、分组统计到可视化对比的全流程,核心要点有三:一是确认坐标体系,二是做好数据清洗与去重,三是把绝对数量和占比结合起来解读。
进阶方向包括:引入 xG 模型做质量加权、按比赛时间段切片分析、用热力图呈现射门位置分布,以及将统计流程封装为可复用的脚本或小型工具,掌握这套方法后,你不仅能统计禁区内射门次数,还能迁移到传球区域、抢断位置等各类空间数据分析场景,让数据真正服务于战术决策。