Expected Goals (xG)¶
Summary¶
Expected goals (xG) is a statistical metric in association football that assigns a probability to each shot resulting in a goal. By summing these probabilities across a match, season, or set of shots, xG estimates how many goals a team or player would be expected to score given the chances created, independent of whether those chances were actually converted.
xG values are produced by statistical or machine-learning models trained on historical shot data. Models typically include features such as shot location (distance and angle), body part used, type of assist, phase of play, and defensive pressure. An xG value of 0.3 means shots of similar characteristics are expected to be scored ~30% of the time — not a prediction about any single shot.
The concept was formalized in football by Sam Green (Opta) in 2012, though earlier work by Ensum, Pollard, and Taylor (2004) on 2002 World Cup data identified distance, angle, defender proximity, and cross as significant factors. The same approach has been applied to ice hockey analytics.
Different providers (Opta, StatsBomb, Stats2, Understat) use different models and event definitions, so xG figures are not directly comparable across sources.
Key Concepts¶
- Shot probability model: Logistic regression or ML model predicting goal probability from shot features
- xG chain: Sum of xG values for all shots in a possession sequence — measures chance quality
- xG against (xGA): Expected goals conceded — measures defensive quality
- Non-penalty xG (npxG): Excludes penalties, which have very high conversion rates (~75–80%) and distort team strength estimates
- xG differential (xGD): xG for minus xG against = total expected goal differential
- Overperformance/underperformance: Teams scoring more/less than their xG suggests regression to the mean (PDO correction)
- Big chance: Many providers tag "big chances" separately (xG > 0.3 typically) — these are high-quality shots
Key Factors in xG Models¶
Research (Ensum/Pollard/Taylor 2004, subsequent Opta/StatsBomb work) identifies:
1. Distance from goal: Primary predictor — exponential decay in probability with distance
2. Angle to goal: Wider angles (facing goal directly) = higher probability
3. Defender proximity: Nearest defender distance significantly reduces probability
4. Body part: Headers have lower conversion than shots with foot (roughly 0.30 vs 0.11 xG for similar positions)
5. Type of shot: Open play vs. set piece vs. penalty (penalties ~0.76 xG)
6. Assist type: Through-ball vs cross vs. rebound
7. Phase of play: Fast break vs. established attack vs. set piece
Formulas¶
Simple xG from distance (logistic model):
$$xG = \frac{1}{1 + e^{-(\beta_0 + \beta_1 \times distance)}}$$
xG for a team over a match:
$$xG_{team} = \sum_{i=1}^{n} xG_i$$
where n = number of shots taken by the team.
Expected scoreline from xG (Poisson):
$$\lambda_{home} = xG_{home} \quad \lambda_{away} = xG_{away}$$
Python Implementation¶
import numpy as np
from sklearn.linear_model import LogisticRegression
def build_xg_model(shots_df):
"""
Build a simple xG model from historical shot data.
shots_df needs: distance, angle, body_part, defender_near, is_big_chance, goal (0/1)
"""
features = ['distance', 'angle', 'defender_near', 'is_big_chance']
X = shots_df[features].values
y = shots_df['goal'].values
model = LogisticRegression()
model.fit(X, y)
return model
def predict_xg(model, shot_features):
"""Predict xG for a single shot."""
return model.predict_proba([shot_features])[0, 1]
def team_xg(shots_df, team_filter):
"""Sum xG for all shots by a team."""
team_shots = shots_df[shots_df['team'] == team_filter]
return team_shots['xg'].sum()
Notes¶
- xG is a descriptive metric, not directly a prediction model — it describes past shot quality, not future match outcomes
- To use xG predictively: use historical xG averages to estimate future λ for Poisson model
- Key issue for betting models: xG data is expensive (requires subscription to Opta/StatsBomb), while free data sources (football-data.co.uk) don't include shot-level data
- Some APIs (API-Football) provide basic xG stats at the match level without per-shot granularity
- For World Cup model: xG historical data may be limited for national team matches compared to club football
- xG vs actual goals comparison reveals overperforming/underperforming teams — useful for identifying regression candidates