← All projects
Machine learning · data engineering · testing

NBA Player Stat Prediction

Predicting how many points, assists, and rebounds an NBA player will record in his next game, using only what was known before tipoff. An end-to-end Python pipeline covering data collection, feature engineering, model training, and evaluation against a simple baseline on games the model never saw during training.

PythonpandasNumPyscikit-learnSQLAlchemySQLiteMatplotlibpytestGitHub Actions

Evaluated on the 2025–26 regular season. The chronological holdout begins March 11, 2026.

1,230
Games ingested
26,547
Player-game rows
4.85
Points MAE
average miss per prediction
23 / 28
7-day windows won
points model vs. baseline

The question

How well can you predict an NBA player's next box score (points, assists, rebounds) from only what was known before tipoff? The answer is: not perfectly. In the games set aside for testing, players averaging 19–21 points over their previous ten games went on to score anywhere from 0 to 41. Recent scoring, minutes, rest, and matchup history all carry useful signal, but individual game outcomes remain highly variable.

A low error can still be easy to fake if a feature quietly peeks at the game being predicted. So useful performance has to survive an honest test: past information only, games evaluated in order, and a simpler method under the same conditions.

System design

From source data to reproducible evaluation.

01
ESPN JSON endpoints
02
Validation + SQLite
03
Feature Engineering
04
Model Training
05
Evaluation + Reports

Keeping the test honest

A model that sees any part of the result it is supposed to predict will look accurate for the wrong reason. That failure is called data leakage, and preventing it is why every historical feature uses a strict date cutoff. Jamal Murray’s 5-game and 10-game scoring averages, recent minutes, rest, and team form can only be derived from information available before the game being predicted. Numbering his appearances in order, a prediction for Game 11 can use Games 1–10 but nothing from Game 11 itself. The database query enforces this by requiring each source game to have an earlier date than the game being predicted. Games he did not play are excluded from the averages rather than counted as zero.

Chronology also governs tuning and evaluation. The most recent 20% of game dates are reserved for the final test. On the earlier data, expanding-window cross-validation divides the dates into four consecutive blocks, producing three rounds that train on the past and validate on the next block. Later games never inform earlier predictions.

Evaluation

A model can look accurate on its own terms and still add nothing if a much simpler rule does the same job. To test whether the added complexity is actually worthwhile, the tuned gradient boosting models are compared head-to-head with a simple 10-game rolling-average baseline, where the player’s average over their previous 10 games is used as the predicted value for that stat in their next game.

The final test contains 5,645 player-game rows that were never used in training. Across that chronological holdout, the points model’s mean absolute error is 4.85 — a little under five points per prediction on average. Across the 5,612 rows for which both methods produced predictions, the model achieved a 4.841 MAE compared to 4.883 for the 10-game baseline. Assists showed the same small edge, 1.407 against 1.417, while rebounds were effectively tied.

The 10-game baseline already captures much of the signal in a player’s recent performance, leaving less room for a more complex model to improve the overall error. The model’s advantage was small in aggregate, but it was consistent over time: it outperformed the baseline in 23 of 28 trailing seven-day windows for points and 22 of 28 for assists.

The NBA model visualizations across this site are rebuilt natively from the project’s generated reporting data.

Chronological holdout · points

Trailing 7-day MAE

23/28windows won
Gradient boosting10-game baseline
Rolling mean absolute error for the points model versus baselineAcross 28 complete trailing seven-day windows, the gradient boosting model has lower error than the ten-game rolling-average baseline in 23 windows.4.504.755.005.255.50Mar 17Mar 24Mar 31Apr 7Apr 13Mar 17Model4.85Baseline4.87Win · 0.01 lowerMar 18Model4.90Baseline4.93Win · 0.03 lowerMar 19Model4.81Baseline4.81Loss · 0.00 higherMar 20Model4.79Baseline4.79Loss · 0.00 higherMar 21Model4.76Baseline4.76Loss · 0.00 higherMar 22Model4.71Baseline4.72Win · 0.01 lowerMar 23Model4.61Baseline4.63Win · 0.01 lowerMar 24Model4.77Baseline4.79Win · 0.01 lowerMar 25Model4.63Baseline4.63Win · 0.01 lowerMar 26Model4.59Baseline4.60Win · 0.02 lowerMar 27Model4.62Baseline4.64Win · 0.03 lowerMar 28Model4.63Baseline4.64Win · 0.01 lowerMar 29Model4.62Baseline4.62Loss · 0.01 higherMar 30Model4.64Baseline4.64Loss · 0.00 higherMar 31Model4.51Baseline4.52Win · 0.01 lowerApr 1Model4.59Baseline4.59Win · 0.01 lowerApr 2Model4.73Baseline4.76Win · 0.02 lowerApr 3Model4.60Baseline4.66Win · 0.05 lowerApr 4Model4.66Baseline4.75Win · 0.09 lowerApr 5Model4.65Baseline4.74Win · 0.09 lowerApr 6Model4.65Baseline4.72Win · 0.06 lowerApr 7Model4.73Baseline4.81Win · 0.08 lowerApr 8Model4.73Baseline4.82Win · 0.10 lowerApr 9Model4.71Baseline4.78Win · 0.08 lowerApr 10Model4.77Baseline4.80Win · 0.03 lowerApr 11Model4.87Baseline4.91Win · 0.04 lowerApr 12Model5.02Baseline5.08Win · 0.06 lowerApr 13Model5.31Baseline5.42Win · 0.11 lower
Each point evaluates the same chronological holdout rows for the model and baseline.23 wins · 5 losses
View rolling error data

