如何看双方射门转化率差异(Python案例解析)
在足球数据分析中,射门转化率 = 进球数 / 射门数,下面给你一个完整的 Python 案例和分析思路。

核心思路
看双方转化率差异,一般分三层:
- 数值对比:两队转化率谁高谁低、差多少
- 显著性检验:这个差异是真实水平,还是运气/样本小造成的?
- 可视化:直观看差距
完整代码案例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import chi2_contingency, fisher_exact
# 示例数据:某场比赛两队射门与进球
data = {
"球队": ["A队", "B队"],
"射门数": [18, 9],
"进球数": [3, 1],
}
df = pd.DataFrame(data)
df["转化率"] = df["进球数"] / df["射门数"]
df["转化率%"] = (df["转化率"] * 100).round(1)
print(df)
输出示例:
球队 射门数 进球数 转化率 转化率%
0 A队 18 3 0.1667 16.7
1 B队 9 1 0.1111 11.1
怎么读:A 队转化率 16.7% 高于 B 队 11.1%,绝对差 5.6 个百分点,但要进一步判断这个差异是否可靠。
判断差异是否显著(关键一步)
足球射门样本往往很小,光看百分比容易误判,用 卡方检验 或 Fisher 精确检验:
# 构建列联表:[[进球, 未进球], [进球, 未进球]]
table = [
[df.loc[0, "进球数"], df.loc[0, "射门数"] - df.loc[0, "进球数"]],
[df.loc[1, "进球数"], df.loc[1, "射门数"] - df.loc[1, "进球数"]],
]
# 卡方检验
chi2, p_chi, dof, expected = chi2_contingency(table)
# Fisher 精确检验(小样本更合适)
oddsratio, p_fisher = fisher_exact(table)
print(f"卡方检验 p = {p_chi:.3f}")
print(f"Fisher 检验 p = {p_fisher:.3f}")
判断标准:
p < 0.05→ 差异显著,两队转化能力有实质区别p ≥ 0.05→ 差异不显著,可能只是运气/样本波动
⚠️ 单场比赛的小样本几乎永远不显著,所以分析转化率时最好用整赛季或一段时间的累计数据。
可视化对比
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
# 左图:转化率柱状图
axes[0].bar(df["球队"], df["转化率%"], color=["#1f77b4", "#ff7f0e"])
axes[0].set_title("射门转化率对比")
axes[0].set_ylabel("转化率 (%)")
for i, v in enumerate(df["转化率%"]):
axes[0].text(i, v + 0.3, f"{v}%", ha="center")
# 右图:射门/进球堆叠
axes[1].bar(df["球队"], df["进球数"], label="进球", color="green")
axes[1].bar(df["球队"], df["射门数"] - df["进球数"],
bottom=df["进球数"], label="未进球", color="lightgray")
axes[1].set_title("射门构成")
axes[1].legend()
plt.tight_layout()
plt.show()
分析时容易踩的坑
| 坑 | 说明 |
|---|---|
| 样本太小 | 单场 9 脚射门进 1 球,不能说转化率"差",波动极大 |
| 忽视射门质量 | 点球、禁区内、远射难度完全不同,应加权(xG 思路) |
| 只比百分比 | 6 个百分点的差距在 20 脚以内样本里毫无统计意义 |
| 忽略比赛情境 | 落后方狂攻射门多,转化率反而可能被拉低 |
进阶做法:用 xG(预期进球) 代替实际进球,
转化率差异 = 实际进球 - xG,这样能剔除射门难度的影响,更能反映真实终结能力。
一句话总结
看转化率差异 = 先算百分比差 → 再做显著性检验 → 最后结合样本量和射门质量解读,单看百分比会骗人,p 值和样本量才是判断"差异是否真实"的关键。
需要的话我可以给你写一个读取真实比赛数据(如 FBref/StatsBomb)自动算转化率并检验的完整脚本。