yips
a data-science essay · applied strength × machine learning

The Statistical Significance of Getting Strong

What ten thousand logged sets reveal about strength, progress, plateaus, and whether I am actually getting stronger.

Two gym-mirror selfies side by side, 2022 and 2026, showing a leaner and more muscular build in the later photo.
Four years under (and away from) the bar: 2022 on the left, 2026 on the right, same genre of gym-mirror selfie. A photo like this is where everyone starts the story, but it cannot tell you whether the numbers actually moved, or when, or why. That is what the other ten thousand sets are for.personal photos · 2022 & 2026

I have been training, on and off, since about 2013. For most of that decade the consistency was patchy: a handful of workouts in JEFIT (remember that?), some loose change in the Android notes app, and long stretches of nothing at all. Having gone from student to full-time desk-bound drone, I drifted from the university gym to the blue fluorescence of Jetts, learning (badly, at first) to juggle work and a life.

Then, in March 2022, nudged along by one too many GymTok edits (we’re all gonna make it brah) and freshly released as the last of the lockdowns lifted, I started logging every set in Strong, and I have not really stopped since. The gyms have changed again (from the red overtone of Virgin Active to the muffled crimson of Fitness First); the constant has been the log. That log, four years and close to ten thousand sets, is the dataset for this essay.

Progressive overload (see Currier et al., 2026) is the notion that you grow by asking more of the body each week, and it is rather trivial to state. Living it is another matter, because progress is oddly hard to see. The body changes slowly and the exercises constantly. Life intervenes: holidays, work, love, and a bad knee. And fatigue actively masks fitness, so that the days you are strongest often feel the worst. Training without records is like driving around with astigmatism: you can see the haze of lights, but you cannot read any of the road signs. Keep logs for four years and the real signal still doesn’t surrender itself easily - digging it out is what we’re doing in this essay.

One caveat before we start. A training log records what I lifted, never what I looked like, so this is a story about strength, the load on the bar, which is only ever a proxy for size. The two travel together, but they are not the same thing, and the barbell is the one my data actually saw. When I ask whether I am getting stronger, that is the question the log can honestly answer.

I have written it on two tracks. For the gym reader, each section asks what the data says about training and holds it against the modern literature. For the machine-learning reader, it explains the method doing the work, with a little maths kept in expandable asides so you can go as deep as you like. Read either track, or both.

Before any of the modelling, though, it helps to simply see all of it at once, and, let us be honest, who does not love a good contribution graph.

Plate I
Daily training volume (total kilograms moved), March 2022 to today. Darker is heavier; blank cells are rest or absence. Hover any day for detail.strong export · 491 sessions
1,875 t
total tonnage lifted
about 1,594 Mazda MX-5s
491
logged sessions
over 4.3 years
493
personal records
new estimated-1RM highs
36 wk
longest unbroken streak
of weekly training

A wall of squares is nice to look at. What was my journey?

chapter 01 · the arc

A Gym-Goer’s Odyssey

To turn that calendar into a trajectory, I need a single number for how strong I was in any given week. A bench press and a preacher curl are not measured on the same ruler, and I almost never test a true one-rep max (while fun, it is generally counter-hypertrophic). So for every working set I estimate a 1RM from the weight and the reps (much more on how badly the standard formulas disagree, in Chapter 03), then standardise each exercise against its own history and average across the main lifts.

The result is a strength index. It is unitless: zero is my four-year average, and plus or minus one is a standard deviation above or below it.

Unitless makes it tidy but hard to feel, so to keep it concrete, here is roughly what one point (one standard deviation) is worth in real weight on three of my lifts:

what one index point is worth
chest press~14kg
deadliftest.~18kg
biceps curl~5kg

So when the 2024 crunch later costs me 1.18 points, that is about 16 kg off my chest press, or 21 kg off my deadlift.

the maths: the strength index

A raw one-rep-max estimate is in kilograms, so a deadlift and a lateral raise live on wildly different scales and cannot simply be averaged. We fix this by standardising each exercise against its own history. For lift ii on a given day,

zi=xiμiσi,z_i = \frac{x_i - \mu_i}{\sigma_i},

where xix_i is that day’s estimated 1RM, and μi,σi\mu_i, \sigma_i are the mean and standard deviation of that lift across all four years. Now every lift speaks the same language: zi=0z_i = 0 means “a typical day for this lift”, zi=+1z_i = +1 means “one standard deviation stronger than usual”. Averaging ziz_i across the main lifts each week gives one dimensionless index of overall strength, which is what the vertical axis of Figure 2 reports (in units of σ\sigma).

Plotted raw, that index is jittery, because any single session is the true strength value plus noise: sleep, caffeine, whether the bar felt right on the day. To recover the signal I pass it through a Kalman filter (see Kalman, 1960), a method that treats my real strength as a drifting hidden state and each session as a noisy view of it. It is the same idea that keeps a GPS on course, pointed here at a more modest target.

the maths: the Kalman filter

We assume there is a true strength I cannot observe directly, only glimpse through noisy sessions. The filter models it as a state-space system with two equations:

levelt=levelt1+trendt1+εthow true strength evolves,yt=levelt+ηtwhat I actually log.\underbrace{\text{level}_t = \text{level}_{t-1} + \text{trend}_{t-1} + \varepsilon_t}_{\text{how true strength evolves}}, \qquad \underbrace{y_t = \text{level}_t + \eta_t}_{\text{what I actually log}}.

level is my true strength this week; trend is its momentum, the slope it carries. The random terms are the two dials that make the whole thing work: εtN(0,Q)\varepsilon_t \sim \mathcal{N}(0, Q) is process noise, the body genuinely changing week to week, and ηtN(0,R)\eta_t \sim \mathcal{N}(0, R) is measurement noise, the bad-sleep, missed-lift wobble in what I log.

How the estimate x^t\hat{x}_t is actually computed. The filter never sees the true level; it maintains a running best guess x^\hat{x} together with its own uncertainty, a variance PP. Each week it takes two steps. First it predicts: it rolls the guess forward by the trend and, because the world is noisy, lets its uncertainty grow, PP+QP \to P + Q. Then it updates: the new session yty_t arrives, it measures the surprise (the residual ytx^tt1y_t - \hat{x}_{t\mid t-1}), and nudges the guess toward the reading by a fraction KtK_t:

x^t=x^tt1+Kt(ytx^tt1),Kt=Ptt1Ptt1+R.\hat{x}_t = \hat{x}_{t\mid t-1} + K_t\,(y_t - \hat{x}_{t\mid t-1}), \qquad K_t = \frac{P_{t\mid t-1}}{P_{t\mid t-1} + R}.

Read the gain KtK_t as a trust dial between 0 and 1. It is large when the filter’s own uncertainty PP dwarfs the measurement noise RR (trust the data), and small when the reading is noisy relative to a confident prediction (trust the prediction). After using the reading, the filter is more certain, so its variance shrinks, Pt=(1Kt)Ptt1P_t = (1 - K_t)\,P_{t\mid t-1}. Through a training gap there is no yty_t, so it only ever predicts: the guess coasts on the trend and PP keeps inflating, which is the shaded band fanning out over the empty months.

A worked week. Say that going into some week the filter believes my level is 0.40σ0.40\,\sigma with an upward momentum of trend=+0.03σ\text{trend} = +0.03\,\sigma/week, and it carries uncertainty P=0.05P = 0.05. The learned noises are Q=0.01Q = 0.01 (the body drifts a little) and R=0.04R = 0.04 (a single session is fairly noisy).

  1. Predict. Roll the guess forward by the trend: x^tt1=0.40+0.03=0.43\hat{x}_{t\mid t-1} = 0.40 + 0.03 = 0.43. Let uncertainty grow: Ptt1=0.05+0.01=0.06P_{t\mid t-1} = 0.05 + 0.01 = 0.06.
  2. Observe. This week I have a good day, and the session standardises to yt=0.55σy_t = 0.55\,\sigma. The surprise is 0.550.43=+0.120.55 - 0.43 = +0.12.
  3. Gain. Kt=0.060.06+0.04=0.60K_t = \dfrac{0.06}{0.06 + 0.04} = 0.60, so trust the reading 60%.
  4. Update. x^t=0.43+0.60×0.12=0.50\hat{x}_t = 0.43 + 0.60 \times 0.12 = 0.50. The level lands most of the way to the reading, but not all the way, because a single session is noisy. Uncertainty shrinks: Pt=(10.60)×0.06=0.024P_t = (1 - 0.60)\times 0.06 = 0.024. The momentum ticks up too, from +0.03+0.03 to about +0.04+0.04: one strong week is weak evidence that I am now trending up a little faster.

Next week starts from 0.500.50 with trend +0.04+0.04, and the whole thing repeats. Stitch 226 of these updates together and you get the clean line in Figure 2.

Where the numbers come from. Nothing above is hand-tuned. The two variances QQ and RR are learned from my data by maximum likelihood: we run the filter forward, and it emits, each week, a predicted distribution for yty_t; the likelihood is how well those predictions matched what actually happened, and the loss we minimise is the negative log-likelihood

logL(Q,R)=12t[log(2πFt)+(ytx^tt1)2Ft],-\log L(Q, R) = \tfrac{1}{2}\sum_t \left[ \log(2\pi F_t) + \frac{(y_t - \hat{x}_{t\mid t-1})^2}{F_t} \right],

where Ft=Ptt1+RF_t = P_{t\mid t-1} + R is the predicted variance of the surprise. An optimiser (here, statsmodels) searches Q,RQ, R to make the filter’s forecasts as calibrated as possible; the smooth line in Figure 2 is the result run back over the whole series.

The change-points that carve the timeline into four chapters (marked by the vertical shading) were not placed by hand. An algorithm (see Killick et al., 2012) scans the series for a few moments of behavioural change, and it recovered the epochs of my life I lived.

Interestingly, we recover a journey. There is a long toil (ponoi) through 2022 and 2023, as raw effort slowly compounds. Then a plateau (stasis), where the line stops rising. Then a fall. And finally the climb back toward excellence (arete), the thing the whole endeavour is quietly reaching for.

Plate II
strength (kalman)95% uncertaintyweek with a PRcomebackinjuryrelationshiptravelwork / pause
The strength index (standardised, in units of σ), smoothed by a Kalman filter; the shaded band is 95% uncertainty. Coloured dots are life events (hover for detail); the aqua rug marks weeks with a personal record. Vertical shading separates the auto-detected chapters.491 sessions · kalman filter + PELT change-points

The events reflect my life. I started dating my girlfriend in February 2024. That winter, work got genuinely busy, a four-month crunch that, in an earlier era, would have ended my training entirely. This time it did not: the line sags but ultimately holds.

kartería
endurance under strain

Holding on through a bad stretch is its own quiet form of progress. I am proud of this kartería.

Can we do better than eyeballing that dip? There is a causal inference tool called the interrupted time series (see Bernal et al., 2017). The idea is simple. Take the trend the line was on before the crunch and let it coast onward, a dotted “what would have happened if work had stayed quiet”. Then fit a version of the line that is allowed exactly one kink at the event date, September 2024, and measure how far the real line steps away from that projection. If the step is large next to the ordinary week-to-week jitter, then the crunch, and not chance, is the likely cause.