Mean absolute error (MAE) is the average size of a prediction error, in points. Lower is better. Each row covers the seven days ending on the listed date.

Scroll the table sideways to see all columns.

Points model and 10-game baseline across 28 trailing windows.
Window endModel MAEBaseline MAELower error
2026-03-174.8544.868Model
2026-03-184.8984.927Model
2026-03-194.8104.807Baseline
2026-03-204.7954.792Baseline
2026-03-214.7644.761Baseline
2026-03-224.7104.721Model
2026-03-234.6134.627Model
2026-03-244.7744.786Model
2026-03-254.6264.633Model
2026-03-264.5864.602Model
2026-03-274.6184.644Model
2026-03-284.6314.638Model
2026-03-294.6214.616Baseline
2026-03-304.6384.637Baseline
2026-03-314.5134.521Model
2026-04-014.5864.595Model
2026-04-024.7354.756Model
2026-04-034.6004.655Model
2026-04-044.6634.749Model
2026-04-054.6554.744Model
2026-04-064.6524.715Model
2026-04-074.7334.812Model
2026-04-084.7274.823Model
2026-04-094.7064.782Model
2026-04-104.7704.799Model
2026-04-114.8734.915Model
2026-04-125.0185.076Model
2026-04-135.3105.419Model
Prediction quality

Useful signal, with a visible ceiling.

The chronological holdout contains 5,645 player-game observations. The model tracks typical scoring outcomes reasonably well, but predictions compress toward the middle instead of following extreme performances all the way up.

That limitation is especially clear on ceiling games: every one of the 188 holdout performances of 30+ points was underpredicted, with an average underprediction of 13.9 points.

Chronological holdout · points

Actual vs. predicted

