Why Do Some Players Seem to Be Cursed By the Injury Bug?
As a hockey fan and someone with a medical background, I thought to myself - there HAS to be something going on here under the surface. Although I was limited to just public data, I was able to put together a fairly comprehensive deep dive into the mechanics of injury timelines and player availability.
01
The genesis
A Fascination with the Concept of 'Hard Miles'
As I paid more and more attention to hockey, I kept noticing that players with similar ages, positions, minutes and physical demands could have completely different injury histories. Some absorbed the workload and regularly topped the coveted 'Iron Man' list. But others seemed to be perpetually doomed to the same cycle of injury, rehab, and re-injury. Because of my medical background, I wanted to understand why.
But, the main problem with digging deeper is that the most important variables are private. Public data basically never tells me the diagnosis, imaging, tissue healing, strength testing, rehab progression, practice load or the logic behind a return-to-play decision. So instead, I sought to answer a narrower, but still interesting question: what patterns are visible in the public data, and how much confidence can I put in them?
02
The logic
Building the Model 1 'Player-Day' At a Time
01 / OBSERVE
The model sees only the past.
For player i on day t, I freeze the clock. The model can see public absence history, workload, role, age, travel, contract context and source quality—but nothing that happened after that date.
xi,t=public information known at t02 / LABEL
Then I ask one forward question.
Did a model-eligible public time-loss episode occur in the next 82 days? I tried my best to categorize scratches, transactions and ambiguous absences in my best attempt to make sure that they were not incorrectly classified as injury labels.
yi,t82∈{0, 1}03 / FIT
I make it prove itself on unseen seasons.
Each rolling-origin split fits on earlier seasons, calibrates on the next season and validates on the following held-out season. I compare logistic, random-forest and histogram gradient-boosting candidates. Only the 82-day time-loss lane that passed every gate drives the main ranking.
I keep a model in production only when it has complete-season coverage, no leakage flags, acceptable discrimination and probability error, calibration evidence, stable slices and serving alignment. The current 82-day lane reports mean AUC 0.788 and mean Brier 0.085 in backtesting.
gate_status=passed
03
The translation
The probability is the starting point, not the answer.
I first compare the 82-day probability with the player’s actual peers: season, position and age band when at least 20 records are available, then position and age or position alone when they are not. High means the 90th percentile or above; elevated is the 75th–90th; watch is the 50th–75th; low is below the 50th.
Public durability pressure100 × weighted percentiles of availability, absence, recovery, recurrence, aging, contract and injury-time-loss signals
The weights are fixed as follows: 28% availability, 8% any public absence, 12% recovery tail, 18% recurrence, 14% aging, 12% contract downside, and 8% injury time-loss.
AUC tells me how well a model ranks cases; Brier tells me how wrong its probabilities are. It's important to report both because a model can sort players reasonably well and still be badly calibrated.
Start here
Priority board
Select a player. Total GP includes regular season + playoffs.
More filters
Use the filter rail for season, team, position, and tier combinations.
Players
Selected player
Checks
Use with context
Cap by availability tier
Absence load
Players
Player Explorer
Compare the signal, cap hit, and source strength.
View options
Players
Public cap snapshot · Aug 2, 2026
Teams
See where availability meets cap.
Choose a club, then scan its highest-priority public records.
Team Availability
Rank
Player
Pos
Availability tier
Missed-games chance
Cap hit
Public record
Contracts
Pressure-test the range.
Public availability can shape a review. It cannot set a player’s price.
Contract watch
Rank
Player
Team
Cap review
Contract
Missed-games chance
Contract read
Public only
Methodological note · public-data proof of concept
A transparent player-day framework for studying NHL availability
Decision window 2018–19 → 2025–26Unit player-dayProduction target time loss, next 82 daysLoading checks…
01
Define the estimand before touching the data.
The project began with a workflow problem. I wanted a disciplined way to decide which public records deserved a closer look when a roster or contract decision was being made. The estimand is intentionally narrower than “injury risk”: for player i observed on day t, I estimate the probability that a model-eligible public time-loss episode begins during the next 82 days.
Pi,t82 = Pr(Yi,t82 = 1 | Xi,t)
02
Assemble a dated public record.
I use the complete decision window from 2018–19 through 2025–26. The record joins public game and availability information with roster and transaction context, contract fields where a public join is available, and source-strength metadata. Each observation keeps its season, date, player identity, team, position and provenance so that later calculations can be audited back to the record rather than detached from it.
The dataset is public by construction. Missing private diagnosis, imaging, tissue-healing, rehabilitation, strength, practice-load and return-to-play information is a documented limitation—not an imputed fact.
03
Freeze the information set at one player-day.
For every player i and observation day t, I freeze the clock. Features may use only information available on or before t: prior public absences, workload and role, age, travel and schedule context, contract context, return history, and the quality or completeness of the source record. Nothing published after t is allowed to flow backward into that row.
Xi,t = {public information known by t}; Xi,t ∩ future information = ∅
04
Construct the forward label conservatively.
I look forward 82 days and assign a binary label only when the public episode is eligible for the target. I tried my best to categorize scratches, transactions and ambiguous absences so that they would not be incorrectly presented as injury labels. Those records remain visible for audit and context, but they are excluded from the injury time-loss training target when the public evidence cannot support that claim.
Yi,t82 ∈ {0, 1}; 1 = eligible public time-loss episode within [t, t + 82]
05
Engineer features in interpretable families.
I organize the predictors into feature families rather than treating a player’s history as an opaque vector. The families cover identity and biography; game and schedule context; contract context; role and archetype; rolling workload; shift timing; travel; return history; and source quality. This structure makes it possible to report where coverage is thin and to keep a missing field from silently looking like a measured zero.
Fit several model families, not one preferred story.
I test logistic stochastic-gradient descent, random forest, histogram gradient boosting, group-rate baselines, and a calibrated logistic/tree contract blend. The candidates answer related but distinct questions: near-term public time loss, injury-coded time loss, any public absence, multi-game absence, lower-body or soft-tissue context, longer recovery, repeat absence after return, aging decline and contract downside.
The product keeps the model lane and target visible. A high score from a weaker target does not silently replace the promoted 82-day availability lane.
07
Respect time with rolling-origin validation.
Each split trains on earlier seasons, calibrates on the next pre-validation season, and tests on the following held-out season. This preserves the chronology of an actual decision: the model may learn from yesterday, but it cannot learn from the season it is being asked to forecast. The serving export is checked against the registry so the model reported in the product is the model that passed the evaluation.
fitpast → sigmoid calibrationnext season → validationheld-out season
08
Evaluate ranking and probability error separately.
AUC tells me how well a model orders cases; Brier score tells me how wrong its probabilities are. I report both because a model can sort players reasonably well and still be badly calibrated. I also inspect calibration, risk bands, season coverage, leakage flags, slice reliability, feature missingness and serving alignment. The current production lane reports mean AUC 0.788 and mean Brier 0.085 across the rolling-origin backtest.
AUCranking discrimination · higher is betterBrierprobability error · lower is better
09
Promote only what clears explicit gates.
The quality gates require a complete decision window, a single production target, a minimum performance floor, clean recovery leakage contracts, visible feature-coverage warnings, a complete promoted-model card, and alignment between the evaluated and served model versions. Only time_loss_next_82d currently carries the production-usable claim. Recovery, aging and separated absence targets remain experimental; reinjury and contract downside remain weak-context layers.
gate_status = passed iff all required gates = 1
10
Translate a probability into a review queue.
I compare each 82-day probability with a peer cohort—season, position and age band when at least 20 comparable records exist, then position and age or position alone as the sample thins. High is at or above the 90th percentile; elevated is the 75th–90th; watch is the 50th–75th; low is below the 50th. The public durability pressure score is a weighted percentile composite, not a diagnosis.
The terms represent availability, any public absence, recovery tail, recurrence, aging, contract downside and injury time-loss. Missing components receive no invented signal; the product keeps the uncertainty visible and applies its documented confidence and recency adjustments when building review priority.
Results register
What each model is allowed to do
The register below is populated from the same promotion policy that controls the product surface.
Order
Model use
Target
Status
Past AUC
Interpretive note
These are rolling-origin means, not guarantees about an individual player. I show the lane, the metric and the permission level together so a reader can see both the result and the boundary around it.
Promotion map
Where the evidence sits
Loading target status…
Quality gates
What must remain true
Loading checks…
Interpretation
What this method can—and cannot—support
It can support
A reproducible review order, a dated public record, peer-relative context, transparent model comparisons, and a clear list of questions to take into a deeper hockey, medical or cap discussion.
It cannot support
A diagnosis, a medical clearance, a causal claim about why an athlete was hurt, an optimal rehab plan, a trade price, or a conclusion that public absence history is the same thing as underlying tissue risk.
Appendix
Metric definitions and gate detail
For readers who want the implementation-facing record alongside the prose.
Metric guide
Loading documented measures…
Gate detail
Loading gate status…
Absences
Inspect the public record.
Filter reported absences, then open a row for its evidence and hockey context.
More filters
Absence summary
Public record
Absence records
Rank
Player
Team
Dates
Games
Absence type
Context
Data
Know what is—and is not—here.
Check the public files, their freshness, and their hard limits.
Data status
Downloads
Public files
Browse published filesJSON endpoints and supporting records
Limits
Boundaries
Review known limitsUnsupported claims and coverage gaps