the maths: interrupted time series

The line-with-one-kink is a single regression. With tt the week index, Dt=1D_t = 1 once the event has happened (and 0 before), and t0t_0 the event week:

yt=β0+β1t+β2Dt+β3(tt0)Dt+ϵt.y_t = \beta_0 + \beta_1 t + \beta_2 D_t + \beta_3 (t - t_0)\,D_t + \epsilon_t.

Each coefficient has a plain reading. β1\beta_1 is the slope the line was already on. β2\beta_2 is the instant step at the moment of the event, up or down, and this is the “level change” we quote. β3\beta_3 is how much the slope bends afterwards. Fit it by least squares, read off β2\beta_2 and its p-value, and you have both the size of the shock and the odds that a jump this big is mere noise. The comeback uses the same equation with t0t_0 moved to February 2026, where the informative coefficient is the slope-bend β3\beta_3.

For my two events the fitted steps are unlikely to be chance: the crunch’s level drop lands at p<0.001p < 0.001, and the comeback’s slope-bend at p=0.003p = 0.003.

Plate III
what actually happenedcounterfactual: had the event not occurredthe gap = the estimated effect
Interrupted time series for the two interventions. Solid is the observed trajectory, dashed is the counterfactual (the pre-event trend projected forward), and the shaded gap is the estimated effect: a step down for the crunch, a steeper slope for the comeback.segmented regression on the weekly strength index

Figure 3 makes the method visible. In the left panel, the dashed line is the pre-crunch trajectory projected onward, the strength I would plausibly have had if work had stayed quiet; the solid line is what actually happened; and the red gap between them is the loss. It is a large one: an immediate step down of 1.18 standard deviations in the strength index. The fitness rationale is obvious: fewer sessions, thinner sleep, and no mental capacity leftover from building agents all day.

And then, in 2026, the comeback is not merely a recovery. The right panel anchors the same procedure at February 2026: this time the informative quantity is the slope, and the real line pulls away above its flat counterfactual, bending upward by 0.05 standard deviations per week, the steepest sustained strength climb in the last four years.

From 2013 to 2021, a stretch like the 2024 crunch would have ended my training for a year; this time it did not. As Confucius stated: it does not matter how slowly you go, so long as you do not stop.

All the tools here required my personal input. The next chapter builds a model to infer those stages itself.

So let us stop naming the seasons by hand, and ask the model to find them.

chapter 02 · hidden states

Epochs, According to the Machine Spirit

Suppose I never told a model about my story, and simply let it watch two numbers each week: how much I trained, and whether my strength was rising or falling. Could it work out, on its own, that training moves through a few recurring stages, and say which one I was in at any given moment? This is exactly the job of a hidden Markov model (see Rabiner, 1989), and it is the natural sequel to the change-points we used before, because it replaces their hard borders with a probabilistic view of each stage every week.

The premise is that behind the messy weekly numbers sit a small number of hidden states, and here I ask for three, which turn out to correspond neatly to building, maintaining, and detraining. The model discovers three clusters (akin to unsupervised clustering) given nothing but two weekly numbers, and I named them afterwards, from their character. Building weeks carry high volume and rising strength; detraining weeks, the opposite; maintaining sits in between.

the maths: the hidden Markov model

Two ingredients. First, the hidden state st{1,2,3}s_t \in \{1,2,3\} follows a Markov chain: where it goes next depends only on where it is now, governed by a transition matrix AA with Aij=P(st=jst1=i)A_{ij} = P(s_t = j \mid s_{t-1} = i). Its diagonal is large, because states are sticky: a building week is usually followed by another building week. Second, each state emits the things I actually observe. My weekly observation is ot=(standardised volume,change in strength)o_t = (\text{standardised volume}, \, \text{change in strength}), and state ii draws it from its own Gaussian, otst=iN(μi,Σi)o_t \mid s_t{=}i \sim \mathcal{N}(\mu_i, \Sigma_i), so “building” is simply the state whose cloud sits at high volume and positive strength-change.

I never see sts_t, only oto_t, so both the dynamics AA and the emission clouds (μi,Σi)(\mu_i, \Sigma_i) have to be learned from the observations alone. That is done by Baum-Welch, an expectation-maximisation loop: guess the parameters, use the forward-backward algorithm to compute the posterior γt(i)=P(st=io1:T)\gamma_t(i) = P(s_t = i \mid o_{1:T}) for every week, then re-estimate AA and the Gaussians as posterior-weighted averages, and repeat until the likelihood stops improving (here via hmmlearn). The ribbon in Figure 4 is exactly those posteriors γt\gamma_t, the model’s smoothed belief about which mode each week belonged to.

A worked week. The machinery is easier to trust on a toy version. Shrink it to two states, training and detraining, and a single weekly observation, the change in my strength index, oto_t. Suppose the learned pieces are a sticky transition matrix and two emission means,

A=(.85.15.15.85),μtrain=+0.4,μdetrain=0.4,σ=0.5,A = \begin{pmatrix} .85 & .15 \\ .15 & .85 \end{pmatrix}, \qquad \mu_{\text{train}} = +0.4,\quad \mu_{\text{detrain}} = -0.4,\quad \sigma = 0.5,

where μtrain=+0.4\mu_{\text{train}} = +0.4 just says a typical training week nudges my strength up by 0.40.4, and a detraining week down by the same; σ\sigma is how noisy that is. Coming into the week I lean training, γt1=(0.70,0.30)\gamma_{t-1} = (0.70,\, 0.30), and then my strength rises, ot=+0.6o_t = +0.6.

  1. Predict with AA. Before seeing anything, roll the belief forward through the transition matrix: γt1A=(0.64,0.36)\gamma_{t-1}A = (0.64,\, 0.36). Still leaning training, but softened, because a state can always flip.
  2. Weigh the evidence. Each state scores the observation with its Gaussian. Here i\ell_i is the likelihood: the probability density that state ii assigns to seeing exactly oto_t, that is, the height of state ii‘s bell curve at oto_t (the PDF), not an area under it (which would be a CDF). In full, i=1σ2πexp ⁣((otμi)2/2σ2)\ell_i = \frac{1}{\sigma\sqrt{2\pi}}\exp\!\big(-(o_t-\mu_i)^2/2\sigma^2\big), but the leading constant is identical for both states, so it cancels when we normalise and I drop it, writing iexp ⁣((otμi)2/2σ2)\ell_i \propto \exp\!\big(-(o_t-\mu_i)^2/2\sigma^2\big). A +0.6+0.6 week sits close to training’s mean of +0.4+0.4 and far from detraining’s 0.4-0.4, giving train0.92\ell_{\text{train}} \approx 0.92 and detrain0.14\ell_{\text{detrain}} \approx 0.14.
  3. Combine and normalise, which is just Bayes: belief \propto prior ×\times evidence. Elementwise, (0.64×0.92,  0.36×0.14)=(0.59,0.05)(0.64 \times 0.92,\; 0.36 \times 0.14) = (0.59,\, 0.05), and dividing by the sum gives γt=(0.92,0.08)\gamma_t = (0.92,\, 0.08). One good week has firmed “probably training” into a 92% belief.

That is the forward pass. The ribbon also runs a backward pass that folds in later weeks too, so a single bad week wrapped in good ones is discounted as noise, but the arithmetic is identical.

Learning AA and the means in the first place is that same machinery run backwards, the expectation-maximisation loop, and it is worth seeing once with numbers.

the maths: one full EM iteration, by the numbers

Take four weeks of strength-changes, o=(+0.6,+0.5,0.6,0.4)o = (+0.6,\, +0.5,\, -0.6,\, -0.4), and start from deliberately vague guesses: μtrain=+0.1\mu_{\text{train}} = +0.1, μdetrain=0.1\mu_{\text{detrain}} = -0.1, σ=0.5\sigma = 0.5, flat transitions A=(.5.5.5.5)A = \left(\begin{smallmatrix}.5 & .5 \\ .5 & .5\end{smallmatrix}\right), and prior (0.5,0.5)(0.5, 0.5).

E-step (estimate the beliefs). Run forward-backward with the current guesses to get the responsibility γt(train)\gamma_t(\text{train}), the model’s present belief that each week was a training week. With flat transitions these follow the emission likelihoods, and come out to roughly (0.62,0.60,0.38,0.42)(0.62,\, 0.60,\, 0.38,\, 0.42): the two positive weeks lean training, the two negative weeks lean detraining, but only weakly, because the means barely differ yet.

M-step (re-estimate the emissions). Set each mean to the responsibility-weighted average of the observations:

μtraintγt(train)ottγt(train)=(.62)(.6)+(.60)(.5)+(.38)(.6)+(.42)(.4).62+.60+.38+.42=0.2762.020.14.\mu_{\text{train}} \leftarrow \frac{\sum_t \gamma_t(\text{train})\, o_t}{\sum_t \gamma_t(\text{train})} = \frac{(.62)(.6) + (.60)(.5) + (.38)(-.6) + (.42)(-.4)}{.62 + .60 + .38 + .42} = \frac{0.276}{2.02} \approx 0.14.

The mirror computation, with weights 1γt1 - \gamma_t, gives μdetrain0.09\mu_{\text{detrain}} \approx -0.09. The two clouds have edged apart, from ±0.1\pm 0.1 toward ±0.4\pm 0.4.

M-step (re-estimate the transitions). Likewise Atraintrain=tξt(train,train)tγt(train)A_{\text{train}\to\text{train}} = \dfrac{\sum_t \xi_t(\text{train}, \text{train})}{\sum_t \gamma_t(\text{train})}, the expected count of training-followed-by-training over the expected time spent training, where ξt(i,j)=P(st=i,st+1=jo)\xi_t(i,j) = P(s_t{=}i,\, s_{t+1}{=}j \mid o). A small mercy on this first pass: because we started AA uniform, consecutive weeks are independent given the data, so ξt(i,j)\xi_t(i,j) is simply γt(i)γt+1(j)\gamma_t(i)\,\gamma_{t+1}(j). Writing γt\gamma_t for γt(train)\gamma_t(\text{train}) and summing over the three consecutive pairs,

Atraintrainγ1γ2+γ2γ3+γ3γ4γ1+γ2+γ3=(.62)(.60)+(.60)(.38)+(.38)(.42).62+.60+.38=0.7601.60=0.475.A_{\text{train}\to\text{train}} \leftarrow \frac{\gamma_1\gamma_2 + \gamma_2\gamma_3 + \gamma_3\gamma_4}{\gamma_1 + \gamma_2 + \gamma_3} = \frac{(.62)(.60) + (.60)(.38) + (.38)(.42)}{.62 + .60 + .38} = \frac{0.760}{1.60} = 0.475.

So it barely moves off 0.50.5. But once the means separate and the positive weeks start clustering together, the like-follows-like products γtγt+1\gamma_t\gamma_{t+1} swell, the diagonal of AA climbs, and the model learns that states are sticky.

