用Python案例分析:射正数能说明进攻质量吗?
这是一个很好的数据分析问题,让我用Python做一个完整分析来探讨这个问题。

核心结论(先给答案)
射正数只是进攻质量的"部分代理指标",单独使用会误导。 更好的指标组合应该是:xG(预期进球)+ 射正率 + 射门位置分布 + 转化率。
下面用代码演示为什么。
构造模拟数据
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import pearsonr, spearmanr
np.random.seed(42)
# 模拟20支球队一个赛季的数据
n_teams = 20
teams = [f"Team_{i}" for i in range(1, n_teams+1)]
df = pd.DataFrame({
"team": teams,
"shots": np.random.randint(300, 600, n_teams), # 总射门
"shots_on_target": np.random.randint(90, 200, n_teams), # 射正
"goals": np.random.randint(30, 90, n_teams), # 进球
"xg": np.random.uniform(35, 75, n_teams), # 预期进球
})
# 派生指标
df["shot_accuracy"] = df["shots_on_target"] / df["shots"] # 射正率
df["conversion"] = df["goals"] / df["shots_on_target"] # 射正转化率
df["goals_per_shot"] = df["goals"] / df["shots"] # 每射门进球
print(df.head())
关键相关性分析:射正数 vs 进球 / xG
# 射正数 与 进球的相关性
r_goals, p1 = pearsonr(df["shots_on_target"], df["goals"])
# 射正数 与 xG 的相关性
r_xg, p2 = pearsonr(df["shots_on_target"], df["xg"])
# 射正率 与 进球的相关性
r_acc, p3 = pearsonr(df["shot_accuracy"], df["goals"])
# 转化率 与 进球的相关性
r_conv, p4 = pearsonr(df["conversion"], df["goals"])
print(f"射正数 ↔ 进球: r = {r_goals:.3f}, p = {p1:.3f}")
print(f"射正数 ↔ xG: r = {r_xg:.3f}, p = {p2:.3f}")
print(f"射正率 ↔ 进球: r = {r_acc:.3f}, p = {p3:.3f}")
print(f"转化率 ↔ 进球: r = {r_conv:.3f}, p = {p4:.3f}")
解读:射正数与进球相关性通常较高(0.6~0.8),说明它确实包含进攻质量信息,但相关性 ≠ 因果,也 ≠ 完整描述。
反例:射正多但质量差 vs 射正少但质量高
# 找两个典型反例
df["quality_score"] = df["xg"] / df["shots"] # 单次射门平均质量
team_many_shots = df.loc[df["shots_on_target"].idxmax()]
team_high_quality = df.loc[df["quality_score"].idxmax()]
print("射正最多的队:\n", team_many_shots[["team","shots_on_target","goals","xg","conversion"]])
print("\n单射门质量最高的队:\n", team_high_quality[["team","shots_on_target","goals","xg","conversion"]])
常见现象:
- 弱队远射多、射正多,但 xG 低,转化率低("打飞机"和"远程射正"都算射正)
- 强队射门少但位置好,射正少但 xG 高、转化率高
可视化对比
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].scatter(df["shots_on_target"], df["goals"])
axes[0].set_xlabel("射正数"); axes[0].set_ylabel("进球")
axes[0].set_title(f"射正 vs 进球 (r={r_goals:.2f})")
axes[1].scatter(df["shots_on_target"], df["xg"])
axes[1].set_xlabel("射正数"); axes[1].set_ylabel("xG")
axes[1].set_title(f"射正 vs xG (r={r_xg:.2f})")
axes[2].scatter(df["shot_accuracy"], df["conversion"])
axes[2].set_xlabel("射正率"); axes[2].set_ylabel("射正→进球转化率")
axes[2].set_title("射正率高 ≠ 转化率高")
plt.tight_layout(); plt.show()
用多元回归看"射正数"的独立解释力
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
X = df[["shots_on_target", "xg", "shot_accuracy"]]
y = df["goals"]
model = LinearRegression().fit(X, y)
print("系数:", dict(zip(X.columns, model.coef_.round(3))))
print("R²:", round(r2_score(y, model.predict(X)), 3))
# 只用一个指标时
for col in ["shots_on_target", "xg"]:
m = LinearRegression().fit(df[[col]], y)
print(f"仅用 {col}: R² = {r2_score(y, m.predict(df[[col]])):.3f}")
xG 单独解释力 > 射正数单独解释力,因为 xG 已经内含射门位置信息。
结论与建议
| 指标 | 能说明什么 | 局限 |
|---|---|---|
| 射正数 | 进攻"量"的一部分,粗略质量 | 距离无关、防守压力无关、易被刷 |
| 射正率 | 射术稳定性 | 分母(射门数)受战术影响大 |
| 转化率 | 把握机会能力 | 样本小、偶然性大 |
| xG | 射门位置/质量 | 需要数据源,模型有误差 |
| xGOT(射正后xG) | 射正后的实际威胁 | 更精细的射正质量 |
推荐做法
# 综合进攻质量评分
from sklearn.preprocessing import MinMaxScaler
features = ["xg", "shot_accuracy", "conversion", "shots_on_target"]
scaler = MinMaxScaler()
df["attack_quality"] = scaler.fit_transform(df[features]).mean(axis=1)
print(df.sort_values("attack_quality", ascending=False)[["team","attack_quality","xg","shots_on_target"]].head())
一句话总结:射正数是"进攻质量"的必要但不充分指标——它捕捉了部分信息,但必须结合 xG、射门位置、转化率才能真实反映进攻质量,单看射正数,容易把"乱射型"球队误判为"进攻强队"。
如需用真实数据(如 StatsBomb / Football-Data.co.uk 的英超数据)跑一遍,我可以补上数据加载和清洗部分。