Before our player projections went public, the machine-learning model behind them had to pass one test: beat simple methods that anyone could compute in a spreadsheet. A complex model that can't beat a simple rule isn't earning its keep.
We used two simple methods, expecting one of them to be the hard one to beat. It turned out to be the other way around.
Two simple ways to project a player
- The 10-game average. Tonight's projection is the player's average over his last 10 games. Nothing else.
- Marcel. Named after "Marcel the Monkey," a deliberately simple baseball projection system created by the analyst Tom Tango. Its idea: a player is what he has recently been, pulled back toward average. Our basketball version blends a player's per-minute production over his last 10 games, this season and last season; pulls that blend toward the league average, harder for players with only a few games; and multiplies by his projected minutes.
Marcel has a strong reputation in baseball, where it's famously hard to beat by much. We expected it to be the tougher of the two. We scored all three predictors on every player-game in three seasons, 62,156 in all, each season predicted only from the seasons before it.
The surprise: Marcel lost to the 10-game average
Marcel beat the plain 10-game average on only 3 of the 11 stats: minutes, steals and turnovers. On the other eight it was worse, and on 3-point attempts it was 5.3% worse.
Figure 1. Change in average error compared with a plain 10-game average, by stat
Source: HoopModel player-projection validation before launch: 62,156 player-games over three test seasons (2023-24, 2024-25, 2025-26), each predicted by a model trained only on earlier seasons.
Show the data
| Stat | 10-game avg error | Marcel error | Model error | Marcel vs 10-game | Model vs 10-game |
|---|---|---|---|---|---|
| MIN | 5.140 | 4.969 | 4.639 | -3.3% | -9.7% |
| PTS | 5.166 | 5.174 | 4.973 | +0.2% | -3.7% |
| REB | 2.064 | 2.083 | 1.995 | +0.9% | -3.3% |
| AST | 1.525 | 1.537 | 1.479 | +0.8% | -3.0% |
| STL | 0.789 | 0.766 | 0.764 | -3.0% | -3.1% |
| BLK | 0.576 | 0.590 | 0.576 | +2.5% | +0.0% |
| TOV | 1.000 | 0.982 | 0.961 | -1.8% | -3.8% |
| 3PM | 1.006 | 1.021 | 0.979 | +1.5% | -2.7% |
| 3PA | 1.662 | 1.751 | 1.602 | +5.3% | -3.6% |
| FTM | 1.511 | 1.531 | 1.483 | +1.3% | -1.8% |
| FTA | 1.807 | 1.826 | 1.768 | +1.1% | -2.1% |
Our reading of the pattern: Marcel's two main ideas, leaning on past seasons and pulling toward the league average, help most when the last 10 games are noisy and a player's long-run level is the better guide. Steals and turnovers are like that. They hurt when a player's role changes. A guard who starts taking twice as many threes this season is doing something new, and a projection anchored to last season and the league average is slow to follow. The 10-game average follows immediately.
The model beat both, almost everywhere
Against Marcel, our model was better on 10 of the 11 stats in every season, each confirmed with a paired test on the same player-games. The biggest gains were on 3-point attempts (-8.5%) and minutes (-6.7%).
Figure 2. Our model against Marcel: change in average error, by stat
Source: HoopModel player-projection validation before launch: 62,156 player-games over three test seasons (2023-24, 2024-25, 2025-26), each predicted by a model trained only on earlier seasons.
Show the data
| Stat | Avg change | Best season | Worst season | Seasons significantly better | Largest p-value |
|---|---|---|---|---|---|
| MIN | -6.65% | -7.41% | -5.43% | 3 / 3 | 1.5e-54 |
| PTS | -3.88% | -4.19% | -3.43% | 3 / 3 | 6.5e-32 |
| REB | -4.21% | -4.55% | -3.85% | 3 / 3 | 3.7e-37 |
| AST | -3.75% | -4.35% | -3.42% | 3 / 3 | 1.9e-27 |
| STL | -0.21% | -0.45% | 0.08% | 1 / 3 | 0.63 |
| BLK | -2.43% | -2.96% | -1.98% | 3 / 3 | 7.6e-21 |
| TOV | -2.11% | -2.69% | -1.71% | 3 / 3 | 1.6e-15 |
| 3PM | -4.13% | -4.26% | -4.00% | 3 / 3 | 1.9e-77 |
| 3PA | -8.50% | -8.88% | -8.15% | 3 / 3 | 4.5e-156 |
| FTM | -3.11% | -3.25% | -3.02% | 3 / 3 | 5.5999999999999996e-39 |
| FTA | -3.20% | -3.28% | -3.09% | 3 / 3 | 2.7e-35 |
Two exceptions are worth stating plainly:
- Steals are a tie with Marcel. We tested serving Marcel's steals number instead of the model's. It was significantly worse in 2024-25 with every random seed we tried, and never significantly better, so the model kept steals.
- Blocks are a tie with the 10-game average. On average the two are within a few hundredths of a percent, and the plain average was slightly ahead in 2 of the 3 seasons. For blocks, the model's extra machinery buys essentially nothing over the simplest possible method.
What it adds up to for fantasy
Fantasy scores combine all the stats, so small per-stat gains add up. Our model misses by about 8.1 fantasy points per player-game, against 8.4 for Marcel and 8.5 for the 10-game average.
Figure 3. Fantasy points: average miss per player-game, by season
Source: HoopModel player-projection validation before launch: 62,156 player-games over three test seasons (2023-24, 2024-25, 2025-26), each predicted by a model trained only on earlier seasons.
Show the data
| Season | Player-games | 10-game average | Marcel | Our model |
|---|---|---|---|---|
| 2023-24 | 21,099 | 8.45 | 8.4 | 8.11 |
| 2024-25 | 20,366 | 8.53 | 8.47 | 8.06 |
| 2025-26 | 20,691 | 8.48 | 8.41 | 7.98 |
That's a real improvement, but notice how large all three numbers are. A single NBA game swings a player's line far more than any projection can anticipate, which is why the player deep-dive shows how often a player clears a given line, not just a single projected number.
What we take from it
- Test against more than one baseline. Had we used only Marcel, the model would have looked better than it is on blocks.
- A method with a strong reputation in one sport isn't automatically strong in another. Check it on your own data first.
- Say where the model doesn't win. Blocks and steals are where a simple method is as good as ours.
These numbers come from the validation we ran before launch. The model has been tuned since (see our post on signal and noise), and blocks were the one stat where tuning didn't help either.