Repeat. Feed the updated parameters into a fresh E-step. Each loop sharpens the beliefs, which sharpen the parameters; after a handful of iterations the means settle near ±0.5\pm 0.5, AA grows its sticky diagonal, and the weekly beliefs harden into the confident ribbon of Figure 4. EM guarantees the data’s likelihood climbs at every step, which is exactly why the loop is safe to trust.

Plate IV
buildingmaintainingdetrainingchange-points from chapter 01
The model's weekly belief about which stage I was in, stacked to 1. Green is building, yellow maintaining, red detraining; the dashed verticals are the hard change-points from Chapter 01. Hover for the exact probabilities in any week.3-state gaussian HMM · learned by Baum-Welch

Two things stand out. The first is that the model, given nothing but two weekly numbers, rediscovers a similar story to Chapter 01: the green building band swells through 2022 and 2023 (the ponoi), collapses into red during the 2024 crunch, and surges back in 2026 (the arete). Its transitions sit close to the dashed change-points, but not on them. Where the change-point algorithm was forced to name a single week, the HMM lets the belief cross over across a month or two, which is how the seasons actually change, by degrees rather than at a stroke.

The second is insightful: the model keeps surfacing periods I had forgotten about. That block of red in early 2023 is when I started a new job, and training evaporated for a while. And 2025, which I remembered as a sedated year, reads on the ribbon as a near-unbroken stretch of detraining, considerably worse than my reflection.

Rosy retrospection is a highly documented cognitive bias. Memory, as Freud liked to insist, is less a recording than a reconstruction, quietly edited in our own favour. Fortunate for me, I’m held to account by the power of numbers and unlimited cloud storage.

Of the 226 weeks logged, the model’s best guess is detraining for 121 of them, more than half, against 76 building and 29 maintaining. When I do train, I exonerate myself and train hard; the trouble is that hard is not the same as often, and the ribbon is really a map of frequency. If I had to bet on the single thing throttling my progress, it would not be intensity but rather the red that is volume. We will put the hypothesis, that volume is what matters most, to a proper test in Chapter 05.

So far, what we have learned is that the model can tell us when I was building or detraining. But the next question is: what did that building amount to in definitive, real kilograms?

How strong did this make me?

chapter 03 · how strong, really

Am I Strong?

To ask whether all that building actually worked, I have to put a number on the bar, in kilograms this time, not standard deviations. Two issues get in the way:

  1. I almost never test a true one-rep max, so the number itself has to be estimated.
  2. More interestingly, even a perfect number is meaningless in isolation. “Strong” is always strong compared to someone, and the answer swings wildly depending on whom you pick.

1RM estimate

Given a set of, say, 60 kg for 8 reps, a one-rep-max formula guesses the single heaviest lift you could have managed. There are many such formulas, most dating to the 1980s and 1990s, and no two of them agree; each was fitted to its own author’s athletes or a modest sample, so each bakes in a slightly different guess about the same curve. At one rep they must all return the weight on the bar, but as the reps climb they fan apart, because extrapolating from a set of twelve up to a hypothetical single is genuinely uncertain. I computed seven of them; Figure 5 shows how far they drift.

Plate V
O’Conner & Lombardi (fit me best)the other five formulas
Seven one-rep-max formulas applied to a single 60 kg set, as a function of its rep count. They coincide at one rep and diverge as reps rise. Hover to read the spread at any rep count.epley, brzycki, lombardi, o'conner, mcglothin, wathan, mayhew

To pick one, I checked each formula’s estimate against the rare days I actually ground out a heavy single or double: O’Conner and Lombardi came closest (about 10.6 and 10.9 kg off), while the popular Epley and the exponential Wathan overestimated the most. Those two winners are the estimators behind every kilogram that follows. The full bake-off, formulas and all, is in Appendix C.

Relative strength

Now that I have my one-rep maxes, how strong am I? For years I religiously checked strengthlevel.com, which takes a lift, quietly routes it through this same kind of formula, and hands back a flattering verdict: novice, intermediate, advanced, and a percentile. Vainly, I felt a sense of accomplishment as my lifts crept up the tiers. It was answering one specific question, “strong compared to the general population of people who log lifts”, and to that question my 2.24 times bodyweight deadlift is a respectable intermediate.

Tougher crowds exist, and against them the verdict flips. OpenPowerlifting (OpenPowerlifting, 2026) publishes the meet results of people who actually compete, and who therefore presumably train specifically for strength. Filtering to the 66,000 male raw lifters within five kilograms of my bodyweight and dropping my lifts onto their distribution (Figure 6) gives a very different picture. The same deadlift that strengthlevel calls intermediate lands at the 24th percentile among competitors, and my bench at the 40th. Blue is everyone I out-lift; grey, the majority who out-lift me.

Plate VI
My deadlift and bench dropped onto the distribution of ~66,000 competitive male raw lifters within 5 kg of my bodyweight (OpenPowerlifting). Blue bars are lifters I out-lift; the dashed line is the group's median.openpowerlifting · male · raw · 53–63 kg

Somewhere between the flattering mirror and the merciless one sits a third, more considered measure. In How Strong Should You Be? (Noob to Freak), Jeff (Nippard, 2024) builds a scale from a decade of coaching and competition data, sorting lifters from noob through intermediate and advanced up to the genetic freak. On his ladder my 2.24 times bodyweight deadlift lands right on the intermediate-to-advanced rung: better than OpenPowerlifting’s crowd made me feel, worse than strengthlevel let me believe, and probably the most honest of the three.

NovicePowerlifters
IntermediateJeff Nippard
High Intermediatestrengthlevel.com

The video’s sharpest insight, though, is not in the video at all but in its top comment, which compresses the entire problem of reference classes into a great mindset:

Top YouTube comment: everyone stronger than me is a freak, and everyone weaker than me is a noob.

Nevertheless, the resonance is the same in every percentile: “how strong am I” turns out to be less a question about me than about whom I choose to stand beside. And unless you are chasing a competition total, the only person you should be standing beside is the version of yourself who trained last. You just have to beat him a little, and often enough.

At this point, I would love to do a strength analysis based on the exercise: a chest press, a row, a curl. The issue is that endless variations exist: dumbbell benches and Smith benches and inclines, machine rows and cable rows. Before any exercise’s strength level can be trusted, those scattered variants have to be put into a common currency.

I never do the same exercise for long. So how do you measure progress on a movement that keeps changing shape?

chapter 04 · the moving target

The Ship of Theseus

The Greeks liked to ask whether a ship whose planks are replaced one by one, until not a single original board remains, is still the same ship. Assume my bench press now resembles a ship.

I have pressed it with a barbell, with dumbbells, on a Smith machine, on an incline, on a chest-press machine, and on whatever the new gym happened to have bolted to the floor, and wasn’t taken. Not one of those is the lift I started with, and yet, in every way that matters to me, it is all still “chest press”. The plates change, the angle changes, the hardware changes. The muscle underneath does not much care (for the most part).

This is a pleasant philosophy, and a headache for measurement. Every time I swap variants, the naive record resets. A dumbbell that tops out at 32 kg per hand and a Smith bench that moves 80 kg are not remotely the same number, so a chart of any single exercise is a string of false stalls: a lift climbs, gets abandoned for its cousin, and the cousin appears to start over from nothing. Look at exercises, and I plateau constantly. Instead, if we look at the general movements, the gains are there.

Stitching the planks together

The trick is that swaps are rarely clean breaks. For a week or two I usually do the old exercise and the new one side by side, and those overlaps are the seam. If, across the weeks I did both, my incline bench sits reliably at about 1.15 times my flat dumbbell bench, then I have a conversion rate between them, an exchange rate in the currency of estimated one-rep maxes. Pin one variant as the anchor, chain those exchange rates across every overlap, and all fifteen presses collapse onto a single scale. The stitched line each week is simply my best set that week, converted onto that one shared scale.

Plate VII
Pick a movement. Each faint dot is one variant's best estimated 1RM that week, converted onto one shared scale; the white line is the stitched movement. Watch the colour of the cloud change as I abandon one variant for another while the line holds its climb. Hover any dot or the line.strong export · e1rm · overlap-calibrated
the maths: an exchange rate between exercises

What the data looks like. For each variant vv (flat dumbbell bench, incline barbell, and so on) I have a sparse weekly series ev(t)e_v(t): my best estimated 1RM for that variant in week tt, taken from the formulas of the previous chapter. Sparse, because I only log a value in the weeks I actually did vv. Laid out as a table it is mostly holes, with the occasional week where two variants happen to co-occur:

week teflat dbeincline bbesmith14681570611662177160\begin{array}{c|ccc} \text{week } t & e_{\text{flat db}} & e_{\text{incline bb}} & e_{\text{smith}} \\ \hline 14 & 68 & \cdot & \cdot \\ 15 & 70 & 61 & \cdot \\ 16 & \cdot & 62 & \cdot \\ 17 & 71 & \cdot & 60 \end{array}

The model. Assume every variant tracks one hidden movement strength, offset by a constant factor: on the log scale, logev(t)=s(t)+βv+ε\log e_v(t) = s(t) + \beta_v + \varepsilon, where s(t)s(t) is the shared latent strength that week and βv\beta_v is variant vv‘s fixed log-offset from it (a per-hand dumbbell reads systematically lighter than a barbell, so its β\beta is more negative). Logs turn the multiplicative exchange rate into an additive offset, which is what makes the whole thing linear.

The pairwise measurements. In any week both uu and vv appear, the shared s(t)s(t) cancels when you subtract, leaving an estimate of βuβv\beta_u - \beta_v. Averaging over the overlap weeks WuvW_{uv} gives one clean number per pair,

duv  =  1WuvtWuv(logeu(t)logev(t))    βuβv.d_{uv} \;=\; \frac{1}{|W_{uv}|}\sum_{t \in W_{uv}} \big(\log e_u(t) - \log e_v(t)\big) \;\approx\; \beta_u - \beta_v .

Solving for the offsets. Each duvd_{uv} is one noisy equation βuβvduv\beta_u - \beta_v \approx d_{uv}. Stack them all and it is a weighted least-squares fit for the vector β=(β1,,βV)\boldsymbol\beta = (\beta_1, \dots, \beta_V),

β^  =  argminβ (u,v)wuv(βuβvduv)2,\hat{\boldsymbol\beta} \;=\; \arg\min_{\boldsymbol\beta}\ \sum_{(u,v)} w_{uv}\,\big(\beta_u - \beta_v - d_{uv}\big)^2 ,

with weight wuv=Wuvw_{uv} = |W_{uv}|, so pairs that overlapped for many weeks pull harder. This is the same shape as pose-graph or ranking problems (think of it as solving for each variant’s “handicap” from head-to-head margins). It is a small convex quadratic, a few dozen unknowns at most, so there is no need for gradient descent: the normal equations Lβ^=bL\hat{\boldsymbol\beta} = b solve it in closed form, where LL is the (weighted) graph Laplacian over the variants and bb collects the duvd_{uv}.

The gauge. The objective only ever sees differences of β\beta, so adding a constant to every offset leaves it unchanged; the solution is undetermined up to that one global shift. We nail it down by fixing a gauge, βanchor=0\beta_{\text{anchor}} = 0, choosing the best-connected variant as the anchor. Every other βv\beta_v is then read as an offset from that anchor.

