Epley’s equation, popularized by Jim Wendler with his 5/3/1 books, is oftentimes used to estimate 1RM from maximal number of repetitions using sub-max load (ideally under 10 max reps, or nRM).
Epley’s equation has multiple forms:
\[ \begin{equation} \begin{split} 1RM &= (Load \times nRM \times 0.0333) + Load \\ \\ \%1RM &= \frac{1}{(0.0333 \times nRM + 1)} \\ \\ nRM &= \frac{30.03}{\%1RM} - 30.03 \end{split} \end{equation} \]
Using the equations, we can devise the Max Reps Table:
| nRM | %1RM |
|---|---|
| 2 | 94 |
| 3 | 91 |
| 4 | 88 |
| 5 | 86 |
| 6 | 83 |
| 7 | 81 |
| 8 | 79 |
| 9 | 77 |
| 10 | 75 |
One of the problems with this equation, is that it predicts 0 reps with 100% 1RM. This is the result of estimating parameter k (i.e., 0.0333) using the following model definition:
\[ \%1RM = \frac{1}{(k \times nRM + 1)} \]
Let’s estimate parameter k, using simulated data, assuming Epley’s equation as data-generating-process (DGP)
df <- df %>%
# add small noise so the model can be fit
mutate(
`%1RM` = `%1RM` + rnorm(n(), 0, 10^-4)
)Now, let’s fit two non-linear models: (1) original Epley’s model definition, and (2) modified model definition with \(nRM - 1\). To provide practical model fit, mean-absolute-error (MAE) estimator will be provided:
\[ \%1RM = \frac{1}{(k \times (nRM - 1) + 1)} \]
MAE <- function(model) {
mean(abs(resid(model)))
}
m1 <- nls(
`%1RM` ~ 100 * 1/(k * nRM + 1),
data = df,
start = list(k = 0)
)
summary(m1)
#>
#> Formula: `%1RM` ~ 100 * 1/(k * nRM + 1)
#>
#> Parameters:
#> Estimate Std. Error t value Pr(>|t|)
#> k 3.330e-02 4.693e-08 709524 <2e-16 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 5.861e-05 on 8 degrees of freedom
#>
#> Number of iterations to convergence: 4
#> Achieved convergence tolerance: 8.284e-06
MAE(m1)
#> [1] 4.602527e-05m2 <- nls(
`%1RM` ~ 100 * 1/(k * (nRM-1) + 1),
data = df,
start = list(k = 0)
)
summary(m2)
#>
#> Formula: `%1RM` ~ 100 * 1/(k * (nRM - 1) + 1)
#>
#> Parameters:
#> Estimate Std. Error t value Pr(>|t|)
#> k 0.039162 0.001242 31.54 1.11e-09 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 1.305 on 8 degrees of freedom
#>
#> Number of iterations to convergence: 5
#> Achieved convergence tolerance: 1.129e-07
MAE(m2)
#> [1] 1.002432The estimate parameter k for the model 1 is 0.033 and 0.039 for the model 2. The following forms of equations can be used for the model 1:
\[ \begin{equation} \begin{split} 1RM &= (Load \times nRM \times k) + Load \\ \\ \%1RM &= \frac{1}{(k \times nRM + 1)} \\ \\ nRM &= \frac{1 - \%1RM}{k \times \%1RM} \end{split} \end{equation} \]
While the following forms of equations can be used for the model 2:
\[ \begin{equation} \begin{split} 1RM &= (Load \times (nRM-1) \times k) + Load \\ \\ \%1RM &= \frac{1}{(k \times (nRM-1) + 1)} \\ \\ nRM &= \frac{(k - 1) \times \%1RM + 1}{k \times \%1RM} \end{split} \end{equation} \]
Now, let’s create a table with original and corrected Epley’s equation:
df <- tibble(
nRM = seq(1, 10),
`%1RM (Epley)` = 1/(coef(m1) * nRM + 1) * 100,
`%1RM (Epley corrected)` = 1/(coef(m2) * (nRM-1) + 1) * 100
)
kable(df, digits = 0)| nRM | %1RM (Epley) | %1RM (Epley corrected) |
|---|---|---|
| 1 | 97 | 100 |
| 2 | 94 | 96 |
| 3 | 91 | 93 |
| 4 | 88 | 89 |
| 5 | 86 | 86 |
| 6 | 83 | 84 |
| 7 | 81 | 81 |
| 8 | 79 | 78 |
| 9 | 77 | 76 |
| 10 | 75 | 74 |
So far we have used data generated using Epley’s equation as underlying DGP. Let’s now consider a single individual tested in the bench press, using 150, 135, 120, and 105kg. This corresponds to athlete’s 1RM, 3RM, 6RM and 12RM. Let’s see how the two model definitions differ.
athlete_df <- tibble(
weight = c(150, 135, 120, 105),
nRM = c(1, 3, 6, 12),
`1RM` = 150,
`%1RM` = weight / `1RM` * 100
) %>%
select(-`1RM`)m1 <- nls(
`%1RM` ~ 100 * 1/(k * nRM + 1),
data = athlete_df,
start = list(k = 0)
)
summary(m1)
#>
#> Formula: `%1RM` ~ 100 * 1/(k * nRM + 1)
#>
#> Parameters:
#> Estimate Std. Error t value Pr(>|t|)
#> k 0.036958 0.003145 11.75 0.00132 **
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 2.356 on 3 degrees of freedom
#>
#> Number of iterations to convergence: 4
#> Achieved convergence tolerance: 3.314e-07
MAE(m1)
#> [1] 1.539243m2 <- nls(
`%1RM` ~ 100 * 1/(k * (nRM-1) + 1),
data = athlete_df,
start = list(k = 0)
)
summary(m2)
#>
#> Formula: `%1RM` ~ 100 * 1/(k * (nRM - 1) + 1)
#>
#> Parameters:
#> Estimate Std. Error t value Pr(>|t|)
#> k 0.043161 0.003431 12.58 0.00108 **
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 2.167 on 3 degrees of freedom
#>
#> Number of iterations to convergence: 5
#> Achieved convergence tolerance: 3.492e-06
MAE(m2)
#> [1] 1.624158Here is the max reps table combining observed max number of reps and model predicted:
athlete_df <- athlete_df %>%
mutate(
`%1RM (Epley)` = 1/(coef(m1) * nRM + 1) * 100,
`%1RM (Epley corrected)` = 1/(coef(m2) * (nRM-1) + 1) * 100
)
kable(athlete_df, digits = 0)| weight | nRM | %1RM | %1RM (Epley) | %1RM (Epley corrected) |
|---|---|---|---|---|
| 150 | 1 | 100 | 96 | 100 |
| 135 | 3 | 90 | 90 | 92 |
| 120 | 6 | 80 | 82 | 82 |
| 105 | 12 | 70 | 69 | 68 |
What if we remove the 1RM from the table and repeat the estimation?
m1 <- nls(
`%1RM` ~ 100 * 1/(k * nRM + 1),
data = athlete_df,
start = list(k = 0)
)
summary(m1)
#>
#> Formula: `%1RM` ~ 100 * 1/(k * nRM + 1)
#>
#> Parameters:
#> Estimate Std. Error t value Pr(>|t|)
#> k 0.037565 0.001857 20.23 0.00243 **
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 1.369 on 2 degrees of freedom
#>
#> Number of iterations to convergence: 5
#> Achieved convergence tolerance: 1.944e-07
MAE(m1)
#> [1] 0.9354522m2 <- nls(
`%1RM` ~ 100 * 1/(k * (nRM-1) + 1),
data = athlete_df,
start = list(k = 0)
)
summary(m2)
#>
#> Formula: `%1RM` ~ 100 * 1/(k * (nRM - 1) + 1)
#>
#> Parameters:
#> Estimate Std. Error t value Pr(>|t|)
#> k 0.043161 0.004202 10.27 0.00935 **
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 2.654 on 2 degrees of freedom
#>
#> Number of iterations to convergence: 5
#> Achieved convergence tolerance: 3.551e-06
MAE(m2)
#> [1] 2.165544Here is the max reps table combining observed max number of reps and model predicted:
athlete_df <- athlete_df %>%
mutate(
`%1RM (Epley)` = 1/(coef(m1) * nRM + 1) * 100,
`%1RM (Epley corrected)` = 1/(coef(m2) * (nRM-1) + 1) * 100
)
kable(athlete_df, digits = 0)| weight | nRM | %1RM | %1RM (Epley) | %1RM (Epley corrected) |
|---|---|---|---|---|
| 135 | 3 | 90 | 90 | 92 |
| 120 | 6 | 80 | 82 | 82 |
| 105 | 12 | 70 | 69 | 68 |
The conclusion is the following: