Opportunity, Role and Ability Context for statLines Estimates
Research log
The projection model page explains what the model does. This page is the working record behind it: what was tried, what came back null, what is known to be broken, and what is still untested. The failures are listed as prominently as the successes — most ideas tried here came back null, and keeping that visible is what stops them being rebuilt.
One model, four switches
What used to be three separate projection models were never three models. They were one pipeline with four settings, duplicated three times in code. They are now a single configurable path — the settings below are the only thing that differs between them.
| Setting | What it controls | Options |
|---|---|---|
| Recency window | Which view of recent form feeds the rate step. | Standard 10-game · per-category half-lives · none (ignore the season in progress entirely, for a preseason read) |
| Absence scope | Which teammate absences shift a player’s production. | None · any current absence · only absences lasting the rest of the season |
| Age curve | Whether an age/development term is applied. | On / off |
| Schedule adjustment | Opponent strength and rest. | On / off — currently inert, no forward schedule data exists |
Combining them was deliberately a restructure, not a redesign: the merged pipeline was required to reproduce all three previous models exactly, and does — identical to the last decimal across 18,112 projections each. An accuracy check would have let a mis-copied setting pass as noise; an equality check cannot. Adding a new part is now a setting rather than a fourth copy of the code.
What a projection is made of
Three questions, asked in order. Each is a separate model, and keeping them apart is deliberate — a player who is excellent but fragile, one who is durable but buried on the bench, and one who gets the ball constantly but does little with it are three different problems, and a single blended number hides which one you are looking at.
| Factor | The question | What it covers | Status |
|---|---|---|---|
| Availability | Can he play? | How many of the games in the window he is actually available for. Depends on his own history alone — nothing about his team. | Validated |
| Opportunity | How much does he get to do? | Minutes on court, and how many chances those minutes carry — shots, trips to the line, rebounding chances, and since 2026-08-05 minutes actually holding the ball. This is where teammates matter: a team has only so many minutes, so many shots and so much time with the ball, so opportunity is shared and has to be settled for the whole roster at once. | Validated |
| Ability | What does he do with it? | His conversion — points per scoring chance, rebounds per rebounding chance, and so on. A property of the player, largely independent of how much opportunity he is given. | Validated |
The projection is these three multiplied together: how often he plays, times how many chances he gets, times what he does with them. That separation is what makes the parts individually testable — the opportunity blend, for instance, improves the middle factor and leaves the other two untouched, so its effect can be measured in isolation.
The order matters, but it is shallower than it looks. Availability runs first and depends on nothing else — and running it across the whole roster is what tells the next step who is available, so there is no separate teammate model. Opportunity comes next, and is the only step where players are coupled to each other, because minutes and shots are shared. Ability is independent of both and is computed alongside them rather than after. That independence is measured, not assumed — every category’s season total sits within a narrow band around the minutes total, which is what a cleanly separable ability layer looks like.
A team plays exactly 240 player-minutes per game. A roster’s per-game averages sum to far more than that, because nobody plays every game and different players fill the same slots on different nights. Weighted by expected availability, teams with a fully covered roster now land at 99.2% of that budget, and every category sits within a narrow band around the minutes figure — which is the check that the three factors are consistent with each other.
What works
Shipped, live, and validated against a real baseline on data held out from fitting.
| Mechanism | Status | What it does | Measured result |
|---|---|---|---|
| Games played (availability) | Live | How many of the horizon’s games a player actually suits up for. | 43% more accurate than assuming everyone plays every game, over a full season. Over short windows nothing beats that assumption, so the model deliberately does not try — about 87% of rostered players really do play all of their next five games. |
| Team-crowding minutes | Live | When a roster collectively expects more minutes than exist, takes the excess back down the depth chart. | 5.5% better on over-committed rosters and 10.6% better for players who changed teams, across 2,934 player-seasons. Held in 7 of 8 seasons, with no part of the rotation made worse. |
| Opportunity blend | Live | Shrinks a player’s pre-season opportunity estimate toward his 3-year baseline instead of copying last season outright. | 6 of 7 categories better on the rate itself; 4 of 4 publishable categories better end-to-end, but only by 0.33% once multiplied through minutes and efficiency. |
| Shot-attempt opportunity (FGA) | Live | Projects how many shots a player gets, from who else on the roster competes for them. Now published as its own number, not buried inside the FG% record. | 4.25% better than assuming last season repeats, winning all 20 held-out splits. It is worth about three quarters of everything the points projection has gained. Validated by rebuilding the same term from rebounding — a skill nobody competes with a shooter for — which cannot beat the baseline at all. |
| Free-throw-attempt opportunity (FTA) | Live | Projects trips to the line from foul-drawing history, how often a player attacks the rim, and his projected shots. | Built on the finding that free throws are crowded by teammates who DRAW FOULS, not by teammates who shoot — those are different rosters. The rebound-built placebo is worse than having no crowding term at all, which is the cleanest such result in the model. Correct on 20 of 28 players across three named roster shake-ups. |
| Offensive rebounds as a skill | Live | Chances at the offensive glass are projected as something a player creates, not as a share of a team pool. | Treating them as a share of a fixed team total was the single biggest error in the rebuilt rebounding model. Removing the pool entirely cut the error on defensive chances by 15% and, more tellingly, restored the model’s agreement with real team totals: forcing teams onto a budget had collapsed that correlation from 0.81 to 0.19. A conserved quantity does not imply a conserved allocation. |
| Playmaking measured before the shot | Live | Splits assists into time with the ball, shot-creating passes per minute of it, and the share teammates finish. | The creation step is the one place the rebuilt model clearly beats what came before: 22% more accurate, and it removes three quarters of a systematic bias. It is also the cleanest separation in the model — whether the pass falls is largely the teammate’s doing, and it only enters at the last step. |
| Ranges built from counting, not percentages | Live | The published range around a stat now comes from how many events are actually expected, rather than a flat percentage of the projection. | A plus-or-minus percentage grows in proportion to the number; real counting noise grows with its square root. So the old shape was far too narrow for low-volume players and far too wide for stars. On steals the new range is 23% narrower at the same accuracy, and the share of players whose range is right stopped depending on how good they are — spread across ability tiers fell from 20.8 points to 6.0. |
| Estimates that update game by game | Live | Player estimates are carried forward and nudged by each game, rather than recomputed from a season average. | Worth 3.0% on steals across 16,835 measurements, and the gain grows through the season as more of it is known. Staleness costs about a thousandth of a point of accuracy per day, with no plateau — so the right cadence is every game, not every fortnight. |
| Rest-of-season projections | Live | The same model run to the end of the season — next-N-games where N is the team’s remaining games. | New. Replaces a placeholder where the site showed next-10-games numbers under a rest-of-season label. |
| Season-ending absence exception | Live | When a teammate is out for the season (not just a few weeks), his minutes and production genuinely transfer. | Reuses the already-validated teammate-absence numbers. Injury records do not go back far enough to test the exception itself; verified against a real current case. |
What did not work
Tested and deliberately not shipped. A minority of the mechanisms tried here have survived a properly controlled test — the ratio is the most useful thing on this page. Several of these looked strong before the control was applied: the pecking-order rank fixed a famous case, and the arrival/departure split produced textbook opposite signs at high significance. Both were measuring something else.
| Idea | The hypothesis | Why it was rejected |
|---|---|---|
| Proportional roster rescaling | Shrink every player on a crowded roster by the same percentage. | Contradicted by the data. The back of a rotation loses roughly five times as many minutes as the stars do, so an even cut is the wrong shape entirely. |
| Short-horizon availability | Predict what fraction of the next 5–15 games a player appears in. | Loses badly to simply assuming he plays. The outcome is close to all-or-nothing, so the most likely answer is "all of them" and no fractional estimate can beat it. |
| Age as a durability signal | Older players miss more games. | Essentially no relationship (correlation −0.014), and non-monotonic — 34-year-olds looked worse than 36-year-olds, because fragile veterans stop being rostered. Made predictions worse even when fitted directly on the data it was tested on. |
| RAPM rebounding pilot | Lineup-level plus/minus to explain rebounding. | No usable signal. |
| Roster continuity | Teams returning more of last year’s roster behave more predictably. | No usable signal. |
| Team-change-aware blending | Players who changed teams should trust last season less than players who stayed. | Splitting the blend by whether a player moved was worse than one setting for everyone — it hurt assists, turnovers and made 3s. |
| Reacting to current injuries | When a teammate is out, bump the remaining players’ projected minutes. | The effect is real but not predictable. When a 30-minute starter actually sits, his minutes do redistribute and they add up — teammates gain about 16 minutes and the bench about 15. But half of all absences last exactly one game and 64% last two or fewer, so applying the bump across the next 5–15 games assumes an absence that has usually already ended. Measured over 2,632 real teammate-out situations, the best weight to put on the adjustment is zero: it adds about 5 minutes where the true average change is −0.04. The fix is not to shrink it but to gate it on absences known to last, which is what the season-ending exception already does. |
| Pecking order as a ranking term | Rank players on a star composite (volume, playmaking, efficiency) and use the change in rank. | Falsified by ranking the same rosters on five RANDOM keys, which scored identically. A rank term mostly measures how much the roster changed, not where a player sits in it — the sort key was decorative. It fixed one famous case and went 16/28 on the full rosters, a coin flip. |
| Roster churn and arrivals/departures | Measure how much shooting arrived and departed and split the two. | Both looked strong — the arrival and departure terms even came out with correctly opposite signs and high significance. Both were minutes turnover in disguise: the departure term correlates 0.986 with the raw minutes that left, and stripping the minutes out drops it below doing nothing. Adding a minutes-shaped term to a per-minute rate charges the same roster change twice. |
| A shooting ceiling that fills up | A player caps out, so freed shots cascade to whoever has room. | No wall exists — the top of the distribution is if anything fatter than a normal curve. Absorption is an inverted U in prior volume: the mid-volume players absorb most, and the LOWEST-volume players absorb least despite having the most room. So it is standing AND room, not room alone. Neither a cap-and-cascade allocation nor the inverted U as a fitted term beat the simpler version. |
| A team budget for rebounding chances | Defensive rebound chances are a fixed team pool, so shares must add up. | The pool is genuinely tight — that is what made it tempting. But how much of it reaches the dozen players who actually play is not tight at all, and forcing the constraint destroyed exactly the variation the free model was capturing: agreement with real team totals fell from 0.81 to 0.19. It was also worse on high-turnover rosters, the opposite of what an early-warning mechanism looks like. Fourth independent time this project has found that a conserved quantity does not imply a conserved allocation. |
| A learned shot diet | Let the model learn where each player shoots from, rather than carrying his recent mix forward. | Beat a naive league-average diet by 34–50% and still lost to simply decaying the player’s own recent shares by 26%. Beating a weak baseline is not evidence. A 30-setting sweep in every direction that would let the learned version move faster found nothing better, so this is structural rather than a tuning failure. |
| Uncertainty taken from the model itself | The model already computes how sure it is about each player — use that as the published range. | It knows which players it knows well: its confidence ranks future error correctly in all 48 checks, and carries information beyond simply counting how much a player has played. But it does not improve a range. Tested on six categories and then on sixteen underlying estimates separately, it helped in one of sixteen, and that one did not survive re-testing on the players it should work best for. It describes uncertainty about a player’s ability; it does not describe uncertainty about what he will produce. Those turn out to be different things. |
| A richer free-throw model | Model a shooter as a long-run stroke plus current form, or allow hot and cold nights. | Both lost to the plain version. The hot-hand form is significantly worse and mis-calibrated. Free-throw shooting is the one thing in basketball with no defender and no teammate, and the simplest possible treatment of it is the right one. |
| A longer memory for the running estimates | Give the game-by-game estimates more seasons of history. | Doubling the window from four seasons to eight changes nothing measurable — while the same doubling does help the older, fixed-window form. A running estimate that fades the past cannot use history it has already forgotten, so the two respond to more data in completely different ways. |
| Legacy DARKO-style rating | A single overall value rating plus per-category predictions. | Worse than "just use last season" on 20 of 21 tests, and occasionally produced impossible values including a negative shooting percentage. Dormant. |
Known problems
Measured, documented in the code, and not hidden. Some are genuine defects, some are accepted limits, and one — the scoring crowding that had never actually run — was found only because the numbers were re-derived rather than trusted.
| Area | Status | Detail |
|---|---|---|
| Teammate-absence attribution | Known broken | When a player misses a game his minutes should reappear elsewhere on the roster. Only 62% of them do — and some cases imply a team collectively played fewer minutes, which is impossible. Cause: each teammate pair is measured in isolation, with no account of several players being out at once, and anyone who only plays when the star sits is excluded outright. Used as a weak directional hint, never as a minutes budget. |
| Rotation-size ceiling | No-op | Intended to cap a player’s minutes based on how deep his team’s rotation is. In practice the cap lands between 38.9 and 40 minutes while the highest projection in the league is 38.0, so it has never once bound. Documented rather than removed — the underlying statistic is useful elsewhere. |
| Crowding under-corrects | Accepted | About 11% of players with a real role leave the league each year. Real teams absorb part of a squeeze by shedding players, which a model that only adjusts players it can see cannot capture. The adjustment is therefore a floor on the true effect, deliberately not scaled up. |
| Availability assumes he plays at all | Known broken | The games-played model answers "how many games given he plays at all", not "how many games". Roughly half of chronically unavailable players do not appear the following season, and every rate the model is calibrated on was measured only on the ones who did — so it over-projects fragile players by about 21 games and healthy ones by about 8. Note that most of those who vanish left the league entirely rather than being injured all year, so the unconditional figure is not the right answer for a contracted starter either; the fix is to separate "stays rostered" from "healthy enough to appear", and neither has been measured. |
| Efficiency is smoothed by the wrong constant | Partly fixed | Every category smooths a player’s efficiency toward a prior by an amount meant to reflect how noisy that efficiency is. Those amounts were measured on how fast the CATEGORY settles rather than the EFFICIENCY inside it. Measured properly, on disjoint halves of each season, most categories land within about 2.5x — the pipeline’s own units conversion absorbs much of the error. Assists was the worst at 11.3x under-smoothed, and was rebuilt on 2026-08-05: it now smooths by 340 shot-creating passes rather than five games, and toward the league rather than toward the player’s own career average, which turned out to be worse than a flat league number because it smooths a noisy estimate toward another noisy estimate of the same player. Made threes (25x under-smoothed) and FG% (3.8x over-smoothed) are still open, both because their constants are never rescaled. |
| Settling fast is not the same as staying put | Open | The method above measures how quickly a rate settles WITHIN one season. That is not the same question as how well it predicts the NEXT season, and for one quantity the two answers differ by a factor of forty. Creation skill is measured almost perfectly inside a season yet only about half of it carries to the following year — smoothing it by the within-season figure made the projection measurably worse. Caught while building that model; the assists efficiency constant above was measured the same way and has not been re-checked against the same test. |
| Scoring crowding was silently doing nothing | Fixed | The adjustment that lowers a scorer’s projection when his team signs another scorer had never actually run. A single unresolvable player produced a "not a number" value, which spread across his team and then the whole league; because that is not the same as a missing value, every safety check passed and the model quietly returned the uncrowded answer for everybody. Found and fixed on 2026-08-04. It had been recorded as working for months. |
| Points total drifts up 1.5% | Open | The rebuilt scoring model raises the league-wide points total by about 1.5%. A model that reallocates production between players should leave the total roughly where it was, so this is an unexplained level shift rather than redistribution. Published deliberately, with the cause still being traced — the leading suspect is that free-throw percentages are lopsided, so smoothing lifts poor shooters more than it lowers good ones. |
| Who absorbs freed shots is unpredictable | Accepted | When a high-volume shooter arrives, the model correctly moves every teammate down — on one such roster it moved all eleven the right way and cut the error by 28%. When one LEAVES, someone takes those shots and nothing tested identifies who: ten different formulations have failed. The clearest case is a player who went from 13.5 to 20.3 shots per 36 minutes on a roster change, whom every version moves by essentially nothing. Crowding is forecastable at roughly twice the strength of freeing, so a projection for a player whose team lost a shooter deserves less trust than one whose team gained one. |
| Blocks ship slightly worse than before | Accepted | Blocks is the one category where the rebuilt estimator is measurably WORSE than what it replaced — by about 1.2%, consistently across all five seasons tested. Eight explanations were ruled out one by one: the overall level, new players, how hard the estimate is smoothed, whether it mis-ranks the best shot-blockers (it does not — it is slightly better there), how wide its spread is, how much history it uses, and the contest half of the model, which is fine on its own. What is left is the block-per-contest half, the noisiest quantity in the nine categories. Published deliberately with the cost recorded rather than quietly reverted. |
| One published identity no longer closes | Accepted | The site serves the rebuilt model wherever it has an estimate and the previous one for four supporting columns it cannot produce — contests, rim attempts, rim defended, block conversion. Everywhere else the components still multiply back to the stat exactly. Blocks is the exception: its contest counts come from the older estimator and its projection from the newer one, so 6.7 rim contests at 10.9% reads 0.73 against a published 0.75. Marked on the projection-model page rather than smoothed over. |
| Roster coverage | Accepted | Projections cover about 17 players per team; real teams use around 22 across a season. Season totals land at roughly 87% of reality as a result — and teams whose roster is fully covered reach 99.2%, which is what confirms this is coverage rather than a modelling error. |
Still to test
Open questions, and whether they can be answered today or need the season to start.
| Question | Status | What it would take |
|---|---|---|
| Season-ending exception | Testable now | Whether the exception for absences that last the rest of the season earns its place. Same backtest approach that settled event-awareness, narrowed to long absences — which is the one case where the mechanism should work. |
| Which efficiency estimate to use | Contradictory | Estimating a player’s efficiency from his own game history, weighted so recent games count more, is 8-9% better at predicting that efficiency than using last season alone. But it is 0.09% WORSE at predicting points. Two explanations were chased and eliminated. The remaining hypothesis is that when opportunity and efficiency both come from the same season their errors partly cancel. Unresolved, and worth 0.09%. |
| Blocks decomposition | Never verified | Every other category’s opportunity and efficiency have been checked to multiply back exactly to the stat. Blocks is the one that never has, because the contested-shot data behind it is too sparse at game level to test. |
| Opponent strength | Not started | Coded but inert. Needs a real forward schedule with home/away, which this dataset does not yet carry. |
| A player-impact rating | In progress | A separate estimate of a player’s underlying offensive and defensive ability, from what happens while he is on the floor rather than from his own box score. The measuring stick is built and deliberately came first: predicting the final margin of games, where knowing nothing scores 15.1, knowing home court 15.0, and knowing team strength 13.55. Only the last is a real opponent. The honest warning is that the room is small — the published comparison this follows improved on its own team baseline by about 0.11, and this data can resolve about 0.095, so a single positive result will not be enough. |
| Everything, in-season | Waiting | No games of the current season have been played, so every projection is currently a cold-start extrapolation from history. The first genuine out-of-sample read comes once about 15 games are played. |
How things get accepted
Every mechanism is measured against the simplest honest alternative — usually “assume nothing changes from last season” — on seasons held out from fitting. If it does not beat that baseline it does not ship, per category and per time horizon. A change validated on one ingredient is not assumed to improve the final projection: the opportunity blend beat its baseline by about 5% on the raw rate but only 0.33% once multiplied through into a published number, and that distinction is why several ideas that looked promising are in the rejected list above.