Rescaling and stitching. With the offsets in hand, e~v(t)=ev(t)eβv\tilde e_v(t) = e_v(t)\,e^{-\beta_v} maps each variant onto the anchor’s scale, and the movement’s stitched strength in week tt is the best rescaled evidence I actually produced that week,

S(t)  =  maxvtrained(t)e~v(t).S(t) \;=\; \max_{v \,\in\, \text{trained}(t)} \tilde e_v(t) .

The max\max (rather than an average) treats each logged set as a lower bound on true strength and takes the strongest showing, which is what a personal record is.

The catch. All of this needs the overlap graph to be connected: only then can every variant reach the anchor through some chain of shared weeks. Where a variant never overlaps anything, its βv\beta_v is unidentified, and the code falls back on βv=0\beta_v = 0 (an exchange rate of exactly 1.00). That default is harmless when the orphan really does resemble the anchor and quietly wrong when it does not, which is precisely the failure the next figure goes looking for.

The trap of the absolute axis

There is a catch though with absolute weights. Even benchmarked to a barbell curl, my biceps top out under 40 kg, less than half of what my chest press moves, so on a shared kilogram axis they hug the floor and look like they never budged. This makes plotting difficult.

The fix is to stop reading absolute kilograms and re-base every movement to 100 at its first week, so the y-axis becomes percent of baseline. Now a 19 kg curl and an 86 kg press are asked the same question, how far have you come, and answer on the same scale. Far from flat, my biceps turn out to have ratcheted up the most of anything, about +180 percent at their best, with the honest caveat that a tiny starting number flatters the ratio.

Each solid line in Figure 8 is that best-to-date ratchet: records only rise, so the line only climbs, and every step is a movement PR. Re-based this way the chapter’s point finally lands. My biceps, dead flat in kilograms, ratchet up as hard as anything I train.

Plate VIII
Every stitched movement re-based to 100 at its first logged week, drawn as a best-to-date ratchet so records only rise. Exchange rates the model never measured are filled in from typical population ratios. Hover to compare any week.strong export · stitched · backfilled · indexed to baseline = 100

It is a reassuring picture, though the first draft of it was not. Every climb here rests on a pile of conversions between variants, and on the raw rates a few of those conversions were simply wrong, enough to show my arms drifting backwards in the months I was training them hardest. I am, in fact, stronger than ever. So before trusting the chart, I had to check the rates it stands on; that full audit lives in Appendix A, with the short answer being: where overlaps exist the rates are solid, but 17 of the 61 were never measured directly and had to be filled in from typical population ratios.

Conclusively, variation is, for the most part, fine. Modern work on exercise selection is fairly relaxed about the exact tool, and increasingly interested in where in the range of motion the tension lands: lengthened, stretched-position training holds up well against full-range work (see Wolf et al., 2025), and the technique reviews treat variant choice as a lever for emphasis and joint comfort rather than a make-or-break decision (Androulakis-Korakakis et al., 2024). My drift toward inclines and machines was not self-sabotage; it reflected that slow, fashionable reshuffle.

You can watch the ship get re-planked. Split every kilogram I have ever lifted by the tool that moved it, quarter by quarter, and the mix never holds still. I came up on free weights: barbells and dumbbells were 71 percent of my volume at the very start. By 2024 they had ceded the floor to the machines and cables, which together crossed sixty percent of everything I did. The 2026 comeback dragged some barbell work back in, but the arc is unmistakable, a slow migration from the free-weight rack to the pin-loaded stack. Every one of those swaps is a plank replaced; the movement underneath, and the strength I built on it, carried on regardless.

Plate IX
Every kilogram I have lifted, split by equipment and stacked to 100% each quarter. Free weights (warm) give way to the machines and cables (cool) across four years, with a partial return to the barbell in the 2026 comeback. Hover any quarter.strong export · quarterly volume share by equipment

Exercise selection aside, what actually pushes the index up? The most-argued dose in all of training: volume.

chapter 05 · volume

Volume: How Much Is Enough?

I have counted many sets. My training has been a slow tour of other people’s programs, each a different bet on how much work is the right amount. I came up on StrongLifts 5x5, all low reps and heavy frequency; drifted into the bodybuilding gospel of 4x12 and its wall of pump; and I have mostly settled into a hard, honest 3x6–8, three sets taken close to the edge and not much else, though I will note that 2x8 is now all the rage. The through-line, if I am honest, is that I have been slowly buying less volume and paying for it with more effort. This chapter asks whether that was wise, and whether the dose even does what everyone says it does.

The framework the set-counters swear by comes from Renaissance Periodization (see Israetel et al., 2021), which gives every muscle three weekly landmarks: the MEV, the minimum effective volume you need to grow at all; the MAV, the adaptive band where most of the productive work happens; and the MRV, the maximum you can recover from before the returns go negative. They are not laws of physics. They are heuristics, distilled from the hypertrophy dose-response literature and a lot of coaching, individual and fuzzy at the edges. The modern meta-regressions broadly back their shape: more sets keep helping, with diminishing returns rather than a cliff (see Pelland et al., 2025), which sharpens the older picture (Schoenfeld et al., 2017). The one thing to hold onto is the unit. These are weekly numbers, per muscle.

Plate X
Weekly hard sets for one muscle over four years, against the weekly per-muscle MEV / MAV / MRV landmarks. Each bar is a week (red under the minimum, green in the productive band); the pale line is my median. Flip through the muscles.strong export · hard sets · by muscle group

Flip through the muscles and a confession assembles itself. Upper body sits just over the minimum; legs barely register. Hamstrings are trained in fifteen percent of weeks. Leg day is mostly what I skip (haha), and it is the clearest thing I need to fix.

Does the dose actually work, though?

The whole framework assumes volume causes growth. My logs can show that, but beware the old statistician’s adage: correlation is not causation. Weeks when I do more sets are also weeks when I am heavier, already stronger, better rested, and in a better mood, and any of those could be doing the work while volume takes the credit. Simply correlating sets with next-month gains would hand the prize to whichever lurking cause shouted loudest.

A rigorous way to check for causation is to use a causal graph (see Pearl, 2009): weekly sets point at strength gain, but thirteen other variables point at both, so they have to be held still. I control for prior strength, modelled fitness and fatigue, intensity habits, workload ratio, mood, and flu, to assess whether volume is really a driver. But volume is really two levers: more sets within a session, and more sessions within a week. To separate them I put both in a joint model, each controlling for the other.

Figure 11 shows both overlaid. The sets slope (blue) goes negative; the days slope (aqua) stays steep at +0.16 points of the strength index per extra training day. Sets and days are highly correlated, so it takes this joint model to tell them apart: it is not how much I do when I show up, but how often I show up.

Plate XI
Two treatments, one graph. Blue dots are weekly sets; aqua dots are training days per week, both with the thirteen confounders (and each other) held constant. Hover to isolate a cloud. The sets slope goes negative; the days slope stays steep. Frequency wins.ols backdoor · frisch-waugh-lovell · joint model · 13 confounders
the maths: shutting the back door (with two treatments)

The contamination, first. Suppose I ignore all of this and fit the obvious regression, next-month gain against weekly sets, Y=a+bTY = a + b\,T. The slope bb measures how the two move together, but moving together is not causing. On the weeks I happen to train more I both accumulate more sets and gain faster, for reasons that owe nothing to the per-session volume: more exposure, more practice, more recovery days consumed. That shared cause leaks into bb and puffs it up, so per-session volume ends up taking credit that really belongs to frequency. This leakage is confounding, and it is exactly why a raw slope cannot be read as an effect.

The picture, and the doors. Draw the causes as arrows. I have two treatments now: TST_S (weekly hard sets) and TDT_D (training days per week), both pointing at the outcome YY (next-month gain). But they are correlated at r=0.94r = 0.94: weeks with more days almost always carry more sets. If I regress YY on TST_S alone, the slope soaks up the effect of TDT_D too, because the two are nearly collinear. A long list of confounders ZZ (prior strength, fitness, fatigue, intensity habits, workload ratio, mood, flu) each prop open a back door of their own.

The joint model. The fix is to put both treatments in the same regression, so each controls for the other:

Y  =  βSTS  +  βDTD  +  γZ  +  ε.Y \;=\; \beta_S\,T_S \;+\; \beta_D\,T_D \;+\; \gamma^{\top} Z \;+\; \varepsilon .

Now βS\beta_S answers “if I trained the same number of days but crammed in more sets per session, would I gain?” and βD\beta_D answers “if I did the same sets per session but showed up more days, would I gain?”. The confounders ZZ shut every other door; the two treatments shut each other’s.

Why the signs flip. When sets is fitted alone (without days), it gets credit for frequency, because weeks with more sets are almost always weeks with more days (r=0.94r = 0.94). The slope reads positive. In the joint model, days absorbs that credit and sets is left with only its own, within-session contribution, which turns out to be essentially zero (slightly negative, possibly from junk volume or accumulated fatigue). The separate-model result was never wrong; it was just answering a different, blurrier question.

Frisch-Waugh-Lovell, twice. Figure 11 overlays both treatments on one chart. For sets, I residualise TST_S and YY against ZZ plus TDT_D, stripping out everything the confounders and frequency explain, and plot what remains. For days, the mirror: residualise TDT_D and YY against ZZ plus TST_S. Each scatter’s slope equals its β\beta exactly, and because I standardise the x-residuals to unit variance, the two clouds share a common scale: the slope in the chart is the effect per standard deviation of each treatment.

Refuters. Each treatment gets its own stress tests. A placebo shuffles the treatment column: a genuine effect should collapse to zero. A random common cause bolts on a pure-noise confounder, which a real effect should shrug off. A subset refits on a random 85 percent of weeks. The days treatment clears all three; the sets treatment, already near zero, stays there.

Two caveats keep this honest:

  1. The effect is calculated from my own noisy life, not a controlled trial, so it is strong suggestion, not proof.
  2. While thirteen confounders shut most of the obvious doors, a lurking one I forgot could still be whispering.

If I wanted to prove it I would run the experiment: randomly assign each coming block to high or low frequency, hold everything else fixed, and read off the difference. That would be a real trial. I humour these ideas, but they stand in the way of gains.

There is a blunter way to see the same thing. Skip the causal machinery and just plot the dose of volume against the strength payoff: bin every week by how much I did, and read off the strength change that followed. Figure 12 settles the chapter’s question in two curves. More training days keep paying off, climbing until about four or five a week. But sets piled into a single session peak near fifteen (about five exercises for me) and then bend negative, the point past which more work in one sitting quietly costs me. Frequency is a dose I can keep raising; per-session volume is one I cannot.

Plate XII
Dose-response. Each dot groups weeks by how much I did, showing the average strength change that followed (±1 standard error); the line is a smoothed trend. Training more days a week keeps paying off, but piling sets into one session peaks near fifteen and then turns against me.strong export · quantile bins · LOWESS

My median is three sessions a week. I have run Push-Pull-Legs before, and I am running it again now, but my history is mostly a loose Upper-Lower-Upper where the second Lower rarely happens. The joint model says the fix is clear: get beyond three days. The extra session buys more than extra sets ever did, and by the time I am deep into a long workout I am tiring out anyway, so keeping per-session volume relatively lean and spreading the work across more days is the honest next move.