5,645 rows2-point density cells
More observations = brighterPerfect prediction
Actual versus predicted points on the chronological holdoutAll points-model holdout observations are aggregated into two-point density cells. The model shows useful predictive signal but compresses extreme scoring outcomes toward more typical values.001010202030304040505060602-point density cellActual avg2.4Predicted avg4.9Observations133Overpredicted by 2.5 pts2-point density cellActual avg4.5Predicted avg5.0Observations108Overpredicted by 0.5 pts2-point density cellActual avg28.8Predicted avg24.9Observations4Underpredicted by 3.8 pts2-point density cellActual avg0.1Predicted avg4.9Observations155Overpredicted by 4.8 pts2-point density cellActual avg8.8Predicted avg19.0Observations12Overpredicted by 10.2 pts2-point density cellActual avg0.1Predicted avg3.1Observations288Overpredicted by 3.0 pts2-point density cellActual avg8.4Predicted avg8.9Observations111Overpredicted by 0.5 pts2-point density cellActual avg24.5Predicted avg19.0Observations21Underpredicted by 5.5 pts2-point density cellActual avg12.5Predicted avg17.0Observations26Overpredicted by 4.5 pts2-point density cellActual avg2.3Predicted avg3.2Observations186Overpredicted by 0.9 pts2-point density cellActual avg2.5Predicted avg7.0Observations141Overpredicted by 4.5 pts2-point density cellActual avg22.7Predicted avg15.4Observations23Underpredicted by 7.3 pts2-point density cellActual avg24.6Predicted avg17.0Observations22Underpredicted by 7.6 pts2-point density cellActual avg6.5Predicted avg7.0Observations111Overpredicted by 0.5 pts2-point density cellActual avg22.5Predicted avg17.1Observations22Underpredicted by 5.4 pts2-point density cellActual avg6.5Predicted avg17.0Observations12Overpredicted by 10.5 pts2-point density cellActual avg14.5Predicted avg21.0Observations24Overpredicted by 6.5 pts2-point density cellActual avg6.5Predicted avg5.1Observations87Underpredicted by 1.4 pts2-point density cellActual avg20.5Predicted avg8.9Observations24Underpredicted by 11.6 pts2-point density cellActual avg22.4Predicted avg22.6Observations7Near parity2-point density cellActual avg22.5Predicted avg8.8Observations8Underpredicted by 13.7 pts2-point density cellActual avg32.5Predicted avg13.4Observations2Underpredicted by 19.1 pts2-point density cellActual avg12.5Predicted avg20.9Observations21Overpredicted by 8.4 pts2-point density cellActual avg26.4Predicted avg22.8Observations11Underpredicted by 3.5 pts2-point density cellActual avg6.5Predicted avg9.0Observations101Overpredicted by 2.6 pts2-point density cellActual avg12.5Predicted avg15.0Observations46Overpredicted by 2.5 pts2-point density cellActual avg14.4Predicted avg15.2Observations49Overpredicted by 0.7 pts2-point density cellActual avg12.5Predicted avg9.0Observations82Underpredicted by 3.5 pts2-point density cellActual avg4.5Predicted avg7.0Observations124Overpredicted by 2.5 pts2-point density cellActual avg8.4Predicted avg5.2Observations71Underpredicted by 3.1 pts2-point density cellActual avg34.7Predicted avg17.3Observations3Underpredicted by 17.3 pts2-point density cellActual avg8.3Predicted avg25.2Observations3Overpredicted by 16.9 pts2-point density cellActual avg10.6Predicted avg11.0Observations85Overpredicted by 0.5 pts2-point density cellActual avg22.7Predicted avg3.5Observations3Underpredicted by 19.2 pts2-point density cellActual avg4.7Predicted avg10.9Observations51Overpredicted by 6.2 pts2-point density cellActual avg0.1Predicted avg13.1Observations9Overpredicted by 13.0 pts2-point density cellActual avg18.5Predicted avg15.2Observations35Underpredicted by 3.4 pts2-point density cellActual avg8.5Predicted avg10.9Observations67Overpredicted by 2.4 pts2-point density cellActual avg38.7Predicted avg22.5Observations3Underpredicted by 16.2 pts2-point density cellActual avg16.5Predicted avg15.0Observations41Underpredicted by 1.5 pts2-point density cellActual avg12.5Predicted avg10.7Observations79Underpredicted by 1.8 pts2-point density cellActual avg2.5Predicted avg11.0Observations55Overpredicted by 8.5 pts2-point density cellActual avg10.6Predicted avg9.0Observations90Underpredicted by 1.5 pts2-point density cellActual avg22.6Predicted avg13.0Observations13Underpredicted by 9.6 pts2-point density cellActual avg2.5Predicted avg9.0Observations100Overpredicted by 6.5 pts2-point density cellActual avg16.5Predicted avg5.3Observations17Underpredicted by 11.2 pts2-point density cellActual avg16.5Predicted avg7.1Observations36Underpredicted by 9.4 pts2-point density cellActual avg8.8Predicted avg17.0Observations25Overpredicted by 8.2 pts2-point density cellActual avg40.8Predicted avg17.0Observations4Underpredicted by 23.8 pts2-point density cellActual avg20.6Predicted avg15.0Observations34Underpredicted by 5.6 pts2-point density cellActual avg8.4Predicted avg6.9Observations125Underpredicted by 1.5 pts2-point density cellActual avg4.3Predicted avg20.5Observations3Overpredicted by 16.2 pts2-point density cellActual avg10.5Predicted avg13.0Observations55Overpredicted by 2.5 pts2-point density cellActual avg22.7Predicted avg20.8Observations16Underpredicted by 1.9 pts2-point density cellActual avg12.5Predicted avg5.3Observations35Underpredicted by 7.1 pts2-point density cellActual avg0.1Predicted avg6.8Observations85Overpredicted by 6.7 pts2-point density cellActual avg10.5Predicted avg5.1Observations47Underpredicted by 5.4 pts2-point density cellActual avg16.6Predicted avg13.0Observations29Underpredicted by 3.6 pts2-point density cellActual avg14.5Predicted avg19.0Observations22Overpredicted by 4.5 pts2-point density cellActual avg16.4Predicted avg18.9Observations29Overpredicted by 2.5 pts2-point density cellActual avg12.4Predicted avg13.0Observations65Overpredicted by 0.7 pts2-point density cellActual avg4.5Predicted avg15.1Observations15Overpredicted by 10.6 pts2-point density cellActual avg29.0Predicted avg9.5Observations3Underpredicted by 19.5 pts2-point density cellActual avg0.1Predicted avg8.9Observations40Overpredicted by 8.7 pts2-point density cellActual avg6.5Predicted avg15.0Observations30Overpredicted by 8.5 pts2-point density cellActual avg4.5Predicted avg8.9Observations96Overpredicted by 4.5 pts2-point density cellActual avg24.0Predicted avg8.7Observations5Underpredicted by 15.3 pts2-point density cellActual avg14.4Predicted avg7.2Observations43Underpredicted by 7.3 pts2-point density cellActual avg8.5Predicted avg15.0Observations29Overpredicted by 6.5 pts2-point density cellActual avg14.5Predicted avg17.0Observations37Overpredicted by 2.5 pts2-point density cellActual avg30.7Predicted avg28.5Observations3Underpredicted by 2.1 pts2-point density cellActual avg30.5Predicted avg17.0Observations11Underpredicted by 13.5 pts2-point density cellActual avg4.4Predicted avg3.2Observations86Underpredicted by 1.2 pts2-point density cellActual avg12.6Predicted avg3.1Observations14Underpredicted by 9.5 pts2-point density cellActual avg10.5Predicted avg7.0Observations81Underpredicted by 3.5 pts2-point density cellActual avg14.3Predicted avg27.2Observations4Overpredicted by 13.0 pts2-point density cellActual avg0.1Predicted avg11.0Observations19Overpredicted by 10.9 pts2-point density cellActual avg24.6Predicted avg24.9Observations10Overpredicted by 0.3 pts2-point density cellActual avg12.5Predicted avg7.0Observations59Underpredicted by 5.4 pts2-point density cellActual avg30.1Predicted avg19.3Observations9Underpredicted by 10.9 pts2-point density cellActual avg34.5Predicted avg20.8Observations4Underpredicted by 13.7 pts2-point density cellActual avg18.4Predicted avg16.8Observations30Underpredicted by 1.6 pts2-point density cellActual avg4.6Predicted avg13.1Observations29Overpredicted by 8.5 pts2-point density cellActual avg22.5Predicted avg10.9Observations18Underpredicted by 11.6 pts2-point density cellActual avg26.5Predicted avg15.2Observations14Underpredicted by 11.3 pts2-point density cellActual avg22.4Predicted avg19.1Observations28Underpredicted by 3.3 pts2-point density cellActual avg16.6Predicted avg9.1Observations45Underpredicted by 7.4 pts2-point density cellActual avg18.5Predicted avg18.9Observations31Overpredicted by 0.4 pts2-point density cellActual avg28.5Predicted avg21.0Observations8Underpredicted by 7.5 pts2-point density cellActual avg6.4Predicted avg13.0Observations34Overpredicted by 6.6 pts2-point density cellActual avg26.5Predicted avg4.9Observations4Underpredicted by 21.6 pts2-point density cellActual avg20.5Predicted avg21.0Observations28Overpredicted by 0.5 pts2-point density cellActual avg6.5Predicted avg10.9Observations73Overpredicted by 4.4 pts2-point density cellActual avg28.4Predicted avg14.9Observations10Underpredicted by 13.5 pts2-point density cellActual avg14.4Predicted avg12.9Observations57Underpredicted by 1.5 pts2-point density cellActual avg0.0Predicted avg14.8Observations6Overpredicted by 14.8 pts2-point density cellActual avg16.0Predicted avg29.6Observations1Overpredicted by 13.6 pts2-point density cellActual avg10.6Predicted avg25.1Observations5Overpredicted by 14.5 pts2-point density cellActual avg30.3Predicted avg22.9Observations11Underpredicted by 7.4 pts2-point density cellActual avg18.4Predicted avg12.9Observations39Underpredicted by 5.5 pts2-point density cellActual avg10.6Predicted avg15.1Observations44Overpredicted by 4.5 pts2-point density cellActual avg16.6Predicted avg17.0Observations22Overpredicted by 0.4 pts2-point density cellActual avg10.4Predicted avg20.9Observations9Overpredicted by 10.5 pts2-point density cellActual avg39.0Predicted avg14.4Observations1Underpredicted by 24.6 pts2-point density cellActual avg26.3Predicted avg13.0Observations9Underpredicted by 13.3 pts2-point density cellActual avg8.3Predicted avg12.7Observations38Overpredicted by 4.4 pts2-point density cellActual avg20.4Predicted avg19.1Observations18Underpredicted by 1.3 pts2-point density cellActual avg10.5Predicted avg16.8Observations29Overpredicted by 6.3 pts2-point density cellActual avg6.4Predicted avg3.2Observations57Underpredicted by 3.2 pts2-point density cellActual avg14.5Predicted avg9.1Observations61Underpredicted by 5.4 pts2-point density cellActual avg18.4Predicted avg10.9Observations52Underpredicted by 7.5 pts2-point density cellActual avg10.4Predicted avg3.2Observations23Underpredicted by 7.3 pts2-point density cellActual avg36.0Predicted avg26.5Observations2Underpredicted by 9.5 pts2-point density cellActual avg45.0Predicted avg26.1Observations1Underpredicted by 18.9 pts2-point density cellActual avg8.0Predicted avg28.4Observations2Overpredicted by 20.4 pts2-point density cellActual avg14.5Predicted avg11.0Observations66Underpredicted by 3.6 pts2-point density cellActual avg43.0Predicted avg23.8Observations1Underpredicted by 19.2 pts2-point density cellActual avg36.0Predicted avg19.1Observations4Underpredicted by 16.9 pts2-point density cellActual avg28.1Predicted avg12.5Observations7Underpredicted by 15.6 pts2-point density cellActual avg18.2Predicted avg24.8Observations6Overpredicted by 6.7 pts2-point density cellActual avg24.4Predicted avg5.3Observations5Underpredicted by 19.1 pts2-point density cellActual avg18.5Predicted avg6.8Observations18Underpredicted by 11.7 pts2-point density cellActual avg20.5Predicted avg26.8Observations4Overpredicted by 6.3 pts2-point density cellActual avg28.0Predicted avg10.7Observations1Underpredicted by 17.3 pts2-point density cellActual avg16.5Predicted avg11.0Observations51Underpredicted by 5.5 pts2-point density cellActual avg13.0Predicted avg24.5Observations1Overpredicted by 11.5 pts2-point density cellActual avg20.4Predicted avg17.0Observations30Underpredicted by 3.5 pts2-point density cellActual avg34.7Predicted avg11.3Observations3Underpredicted by 23.4 pts2-point density cellActual avg31.0Predicted avg30.3Observations1Underpredicted by 0.7 pts2-point density cellActual avg39.0Predicted avg24.5Observations1Underpredicted by 14.5 pts2-point density cellActual avg26.5Predicted avg16.8Observations12Underpredicted by 9.7 pts2-point density cellActual avg18.3Predicted avg5.2Observations6Underpredicted by 13.2 pts2-point density cellActual avg30.3Predicted avg15.1Observations6Underpredicted by 15.3 pts2-point density cellActual avg20.6Predicted avg7.2Observations14Underpredicted by 13.3 pts2-point density cellActual avg34.3Predicted avg22.7Observations4Underpredicted by 11.5 pts2-point density cellActual avg34.8Predicted avg28.4Observations4Underpredicted by 6.3 pts2-point density cellActual avg2.5Predicted avg12.6Observations15Overpredicted by 10.2 pts2-point density cellActual avg51.0Predicted avg28.9Observations1Underpredicted by 22.1 pts2-point density cellActual avg16.7Predicted avg25.1Observations6Overpredicted by 8.4 pts2-point density cellActual avg28.8Predicted avg23.1Observations5Underpredicted by 5.7 pts2-point density cellActual avg30.6Predicted avg25.2Observations8Underpredicted by 5.4 pts2-point density cellActual avg16.8Predicted avg20.9Observations15Overpredicted by 4.1 pts2-point density cellActual avg28.6Predicted avg16.9Observations5Underpredicted by 11.7 pts2-point density cellActual avg24.4Predicted avg20.9Observations18Underpredicted by 3.6 pts2-point density cellActual avg8.4Predicted avg3.3Observations53Underpredicted by 5.1 pts2-point density cellActual avg32.1Predicted avg23.1Observations7Underpredicted by 9.0 pts2-point density cellActual avg5.0Predicted avg18.9Observations5Overpredicted by 13.9 pts2-point density cellActual avg14.4Predicted avg22.7Observations5Overpredicted by 8.3 pts2-point density cellActual avg42.7Predicted avg27.3Observations3Underpredicted by 15.4 pts2-point density cellActual avg24.9Predicted avg15.0Observations14Underpredicted by 9.9 pts2-point density cellActual avg24.4Predicted avg13.3Observations7Underpredicted by 11.1 pts2-point density cellActual avg18.4Predicted avg9.2Observations23Underpredicted by 9.3 pts2-point density cellActual avg14.4Predicted avg3.3Observations12Underpredicted by 11.1 pts2-point density cellActual avg20.5Predicted avg13.1Observations22Underpredicted by 7.4 pts2-point density cellActual avg26.7Predicted avg18.8Observations15Underpredicted by 7.9 pts2-point density cellActual avg28.3Predicted avg27.3Observations8Underpredicted by 1.0 pts2-point density cellActual avg14.4Predicted avg5.1Observations20Underpredicted by 9.3 pts2-point density cellActual avg6.4Predicted avg19.2Observations12Overpredicted by 12.8 pts2-point density cellActual avg24.3Predicted avg11.1Observations15Underpredicted by 13.2 pts2-point density cellActual avg20.5Predicted avg23.0Observations12Overpredicted by 2.5 pts2-point density cellActual avg20.5Predicted avg11.0Observations33Underpredicted by 9.4 pts2-point density cellActual avg24.0Predicted avg30.6Observations1Overpredicted by 6.6 pts2-point density cellActual avg5.0Predicted avg25.0Observations1Overpredicted by 20.0 pts2-point density cellActual avg32.2Predicted avg19.1Observations6Underpredicted by 13.1 pts2-point density cellActual avg32.3Predicted avg15.1Observations4Underpredicted by 17.1 pts2-point density cellActual avg20.0Predicted avg28.6Observations4Overpredicted by 8.6 pts2-point density cellActual avg18.8Predicted avg27.5Observations4Overpredicted by 8.7 pts2-point density cellActual avg26.2Predicted avg24.8Observations5Underpredicted by 1.4 pts2-point density cellActual avg26.4Predicted avg21.0Observations14Underpredicted by 5.4 pts2-point density cellActual avg32.4Predicted avg11.0Observations5Underpredicted by 21.4 pts2-point density cellActual avg22.5Predicted avg7.1Observations11Underpredicted by 15.3 pts2-point density cellActual avg41.0Predicted avg18.9Observations2Underpredicted by 22.1 pts2-point density cellActual avg18.3Predicted avg21.0Observations25Overpredicted by 2.7 pts2-point density cellActual avg12.6Predicted avg18.7Observations16Overpredicted by 6.1 pts2-point density cellActual avg40.5Predicted avg25.4Observations4Underpredicted by 15.1 pts2-point density cellActual avg40.5Predicted avg22.1Observations2Underpredicted by 18.4 pts2-point density cellActual avg36.0Predicted avg29.2Observations1Underpredicted by 6.8 pts2-point density cellActual avg40.3Predicted avg28.8Observations4Underpredicted by 11.5 pts2-point density cellActual avg6.0Predicted avg25.3Observations2Overpredicted by 19.3 pts2-point density cellActual avg30.0Predicted avg9.4Observations3Underpredicted by 20.6 pts2-point density cellActual avg2.3Predicted avg15.1Observations9Overpredicted by 12.8 pts2-point density cellActual avg18.5Predicted avg22.8Observations12Overpredicted by 4.3 pts2-point density cellActual avg32.5Predicted avg16.8Observations2Underpredicted by 15.7 pts2-point density cellActual avg34.3Predicted avg27.1Observations4Underpredicted by 7.1 pts2-point density cellActual avg8.4Predicted avg20.6Observations7Overpredicted by 12.2 pts2-point density cellActual avg12.7Predicted avg22.8Observations3Overpredicted by 10.1 pts2-point density cellActual avg4.8Predicted avg16.8Observations5Overpredicted by 12.0 pts2-point density cellActual avg2.0Predicted avg21.4Observations2Overpredicted by 19.4 pts2-point density cellActual avg2.5Predicted avg18.6Observations2Overpredicted by 16.1 pts2-point density cellActual avg10.7Predicted avg18.5Observations6Overpredicted by 7.9 pts2-point density cellActual avg26.0Predicted avg10.8Observations1Underpredicted by 15.2 pts2-point density cellActual avg24.8Predicted avg27.5Observations4Overpredicted by 2.7 pts2-point density cellActual avg29.0Predicted avg29.0Observations1Near parity2-point density cellActual avg20.0Predicted avg24.6Observations7Overpredicted by 4.6 pts2-point density cellActual avg60.0Predicted avg29.0Observations1Underpredicted by 31.0 pts2-point density cellActual avg36.0Predicted avg21.0Observations5Underpredicted by 15.0 pts2-point density cellActual avg22.6Predicted avg28.3Observations7Overpredicted by 5.8 pts2-point density cellActual avg34.0Predicted avg25.0Observations3Underpredicted by 9.0 pts2-point density cellActual avg38.0Predicted avg17.2Observations2Underpredicted by 20.8 pts2-point density cellActual avg24.7Predicted avg22.8Observations7Underpredicted by 1.9 pts2-point density cellActual avg22.3Predicted avg26.8Observations4Overpredicted by 4.6 pts2-point density cellActual avg31.0Predicted avg21.0Observations4Underpredicted by 10.0 pts2-point density cellActual avg32.5Predicted avg28.7Observations2Underpredicted by 3.8 pts2-point density cellActual avg16.8Predicted avg3.4Observations8Underpredicted by 13.4 pts2-point density cellActual avg32.7Predicted avg27.8Observations3Underpredicted by 4.9 pts2-point density cellActual avg41.0Predicted avg20.3Observations1Underpredicted by 20.7 pts2-point density cellActual avg22.8Predicted avg24.5Observations5Overpredicted by 1.7 pts2-point density cellActual avg25.0Predicted avg28.4Observations2Overpredicted by 3.4 pts2-point density cellActual avg6.5Predicted avg21.8Observations2Overpredicted by 15.3 pts2-point density cellActual avg37.0Predicted avg23.1Observations1Underpredicted by 13.9 pts2-point density cellActual avg36.5Predicted avg17.4Observations2Underpredicted by 19.1 pts2-point density cellActual avg20.3Predicted avg5.2Observations8Underpredicted by 15.0 pts2-point density cellActual avg42.0Predicted avg24.1Observations1Underpredicted by 17.9 pts2-point density cellActual avg36.7Predicted avg24.9Observations3Underpredicted by 11.7 pts2-point density cellActual avg17.0Predicted avg27.3Observations2Overpredicted by 10.3 pts2-point density cellActual avg26.4Predicted avg9.1Observations5Underpredicted by 17.3 pts2-point density cellActual avg53.0Predicted avg19.9Observations1Underpredicted by 33.1 pts2-point density cellActual avg26.3Predicted avg27.5Observations3Overpredicted by 1.2 pts2-point density cellActual avg10.7Predicted avg22.9Observations3Overpredicted by 12.2 pts2-point density cellActual avg36.0Predicted avg13.8Observations1Underpredicted by 22.2 pts2-point density cellActual avg16.5Predicted avg22.2Observations4Overpredicted by 5.7 pts2-point density cellActual avg30.4Predicted avg12.6Observations5Underpredicted by 17.8 pts2-point density cellActual avg26.0Predicted avg7.0Observations4Underpredicted by 19.0 pts2-point density cellActual avg28.3Predicted avg19.4Observations4Underpredicted by 8.9 pts2-point density cellActual avg26.0Predicted avg28.5Observations3Overpredicted by 2.5 pts2-point density cellActual avg0.0Predicted avg16.5Observations1Overpredicted by 16.5 pts2-point density cellActual avg36.0Predicted avg7.8Observations1Underpredicted by 28.2 pts2-point density cellActual avg32.7Predicted avg25.3Observations3Underpredicted by 7.4 pts2-point density cellActual avg30.0Predicted avg27.5Observations1Underpredicted by 2.5 pts2-point density cellActual avg34.0Predicted avg19.5Observations1Underpredicted by 14.5 pts2-point density cellActual avg47.0Predicted avg27.9Observations1Underpredicted by 19.1 pts2-point density cellActual avg8.0Predicted avg22.3Observations1Overpredicted by 14.3 pts2-point density cellActual avg12.0Predicted avg26.1Observations1Overpredicted by 14.1 pts2-point density cellActual avg42.0Predicted avg29.1Observations1Underpredicted by 12.9 pts2-point density cellActual avg39.0Predicted avg19.1Observations1Underpredicted by 19.9 pts2-point density cellActual avg32.0Predicted avg20.9Observations1Underpredicted by 11.1 pts2-point density cellActual avg24.5Predicted avg6.6Observations2Underpredicted by 17.9 pts2-point density cellActual avg36.0Predicted avg11.4Observations1Underpredicted by 24.6 pts2-point density cellActual avg15.0Predicted avg28.9Observations1Overpredicted by 13.9 pts2-point density cellActual avg12.0Predicted avg28.9Observations1Overpredicted by 16.9 pts2-point density cellActual avg51.0Predicted avg22.4Observations1Underpredicted by 28.6 pts2-point density cellActual avg40.0Predicted avg26.8Observations1Underpredicted by 13.2 pts2-point density cellActual avg15.0Predicted avg24.3Observations2Overpredicted by 9.3 pts2-point density cellActual avg45.0Predicted avg24.4Observations1Underpredicted by 20.6 pts2-point density cellActual avg34.0Predicted avg7.7Observations1Underpredicted by 26.3 pts2-point density cellActual avg33.0Predicted avg3.8Observations1Underpredicted by 29.2 pts2-point density cellActual avg14.0Predicted avg31.7Observations1Overpredicted by 17.7 pts2-point density cellActual avg5.0Predicted avg1.9Observations1Underpredicted by 3.1 pts2-point density cellActual avg0.0Predicted avg20.9Observations1Overpredicted by 20.9 pts2-point density cellActual avg22.5Predicted avg5.5Observations4Underpredicted by 17.0 pts2-point density cellActual avg2.0Predicted avg16.2Observations1Overpredicted by 14.2 pts2-point density cellActual avg35.0Predicted avg12.8Observations1Underpredicted by 22.2 pts2-point density cellActual avg20.5Predicted avg2.9Observations2Underpredicted by 17.6 pts2-point density cellActual avg27.0Predicted avg3.5Observations1Underpredicted by 23.5 pts2-point density cellActual avg30.0Predicted avg7.8Observations1Underpredicted by 22.2 pts2-point density cellActual avg31.0Predicted avg3.7Observations1Underpredicted by 27.3 pts2-point density cellActual avg28.0Predicted avg7.9Observations1Underpredicted by 20.1 pts2-point density cellActual avg18.5Predicted avg3.4Observations2Underpredicted by 15.1 pts2-point density cellActual avg23.0Predicted avg30.1Observations1Overpredicted by 7.1 ptsActual pointsPredicted points
Typical outcomes track the parity line; high-scoring games fall below it as predictions compress toward the mean.Max: 31.7 predicted · 60 actual
View prediction density data

