DEV Community

review
review

Posted on Originally published at casinothaibetzone.pages.dev

Expected Goals (xG): A Practical Introduction for Football Analytics

Expected Goals (xG) is the most important metric in modern football analytics. It measures the quality of chances a team creates, not just the goals they score. Once you understand xG, you can evaluate team performance far better than the raw scoreline. Here is a practical introduction.

What xG actually is

Every shot is assigned a probability (0 to 1) of becoming a goal, based on historical data of similar shots. Key factors:

  • Distance from goal — the biggest factor.
  • Shot angle — wide angles have lower xG.
  • Body part — headers score less than feet.
  • Assist type — through balls > crosses > rebounds.
  • Game state — open play vs set piece.

A penalty is ~0.76 xG. An open goal tap-in is ~0.90. A 30-yard screamer is ~0.03. Team xG = the sum of all their shots' xG in a match.

Why xG beats the scoreline

Goals are rare and noisy events. A team can win 1-0 while being badly outplayed — a low xG with a lucky goal. xG smooths the noise:

  • Team creates 2.8 xG but scores 1 → unlucky, likely to regress up.
  • Team creates 0.4 xG but wins 2-0 → lucky, likely to regress down.

For prediction, xG trends over 5-10 matches are more informative than recent results. This is the foundation of "value betting" — finding when the market prices a team based on results rather than underlying performance. See how we apply xG and value betting concepts for the full framework.

Computing xG with Python

A minimal Poisson-based match model:

import numpy as np
from scipy.stats import poisson

def match_probabilities(xg_home, xg_away, max_goals=8):
    # probability of each scoreline
    probs = np.zeros((max_goals+1, max_goals+1))
    for i in range(max_goals+1):
        for j in range(max_goals+1):
            probs[i, j] = poisson.pmf(i, xg_home) * poisson.pmf(j, xg_away)
    return probs

probs = match_probabilities(1.8, 1.1)
home_win = np.tril(probs, -1).sum()
draw = np.trace(probs)
away_win = np.triu(probs, 1).sum()
print(f"Home {home_win:.1%} | Draw {draw:.1%} | Away {away_win:.1%}")
Enter fullscreen mode Exit fullscreen mode

Feed the model your xG estimates and it outputs win/draw/loss probabilities — which you then compare against the bookmaker's implied probabilities to find value.

Practical workflow

  1. Track xG for your target league (there are free public datasets).
  2. Update your team ratings after every round using recent xG, weighted toward the last 5 matches.
  3. Convert your model's probabilities to fair odds (1 / p).
  4. Bet only when the market odds exceed your fair odds by a meaningful margin (your edge).
  5. Log everything to measure whether your edge is real.

The takeaway

xG is not a crystal ball — it is a better estimator of team strength than results. Combined with a simple Poisson model, it gives you a systematic, repeatable way to find value. That is real analytics, not gambling folklore.

Originally published on CASINO THAI BET ZONE.

Top comments (0)