← All articles
Analytics2026-05-29

Catching injury noise: empirical-Bayes smoothing for NFL snap shares

By Nick5 min read

The week-3 problem

A starter plays 24, 25, and 23 offensive snaps in Weeks 1, 2, and 4 — call it a 40% snap share, give or take. In Week 3 the rotation in front of him gets banged up, he plays 57 snaps out of 60, and his weekly share is 95%.

If you sort the season-stats table by snap share, the player jumps from "rotational piece" to "every-down starter" for a single week, then snaps back. If you sort by rolling 3-week mean snap share, Week 4's number includes that 95% spike and reads 50-55% — still wrong, just less so.

What you actually want is a number that uses Week 3's 95% as information about the rest of the season without letting it dominate the read on the player's role. That's what empirical-Bayes shrinkage does.

Beta-Binomial in one line

If you assume a player's underlying snap share is drawn from a Beta(α, β) distribution and his weekly snaps are Binomial(team_snaps, share), the posterior after observing s snaps out of n team snaps is:


share | data ~ Beta(s + α, n - s + β)
mean = (s + α) / (n + α + β)

That's it. No MCMC, no sampling, just two adds and a divide.

The interesting question is where (α, β) comes from.

Choosing the prior: empirical, rolling, and honest

Three reasonable choices for the prior:

  1. Flat prior, Beta(1, 1). Always uniform between 0 and 1. The posterior is essentially the raw share with two "pseudo-snaps" added — meaningful only when team_snaps is small (single-digit cameos), which is when we genuinely don't know what we're looking at.
  1. Season-long prior. Fit (α, β) from this player's whole season. Stable, but reactive to role changes the wrong way — when a player does legitimately ascend mid-season, the prior keeps pulling him back down.
  1. Rolling-window empirical prior. Fit (α, β) from this player's last N weeks. Adapts to role changes; resists single-week spikes. We use N = 3.

The rolling prior is the one shipped. For each (player, week), we look at the player's three most recent weeks of (snaps, team_snaps), fit a Beta via the method of moments, and use that Beta as the prior for the current week's posterior. We cap the prior's effective sample size at 30 so a tight 3-week cluster doesn't drown out genuine signal in the current week.

For the first two weeks of a season (no history), we fall back to a flat Beta(1, 1). The response object tags those rows with prior_kind: "flat"; we use "rolling_3wk" once history is available.

The worked example

Take the same 24 / 25 / 57 / 23 sequence over four weeks with 60 team_snaps each:

WkSnapsRaw sharePriorSmoothed share
12440%Beta(1, 1) (flat)40.3%
22542%Beta(1, 1) (flat)41.9%
35795%Beta(1, 1) (flat)93.5%
42338%Beta(α, β) from weeks 1-338–48%

Two things to notice.

First: even in the flat-prior weeks, the smoother only nudges the raw share gently — the player took 60 snaps, the binomial likelihood is sharp, the prior contributes 2 pseudo-snaps. So Week 3 stays close to 95%, and that's correct. We can't pretend we didn't see a 95% week.

Second: by Week 4, the rolling prior is fit on shares of (0.40, 0.42, 0.95). Method-of-moments produces a prior with mean ~0.59 but high effective variance (the three observations span 0.40-0.95). The posterior on the Week 4 share lands between the prior mean and the raw 38% — somewhere in the high 30s to high 40s, with a credible band that respects how mixed the recent signal has been.

This is the point of the smoother. The model is admitting it doesn't know yet whether Week 3's 95% was the player's new role or an injury fill-in. The credible band stays wide. By Week 5 or 6, depending on whether the role sticks, the smoothed share either drifts back to 0.42 (it was a fill-in) or anchors at 0.65 (it was a real role expansion).

Why this isn't just a moving average

A 3-week moving average of (0.42, 0.95, 0.38) gives 0.583. A Beta-Binomial smoother gives something near 0.45 with explicit uncertainty bands. The difference matters in two cases that recur every week of every season:

  1. A player who plays once at high volume after long inactivity. Moving average pulls him up. Smoother says: prior pseudo-counts dominate; we're not sold yet.
  1. A player coming off injury, ramping up. Moving average lags real role changes by half its window. Smoother updates faster because the new week's observation is genuinely informative, and the prior weakens once it sees the new data is consistent.

What the chart shows

On the player-profile page, the new "Snap share stability" panel renders three things:

- Light bars — raw weekly snap share. - A solid linesnap_share_stable, the posterior mean. - A shaded band — the 95% credible interval, [ppf(0.025), ppf(0.975)] of the Beta posterior.

Hover any point and you'll see the raw value, the smoothed value, and which prior was used. Width of the band is the model's honest admission of uncertainty — a player with three close weeks gets a tight band; a player whose recent weeks ping-pong gets a wide one.

Where on the site

The smoothed share also powers the "Stable snap %" column on /nfl/rankings, computed at request time as the trailing-3-week mean of snap_share_stable for each player's most recent synced season. The column is sortable; a clean rolling-prior smoothing produces the most stable cross-player comparison you can build out of weekly snap counts.

Caveats

- Mid-season trades create two rows for the same player in the same week (one per team). We dedup by (player_id, season, week), sum the snaps across both teams, and attribute the row to the team with the larger share that week. The rolling prior incorporates all prior weeks regardless of team, which is the right call: it's a player attribute, not a team-of-week attribute.

- First two weeks of a season use a flat prior. They're tagged prior_kind: "flat" in the API response. They're more like raw shares than smoothed shares — the bar height and the line height will be nearly identical.

- Special teams snaps aren't included. The smoother is parameterized for the offensive snap share, which is what every downstream fantasy decision wants — special-teamers don't return on the columns we ship.

If you want the longer version of the Bayesian story across player-level and team-level signals, see Bayesian opponent-adjusted FVOA — the companion article on the team rating that now lives next to this column.

Free · No card

Run this on your own league.

Connect ESPN or Sleeper to get trade suggestions, league-aware rankings, and manager grades.

Sign up free

More articles