← projects

Premier League
Prediction Pipeline

Premier League match prediction using rolling team form, pre-kickoff features, and a chronological test set from 2020 to 2025.

Pre-match features only

Features were calculated from matches played before kickoff. Goals, shots, and the result from the match being predicted were excluded.

53.8% held-out accuracy
Matches modeled
8,827
Predictive features
43
Rolling window
14 games
Future holdout
2020–25

Method at a glance

Pipeline overview

  1. 01Clean match history2000–2025
  2. 02Build team historieshome + away
  3. 03Roll, then shiftprevious 14 only
  4. 04Assemble 43 featuresform + context
  5. 05Cut chronologically80% / 20%
  6. 06Evaluate the modelsfuture matches

01 / The data

Preparing the match data

The raw files include final goals, shots, corners, cards, and the result. Those columns helped build each team’s history, but they were removed from the prediction row because they reveal the result.

The dataset began with 12,153 Premier League matches from 1993 to 2025. Early seasons lacked the detailed match statistics needed to build consistent form features, leaving 9,329 complete matches from 2000 onward. Once every team had enough history for its rolling window, 8,827 matches remained for modeling.

12,153 raw matches
9,329 complete matches
8,827 model rows

What the raw data showed

Home teams won 45.9% of the complete history, most teams scored between zero and two goals, and shots on target correlated with goals more strongly than corners. These results set the baseline and guided the feature set.

Pie chart of Premier League outcomes showing 45.9 percent home wins, 29.3 percent away wins, and 24.8 percent draws
Home advantage shaped the baseline Home wins made up 45.9% of the complete history, so accuracy alone could flatter a model that mostly chose home teams.
Histogram comparing home and away goal distributions
Most matches stayed low-scoring Both distributions peak between zero and two goals. Small scorelines leave more room for one event to change the class.
Correlation heatmap for goals, shots, shots on target, and corners
Shots on target carried the clearest signal Shots on target correlated with goals more strongly than corners, while home and away shot volumes moved in opposite directions.
Box plots showing outliers in goals and shots
Four extreme scorelines were capped Goals above eight were winsorized. A cap of six scored better, but eight kept rare matches without letting them dominate rolling form.

02 / Feature engineering

Rolling team form

Each match became two team rows: one for the home side and one for the away side. Combining those rows gave every club one continuous history across both venues.

Manchester City 3–1 Liverpool
Man Cityscored 3 · conceded 1 · home
Liverpoolscored 1 · conceded 3 · away

From those histories, the pipeline calculated fourteen-match rolling averages for goals, shots, shots on target, corners, cards, and defensive equivalents. It added rest days, cumulative season points, and direct matchup differences such as home attack against away defense.

Previous 14 matches
.shift(1) move knowledge back
Next match ? result hidden

The fourteen green cells represent the fourteen previous matches, not forty. .shift(1) prevents the current match from entering its own form calculation.

Model input 43 features
24

Rolling form

Goals, shots, corners, cards, and defensive equivalents for both teams

5

Matchup differences

Home attack against away defense, shot volume, and rest advantage

5

Closeness

Form, defense, shots, overall matchup, and season-points similarity

3

Season context

Home points, away points, and the difference before kickoff

2

Rest

Days since each team’s previous match, capped at fourteen

2

Time

Day of week and kickoff hour

2

Team identity

Home and away team encodings fitted only on training data

Season points

Season points before kickoff

Results became three, one, or zero points. Points accumulated separately inside each season, then shifted back one match before joining the model input. The model received each team’s points and the points difference, never the standings produced by the match it was predicting.

1Result
3 / 1 / 0
2Season total
cumulative
3Before kickoff
.shift(1)

Match context

Interaction and closeness features

Five interaction features measured differences such as home attack against away defense, shot volume, and rest. Five closeness features measured whether the teams arrived with similar form, defense, shots, and season points. These were designed to expose close matchups that might end level.

03 / Validation

Chronological train/test split

Matches through June 2020 formed the training set. Matches after that, through January 2025, formed the test set. The order was never shuffled.

7,061 training matches Nov. 2000 → Jun. 2020
1,766 unseen matches Jun. 2020 → Jan. 2025

Team encoders were also fitted only on the training period, with an unknown-team category for promoted clubs that had not appeared before. This kept information from the test period out of both the features and the team encoding.

04 / Models

Model comparison and tuning

Five model configurations used the same 2020–2025 test window. Random Forest scored highest. Grid search evaluated 36 parameter combinations across three folds, 108 fits in total, and finished 0.5 percentage points above the untuned model.

Decision Tree 40.0%
XGBoost 50.4%
Random Forest 53.3%
Draw-weighted Random Forest 53.9%
Tuned Random Forest 53.8%

Across the complete dataset, always predicting a home win gives a 45.9% baseline. In the held-out future window, home wins account for 43.6% of matches. The draw-weighted run posted the highest raw accuracy by a tenth of a point but still recovered almost no draws.

05 / Feature importance

Feature importance

The points difference between the teams was the strongest individual signal, followed closely by recent shots, shots conceded, and corners. The largest importance score was only 0.036, so the model depended on the combined feature set.

Tuned Random Forest Top 10 features
Feature importance Higher means the feature contributed more to the model's decisions.
  1. 01 SeasonPointsDiff 0.0367
  2. 02 AT_Shots_Roll14 0.0357
  3. 03 HT_ShotsConceded_Roll14 0.0323
  4. 04 HT_Shots_Roll14 0.0314
  5. 05 AT_Corners_Roll14 0.0312
  6. 06 AT_ShotsConceded_Roll14 0.0309
  7. 07 HT_CornersConceded_Roll14 0.0306
  8. 08 AT_CornersConceded_Roll14 0.0295
  9. 09 AT_GoalsScored_Roll14 0.0287
  10. 10 HT_Corners_Roll14 0.0286
Exact feature importances from the tuned model, shown on a fixed 0.000–0.040 scale.

06 / Outcome recall

Draw recall remained at 2%

The tuned model recognized home wins reliably and away wins moderately, but predicted only 2% of actual draws. Increasing draw weight changed the errors more than it fixed them and did not produce a useful draw classifier.

Home win 84%
Away win 50%
Draw 2%
Confusion matrix for the tuned Random Forest showing most draw results classified as home or away wins
Confusion matrix for the tuned Random Forest.
403 actual draws 15 correctly classified

The other 388 draws were classified as a home or away win. Changing the class weights changed which matches were missed, but draw recall remained at 2%.

Limits and next steps

What the model did not have

The feature set covers team form, season points, rest, and matchup differences. It does not include match-day information.

The source data had no starting lineups, injuries, tactical changes, expected goals, betting odds, or weather. Those inputs may be useful in the close matches that the current model usually assigns to a home or away win.

A later test could add those inputs and reuse the same chronological split. It should compare probability calibration and class recall alongside overall accuracy.

Also, I implemented this in my beginnings. Don’t be too harsh, machine learning people.