Every skater on an NHL slate carries a line designation that summarizes how heavily the coach has been using their trio (forwards) or pair (defense): first line, second line, third line, fourth line for forwards; first pair, second pair, third pair for defense. The line is not a piece of data the NHL publishes for our consumption. It has to be derived from shift data, every game, retroactively.
This post walks through how we derive it, what the first two attempts got wrong, and the principle that ended up settling the design.
The setup
NHL teams do not publish their lines as a structured field. Coaches roll lines from shift to shift, sometimes mixing them within a game when one trio is producing and another isn’t, and the resulting “line” for any given game is an emergent property of who actually played together — not a roster declaration.
The data we do get is /shiftcharts from the NHL’s API: for every shift, who was on the ice and for how long. From that we reconstruct trios (forwards) and pairs (defense), and then assign each trio or pair to a line designation based on how heavily the coach used them.
There is no ground-truth published line designation to validate against. We measure against time-on-ice: the first line should, on average, be the most-used trio.
First attempt: first-line-wins dedupe
The first algorithm derived trios by frequency — for each game, which set of three forwards skated together most often — then deduplicated overlapping trios by giving the highest-frequency trio “first dibs” on each player.
The result was bad. Across roughly 11,000 team-games, only 4% of the top-TOI player on a team appeared on his team’s first line in the same game. The dedupe was over-correcting: a star player who played heavy minutes alongside two different linemates over the course of a game (which is most star players) ended up assigned to whichever lower-frequency trio happened to win the dedupe competition.
Second attempt: player-anchored
We tried anchoring on heavy-minutes players: pick the highest-TOI forward, declare his most-frequent trio L1, then proceed through the remaining lines.
This was visually appealing — Connor McDavid would always land on Edmonton’s first line, every game — but the fundamental problem was that it forced the answer rather than measuring it. In games where McDavid’s trio genuinely was outplayed in usage by Draisaitl’s, the algorithm would still call McDavid’s line L1. The output looked right by reputation but did not match what the coach actually did on that night.
A pushback during review settled the design principle: truth over what looks good. A line-derivation algorithm that always puts McDavid on L1 is conveying reputation, not the game. We threw the player-anchored variant out.
Final algorithm: pure trio-frequency, first-line-wins
The shipped algorithm is closer to the first attempt than the second, but with one change. We rank trios for the game purely by frequency, with no anchor on stars, then assign first through fourth line in descending frequency order. Players are deduplicated only when a higher-frequency trio has already claimed one of them; in that case, the lower-frequency trio gets reconstructed from the remaining available skaters.
The deduplication cost — that a few players each game end up on a less-clean reconstruction — is the price we pay for not forcing the output. A coach who genuinely rolled three near-identical trios will produce three near-identical lines in the data, and the algorithm will call them in whatever frequency order the data supports.
Across the same ~11,000 team-games, the top-TOI player on a team now lands on his team’s first line in 56.5% of games. McDavid specifically lands on L1 in 75 of 103 games in 2023-24, drifting to L2 in a meaningful number of 2024-25 games where Draisaitl’s line genuinely out-skated his trio. The 75/28 split is not the cleaner “100/0” the player-anchored algorithm would have produced — but it is what actually happened on the ice.
What the line designation does not encode
The line is a description of usage in the most recent game, not a forecast of usage in the next game. A coach who rolled a new combination Tuesday will produce different lines on Tuesday’s data than on Monday’s. We do not project forward; we report.
Power-play and penalty-kill units are tracked separately (pp_unit, pk_unit) because their composition has different dynamics and the same trio rarely runs on both at the same priority. Conflating them into a single “line” would obscure the most decision-relevant usage signal.
Limitations
The largest limitation is data availability. The NHL’s /shiftcharts endpoint returns empty data for some games — several hundred in the 2025-26 season as of this writing — and for those games we have no way to derive lines. The affected games appear without a line designation; they are a known gap, not a silent error.
Mid-game line shuffles are not represented either. The algorithm produces a single first-through-fourth-line set per game, based on aggregate frequency. If a coach broke up his lines at the second intermission and rolled a different set in the third period, the derived lines will reflect the average usage across the full game, not the third-period state.