But back in 2022, the secret sauce was how hard you trained: using failure and dropsets.

chapter 06 · failure & drop sets

The Cult of the Last Rep

In 2022 my feed was one long sermon on intensity. Every set went to the death, because a rep in reserve was a rep half wasted. I chased drop sets for the burn, bought the lengthened partials, the whole liturgy. The gospel was that it was not how much you did but how hard, and I took it to heart. Four years of records let me test the faith.

The session view

The first thing the data reveals is how devout I was: I take at least one set to failure in seventy-nine percent of sessions. That near-universal habit wrecks the obvious test, because my rare no-failure days are deloads and walking-wounded sessions that were going to PR less anyway. So the honest question is not whether I train to failure but what share of each session I do, and how that proportion tracks two things: a record that day, and what is left in the tank afterwards.

Plate XIII
chance of a PR that sessionstrength change next session
Intensity as a proportion. The x-axis is what share of a session's sets I took to failure (or as drop sets); the bars are my chance of a PR that day, the line is the strength change in the very next session. Switch between the two techniques.strong export · 491 sessions · session-matched

A little failure helps: pushing from none to a tenth of my sets lifts my record chance from 48 to 66 percent. But grind more than a third to failure and the rate falls back to 54 percent, and the next-session strength change sinks from +0.07 to −0.09 points (roughly a kilo on the bench either way). Drop sets are kinder: the record rate climbs steadily and the next-session line stays flat, no fatigue penalty. The extra volume at reduced load appears to be genuinely free. Some of that failure bump is a mirage (I redline harder on days I already feel strong), but the saturation and the fatigue cost are real. The literature agrees: failure adds little over stopping a rep or two short, and it costs recovery (see Grgic et al., 2022), with newer work tracing the same curve for proximity to failure (Robinson et al., 2024) and for hypertrophy (Refalo et al., 2023).

The month view

A single session is only one stopwatch. Fatigue is short-lived, so it is worth zooming out. Take the trailing month’s failure proportion, lagged by one week (a lag just shifts the window back, so the current week cannot predict itself), and follow strength one to four weeks later.

Plate XIV
The same failure, read over a trailing month. Each week's failure proportion is averaged over the prior four weeks, lagged by one week; lines follow the average strength change one to four weeks out. The months that kept failure modest pull ahead; the heaviest-failure months stall.strong export · trailing 4-week window, lagged 1 wk · strength index

Lighter-failure stretches surge to +0.09 points (about a kilo on the bench) by week four; months of grinding a fifth or more of every set to failure bank almost nothing (+0.05 points, half that). The payoff comes from the submaximal work, not the redlining.

Not every muscle agrees

“Failure” does not mean the same thing to every muscle. Split each group’s weeks into its heavier- and lighter-failure halves and follow it a month out: back and shoulders shrug the redlining off, even edging ahead on heavier weeks, while arms and quads wilt. Triceps are the extreme, 0.17 points worse (a couple of kilos on a pressing lift) when I grind a big share to failure.

Plate XV
Does failure suit every muscle the same? Each muscle group's weeks are split into heavier- and lighter-failure halves; the bar is the difference in strength four weeks later. Right of zero, the heavier-failure weeks did better; left, they cost that muscle. Exploratory, and noisy at the single-muscle level.strong export · weekly · per muscle group

The pattern is suggestive but physiological: big compound movers bank the effort, small isolation muscles drown in fatigue. “Train to failure” was never one instruction but a dial to set differently for a heavy row than for a cable pushdown.

The prescription

The last rep is not holy. A little failure is useful, it’s how I grind through to a PR, but a lot of it is a cruel central nervous system tax with little return. Drop sets are the smarter tool: they sneak in extra volume at reduced load without the fatigue penalty, and the record rate keeps climbing with the dose. So the smart thing to do? A rep or two left in the tank on most sets, use drop sets more frequently, and save the death-grind for the rare day. Am I going to listen? No.

Techniques, in the end, are a rounding error next to the real disruptor. Not how I trained, but what happened to me: a bad knee, and a brutal season at work.

chapter 07 · counterfactuals

The Roads Not Taken

Two things happened to me that no amount of good programming could out-argue. In 2024, a brutal, months-long stretch of overtime and burnout, when there was simply nothing left after a day of building agents. And in late 2025, a meniscus, after which I quietly stopped training legs and kept hammering everything in a Johnny Bravo fashion. To ask what each cost me is to ask a counterfactual question: not what happened, but what would have, on the road I did not take.

Pricing a road not taken means building it, a plausible version of me who was never interrupted, and measuring the distance to the one who was. The catch is that my two shocks have different shapes, and different shapes need different scaffolding.

The 2024 crunch was pervasive. It hit everything at once, so there is no healthy corner of my training to borrow as a control, every lift sagged together. When the whole system moves, you have to become your own control: fit the trend from before the crunch and let it run on untouched, an interrupted time series (see Bernal et al., 2017), and read the toll off the gap between that projection and reality. We already watched this happen in Chapter 01.

The meniscus was the opposite shape: contained. Only my legs went down; my chest and back and arms carried on regardless. That is the textbook setting for a synthetic control (see Abadie & Gardeazabal, 2003), which builds the missing counterfactual out of the parts that were spared. So I fit my leg pressing against my healthy upper body over the years I have records for both, then let whatever relationship there is predict what it should have done through the injury and after. The dashed line below is that prediction. The solid line is the hard-hitting truth.

Plate XVI
my actual leg presscounterfactual (predicted from upper body)the deficit
A synthetic control for the meniscus. The dashed line is my leg-press strength as predicted from a composite of my (unaffected) upper-body lifts; the solid line is what it actually did. The shaded band is the deficit, and the red marker is the injury. Note where the gap actually opens.upper-body composite · a loose pre-injury fit, R² 0.16

And the chart refuses to tell the story I wanted it to. If the injury were the villain, the two lines would track together and split at the red marker. Spoiler: they don’t. They begin peeling apart in early 2024, well over a year before the knee gave out. By the eve of the injury my leg press already sat some 38 kilograms adrift of where my upper body put it; the meniscus, for all the blame I heaped on it, added under ten more. Chapter 05 already confessed as much: I had been quietly abandoning my legs for a very long time, and the knee merely handed me a respectable alibi to finish the job. This was a reality check I didn’t expect.

the verdict

The meniscus got the blame. The data says my neglect had already done most of the damage, and the knee was just a scapegoat.

the maths: building a road not taken

Interrupted time series is the tool from Chapter 01, and I will not re-derive it: a straight line in time with a single hinge at the shock, whose counterfactual is just the pre-shock trend β0+β1t\beta_0 + \beta_1 t projected past the event. It suits a pervasive shock, because it compares the system only to its own past, with no untouched control required. The meniscus needs the opposite tool.

Synthetic control borrows units that the shock spared. Take a donor pool of my upper-body movements {dj(t)}\{d_j(t)\} and fit weights (here a ridge regression) so the weighted blend tracks the target over the pre-period,

y^(t)  =  jwjdj(t),w=argminwt<t0(ytjwjdj(t))2+αw2.\hat y(t) \;=\; \sum_j w_j\,d_j(t), \qquad w = \arg\min_w \sum_{t<t_0}\Big(y_t - \textstyle\sum_j w_j d_j(t)\Big)^2 + \alpha\lVert w\rVert^2 .

After t0t_0, y^(t)\hat y(t) is the counterfactual and yty^(t)y_t - \hat y(t) the effect. It leans on three assumptions: the donors were genuinely unaffected, the target tracked them before the shock, and you keep observing the target after. My leg pressing quietly broke the second (a feeble pre-period R2R^2 of 0.160.16, having drifted off on its own for a year) and then the third (I stopped doing it), which is exactly why the method can only shrug.

Both shocks were real, but only one was external. The crunch was something that happened to me; the leg deficit was something I did to myself, one skipped session at a time, long before the knee gave me permission to stop entirely. The counterfactual machinery can price an interruption, but it cannot manufacture the data you never bothered to collect. The lesson is less about causal inference and more about honesty: if I had trained legs consistently, the injury would have had something to interrupt.

And yet, even in the long unbroken stretches when nothing interrupted me at all, I still could not reliably feel myself getting stronger. The last suspect is the strangest: my own fatigue.

chapter 08 · fitness & fatigue

You Are Fitter Than You Feel

There is a familiar cruelty to the day after a brutal session. The bar that flew up on Monday feels welded to the floor on Tuesday, and the obvious reading is that I have gone backwards. I have not. The truth is kinder: I am fitter than I was on Monday and, at the same time, more tired, and what my hands report on the bar is not my fitness but the difference between the two. This is why progress is so hard to feel from inside a training block. The thing you are building and the thing that is masking it are the same act of training, running on two different clocks.

Every hard set does two things at once. It builds a slow, durable fitness that accumulates and fades over weeks, and it drops a fast fatigue that piles up in a day and clears in a few. Strength on any given morning is the slow thing minus the fast thing. Rest for a week and fatigue evaporates while fitness barely moves, so you walk in feeling exactly as strong as you have quietly become. Lifters call this a taper; the accidental version is a “deload PR”.

the maths: two memories of the same load

The idea is old and elegant, from a 1970s systems model of an athlete (see Banister et al., 1975)(Calvert et al., 1976). Treat each day’s training as an impulse of load wtw_t, and let the body keep two exponentially-fading memories of it. Fitness is the long memory, fatigue the short one:

Fitnesst=stwse(ts)/τ1,Fatiguet=stwse(ts)/τ2,\text{Fitness}_t = \sum_{s \le t} w_s\, e^{-(t-s)/\tau_1}, \qquad \text{Fatigue}_t = \sum_{s \le t} w_s\, e^{-(t-s)/\tau_2},

with time constants τ1τ2\tau_1 \gg \tau_2 (here 42 days versus 7). Each is just a load impulse convolved with a decaying exponential, computed in one causal pass: carry a running total, shrink it by e1/τe^{-1/\tau} each day, add today’s load. Modelled strength is the difference of the two, weighted by how much each one matters:

form^t=p0+k1Fitnesstk2Fatiguet.\widehat{\text{form}}_t = p_0 + k_1\,\text{Fitness}_t - k_2\,\text{Fatigue}_t.

One point that is easy to miss: Fitnesst\text{Fitness}_t and Fatiguet\text{Fatigue}_t are pure numbers, not kilograms. The load wtw_t is normalised so an average session equals 1 and a rest day equals 0, so each memory is just a running count of recent training, one on a six-week clock, one on a one-week clock. All the physical meaning is injected at the very end, by the coefficients: k1k_1 and k2k_2 convert those unitless counts into strength (σ\sigma). Because the counts have no anchored scale, you can shrink them and inflate k1,k2k_1, k_2 to match, which is a first hint at the identifiability trouble below.

Because fitness and fatigue are fixed once the two time constants are chosen, the only free knobs left, p0,k1,k2p_0, k_1, k_2, enter linearly, so I fit them to my real strength index by ordinary least squares. The two time constants themselves I do not try to learn, for a reason that turns out to matter (below).

