Python案例统计禁区内射门次数对比:从数据采集到可视化分析的完整实战
目录导读
- 为什么禁区内射门次数值得单独统计?
- 数据准备:从哪里获取射门数据
- 核心案例:用Python统计禁区内射门次数
- 对比分析:多支球队/多名球员的禁区内外射门差异
- 可视化呈现:让对比结果一目了然
- 常见问题与优化建议
- 问答环节
为什么禁区内射门次数值得单独统计?
在足球数据分析领域,射门位置是衡量进攻质量的关键指标之一,禁区内的射门由于距离球门更近、角度更佳,其进球转化率通常远高于禁区外远射,据统计,顶级联赛中禁区内射门的平均转化率约为12%—15%,而禁区外射门往往只有3%—5%,单独统计禁区内射门次数,能够帮助教练团队、数据分析师和球迷更准确地评估一支球队的进攻效率与战术倾向。

传统的技术统计往往只记录“总射门数”,这会掩盖很多问题,一支球队可能全场射门20次,但其中15次来自禁区外低质量的远射;另一支球队只射门10次,却有8次发生在禁区内,两者的进攻威胁显然不可同日而语,用Python对禁区内射门次数进行统计与对比,正是把这种“质量差异”量化出来的有效手段。
本文将通过一个完整的Python案例,演示如何从原始事件数据中筛选禁区内射门、按球队或球员分组统计、并进行横向对比与可视化,最终形成一份可复用的分析流程。
数据准备:从哪里获取射门数据
要进行禁区内射门统计,首先需要带有射门坐标的事件数据,常见的数据来源包括:
- StatsBomb开放数据:提供部分赛事(如世界杯、女足欧洲杯)的详细事件数据,字段包含射门位置坐标、是否在禁区内等,非常适合练手。
- FBref:提供球队与球员的射门统计,可导出CSV,但粒度较粗。
- Understat:提供射门坐标数据(xG模型),可抓取每脚射门的位置信息。
- 自建数据集:如果你有比赛录像标注数据,也可以整理成CSV。
本文以一份结构化的CSV数据为例,假设字段包括:match_id、team、player、shot_x、shot_y、is_box(是否禁区内)、is_goal,其中is_box可以直接使用,也可以根据坐标自行判断——通常以球场长度方向坐标判断射门点是否落在禁区矩形范围内。
需要提醒的是,不同数据源的坐标系定义不同(有的以米为单位,有的归一化到0—100或0—120),在判断“是否禁区内”之前,务必先确认坐标体系,避免统计口径出错,这是很多初学者容易踩的坑。
核心案例:用Python统计禁区内射门次数
代码部分,我们使用pandas完成数据读取、清洗与分组统计。
import pandas as pd
# 1. 读取数据
df = pd.read_csv("shots.csv")
# 2. 基础清洗:去除缺失坐标或队伍信息的记录
df = df.dropna(subset=["team", "shot_x", "shot_y"])
# 3. 若数据中没有 is_box 字段,可根据坐标判断
# 假设球场长120、宽80,禁区为靠近球门一侧长18、宽44的区域
def in_box(row):
if "is_box" in row and pd.notna(row["is_box"]):
return bool(row["is_box"])
return row["shot_x"] >= 102 and 18 <= row["shot_y"] <= 62
df["in_box"] = df.apply(in_box, axis=1)
# 4. 按球队统计禁区内、外射门次数
pivot = df.groupby(["team", "in_box"]).size().unstack(fill_value=0)
pivot.columns = ["禁区外射门", "禁区内射门"]
pivot["总射门"] = pivot["禁区内射门"] + pivot["禁区外射门"]
pivot["禁区内占比"] = (pivot["禁区内射门"] / pivot["总射门"] * 100).round(1)
print(pivot.sort_values("禁区内射门", ascending=False))
这段代码的核心逻辑分三步:清洗、判定、分组。groupby配合unstack可以一次性得到每支球队在禁区内外的射门分布,随后计算占比字段,便于横向比较,若你想按球员统计,只需把groupby的键换成player即可。
在实际项目中,还建议增加去重逻辑(避免同一事件重复计入)和异常值过滤(如坐标越界),保证统计结果的可靠性。
对比分析:多支球队/多名球员的禁区内外射门差异
得到基础统计后,对比分析才是价值所在,我们可以从三个维度展开:
第一,总量对比。 比较各队禁区内射门次数绝对值,识别进攻火力最强的球队,但要注意,绝对次数受比赛场次影响,跨联赛对比时应换算为场均数据。
第二,结构对比。 用“禁区内射门占比”衡量进攻质量,占比高的球队更倾向于渗透禁区,占比低的球队可能依赖远射,将占比与总射门数结合,可以绘制散点图,把球队分成“高质量高产量”“高质量低产量”等象限。
第三,效率对比。 进一步引入进球数,计算禁区内射门的转化率,判断哪支球队把握机会能力更强,次数多不等于效率高,二者的对比往往能揭示战术执行力的差异。
可以这样计算场均与转化率:
matches = df.groupby("team")["match_id"].nunique()
pivot["场均禁区内射门"] = (pivot["禁区内射门"] / matches).round(2)
goals = df[df["is_goal"] == 1].groupby("team").size()
pivot["禁区内进球"] = goals.reindex(pivot.index).fillna(0)
pivot["禁区内转化率"] = (pivot["禁区内进球"] / pivot["禁区内射门"] * 100).round(1)
通过这套组合指标,对比就不仅仅停留在“谁射得多”,而是深入到“谁射得准、射得值”。
可视化呈现:让对比结果一目了然
数据表格适合细看,图形则适合快速传达结论,推荐两种图:
- 堆叠柱状图:展示每支球队禁区内与禁区外射门的构成,直观看出结构差异。
- 散点图:横轴为禁区内射门占比,纵轴为场均禁区内射门次数,气泡大小表示转化率,一图呈现多维对比。
import matplotlib.pyplot as plt
pivot[["禁区内射门", "禁区外射门"]].plot(kind="bar", stacked=True, figsize=(10, 6))"各队禁区内 vs 禁区外射门次数对比")
plt.ylabel("射门次数")
plt.tight_layout()
plt.show()
使用matplotlib或seaborn即可快速出图,若面向网页展示,可改用plotly生成交互图表,鼠标悬停查看具体数值,体验更佳,可视化时注意配色区分禁区内外,并在标题中直接点明对比主题,方便读者理解。
常见问题与优化建议
- 坐标口径不统一:不同数据源坐标系差异大,务必先统一再判断禁区。
- 样本量过小:单场比赛的射门次数波动大,建议至少累计多场再对比。
- 忽略比赛情境:领先方可能减少进攻,落后方可能狂攻,对比时应结合比分状态分层。
- 重复事件:部分数据源会记录被挡出后补射,注意去重规则。
优化方向包括:引入xG(预期进球)替代简单计数、按时间段切分统计、结合传球网络分析禁区渗透路径等,让分析更具深度。
问答环节
问:没有现成的is_box字段怎么办?
答:根据坐标自行判断,先确认球场坐标系(长度方向范围),再设定禁区矩形阈值,用条件筛选即可,本文第三节已给出示例函数。
问:禁区内射门次数越多,是否代表进攻越强? 答:不完全等价,次数反映产量,还需结合转化率与xG衡量质量,高次数低转化可能意味着把握机会能力不足。
问:如何按球员而非球队统计?
答:把groupby的键从team改为player,其余逻辑不变,并可增加出场时间字段换算为每90分钟数据。
问:数据量很大时性能如何优化?
答:优先使用向量化操作替代apply,将坐标判断改写为布尔索引,必要时用category类型降低内存占用。
本文围绕“Python案例统计禁区内射门次数对比”这一主题,完整走过了数据准备、清洗判定、分组统计、多维对比与可视化呈现的流程,核心要点有三:一是明确禁区判定口径,保证统计一致;二是用pandas分组聚合高效产出对比表;三是结合占比、场均与转化率等多维指标,避免单一数字误导结论,掌握这套方法后,你可以把它迁移到任意赛事数据上,快速生成有价值的进攻质量对比报告。