FootyStats xG Is 3x Worse Than Shots on Target: The Data Quality Crisis We Fixed
FootyStats xG has corr=0.35 with goals — beaten by SoT×0.32 (corr=0.56) in ALL 20 non-Big-5 leagues. But for regression detection, noise is a feature: FootyStats xG (62.7% regression) beats our precise aggregate model (58%). The variance filter needs process, not outcome.
The Question
We use FootyStats xG for 20 non-Big-5 leagues. Is it any good? And what are our alternatives?
What We Found
FootyStats xG is actively harmful. In every single non-Big-5 league (20/20 tested), a crude shots_on_target × 0.32 formula predicts actual goals better than FootyStats' xG model.
| League | FootyStats xG→Goals Corr | SoT×0.32→Goals Corr | Improvement |
|---|---|---|---|
| Championship | 0.349 | 0.589 | +68% |
| Serie B | 0.209 | 0.544 | +160% |
| Bundesliga 2 | 0.259 | 0.549 | +112% |
| Eredivisie | 0.394 | 0.640 | +62% |
| Brazil A | 0.206 | 0.437 | +112% |
| Argentina | 0.236 | 0.484 | +105% |
| + 14 more | all worse | all better | +23% to +160% |
FootyStats and FotMob are literally the same data (RMSE identical to 5 decimal places). Same match, same numbers — likely the same underlying provider.
The worst part: The code has a fallback (SOT_TO_XG = 0.32 in data-prep.ts line 507) that's 3x better, but FootyStats xG files OVERRIDE it in the loading priority. We were choosing the worse signal because it had a fancier name.
The Nuance
BUT — FootyStats xG works better for regression detection. This is the session's most important discovery. For the variance filter:
| Source | Goal Prediction Corr | Regression Rate |
|---|---|---|
| Our aggregate model | 0.67 (BEST) | 58.0% (WORST) |
| FootyStats xG | 0.35 (WORST) | 62.7% (MIDDLE) |
| Understat xG | 0.63 | 68.3% (BEST) |
| FotMob match-level | 0.35 | 93.8% (BEST on strict test) |
Noise is a feature. FootyStats xG is "bad" at predicting goals but "good" at regression detection because its errors are independent of finishing luck. When actual goals diverge from FootyStats xG, the gap is more likely to be real overperformance than measurement error.
What This Means
Do NOT replace FootyStats xG with a more accurate model for the variance filter. We tried this with our aggregate model (corr 0.67) — it has 58% regression rate, worse than FootyStats' 62.7%. Making xG more accurate REDUCES the regression signal.
Do replace FootyStats xG for DISPLAY purposes. Our aggregate v2 model (corr 0.68 home / 0.67 away) gives more credible numbers on /picks.
The production data hierarchy for the variance filter:
- Big 5: Understat xG (68.3% regression rate)
- Non-Big-5: FotMob match-level xG (62.7-93.8% depending on methodology)
- Fallback: SoT × 0.32 (better than FootyStats but worse than FotMob match-level)
What's Next
FotMob shot-level xG (from the page scraping pipeline) achieves 82.1% regression rate for non-Big-5 — better than match-level. But the marginal ROI improvement is only +0.1%. The existing match-level variance filter already catches the same teams at scale. Shot data's value is in derivative signals (multi-source disagreement, set-piece breakdown), not direct replacement.