Fit that model to four years of my sessions and you can finally watch the two clocks run. Figure 17 draws them on the same strength scale I have used throughout: the blue line is what my strength would be on fitness alone, the ink line is the net form the model actually predicts, and the red band between them is the fatigue tax, thickening for a few days after every heavy stretch and then melting away.

Plate XVII
fitness (slow memory, τ = 42 d)the fatigue tax (fast memory, τ = 7 d)net form = fitness − fatigueobserved strength
The fitted Banister model over four years. Blue is strength from fitness alone (the slow memory); ink is the net form once fatigue is subtracted; the red band is that fatigue tax. Faint dots are the observed weekly strength index the model was fit to. Hover for the decomposition on any date.491 sessions · impulse-response fit by least squares · τ = 42 / 7 d

One thing in that picture is worth being honest about: the red band is invisible. When I fit the model, it settles on two dials: one for how much lasting fitness each session lays down, and one for how much short-term fatigue it drops. Mine come out lopsided. Every hard session deposits more than three times as much durable strength as it costs me in tiredness, and because that tiredness drains away within a week while the fitness lingers for six, almost none of it is left to show up as a fatigue tax on the bar. At my fittest the fitness term is worth roughly thirty kilograms of bench and fifty of deadlift over a fully detrained version of me. The fatigue term, even on my most buried day, subtracts barely two kilograms of bench, four of deadlift. The model captures only about a third of the story, and what it does capture is almost all durable fitness rather than a fatigue mask waiting to be lifted.

Either I do not accumulate fatigue the way the textbook athlete does, or I am permanently fatigued and have simply adapted to it. I have always had high endurance and a tolerance for pain and physical discomfort, and I wonder if this is itself a neurological adaptation, the kind of thing that four years of grinding through sessions has quietly trained into me. Deloading, for me, according to this model, does nothing.

The Banister model was built for athletes who peak and taper, who accumulate real fatigue debt and then cash it in before competition. That is not me. My training is steady, my fatigue is negligible, and the model’s explanatory power is modest. But what it does reveal is reassuring: nearly everything I have built over four years is durable. The slow memory holds. The fitness is real, and it is not hiding behind a wall of tiredness waiting to be unmasked. It is simply there, compounding quietly, session after session.

If the body hides its state behind fatigue, maybe the words I wrote about it can see through. What, in the end, predicts a good day?

chapter 09 · what predicts a pr

The Reckoning

Every chapter so far has pulled back one veil. We filtered out the noise, compared 1RM formulas, looked into volume, failure, fitness and fatigue. What if we looked at it top-down, without prejudice, and let a model decide for itself what earns a record?

What actually predicts the day I set a personal record?

So I handed a machine everything I could know going into a session, the whole recent history behind it: rolling averages of volume, mood, flu levels, and temperature, each computed over the last week and the last month. Then I asked it a simple question at widening horizons, from the next session out to six weeks: are more records coming than usual? The model is a machine-learning method called XGBoost (Chen & Guestrin, 2016); to read its mind I use SHAP, which tells me, for every single session, which way each feature pushed.

the maths: Shapley values, or how to split the credit fairly

Suppose a session sets a record and you want to apportion the credit: how much was the volume, how much the rest, how much sheer freshness? The honest answer has to account for the fact that features act together, not in isolation. A 1953 result from cooperative game theory, the Shapley value (the idea, adapted to ML, is Lundberg & Lee, 2017), gives the unique fair split: a feature’s credit is its marginal contribution to the prediction, averaged over every possible order in which the features could be added to the model. Add volume first and it explains a lot; add it after rest and freshness and it explains a little less. Average that over all orderings and you get one number per feature, per prediction.

Computed naively this is exponential in the number of features, hopeless. TreeSHAP (see Lundberg et al., 2020) exploits the structure of a decision tree to compute the exact values in polynomial time, which is why it is tractable here. The payoff is two properties permutation importance cannot offer. First, it is signed: a positive SHAP value pushed this prediction toward a record, a negative one pushed it away. Second, it is additive, and exactly so: the model’s base rate, plus every feature’s SHAP value for a given session, sums to the model’s prediction for that session, in log-odds. That additivity is not an approximation; it is the property that makes the waterfall below reconcile to the last decimal.

the full feature list, and what I left out

Everything the model was allowed to see, grouped. Every feature is a trailing average over the last 7 or 28 days, lagged by one session, so the model sees only the state going into a session, never its own outcome. No same-session features at all: the question is always “given how I have been training recently, what happens next?”

Trailing training state (25 features): weekly and monthly volume; the acute:chronic workload ratio; sessions per week and per month; average sets, exercises, reps, and session length over the last week and month; trailing-week and trailing-month failure and drop-set proportions; modelled fitness and fatigue from chapter eight; days since my last session; last session’s strength; and how many records the previous month held.

Trailing mood (6 features): average RoBERTa sentiment over the last week and month; and trailing rates of notes mentioning hard effort or frustration.

Trailing outside world (5 features): influenza positivity and temperature over the last week and month, and the time of day I train.

Deliberately excluded: rainfall, day of week, month, and my near-constant bodyweight. None has a mechanism to move a lift, and handed to a few hundred sessions each only offers a seasonal coincidence to overfit. What the outside world genuinely touches, whether I train at all, is measured separately, below.

The honest answer: barely, and only for tomorrow

Before I trust a single feature, I have to ask whether any of this is predictable at all, and I have to ask it honestly. That takes three disciplines. First, a target that is not secretly a calendar. Asking “did I beat my all-time median PR count” sounds fair, but because my PR rate has drifted over four years, it really just asks “is it 2026?”. So instead I ask “did I set more records than my own recent baseline, the last two months”. Second, a validation that respects time: train only on the past, test only on the future, and never let the model peek at a session that shares an era with its training data. And third, a noise floor. I scramble the log so each session keeps its real training history but is handed some other session’s outcome, then run the model again. Whatever it scores on that nonsense is pure luck, and any real result has to beat it.

Scored that way, the verdict is sobering. I grade each model with a measure called AUC, where 0.5 is a coin flip and 1.0 a crystal ball. The next session carries a faint but real signal, 0.55, just clear of the 0.49 luck line. Three days out it is already fading, and from a week onward the model just bobs around inside the luck band, a month ahead included. The grey band in Figure 18 is that luck floor, and every horizon past the first lives inside it.

Plate XVIII
Predictability at each horizon, validated the honest way: train on the past, test on the future, and measure against a luck floor (the grey band) — the same model run on scrambled outcomes. Only the next session clears the noise, and only barely. Past a week, every horizon bobs inside the band, indistinguishable from luck.XGBoost · forward-chaining CV · vs scrambled-label floor · n=454

I sat with that for a while, because I wanted it to say more. Four years of diligent logging, every set and every mood, and the machine’s honest answer is that tomorrow is mostly a mystery and next month is a closed book. Progress, it turns out, is no easier to forecast than it was to feel. But there is that one stubborn flicker at the very next session, so let me follow it.

What faintly moves the next session

So there is a whisper, one session out. What is it made of? Start with the levers I actually pull (Figure 19). Honestly, not much: hard, high-volume grinding tilts the odds a little, failure and drop sets both nudging toward a record, alongside the durable fitness I have banked. But the whole controllable picture clears the noise floor only barely, 0.55 against a 0.49 luck line. The things I decide move the needle, just faintly.

Two of those tilts glance off earlier chapters. Failure pointing toward a PR sits awkwardly against chapter six’s verdict that it is mostly a tax, but the two ask different questions: that chapter measured the causal dose, where a little helps and a lot costs, while this model only sees that I fail sets most in my hard, engaged stretches, the same ones that throw off records. It reads failure as a marker of the grind, not a cause. Fitness, by contrast, points exactly where chapter eight said it would: the durable base is what carries a record, and my fatigue is so slight that a fresh day has nothing to add.

None of it is loud. Now switch on the things I can’t control and the model climbs to 0.59, but look at what carries it. Not the weather, momentum. The single biggest tell in the whole model is how many records I have set in the last month, and a hot streak is not a lever, it is an outcome. Records cluster, so the surest forecast of my next one is just that I have been setting them lately. Temperature muscles in behind it, and I really do set fewer records in the heat, as the next section shows, but that tall bar flatters it: the honest accuracy barely moved, because a gentle seasonal tilt adds little you can bet on a session out. What little I can foresee about tomorrow’s record leans mostly on things I do not decide.

Plate XIX
dot colour = feature value: low → high↑ toward a PR↓ away = each feature’s dominant push
What nudges the very next session. Each dot is one session's SHAP push toward (right) or away from (left) a next-session PR; colour is the feature's value, dim (low) to bright (high). Toggle between the levers I control and the fuller picture that adds what I can't: momentum, flu and heat.XGBoost · TreeSHAP · forward-chaining · n=454

Because SHAP is additive, I can watch the full model work on a single day. Figure 20 takes a real record from March 2024 and pulls its call apart. It opens at an average session’s 59 percent and climbs to 75 percent, but look at how. The named factors nearly cancel: a strong last session, a week of hard failed sets, and a decent mood push up; a late hour and a cold streak push back. What actually does the lifting is everything else, the long tail of small features that, summed, outweighs every headline bar combined. Even on a confident day the model is not pointing at a reason; it is adding up a hundred faint rumours, which is what a weak signal looks like from the inside.

Plate XX
One real next-session PR (19 March 2024), decomposed by the full model. Each factor pushes toward (green) or away from (red) a record; the pushes sum exactly to the model's call. No single factor carries it: the named bars nearly cancel, and the long tail of small pushes ('everything else') outweighs any one.XGBoost · TreeSHAP · one session, 2024-03-19
why trailing features, not same-session?

The temptation is to ask “does this session’s own volume predict its own PR?”, and the answer is yes, trivially, but only because a heavy, high-volume session is largely what a PR is. That model describes a record, it does not forecast one. Using only trailing averages, lagged by one session, strips out that circularity entirely. The model sees how I have been training, recovering, and feeling, never the session it is trying to predict. It is also what keeps the honest AUC so low: strip out the circular tell, and what is left is a genuinely hard forecasting problem.

What I can’t: flu and heat

Those last two, flu and heat, deserve a closer look, pulled as they are from real Australian influenza surveillance (WHO FluNet, 2026) and real Melbourne weather (Open-Meteo, 2023). They leave their fingerprints in opposite places: flu on the door, heat on the bar.

Plate XXI
sessions that monthflu positivity
Monthly training frequency against real flu positivity and Melbourne temperature. Flu thins my attendance a little; the heat leaves it largely alone. What each does to the record rate itself is the story in the text. Toggle the overlay.Open-Meteo (weather) · WHO FluNet (influenza) · monthly

Flu works on attendance. When it is circulating I show up a little less, but cross the threshold into the gym and it vanishes: my record rate in high-flu weeks is 62.1 percent, and in low-flu weeks, 62.1 percent, identical to the decimal. Flu decides, faintly, whether the session happens, never whether it succeeds.

