Does the league table never lie?
"The table never lies," they say at the end of every season. Over 26 Scottish Premiership seasons, luck alone moves a team's points total by about eight either way: enough to reshuffle mid-table, but not enough to fake a title.
Intermediate
Contents
The claim
"The league table never lies."
It comes out every May, usually from a manager whose team has just finished where everyone expected. Over a whole season, the argument goes, the luck evens out and every side ends up where it deserves.
Why people believe it
Over one match, anything can happen: a deflection, a red card, a goalkeeper having the game of his life. Nobody claims a single result proves much. But a season is 38 matches, and it feels as if that should be long enough for the breaks to cancel out. When the champions and the relegated sides look about right, it's easy to believe the rest of the table is right too.
The data
Every Scottish Premiership match from 2000/01 to 2025/26: 5,879 matches and 312 team-seasons, from football-data.co.uk. Final scores only.
The method
Two questions:
- How much can luck move a team's points total? Work out how much a season's points would vary for a team whose results were decided purely by chance, then compare that with how much teams' points actually vary.
- Does the table agree with itself? Split every season in half. If the table told the truth about how good each team is, the first-half table and the second-half table should match.
The evidence
Luck is worth about eight points
Across these seasons, teams won 38.1% of their matches and drew 23.7%. If every result were a random draw at those rates, a team's points total over 38 games would vary with a standard deviation of
$$\sqrt{38 \times 1.76} \approx 8.2 \text{ points}$$
In plain football
- 1.76 is how much a single match's points (3, 1 or 0) swing around the average of 1.38.
- × 38 adds up that swing over a season. Luck grows with the number of games, but more slowly: with its square root.
- 8.2 points is the typical size of the luck in a season's total. About a third of teams will be more than 8 points above or below what their football deserved.
Real teams' points vary far more than that, with a standard deviation of about 18 points, because some teams genuinely are much better than others. Luck accounts for about a fifth of the spread in the final table. The other four-fifths is real quality.
The top is solid. The middle is noise.
Whether eight points matters depends on how far apart the teams are. Here are the typical (median) gaps between neighbouring places in the final table:
At the top, the gaps are bigger than luck can bridge. In the middle they're a point or two, far smaller than the eight points luck can add or take away. From 5th to 10th, the order is largely a draw from a hat.
The table disagrees with itself
Split each season in two and compare the table from the first 19 games with the table from the rest. On average, a team's position changes by 2.2 places between the two halves, and 35% of teams move three places or more. The team top of the first-half table also tops the second half only 16 times in 26.
Replaying each season with the same teams makes the point another way. Estimate every team's true quality from its results, allowing for luck, and play the season again 400 times. The best team wins the league about three times in four. A team finishes exactly where its quality says only about 30% of the time.
Show the mathsWhere the 8.2 comes from, and how much of the table is luck. Optional.
For one match, points X are 3 with probability 0.381, 1 with probability 0.237 and 0 otherwise:
$$E[X] = 3(0.381) + 0.237 = 1.38$$
$$E[X^2] = 9(0.381) + 0.237 = 3.67$$
$$\text{Var}(X) = 3.67 - 1.38^2 \approx 1.76$$
Over 38 independent matches the variance adds up, so the luck in a season's total has standard deviation \(\sqrt{38 \times 1.76} \approx 8.2\).
The observed variance of season points is about \(18.4^2 \approx 338\). Luck's share is \(38 \times 1.76 / 338 \approx 20\%\), leaving a spread in true quality of \(\sqrt{338 - 67} \approx 16.5\) points. The equal-teams assumption slightly overstates luck, because a dominant side's results vary less than an average side's.
Splitting each season into alternate matches gives a correlation of 0.70 between the two halves' points per game. Stretched to a full season (the Spearman–Brown formula, \(2r/(1+r)\)), the table's reliability is about 0.82: mostly signal, but a real share of noise.
Verdict
Mixed. The table doesn't lie about the top or the bottom: the gaps there are bigger than luck. But it tells plenty of white lies in between. A side that finishes 6th and one that finishes 9th are often no different in quality, and the points luck handed out can decide who finishes where. And in a league as top-heavy as Scotland's, the "top" is only ever two clubs deep.
Caveats
- Scotland is unusual. Two clubs are far stronger than the rest, which makes the top of the table unusually easy to get right. In a more even league, luck decides more.
- The Premiership split means teams don't all play the same opponents the same number of times, which adds its own noise.
- 2019/20 was cut short after about 30 games, so that season carries more luck than the others.
- The simulation is a sketch. It treats every match as independent and gives each team one fixed level of quality for the whole season. Injuries, transfers and managers change that.
Reproduce the analysis
The results files are published by football-data.co.uk. Download the Premiership file (SC0) for each season, and save each under its own name, such as SC0_2425.csv; they aren't rehosted on this site. Then:
import csv, glob, statistics
points, results = [], []
for path in glob.glob("SC0_*.csv"):
table = {}
with open(path, encoding="latin-1") as f:
for r in csv.DictReader(f):
if not (r.get("FTHG") and r.get("FTAG")):
continue
hg, ag = int(r["FTHG"]), int(r["FTAG"])
for team, gf, ga in ((r["HomeTeam"], hg, ag), (r["AwayTeam"], ag, hg)):
pts = 3 if gf > ga else 1 if gf == ga else 0
table[team] = table.get(team, 0) + pts
results.append(pts)
points.append(statistics.pvariance(table.values()))
win, draw = results.count(3) / len(results), results.count(1) / len(results)
luck_var = 38 * (9 * win + draw - (3 * win + draw) ** 2)
print(f"luck: ±{luck_var ** 0.5:.1f} points; share of the table's spread: {luck_var / statistics.mean(points):.0%}")
Spotted a flaw in the method, or have a football claim you'd like tested? Suggest it on LinkedIn, where discussion of these articles happens.