Each row corresponds to one occupied cell in the chart. Two-point ranges include the lower bound and exclude the upper bound, except the final 58–60 range, which includes 60. Averages describe the observations within that cell.

Scroll the table sideways to see all columns.

Actual and predicted points for 5,645 holdout observations.
Actual rangePredicted rangeActual avgPredicted avgObservations
0–22–40.13.1288
0–24–60.14.9155
0–26–80.16.885
0–28–100.18.940
0–210–120.111.019
0–212–140.113.19
0–214–160.014.86
0–216–180.016.51
0–220–220.020.91
2–42–42.33.2186
2–44–62.44.9133
2–46–82.57.0141
2–48–102.59.0100
2–410–122.511.055
2–412–142.512.615
2–414–162.315.19
2–416–182.016.21
2–418–202.518.62
2–420–222.021.42
4–60–25.01.91
4–62–44.43.286
4–64–64.55.0108
4–66–84.57.0124
4–68–104.58.996
4–610–124.710.951
4–612–144.613.129
4–614–164.515.115
4–616–184.816.85
4–618–205.018.95
4–620–224.320.53
4–624–265.025.01
6–82–46.43.257
6–84–66.55.187
6–86–86.57.0111
6–88–106.59.0101
6–810–126.510.973
6–812–146.413.034
6–814–166.515.030
6–816–186.517.012
6–818–206.419.212
6–820–226.521.82
6–824–266.025.32
8–102–48.43.353
8–104–68.45.271
8–106–88.46.9125
8–108–108.48.9111
8–1010–128.510.967
8–1012–148.312.738
8–1014–168.515.029
8–1016–188.817.025
8–1018–208.819.012
8–1020–228.420.67
8–1022–248.022.31
8–1024–268.325.23
8–1028–308.028.42
10–122–410.43.223
10–124–610.55.147
10–126–810.57.081
10–128–1010.69.090
10–1210–1210.611.085
10–1212–1410.513.055
10–1214–1610.615.144
10–1216–1810.516.829
10–1218–2010.718.56
10–1220–2210.420.99
10–1222–2410.722.93
10–1224–2610.625.15
12–142–412.63.114
12–144–612.55.335
12–146–812.57.059
12–148–1012.59.082
12–1410–1212.510.779
12–1412–1412.413.065
12–1414–1612.515.046
12–1416–1812.517.026
12–1418–2012.618.716
12–1420–2212.520.921
12–1422–2412.722.83
12–1424–2613.024.51
12–1426–2812.026.11
12–1428–3012.028.91
14–162–414.43.312
14–164–614.45.120
14–166–814.47.243
14–168–1014.59.161
14–1610–1214.511.066
14–1612–1414.412.957
14–1614–1614.415.249
14–1616–1814.517.037
14–1618–2014.519.022
14–1620–2214.521.024
14–1622–2414.422.75
14–1624–2615.024.32
14–1626–2814.327.24
14–1628–3015.028.91
14–1630–3214.031.71
16–182–416.83.48
16–184–616.55.317
16–186–816.57.136
16–188–1016.69.145
16–1810–1216.511.051
16–1812–1416.613.029
16–1814–1616.515.041
16–1816–1816.617.022
16–1818–2016.418.929
16–1820–2216.820.915
16–1822–2416.522.24
16–1824–2616.725.16
16–1826–2817.027.32
16–1828–3016.029.61
18–202–418.53.42
18–204–618.35.26
18–206–818.56.818
18–208–1018.49.223
18–2010–1218.410.952
18–2012–1418.412.939
18–2014–1618.515.235
18–2016–1818.416.830
18–2018–2018.518.931
18–2020–2218.321.025
18–2022–2418.522.812
18–2024–2618.224.86
18–2026–2818.827.54
20–222–420.52.92
20–224–620.35.28
20–226–820.67.214
20–228–1020.58.924
20–2210–1220.511.033
20–2212–1420.513.122
20–2214–1620.615.034
20–2216–1820.417.030
20–2218–2020.419.118
20–2220–2220.521.028
20–2222–2420.523.012
20–2224–2620.024.67
20–2226–2820.526.84
20–2228–3020.028.64
22–242–422.73.53
22–244–622.55.54
22–246–822.57.111
22–248–1022.58.88
22–2410–1222.510.918
22–2412–1422.613.013
22–2414–1622.715.423
22–2416–1822.517.122
22–2418–2022.419.128
22–2420–2222.720.816
22–2422–2422.422.67
22–2424–2622.824.55
22–2426–2822.326.84
22–2428–3022.628.37
22–2430–3223.030.11
24–264–624.45.35
24–266–824.56.62
24–268–1024.08.75
24–2610–1224.311.115
24–2612–1424.413.37
24–2614–1624.915.014
24–2616–1824.617.022
24–2618–2024.519.021
24–2620–2224.420.918
24–2622–2424.722.87
24–2624–2624.624.910
24–2626–2824.827.54
24–2628–3025.028.42
24–2630–3224.030.61
26–282–427.03.51
26–284–626.54.94
26–286–826.07.04
26–288–1026.49.15
26–2810–1226.010.81
26–2812–1426.313.09
26–2814–1626.515.214
26–2816–1826.516.812
26–2818–2026.718.815
26–2820–2226.421.014
26–2822–2426.422.811
26–2824–2626.224.85
26–2826–2826.327.53
26–2828–3026.028.53
28–306–828.07.91
28–308–1029.09.53
28–3010–1228.010.71
28–3012–1428.112.57
28–3014–1628.414.910
28–3016–1828.616.95
28–3018–2028.319.44
28–3020–2228.521.08
28–3022–2428.823.15
28–3024–2628.824.94
28–3026–2828.327.38
28–3028–3029.029.01
30–322–431.03.71
30–326–830.07.81
30–328–1030.09.43
30–3212–1430.412.65
30–3214–1630.315.16
30–3216–1830.517.011
30–3218–2030.119.39
30–3220–2231.021.04
30–3222–2430.322.911
30–3224–2630.625.28
30–3226–2830.027.51
30–3228–3030.728.53
30–3230–3231.030.31
32–342–433.03.81
32–3410–1232.411.05
32–3412–1432.513.42
32–3414–1632.315.14
32–3416–1832.516.82
32–3418–2032.219.16
32–3420–2232.020.91
32–3422–2432.123.17
32–3424–2632.725.33
32–3426–2832.727.83
32–3428–3032.528.72
34–366–834.07.71
34–3610–1234.711.33
34–3612–1435.012.81
34–3616–1834.717.33
34–3618–2034.019.51
34–3620–2234.520.84
34–3622–2434.322.74
34–3624–2634.025.03
34–3626–2834.327.14
34–3628–3034.828.44
36–386–836.07.81
36–3810–1236.011.41
36–3812–1436.013.81
36–3816–1836.517.42
36–3818–2036.019.14
36–3820–2236.021.05
36–3822–2437.023.11
36–3824–2636.724.93
36–3826–2836.026.52
36–3828–3036.029.21
38–4014–1639.014.41
38–4016–1838.017.22
38–4018–2039.019.11
38–4022–2438.722.53
38–4024–2639.024.51
40–4216–1840.817.04
40–4218–2041.018.92
40–4220–2241.020.31
40–4222–2440.522.12
40–4224–2640.525.44
40–4226–2840.026.81
40–4228–3040.328.84
42–4422–2443.023.81
42–4424–2642.024.11
42–4426–2842.727.33
42–4428–3042.029.11
44–4624–2645.024.41
44–4626–2845.026.11
46–4826–2847.027.91
50–5222–2451.022.41
50–5228–3051.028.91
52–5418–2053.019.91
58–6028–3060.029.01
Model interpretability

