Have NHL goaltenders gotten bigger over time? Does being taller actually translate to better performance?
In this code-through, we will use historical NHL goaltender data to explore how goalie size has changed over time and whether height is associated with save percentage. We will learn how to import, manipulate, visualize, and analyze data using R.
By the end of this tutorial, you will be able to:
read_excel().head() and
names().ggplot2.left_join().lm().This tutorial uses two datasets containing historical NHL goaltender information:
Goaltenders with fewer than 10 career NHL games played were excluded from the original Excel files. This helps reduce the influence of extremely small career samples.
Before we can analyze NHL goaltenders, we first need to import our data sets into R and understand how the information is organized.
For this tutorial, we will use the read_excel() function
from the readxl package to import our two Excel files.
Make sure that GoalieBio.xlsx and
GoalieStats.xlsx are saved in the same folder as your R
Markdown document.
After importing our data, we should examine the variables in each data set.
The names() function returns the column names, helping
us identify which variables we can use in our analysis.
The head() function displays the first six rows of a
data set, allowing us to preview how the information is stored.
## [1] "Player" "S/C" "DOB" "Birth City" "S/P"
## [6] "Ctry" "Ntnlty" "Ht" "Wt" "Draft Yr"
## [11] "Round" "Overall" "1st Season" "HOF" "GP"
## [16] "W" "L" "T" "OT" "SO"
## [1] "Player" "S/C" "GP" "GS" "W" "L" "T" "OT"
## [9] "SA" "Svs" "GA" "Sv%" "GAA" "TOI" "SO" "G"
## [17] "A" "P" "PIM"
The bio data set contains information such as player height
(Ht), weight (Wt), and first NHL season
(1st Season).
The stats data set contains performance variables such as games
played (GP), career save percentage (Sv%), and
goals-against average (GAA).
Before we begin analyzing goaltender size and performance, we need to combine our data sets and ensure that the variables are formatted appropriately.
Biographical and statistical information is currently stored in two
separate data sets. Both data sets contain a Player
variable that identifies each goaltender, allowing us to combine them
using left_join() from the dplyr package.
## [1] "Player" "S/C.x" "DOB" "Birth City" "S/P"
## [6] "Ctry" "Ntnlty" "Ht" "Wt" "Draft Yr"
## [11] "Round" "Overall" "1st Season" "HOF" "GP.x"
## [16] "W.x" "L.x" "T.x" "OT.x" "SO.x"
## [21] "S/C.y" "GP.y" "GS" "W.y" "L.y"
## [26] "T.y" "OT.y" "SA" "Svs" "GA"
## [31] "Sv%" "GAA" "TOI" "SO.y" "G"
## [36] "A" "P" "PIM"
The left_join() function matches observations using a
shared variable, which we specify with by = "Player".
Because goalie.bio is the first data set, R keeps its
observations and adds matching information from
goalie.stats.
We saved the result as goalies and used
names() to inspect it.
To examine historical trends, we need to identify the year each goaltender entered the NHL.
We can use class() and head() to examine
how the 1st Season variable is stored.
## [1] "numeric"
## [1] 19721973 20002001 19321933 20122013 20132014 20022003
The class() function tells us that
1st Season is stored as a numeric variable and that each
season is represented by an eight digit number. For example, the
1972–1973 NHL season appears as 19721973.
To make these values more useful for examining trends over time, we
can extract the starting year using floor().
## [1] 1972 2000 1932 2012 2013 2002
By dividing the original season value by 10,000, we move the starting year into the whole number portion of the result.
19721973 / 10000 = 1972.1973
The floor() function rounds a number down to the nearest
whole number, giving us 1972. We save these values in a new
column called start.year using the $
operator.
Now, we need to prepare our career save percentage (Sv%)
variable. We’ll use this as our primary measure of goaltender
performance. Let’s examine how it is stored in R.
As before, we can use class() to identify its data type
and head() to preview its values.
## [1] "character"
## [1] "0.877" "0.91200000000000003" "--"
## [4] "0.90700000000000003" "0.91300000000000003" "0.91200000000000003"
Although save percentage appears to contain numerical values,
class() reveals that R recognizes the variable as character
data.
To perform calculations using this variable, we need to convert it
into numeric data using the as.numeric() function. We’ll
also run class() and head() again to confirm
that the variable is now numeric.
## [1] "numeric"
## [1] 0.877 0.912 NA 0.907 0.913 0.912
Note, values that cannot be converted into numbers, such as “–”, are replaced with NA, indicating a missing value.
Now that our data is prepared, we can use ggplot2 to
explore relationships between goaltender size, debut year, and
performance.
We can create a scatterplot to examine whether goaltender height has
changed over time. The ggplot() function specifies our data
and variables, while geom_jitter() plots individual
observations with slight adjustments to reduce overlapping points.
ggplot(goalies, aes(x = start.year, y = Ht)) +
geom_jitter(width = 0.2, height = 0) +
geom_smooth(method = "lm")The points represent individual goaltenders, while
geom_smooth(method = "lm") adds a linear trend line. The
upward trend suggests that NHL goaltenders have generally gotten taller
over time.
Next, we can examine whether taller goaltenders tend to have higher career save percentages.
Using the same functions, we can create a scatterplot comparing
height (Ht) with career save percentage
(Sv%).
ggplot(goalies, aes(x = Ht, y = `Sv%`)) +
geom_jitter(width = 0.2, height = 0) +
geom_smooth(method = "lm")The trend line slopes upward, suggesting a positive relationship between goaltender height and career save percentage.
However, this graph alone cannot tell us how strong that relationship is or whether other factors might help explain it.
Our visualizations suggest that taller goaltenders tend to have higher career save percentages. However, we can use statistical analysis to better understand this relationship.
The lm() function estimates the relationship between a
dependent variable and one or more independent variables.
First, we will create a simple regression model using career save percentage as our dependent variable and height as our independent variable.
##
## Call:
## lm(formula = `Sv%` ~ Ht, data = goalies)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.075571 -0.009361 0.002348 0.011607 0.031655
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.7028407 0.0188908 37.20 <2e-16 ***
## Ht 0.0026127 0.0002604 10.03 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01613 on 530 degrees of freedom
## (62 observations deleted due to missingness)
## Multiple R-squared: 0.1596, Adjusted R-squared: 0.158
## F-statistic: 100.7 on 1 and 530 DF, p-value: < 2.2e-16
We save the regression as model1, then use
summary() to examine its results.
The height coefficient is approximately 0.00261. This suggests that each additional inch of height is associated with a 0.00261 increase in career save percentage, or about 0.26 percentage points.
The model has an R-squared value of approximately 0.16, meaning height alone explains about 16% of the variation in career save percentage among goaltenders included in the model.
Because goaltender performance may also differ across historical
eras, we can add debut year (start.year) to our regression
model.
This allows us to examine the relationship between height and save percentage while holding debut year constant.
##
## Call:
## lm(formula = `Sv%` ~ Ht + start.year, data = goalies)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.066828 -0.008787 0.002058 0.010208 0.036380
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 8.251e-02 8.433e-02 0.978 0.328368
## Ht 1.093e-03 3.196e-04 3.419 0.000676 ***
## start.year 3.662e-04 4.864e-05 7.529 2.23e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.01535 on 529 degrees of freedom
## (62 observations deleted due to missingness)
## Multiple R-squared: 0.2409, Adjusted R-squared: 0.2381
## F-statistic: 83.95 on 2 and 529 DF, p-value: < 2.2e-16
After adding debut year, the height coefficient decreases from approximately 0.00261 to 0.00109.
This means that each additional inch of height is now associated with an increase of approximately 0.11 percentage points in career save percentage, holding debut year constant.
The model’s R-squared also increases from approximately 0.16 to 0.24, indicating that including debut year improves the model’s explanatory power.
These results suggest that historical era helps explain part of the observed relationship between height and save percentage.
Although height remains positively associated with save percentage, we cannot conclude that being taller directly causes better goaltender performance.
So, does size matter?
Our analysis suggests that NHL goaltenders have generally gotten taller over time and that taller goaltenders tend to have higher career save percentages.
However, after accounting for debut year in our regression model, the relationship between height and save percentage becomes smaller. This suggests that historical era helps explain some of the relationship we initially observed.
While these results demonstrate an association between height and performance, they do not establish that taller goaltenders are inherently better. Other factors, such as changes in equipment, playing styles, and goaltender techniques, may also influence performance.
Throughout this tutorial, we used R to import and combine data sets, create and clean variables, visualize relationships, and estimate linear regression models.
These techniques can be applied to many other data sets.
Thanks for reading!
The following R packages were used in this tutorial:
readxl: Used to import Excel files into R.
dplyr: Used to combine datasets with
left_join().
ggplot2: Used to create scatterplots and visualize relationships.
This code through references and cites the following sources:
National Hockey League. (n.d.). NHL goaltender statistics [Data set]. NHL.com. Retrieved October 9, 2026, from https://www.nhl.com/stats/goalies
The data were obtained from the National Hockey League’s official statistics database and downloaded into two Excel files for this tutorial.