Authors: Ethan Clayburn & McKay Lush
Date: December 2025
Rest and fatigue play a major role in professional sports performance, and the NHL is no exception. Teams routinely play back‑to‑back games, long road trips, and compressed scheduling sequences that can influence scoring, defensive sharpness, and overall win probability.
Despite the clear competitive impact, the NHL offers no official metric that quantifies rest or fatigue. Analysts must therefore calculate their own rest‑based metrics using game schedules and performance data.
Understanding rest effects enables:
This project develops an end‑to‑end analytical workflow to measure these effects automatically.
This project was designed to meet four main goals:
nhlRestEffects, that:
nhlRestEffects Python package (GitHub)All data originates from MoneyPuck.com, which provides detailed NHL game‑level datasets including:
We downloaded team‑level data for 2016–present and stored it in:
data/all_teams.csv
The raw dataset required substantial normalization before analysis. Our package performs the following tasks.
MoneyPuck’s exports contain inconsistent abbreviations. For example:
LA, L.A., LOS, LA KINGS → LAKTB, T.B., TAM → TBLTo handle this, we standardize all team identifiers to uppercase and then apply a mapping so that each franchise is represented by a single abbreviation. This prevents teams from accidentally being split across multiple labels in group‑by operations.
Game dates appear in a mix of formats, including Unix timestamps and plain strings. Our loader function:
gameDate is numeric or stringpandas.to_datetime with the correct uniterrors="coerce" to handle irregularitiesThis step is critical, because rest calculations depend directly on correct game ordering.
Not all MoneyPuck exports use the same column name for expected goals percentage. Common options include:
xG%xGoalsPercentagexg_pctexpectedGoalsPct
Instead of hard‑coding one name, the package scans for all known options and uses whichever appears
in the dataset. The chosen column is converted to numeric and stored internally as xG,
which simplifies later analysis.
Rest days are computed as the difference in days between consecutive games for each team:
days_rest = gameDate[i] − gameDate[i − 1]
This is done within each team’s time series so that only games from the same team are compared.
To make rest effects easier to interpret, we categorize the numeric rest values into four buckets:
"0": Same‑day or no rest (back‑to‑back situations)"1": One day of rest"2": Two days of rest"3+": Three or more days of restThese buckets are used throughout the dashboard and analysis functions.
Several challenges came up during data cleaning:
These issues motivated the decision to build a reusable, well‑tested Python package rather than relying on one‑off notebooks.
nhlRestEffects)The package is organized as follows:
data_loader.py: functions for loading and cleaning the core datasetanalysis.py: core analytical functions (rest summaries, rankings, B2B extraction)utils.py: helper utilities, e.g., logo URLs, goalie headshots, safe conversionsvisualization.py: plotting helpers (used conceptually, though much plotting is done inside the Streamlit app)This structure separates concerns between data acquisition, transformation, analysis, and presentation.
load_rest_data()This is the main entry point for team‑level rest analysis. It:
xG columndays_rest and rest_bucketThe returned DataFrame is ready for downstream analysis.
assign_rest_bucket()
This helper function converts numeric days_rest values into the four
categorical buckets ("0", "1", "2", "3+").
It ensures consistent binning throughout the package.
summarize_rest_buckets()This function aggregates performance by rest bucket. Typical outputs include:
These summaries power the rest‑impact visualizations in the Streamlit app.
rank_rest_sensitivity()This function calculates a “fatigue impact” score for each team. A simple and interpretable version is:
Fatigue Impact = xG%(3+ rest) − xG%(0 rest)
Negative values indicate teams that suffer more on back‑to‑back games, while values close to zero indicate teams that are relatively resilient to schedule compression.
get_back_to_back_pairs()
This function identifies all back‑to‑back game pairs for each team, defined as
consecutive games with days_rest == 0. It returns game pairs that
can be examined more deeply, e.g., comparing Game 1 vs Game 2 performance.
We validated the package in several ways:
These checks gave us confidence that the core logic is correct and robust.
We built a multi‑page Streamlit application to make the analysis interactive and visually intuitive. Each page targets a different part of the story.
The Team Analysis page focuses on single‑team performance over a season. This page allows users to visually track how a team’s performance evolves over time and how it responds to schedule patterns and opponents.
This page is an in-depth analysis of the stats of each goalie. It has the option between looking at one or comparing two separate goalies.
The Goalie Comparison page allows users to select two goalies and compare skills. The page also is supposed to pull up headshot images using player IDs and offer an option to export a PDF comparison report.
This page explores a goalie or compares two goalies over the course of a season. It segments the data into different quarters of the season.
The Rest Impact page brings together many of the package’s rest‑related features. This page is the most direct answer to the core question: “How much does rest matter, and for whom?”
Across all teams and seasons in our dataset, we observe clear patterns:
These trends are consistent with basic expectations about physical recovery.
For the St. Louis Blues, we found:
The Vegas Golden Knights show a different pattern:
Teams like Vegas illustrate that rest effects are not uniform and may be shaped by organizational strategies.
Aggregating goalie results, we observe that:
There are several limitations to our analysis:
Potential extensions include:
nhlRestEffects to PyPI to make it easily installable.
Our analysis demonstrates that rest has a meaningful and quantifiable impact on NHL performance.
Using the nhlRestEffects package, we automated the rest‑calculation pipeline, fixed important
data inconsistencies, and built an interactive Streamlit dashboard that exposes these insights in a
user‑friendly way.
This project showcases our ability to:
Overall, the work illustrates how thoughtful data engineering and modern Python tools can provide deeper understanding of sports performance and scheduling effects.