射门质量与xG差异原因分析
下面给你一套可直接落地的分析框架+Python脚本,用于诊断"某球员/球队实际进球 vs xG 差异"到底来自哪里。

先理清:xG差异的归因框架
实际进球(Goals) − 预期进球(xG) = G−xG 差值,原因可拆为四层:
| 层级 | 含义 | 关键指标 |
|---|---|---|
| ① 射门数量 | 创造机会多少 | 射门数/90、射门占比 |
| ② 射门位置质量 | 射门在哪 | 平均xG/射门、禁区内射门占比、平均距离、角度 |
| ③ 终结能力 | 同样机会把握 | G−xG、射正率、转化率、post-shot xG |
| ④ 样本与噪声 | 是否真实能力 | 样本量、稳定性、回归检验 |
核心结论先行:G−xG 的差异大部分是噪声(尤其小样本),真实"终结能力"差异在统计上很弱且难稳定,脚本要做的就是把"数量/位置/终结/噪声"分开量化。
综合实用脚本(Python)
"""
shot_xg_diagnosis.py
诊断射门质量与xG差异来源。
输入: 射门级数据 shots.csv
必填列: player, team, xg, goal, distance, angle(可选), body_part(可选), match_id
"""
import pandas as pd
import numpy as np
from scipy import stats
# ---------- 1. 读取 ----------
df = pd.read_csv("shots.csv")
df["goal"] = df["goal"].astype(int)
df["xg"] = df["xg"].astype(float)
# ---------- 2. 单实体汇总 ----------
def summarize(d, entity_col="player"):
g = d.groupby(entity_col).agg(
shots = ("xg", "size"),
goals = ("goal", "sum"),
xg = ("xg", "sum"),
xg_per_shot= ("xg", "mean"),
avg_dist = ("distance", "mean"),
).reset_index()
g["g_minus_xg"] = g["goals"] - g["xg"]
g["conversion"] = g["goals"] / g["shots"]
g["shots_per_90"] = np.nan # 若有分钟数据再填
return g.sort_values("g_minus_xg", ascending=False)
player_sum = summarize(df, "player")
# ---------- 3. 位置质量分层: 禁区内/外 ----------
df["in_box"] = df["distance"] <= 16.5 # 近似禁区半径(米), 或直接用坐标列
box = df.groupby("player").apply(lambda x: pd.Series({
"box_shot_share": x["in_box"].mean(),
"box_xg_per_shot": x.loc[x.in_box, "xg"].mean() if x.in_box.any() else np.nan,
"out_xg_per_shot": x.loc[~x.in_box, "xg"].mean() if (~x.in_box).any() else np.nan,
}), include_groups=False).reset_index()
player_sum = player_sum.merge(box, on="player", how="left")
# ---------- 4. 分解 G-xG:数量 vs 位置 vs 终结 ----------
# 用"同位置基准转化率"估算位置应得,剩余归为终结
base_conv_by_zone = df.groupby("in_box")["goal"].mean() # 每区域平均转化
df["expected_by_zone"] = df["in_box"].map(base_conv_by_zone)
decomp = df.groupby("player").apply(lambda x: pd.Series({
"goals": x["goal"].sum(),
"xg": x["xg"].sum(),
"zone_expected": x["expected_by_zone"].sum(), # 按区域平均转化应得
}), include_groups=False).reset_index()
decomp["finishing_effect"] = decomp["goals"] - decomp["zone_expected"] # 终结超出
decomp["position_effect"] = decomp["zone_expected"] - decomp["xg"] # 位置质量差
decomp["g_minus_xg"] = decomp["goals"] - decomp["xg"]
# ---------- 5. 显著性检验:G-xG 是否显著偏离0 ----------
def binom_test_player(x):
n, k = len(x), x["goal"].sum()
p = x["xg"].mean() # 以平均xG作基准概率(近似)
if n < 10: return np.nan
return stats.binomtest(k, n, p).pvalue
pvals = df.groupby("player").apply(
lambda x: pd.Series({"p_value": binom_test_player(x)}), include_groups=False
).reset_index()
player_sum = player_sum.merge(decomp[["player","finishing_effect","position_effect"]], on="player", how="left")
player_sum = player_sum.merge(pvals, on="player", how="left")
# ---------- 6. 稳定性检验(分半信度): 前半赛季 vs 后半赛季 G-xG 相关 ----------
if "match_id" in df:
df["half"] = df["match_id"] % 2
halves = df.groupby(["player","half"]).apply(
lambda x: x["goal"].sum() - x["xg"].sum(), include_groups=False
).unstack()
if {0,1}.issubset(halves.columns):
r, p = stats.pearsonr(halves[0].fillna(0), halves[1].fillna(0))
print(f"[稳定性] 分半 G-xG 相关系数 r={r:.3f}, p={p:.3f} (接近0=多为噪声)")
# ---------- 7. 输出 ----------
cols = ["player","shots","goals","xg","g_minus_xg","xg_per_shot",
"box_shot_share","position_effect","finishing_effect","p_value"]
print(player_sum[cols].head(20).to_string(index=False))
player_sum.to_csv("xg_diagnosis_output.csv", index=False)
如何读结果(归因判断表)
| 现象 | 判断 | 建议 |
|---|---|---|
| shots少但 xg_per_shot 高 | 机会少而精 | 提升创造量 |
| shots多但 xg_per_shot 低 | 浪射/远射多 | 优化射门选择 |
| box_shot_share 低 | 位置质量差 | 打进禁区 |
finishing_effect 大且 p<0.05 |
可能有真实终结力 | 需更大样本确认 |
finishing_effect 大但 p>0.05 |
大概率噪声 | 别过度解读 |
| 分半相关 r≈0 | G−xG 不稳定 | 视为随机波动 |
实用建议(避免踩坑)
- 样本量门槛:<50 射门的 G−xG 基本不可信;单赛季球员波动极大。
- 优先看 xG/射门 和 位置分布,而不是只看 G−xG。
- 用 post-shot xG(射门后门将位置)可分离"射门质量"与"门将扑救",比单纯 G−xG 更细。
- 回归到均值:对 G−xG 极端值做收缩(如贝叶斯/经验贝叶斯),预测下赛季更准。
- 区分点球:点球 xG≈0.76 固定,会污染终结力评估,建议单独剔除。
需要我把脚本改成球队维度、接入 Understat/FBref 抓取,或加上经验贝叶斯收缩预测版本吗?告诉我你的数据源格式即可。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播