Portfolio case study · Python · Sports analytics

EuroLeague Impact Lab

Five EuroLeague seasons (2021-22 to 2025-26) pulled from the official API and modelled in Python. Three questions: how much does a team lose when a key player is missing, which defenses shut down which positions, and does knowing the opponent actually make a points forecast better?

pandasNumPyscikit-learnSciPyridge regressionPoisson GLMk-meansshrinkage estimators

What the data says

Points model0.24%

Error reduction from adding opponent defense and home court to a simple per-minute baseline. Knowing a player's real minutes would cut the error by 21%. Playing time drives forecast error far more than matchups do.

Defensive tiers34%

Share of the "tough defense vs soft defense" gap (1.50 points in-sample) that survives on games the model never saw (0.51 points). Most of the apparent matchup effect is noise.

Player absence197

Players averaging 20+ minutes who missed at least one game, each with team results with and without him, adjusted for opponent strength and home court and shrunk toward zero when the "without" sample is small.

How it works

Every test is chronological inside each season: the first 80% of games train the model, the last 20% check it. No information from the future leaks into the forecast, including player roles.

Player absence explorer

Impact = the team's opponent-adjusted margin with the player minus without him, in points per game, shrunk by n/(n+6) where n is games missed. On/off comes from plus-minus: team margin per 40 minutes with him on the floor versus off. All seasons pooled.

Player Team Min/G With Without Impact On/off

Defense against each position, 2025-26

Points a team allowed to each role, divided by what those same players usually score in the same minutes. 0.90 means opponents scored 10% below their norm. Blue is tougher defense, red is softer. Click a position to sort.

0.83 tougher1.15 softer

Testing on games the model never saw

A model that only looks good on its own training data is not useful. Both checks below come from the held-out last 20% of each season.

Points forecast error by season

Mean absolute error in points per player-game. The four models sit on top of each other; the gap to the right-hand cluster is the error that comes from not knowing minutes in advance.

Opponent tiers: training vs test

Points scored relative to the player's own average against each defensive tier (1 = toughest), with 95% confidence intervals. The slope flattens sharply out of sample.

Training (first 80%)Test (last 20%)

Limitations

  • "Without" samples are small for most players; a player who missed two games can show a 20-point swing. Shrinkage helps, but treat single names with caution.
  • The two impact measures barely agree (correlation 0.05 for players with 5+ missed games), another sign of how noisy single-player effects are.
  • Absences are taken from box scores, so rest days and injuries are not separated.
  • Roles come from clustering, not official positions; a hand-made positions file can override them.

Next steps

  • Forecast minutes as their own model (rotation, foul trouble, back-to-backs), since that is where the error is.
  • Use play-by-play lineups to measure individual defenders, already prepared in the code.
  • Combine seasons with decaying weights instead of testing each season alone.
  • Move the pipeline into a scheduled job with a small SQL store.