Heat is the mirror image. It barely changes whether I show up, but it tracks how I lift. I set records far more often in cool weeks than in the heat, 70.6 percent against 53.5 percent, and the gap holds in every year of the log, not just my beginner surge. Whether it is real physiology or just that I grind harder when I am not melting, Melbourne’s summers are quietly my leanest season for records. That seasonal fingerprint is why the model reached for temperature a moment ago, though the tilt is gentle enough that betting on it a session ahead barely helps.

What I feel: the diary

I had assumed that how I felt, captured in three hundred and seventy-one scribbled notes and scored by a sentiment model (RoBERTa (Barbieri et al., 2020)), would be pure noise. It is not, and it cuts two ways at once. Over months, brighter sentiment tracks a stronger me: my notes’ mood correlates +0.15 with the strength index, the contented entries landing in my strongest stretches. Yet inside the PR model the sign flips. A sour, frustrated recent month, the very thing that drags that sentiment down, is what faintly precedes a record, because frustration is what a hard grind feels like from the inside. Feeling good is where the work has got me; feeling frustrated is often what the next record is made of. Both pulls are faint, and the model leans on the second only lightly.

Plate XXII
The diary. The lifts I write about most sourly (left) and most fondly (right). My notes' sentiment tracks my strength +0.15 over the long haul, yet in the model a sour, frustrated month slightly precedes a record.strong export · 371 notes · RoBERTa (TweetEval)

The emotions themselves are almost embarrassingly transparent. The sentiment model, told nothing about lifting, works out that I am at my sourest about lateral raises and leg extensions, and my fondest about heavy rows and incline pressing. It reads my mood better than I would admit, and my mood, it turns out, carries a faint rumour of the next record.

When: the Monday effect

One last lever, and unlike flu or fatigue it is entirely mine to schedule: when I train. My records are not spread evenly. Monday is my strongest day by a clear margin, 1.41 PRs per session against 0.99 on a tired Thursday, part real recovery cashed in from the weekend, part the fact that I open the week with the lifts I care most about. And because this is a raw count, not a model, it survives everything above. If a day reliably buys a better shot at a record, the attempts belong there.

Plate XXIII
A 24-hour training clock: each ring is a weekday (Monday inside, Sunday out), each wedge an hour. Toggle between when I train and when I set records; both burn brightest in the Monday-to-Thursday, 11am-to-1pm corner.strong export · 491 sessions · hour × weekday
0.55
next session, what I control
a faint signal, just above the 0.49 luck floor
coin flip
a week out and beyond
every horizon bobs inside the luck band
1.41
Monday PRs / session
vs 0.99 on a tired Thursday

So the reckoning is not the one I went looking for. I could not build a model that forecasts my records more than a single session ahead, and the one honest signal, that a record tends to follow a hot streak, is a whisper of information. The outside world, too, only reaches me in a soft tone: the flu gently turning me away, the hot months grinding me down. My own diary, even, is a poor predictor. What predicts a PR? Almost nothing, in advance. But the earlier chapters were never really about prediction; they were about the odds. Frequency beats intensity, a rep left in the tank beats training to failure, the legs I keep skipping are the clearest gain I own, and even here Monday is quietly my best day. None of it tells me when a record will land. All of it makes one likelier. So you show up more often than you feel like, leave a rep in the tank, train the legs, do the volume, and go home to quietly progress. You only find it afterwards, and that is precisely why we write this all down.

the whole essay, in one line

Four years of training, fed to a machine, and it still could not tell me whether tomorrow holds a record. Progress is not only hard to feel. It is hard to foresee. You write it down not to predict it, but to be able to believe it later.

coda

So, Am I Stronger?

The question the whole essay opened with was a simple one, and after twenty-five figures it finally has a plain answer. Yes. The strength index sits at its all-time high. The comeback is, by the interrupted time series of Chapter 01, the steepest sustained climb anywhere in four years. And stitched across every swapped variant, in Chapter 04, every major movement is now above where it has ever been. The log cannot tell me whether I look any different in the mirror, and I promised early on it never could. But on the one question it can honestly answer, the load on the bar, the verdict is not close.

What did four years actually build? Figure 24 lays it over a body. Colour each muscle first by how much its strength grew since the first week I logged it, and almost everything glows: the presses and pulls doubled, and the legs, on the rare stretches I trained them, grew faster than anything else I own. Then flip the toggle to training volume, the hard sets I actually spent, and the legs go dark. That single switch is the whole confession. My quads and calves posted the biggest gains in the record, flattered though they are by how little they could once do, and they sit at the very bottom of everything I trained.

Plate XXIV
Four years, mapped onto the body. Toggle between growth since baseline (what improved) and training volume (what I actually trained). The muscles that grew the most are, damningly, among the ones I trained the least. Hover any muscle.strong export · stitched e1rm growth · effective hard sets

So I will spare myself the pretense that any of this was a mystery, and just write down what the data has been telling me the whole way through:

  • Train legs. It is the clearest fix in the entire dataset. They respond when asked; I simply stop asking, and a meniscus was only ever the alibi (Chapter 07).
  • Show up more often, not harder. The extra session buys more than the extra set ever did, and the sets I cram past fifteen in one sitting quietly cost me. Get past three days a week (Chapter 05).
  • Leave a rep in the tank. Failure is a tool for a good day, not a tax to pay on every set; drop sets sneak in the same volume without the bill (Chapter 06).
  • Bank the records on Monday morning, fresh off the weekend, when both the calendar and the clock are on my side (Chapter 09).
Yes
am I stronger?
strength index at an all-time high
steepest
the 2026 climb
fastest sustained rise in four years
the legs
what I owe the work
grew the most, trained the least

None of it was hidden. It was only hard to see: buried under swapped exercises and bad memory, masked by a fatigue I turned out barely to carry, blamed on a knee that was mostly innocent. I could not feel the progress from inside the training, and I could not remember it honestly from outside. The only reason I can hand any of it back now, as a verdict instead of a vibe, is that four years ago I started writing it all down.

the honest reckoning

I am stronger than I have ever been. I could not see it, could not feel it, and would not have remembered it. The log saw it for me.

appendix A · auditing the exchange rates

How Trustworthy Are the Conversions?

A currency is only as trustworthy as the trades that set its rates. My stitching learns a rate between two variants solely from the weeks I did both; where two variants never met in the log, the model has nothing to price from and quietly assumes they are equal, a factor of exactly 1.00. Across these five movements, 17 of the 61 rates are these un-measured defaults. When the two variants really are near-identical, a machine pulldown and a cable pulldown, the assumption costs nothing. When they are nothing alike it is a silent lie: a weighted pull-up, which hauls my whole bodyweight over the bar, gets booked as equal to a lat pulldown, and a barbell skullcrusher as equal to a cable pushdown.

To see how far the money has drifted from reality, Figure 25 sets each variant’s learned rate beside a real-world reference, drawn from the population standards that sites like strengthlevel and the lifting subreddits converge on. Pick a movement; its anchor is the big compound of the group, pinned at 1.00.

Plate XXV
Each variant's learned exchange rate (how much weight it moves relative to the movement's anchor lift) set against a published population reference. Rates tagged assumed were never measured from an overlap and simply default to the anchor. Pick a movement to switch groups.learned factors vs strengthlevel / community standards

The pattern is not subtle. Where I trained two variants together often, the rate is trustworthy: my dumbbell curl prices at 0.47 of a barbell curl against a population 0.50, my skullcrusher at 0.79 of my pushdown. Where I did not, it breaks. Seventeen of the sixty-one rates never share a week with the rest of their movement, so the model cannot price them and I backfill from the population instead. Even a measured rate carries asterisks: it is assumed constant, though my skullcrusher climbed from 0.45 to nearly level with my pushdown between 2022 and 2026; an anchor has to be a lift I still do; and a logged number is only load when the weight is the work, which cable stacks and bodyweight dips quietly violate. I did not detrain. I simply never gave the model the overlaps it needed, which is the sharpest lesson in an essay about record-keeping: a log can only stitch what you thought, at the time, to measure side by side.

A few of the measured rates are interesting precisely because they are mine and not the crowd’s. My preacher curl comes in at 0.77 of my barbell curl where the average lifter sits near 0.98, so the preacher, which strips out every scrap of body english and holds the biceps at a long, disadvantaged length, punishes me harder than most. I would like to blame short biceps on a short frame, and the reverse curl, feeble at 0.69, keeps the theme going. My neutral and mag-grip pulldowns come in a little under a plain pronated one, 0.84 and 0.93, which points at grip and forearm rather than lats. I hypothesise that the currency has quietly become a biomechanics probe: wherever my number drifts from the population’s, it is likely fingering a leverage, a limb, or a joint angle that suits me a little worse than average.

appendix B · from export to dataset

Turning a Log Into Data

Every figure in this essay stands on one unglamorous act: turning a phone app’s export into something a model can read. The raw material is a single CSV that Strong hands you on request, about ten thousand rows, one per set, four years deep. What sits between that file and the first chart is the part nobody photographs, so here it is.

Parsing the log

Each row is a set: a date, an exercise name as I typed it, a weight, a rep count, and sometimes a rest timer or a note. The first job is to read it faithfully. Strong encodes warm-ups, drop sets and failure sets in small tags, and it logs rest timers as their own zero-weight rows, so a naive read double-counts them as lifts. The parser splits genuine working sets from rest timers, repairs the occasional hour-long “duration” left running when I forgot to stop it, and rolls the sets up into sessions. Ten thousand rows collapse to roughly eight thousand working sets across 491 sessions.

The naming problem

The real mess is the exercise names. Over four years and four gyms I have typed the same movement a dozen ways: Bench Press (Dumbbell), DB Bench, Dumbbell Bench Press, plus outright typos. Left alone, each spelling is a separate exercise with its own lonely history, which is fatal for any analysis of progress. Collapsing them is a three-step funnel. A rule-based pass parses the structured part of each name into a (base, equipment, grip) key. A fuzzy-matching pass (rapidfuzz, similarity ≥ 94) sweeps up the near-duplicate spellings. And where spelling alone cannot tell that two names mean the same lift, an embedding pass does: each name is turned into a vector by Gemini Embedding 2, and names whose vectors sit close together are matched. The 148 raw names become 132 canonical exercises, with the meaningful variants (dumbbell versus Smith, incline versus flat) deliberately kept apart, because Chapter 04 needs them.

why embeddings for exercise names

Fuzzy string matching sees letters, not meaning. It correctly fuses Db Bench and Dumbbell Bench Press, but it has no idea that Skullcrusher and Lying Triceps Extension are the same movement, because the strings share almost no characters. A text embedding does know, because the two phrases are used the same way across the text it was trained on, so their vectors land near each other. Running the match in vector space rather than character space is what catches the synonyms that no edit-distance rule ever could, and it is what lets fifteen ways of writing “chest press” collapse back into one lift with a single continuous history.

Attaching muscles

A set only knows its exercise, not the muscles it trains, and every per-muscle claim in the essay (the volume landmarks of Chapter 05, the body map of the coda) needs that link. Each canonical exercise is matched, again by embedding nearest-neighbour, to the open-source free-exercise-db of 873 catalogued movements, which carries the primary and secondary muscles, the force direction, and whether the lift is compound or isolation. Volume is then attributed with a primary muscle counted at full weight and each secondary at half, so a bench press credits chest fully and shoulders and triceps in part.