Recent scoring drives the model.

Holdout permutation importance measures how much prediction error increases when the values of one feature are randomly rearranged across the holdout data, breaking its relationship with the outcome while leaving the other features unchanged. The player’s 10-game scoring average is by far the strongest signal in the points model, with recent minutes being the strongest secondary signal.

As some of the features contain overlapping information, the importance attributed to any one feature can be reduced because other features may still preserve part of the same signal. These values therefore represent how much the model relies on each feature beyond what is already captured by the other features, rather than the total predictive value of the broader signal that feature represents.

Model interpretability · points

Permutation importance

MAE increase when shuffled
Permutation importance for the points modelThe ten-game points average is the dominant feature. Recent minutes are the strongest secondary feature, followed by activity, shorter-term scoring form, rest, opponent history, and recent rebounds.0.00.51.01.510-game points avg+1.80Recent minutes+0.16Activity proxy+0.0335-game points avg+0.015Days rest+0.010Vs. opponent points+0.006Recent rebounds+0.006Increase in holdout MAE after feature shuffling
Shuffling the 10-game scoring average increases error by about 1.80 points, far more than any other feature. Error bars show standard deviation across shuffles.

Engineering, not just modeling

Reproducible artifacts

Serialized model pipelines include evaluation-window metadata so reported metrics can be reproduced later.

Automated testing

A pytest suite runs in GitHub Actions CI to catch regressions across the data and modeling pipeline.

CLI workflow

Evaluation reports are regenerated from saved models in one command instead of relying on a notebook-only workflow.

Database-backed pipeline

SQLAlchemy and SQLite provide a structured persistence layer for game and player-level records.

Next

See the implementation.

The repository contains the data pipeline, feature engineering, model training, automated tests, evaluation CLI, and generated reporting workflow.

GitHub repo