Keeping it honest

Two habits run through the whole pipeline. The first is that nothing physical is invented: weekly series are all anchored to the same Monday so they align, bodyweight lifts are kept out of the load-based stitching because their logged number is not the work done, and the causal claims are put through placebo and refutation tests whose results are reported whether they pass or not. The second is that the outside world is real, not simulated: the percentile crowds come from OpenPowerlifting, the weather from Open-Meteo, and the flu season from the WHO’s FluNet, each joined onto the days it belongs to. The whole chain, from the raw CSV to every JSON that feeds these charts, regenerates from one command, so every number here is reproducible rather than remembered.

appendix C · estimating a one-rep max

Which Formula to Trust

Almost everything in Chapter 03 and beyond leans on a single move: turning a set of, say, 60 kg for 8 reps into an estimated one-rep max. A one-rep-max formula does exactly that, and there are many of them, most dating to the 1980s and 1990s. No two agree — each was fitted to its own author’s athletes or a modest sample — so each bakes in a slightly different guess about the same curve. They coincide at one rep, where they must all return the weight on the bar, and fan apart as the reps climb, because extrapolating from a set of twelve up to a hypothetical single is genuinely uncertain (that fanning is Figure 5).

With ww the weight and rr the reps, the seven I computed are

Epley=w(1+r/30)O’Conner=w(1+0.025r)Brzycki=w3637rLombardi=wr0.10McGlothin=100w101.32.671rMayhew=100w52.2+41.9e0.055rWathan=100w48.8+53.8e0.075r.\begin{aligned} \text{Epley} &= w\,(1 + r/30) & \text{O'Conner} &= w\,(1 + 0.025\,r) \\ \text{Brzycki} &= w\,\tfrac{36}{37 - r} & \text{Lombardi} &= w\,r^{0.10} \\ \text{McGlothin} &= \tfrac{100\,w}{101.3 - 2.671\,r} & \text{Mayhew} &= \tfrac{100\,w}{52.2 + 41.9\,e^{-0.055 r}} \\ \text{Wathan} &= \tfrac{100\,w}{48.8 + 53.8\,e^{-0.075 r}}. && \end{aligned}

They are just different guesses at the same curve, some linear in reps, some hyperbolic, some exponential, which is why they agree near r=1r = 1 and part ways as rr grows. In order, these come from Epley, 1985, Brzycki, 1993, Lombardi, 1989, O'Conner et al., 1989, Landers, 1985 (the McGlothin/Landers form), Mayhew et al., 1992, and Wathan, 1994; (LeSuer et al., 1997) is the classic head-to-head validation of several against measured maxes.

To decide which one suited me, I evaluated them empirically. Across the lifts where I did occasionally grind out a heavy single or double, I can compare each formula’s estimate, computed from my higher-rep sets, against that demonstrated near-max. Admittedly, reading a one-rep max off a fatiguing multi-rep set muddles fatigue with true strength, which is why every formula overestimates to some degree; but absent a habit of monthly true-max tests, this is the best signal on offer, and it is the relative ranking that matters.

Ranked by average miss, O’Conner and Lombardi win (about 10.6 and 10.9 kg off), while the popular Epley and the exponential Wathan sit at the back, overestimating by the most. That is why those two are the estimators behind every kilogram in this essay.

coda · the sources

References

Strength & hypertrophy science

the dose–response, proximity-to-failure, and volume-landmark literature the training chapters lean on

  • Androulakis-Korakakis, P., Wolf, M., Coleman, M., Burke, R., Piñero, A., Nippard, J., & Schoenfeld, B. J. (2024). Optimizing resistance training technique to maximize muscle hypertrophy: a narrative review. Journal of Functional Morphology and Kinesiology, 9(1), 9. https://doi.org/10.3390/jfmk9010009
  • Currier, B. S., D’Souza, A. C., Fiatarone Singh, M. A., Lowisz, C. V., Rawson, E. S., Schoenfeld, B. J., Smith-Ryan, A. E., Steen, J. P., Thomas, G. A., Triplett, N. T., Washington, T. A., Werner, T. J., & Phillips, S. M. (2026). American College of Sports Medicine position stand: resistance training prescription for muscle function, hypertrophy, and physical performance in healthy adults: an overview of reviews. Medicine & Science in Sports & Exercise, 58(4), 851–872. https://doi.org/10.1249/MSS.0000000000003897
  • Grgic, J., Schoenfeld, B. J., Orazem, J., & Sabol, F. (2022). Effects of resistance training performed to repetition failure or non-failure on muscular strength and hypertrophy: a systematic review and meta-analysis. Journal of Sport and Health Science, 11(2), 202–211. https://doi.org/10.1016/j.jshs.2021.01.007
  • Israetel, M., Hoffmann, J., & Smith, C. W. (2021). Scientific principles of hypertrophy training. Renaissance Periodization, introduces the MEV / MAV / MRV weekly-volume landmarks. https://rpstrength.com/
  • Pelland, J. C., Remmert, J. F., Robinson, Z. P., Hinson, S. R., & Zourdos, M. C. (2025). The resistance training dose response: meta-regressions exploring the effects of weekly volume and frequency on muscle hypertrophy and strength gains. Sports Medicine. https://doi.org/10.1007/s40279-025-02344-w
  • Refalo, M. C., Helms, E. R., Trexler, E. T., Hamilton, D. L., & Fyfe, J. J. (2023). Influence of resistance training proximity-to-failure on skeletal muscle hypertrophy: a systematic review with meta-analysis. Sports Medicine, 53, 649–665. https://doi.org/10.1007/s40279-022-01784-y
  • Robinson, Z. P., Pelland, J. C., Remmert, J. F., Refalo, M. C., Jukic, I., Steele, J., & Zourdos, M. C. (2024). Exploring the dose–response relationship between estimated resistance training proximity to failure, strength gain, and muscle hypertrophy: a series of meta-regressions. Sports Medicine, 54(9), 2209–2231. https://doi.org/10.1007/s40279-024-02069-2
  • Schoenfeld, B. J., Ogborn, D., & Krieger, J. W. (2017). Dose-response relationship between weekly resistance training volume and increases in muscle mass: a systematic review and meta-analysis. Journal of Sports Sciences, 35(11), 1073–1082. https://doi.org/10.1080/02640414.2016.1210197
  • Wolf, M., Androulakis-Korakakis, P., Fisher, J., Nippard, J., & Steele, J. (2025). Lengthened partial repetitions elicit similar muscular adaptations as full range of motion repetitions during resistance training in trained individuals. PeerJ, 13, e18904. https://doi.org/10.7717/peerj.18904

Training-load & fitness–fatigue models

the systems-model lineage behind the impulse–response fits

  • Banister, E. W., Calvert, T. W., Savage, M. V., & Bach, T. (1975). A systems model of training for athletic performance. Australian Journal of Sports Medicine, 7, 57–61.
  • Calvert, T. W., Banister, E. W., Savage, M. V., & Bach, T. (1976). A systems model of the effects of training on physical performance. IEEE Transactions on Systems, Man, and Cybernetics, SMC-6(2), 94–102. https://doi.org/10.1109/TSMC.1976.5409179

Statistics & machine learning

the methods every figure was built from — filtering, change-points, hidden states, gradient boosting, and the causal toolkit

  • Abadie, A., & Gardeazabal, J. (2003). The economic costs of conflict: a case study of the Basque Country. American Economic Review, 93(1), 113–132. https://doi.org/10.1257/000282803321455188
  • Barbieri, F., Camacho-Collados, J., Espinosa-Anke, L., & Neves, L. (2020). TweetEval: unified benchmark and comparative evaluation for tweet classification. Findings of EMNLP 2020, 1644–1650. https://doi.org/10.18653/v1/2020.findings-emnlp.148
  • Bernal, J. L., Cummins, S., & Gasparrini, A. (2017). Interrupted time series regression for the evaluation of public health interventions: a tutorial. International Journal of Epidemiology, 46(1), 348–355. https://doi.org/10.1093/ije/dyw098
  • Chen, T., & Guestrin, C. (2016). XGBoost: a scalable tree boosting system. Proceedings of the 22nd ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 785–794. https://doi.org/10.1145/2939672.2939785
  • Kalman, R. E. (1960). A new approach to linear filtering and prediction problems. Journal of Basic Engineering, 82(1), 35–45. https://doi.org/10.1115/1.3662552
  • Killick, R., Fearnhead, P., & Eckley, I. A. (2012). Optimal detection of changepoints with a linear computational cost. Journal of the American Statistical Association, 107(500), 1590–1598. https://doi.org/10.1080/01621459.2012.737745
  • Lundberg, S. M., & Lee, S.-I. (2017). A unified approach to interpreting model predictions. Advances in Neural Information Processing Systems, 30, 4765–4774. https://papers.nips.cc/paper/7062-a-unified-approach-to-interpreting-model-predictions
  • Lundberg, S. M., Erion, G., Chen, H., DeGrave, A., Prutkin, J. M., Nair, B., Katz, R., Himmelfarb, J., Bansal, N., & Lee, S.-I. (2020). From local explanations to global understanding with explainable AI for trees. Nature Machine Intelligence, 2(1), 56–67. https://doi.org/10.1038/s42256-019-0138-9
  • Pearl, J. (2009). Causality: models, reasoning, and inference (2nd ed.). Cambridge University Press.
  • Rabiner, L. R. (1989). A tutorial on hidden Markov models and selected applications in speech recognition. Proceedings of the IEEE, 77(2), 257–286. https://doi.org/10.1109/5.18626

1RM estimation & benchmarking

the seven prediction equations averaged into every estimated max, plus the strength-standard references

  • Brzycki, M. (1993). Strength testing: predicting a one-rep max from reps-to-fatigue. Journal of Physical Education, Recreation & Dance, 64(1), 88–90.
  • Epley, B. (1985). Poundage chart. Boyd Epley Workout. Body Enterprises.
  • Landers, J. (1985). Maximums based on reps. National Strength and Conditioning Association Journal, 6(6), 60–61.
  • LeSuer, D. A., McCormick, J. H., Mayhew, J. L., Wasserstein, R. L., & Arnold, M. D. (1997). The accuracy of prediction equations for estimating 1-RM performance in the bench press, squat, and deadlift. Journal of Strength and Conditioning Research, 11(4), 211–213.
  • Lombardi, V. P. (1989). Beginning weight training: the safe and effective way. Wm. C. Brown Publishers.
  • Mayhew, J. L., Ball, T. E., Arnold, M. D., & Bowen, J. C. (1992). Relative muscular endurance performance as a predictor of bench press strength in college men and women. Journal of Applied Sport Science Research, 6(4), 200–206.
  • Nippard, J. (2024). How strong should you be? (Noob to freak) [Video]. YouTube. https://www.youtube.com/watch?v=LrDJXIQ_-eg
  • O'Conner, B., Simmons, J., & O'Shea, P. (1989). Weight training today. West Publishing.
  • Wathan, D. (1994). Load assignment. In T. R. Baechle (Ed.), Essentials of strength training and conditioning. Human Kinetics, pp. 435–446.

Data sources

the public datasets I measured my own log against