---
title: "SMA or OLS for a scaling exponent?"
description: "The standardised major axis is ordinary least squares divided by the correlation: why that identity makes SMA steeper, and when it is the unbiased choice."
date: "2026-06-28 09:00"
date-modified: "2026-10-15"
categories: [R, allometry, body size, measurement error, regression, ecology tutorial]
image: thumbnail.png
image-alt: "Scatter with two fitted lines of different slope, one from ordinary least squares and one from the standardised major axis"
---
*Corrected 15 October 2026: the post said the gap between the two exponents is a function of the scatter and nothing else; that holds for their ratio, since the difference also grows with the slope, and the sentence now says ratio.*
*Corrected 7 October 2026: the post said equation error lands entirely in the y variable, as if that were true whatever the line is for; it now says Warton and colleagues count least squares, the major axis and the standardised major axis as alternatives that can each be appropriate when equation error is the only scatter, and that the y placement and the case for least squares belong to the prediction target the post uses. It also called the reciprocal of the correlation the gap between the two slopes, when it is their ratio.*
*Corrected 30 September 2026: the post said Deming regression with an error ratio from repeated measurements or instrument specifications beats both lines; it now says the ratio must include the equation error in y, and that a ratio from measurement error alone makes the line too steep.*
Once you have logged both variables, you still have to draw a line through them, and allometry has two competing conventions. Ordinary least squares minimises vertical distances and treats the x variable as known. The standardised major axis, also called the reduced major axis, treats the two variables symmetrically and almost always gives a steeper slope.
The literature on which to use is long and not entirely friendly. Most of it can be compressed into one identity and one condition, both of which you can verify in a few lines of R. This post derives them by measurement, then shows what happens when the condition fails.
## The identity
Write `r` for the correlation between the logged variables. The ordinary least squares slope is `sd(y) / sd(x)` multiplied by `r`. The standardised major axis slope drops that multiplier:
```{r estimators}
sma_slope <- function(x, y) sign(cor(x, y)) * sd(y) / sd(x)
deming_slope <- function(x, y, delta) {
sxx <- var(x); syy <- var(y); sxy <- cov(x, y)
(syy - delta * sxx + sqrt((syy - delta * sxx)^2 + 4 * delta * sxy^2)) / (2 * sxy)
}
set.seed(380)
n_sp <- 300
x_obs <- rnorm(n_sp, 3, 0.8)
y_obs <- 0.75 * x_obs + rnorm(n_sp, 0, 0.45)
b_ols <- unname(coef(lm(y_obs ~ x_obs))[2])
b_sma <- sma_slope(x_obs, y_obs)
r_obs <- cor(x_obs, y_obs)
c(ols = b_ols, sma = b_sma, r = r_obs,
ols_over_r = b_ols / r_obs,
gap = abs(b_sma - b_ols / r_obs))
```
The standardised major axis slope is the ordinary least squares slope divided by the correlation, and the two agree to within floating-point rounding (the `gap` in the output above): an identity, not an approximation. On these data the correlation is `r sprintf("%.3f", r_obs)`, so the standardised major axis runs `r sprintf("%.1f", 100*(1/abs(r_obs) - 1))` per cent steeper.
That is worth sitting with. The ratio of the two exponents carries no biological information at all. It is a function of the scatter and nothing else.
```{r ratio-table}
rr <- c(0.99, 0.95, 0.90, 0.80, 0.70, 0.50)
data.frame(correlation = rr, sma_over_ols = round(1 / rr, 3))
```
At a correlation of 0.9, a perfectly respectable value for an interspecific dataset, the standardised major axis exponent is `r sprintf("%.1f", 100*(1/0.9 - 1))` per cent above the least squares one. Papers have argued over exponent differences smaller than that.
```{r fig-two-lines}
#| echo: false
#| fig-cap: "The same logged data with both lines. The standardised major axis passes through the same centroid but tilts towards the axis of greatest spread, and the gap widens as the scatter grows."
#| fig-alt: "Scatter of logged points with two straight lines crossing at the centre of the cloud. The steeper line is labelled SMA and the shallower one OLS."
#| fig-width: 6.4
#| fig-height: 4.2
library(ggplot2)
theme_te <- function(base_size = 11) {
theme_minimal(base_size = base_size) +
theme(
panel.grid.minor = element_blank(),
panel.grid.major = element_line(colour = "#dad9ca", linewidth = 0.3),
plot.background = element_rect(fill = "#f5f4ee", colour = NA),
panel.background = element_rect(fill = "#f5f4ee", colour = NA),
axis.title = element_text(colour = "#2c3a31"),
axis.text = element_text(colour = "#5d6b61"),
legend.text = element_text(colour = "#2c3a31"),
legend.title = element_blank(),
plot.title = element_text(colour = "#16241d", face = "bold", size = base_size + 1),
plot.subtitle = element_text(colour = "#5d6b61", size = base_size - 1)
)
}
dd <- data.frame(x = x_obs, y = y_obs)
int_ols <- mean(y_obs) - b_ols * mean(x_obs)
int_sma <- mean(y_obs) - b_sma * mean(x_obs)
ggplot(dd, aes(x, y)) +
geom_point(colour = "#275139", alpha = 0.4, size = 1.6) +
geom_abline(aes(slope = b_ols, intercept = int_ols, colour = "OLS"), linewidth = 0.9) +
geom_abline(aes(slope = b_sma, intercept = int_sma, colour = "SMA"), linewidth = 0.9) +
scale_colour_manual(values = c(OLS = "#c9b458", SMA = "#b5534e")) +
labs(x = "log body size", y = "log trait",
title = "Two lines through one cloud",
subtitle = sprintf("r = %.3f, so the SMA slope is exactly 1/r times the OLS slope", r_obs)) +
theme_te() + theme(legend.position = "top")
```
## The standardised major axis is a Deming fit in disguise
A Deming regression asks you to supply `delta`, the ratio of the error variance in y to the error variance in x, and then minimises a weighted perpendicular distance. Set `delta` to the ratio of the two observed variances and the algebra collapses.
```{r sma-is-deming}
delta_obs <- var(y_obs) / var(x_obs)
c(sma = b_sma,
deming_at_var_ratio = deming_slope(x_obs, y_obs, delta_obs),
gap = abs(b_sma - deming_slope(x_obs, y_obs, delta_obs)))
```
Again exact, to within floating-point rounding (the `gap` above). So the standardised major axis is not an assumption-free method. It is a Deming fit that has quietly assumed the error variance ratio equals the total variance ratio of the data, which is to say it has assumed both variables are measured with the same relative precision. Nobody is asked to defend that assumption because nobody is asked to state it.
## When is that assumption right?
Set up a structural model with known parts. Let the true log body size be `x_t`, let the true relationship be `y_t = b * x_t`, and let each variable pick up its own independent error. Then ask which estimator recovers `b`.
The algebra gives a sharp answer. The observed variances are `var(x) = var(x_t) + var(e_x)` and `var(y) = b^2 * var(x_t) + var(e_y)`, so the standardised major axis estimates the square root of their ratio. That equals `b` exactly when `var(e_y) = b^2 * var(e_x)`, and not otherwise.
```{r sma-condition}
b_str <- 0.75
n_str <- 400
n_mc <- 2000
run_config <- function(sd_ex, sd_ey, seed) {
set.seed(seed)
out <- t(vapply(seq_len(n_mc), function(i) {
xt <- rnorm(n_str, 3, 0.8)
x <- xt + rnorm(n_str, 0, sd_ex)
y <- b_str * xt + rnorm(n_str, 0, sd_ey)
c(unname(coef(lm(y ~ x))[2]),
sma_slope(x, y),
deming_slope(x, y, sd_ey^2 / sd_ex^2))
}, numeric(3)))
colMeans(out)
}
cfg <- list(c(0.30, 0.225), c(0.30, 0.300), c(0.30, 0.100), c(0.30, 0.450))
tab <- do.call(rbind, lapply(seq_along(cfg), function(k) {
m <- run_config(cfg[[k]][1], cfg[[k]][2], 4380 + k)
data.frame(sd_ex = cfg[[k]][1], sd_ey = cfg[[k]][2],
err_ratio = round(cfg[[k]][2]^2 / cfg[[k]][1]^2, 3),
ols = round(m[1], 4), sma = round(m[2], 4),
deming_true = round(m[3], 4))
}))
tab
```
The critical ratio is `b^2`, which for a true exponent of `r sprintf("%.2f", b_str)` is `r sprintf("%.4f", b_str^2)`. The first row sits on it, and the standardised major axis returns `r sprintf("%.4f", tab$sma[1])` against a truth of `r sprintf("%.2f", b_str)`. Move off it and the estimate moves with it: equal error variances give `r sprintf("%.4f", tab$sma[2])`, a precise y variable gives `r sprintf("%.4f", tab$sma[3])`, and a noisy y variable gives `r sprintf("%.4f", tab$sma[4])`. Least squares sits at about `r sprintf("%.3f", mean(tab$ols))` throughout, attenuated by the x error in the usual way, and Deming with the true ratio recovers the exponent in all four rows.
So the standardised major axis is not a fix for measurement error. It is a fix for one specific measurement error ratio, and it is biased in a knowable direction everywhere else: too steep when y is noisy relative to x, too shallow when x is the noisy one.
```{r fig-sweep}
#| echo: false
#| fig-cap: "Mean estimated exponent across a sweep of error variance ratios, with the x error held fixed. The standardised major axis crosses the truth exactly once, at a ratio equal to the squared exponent."
#| fig-alt: "Line chart with error variance ratio on a log axis. The SMA curve rises steadily and crosses a horizontal true-value line at one point, while the OLS curve stays flat and well below it."
#| fig-width: 6.6
#| fig-height: 4
set.seed(4390)
sd_ex_fix <- 0.30
ratios <- c(0.08, 0.15, 0.30, 0.5625, 1.0, 1.8, 3.2)
swp <- do.call(rbind, lapply(ratios, function(rt) {
sd_ey <- sd_ex_fix * sqrt(rt)
o <- t(vapply(seq_len(700), function(i) {
xt <- rnorm(n_str, 3, 0.8)
x <- xt + rnorm(n_str, 0, sd_ex_fix)
y <- b_str * xt + rnorm(n_str, 0, sd_ey)
c(unname(coef(lm(y ~ x))[2]), sma_slope(x, y))
}, numeric(2)))
data.frame(ratio = rt, method = c("OLS", "SMA"), est = colMeans(o))
}))
ggplot(swp, aes(ratio, est, colour = method)) +
geom_hline(yintercept = b_str, colour = "#93a87f", linewidth = 0.5, linetype = "dashed") +
geom_line(linewidth = 0.9) + geom_point(size = 2) +
scale_x_log10() +
scale_colour_manual(values = c(OLS = "#c9b458", SMA = "#b5534e")) +
labs(x = "error variance ratio, var(e_y) / var(e_x)", y = "mean estimated exponent",
title = "Only one ratio makes the SMA unbiased",
subtitle = sprintf("dashed line: true exponent %.2f; the crossing sits at b squared = %.4f",
b_str, b_str^2)) +
theme_te() + theme(legend.position = "top")
```
## Equation error breaks the condition on its own
There is a second source of scatter that has nothing to do with instruments. Two species of the same body mass genuinely differ in metabolic rate, and no amount of careful measurement removes that. Warton and colleagues call this equation error, to distinguish it from measurement error, and when it is the only scatter they count least squares, the major axis and the standardised major axis as alternatives that can each be appropriate. Which is right depends on what the line is for. Here the target is the expected y at a given x, the prediction question, and for that target equation error lands entirely in the y variable.
Notice what that does to the condition. Equation error adds to `var(e_y)` while contributing nothing to `var(e_x)`. If your x variable is measured well, as body mass usually is, the ratio sits far above `b^2` and the standardised major axis runs steep. Take the limiting case where x is measured perfectly:
```{r equation-error}
set.seed(3801)
xt <- rnorm(n_str, 3, 0.8)
x_clean <- xt
y_eqn <- b_str * xt + rnorm(n_str, 0, 0.30)
c(true_b = b_str,
ols = unname(coef(lm(y_eqn ~ x_clean))[2]),
sma = sma_slope(x_clean, y_eqn))
```
With no error in x at all, least squares is the correct estimator for that target and returns `r sprintf("%.3f", unname(coef(lm(y_eqn ~ x_clean))[2]))`, while the standardised major axis returns `r sprintf("%.3f", sma_slope(x_clean, y_eqn))`. Switching to the symmetric method because "both variables are biological" has made the answer worse, not safer.
## Where to go next
The choice of line is a choice of error model, and the data cannot make it for you. Three practical positions:
If your x variable is measured much more precisely than the scatter in y, and the scatter is mostly biological, ordinary least squares estimates the relationship you want. If you are describing the joint spread of two traits rather than predicting one from the other, the standardised major axis is a reasonable descriptive summary, and its symmetry is a genuine advantage: it gives the same line whichever variable you put on the x axis. If you know the ratio of the total error variances, including the equation error in y and not only the measurement error that repeated measurements or instrument specifications reveal, Deming with that ratio removes the bias that affects both lines; a ratio built from measurement error alone leaves out the equation error and tilts the line too steep.
What you should not do is pick the method that gives the exponent closest to the value you expected. The ratio of the two slopes is `1/r`, and it is available to be exploited in either direction.
## Honest limits
Everything above is about the point estimate. Intervals are a separate problem: the sampling distribution of the standardised major axis slope is skewed, and its confidence interval is not the usual symmetric Wald form. The condition `var(e_y) = b^2 * var(e_x)` is also not testable from a single bivariate sample, because the observed variances confound signal and error. That is the same wall the errors-in-variables literature runs into everywhere: a ratio has to come from outside the data.
## References
Warton, Wright, Falster, Westoby 2006 Biological Reviews 81(2):259-291 (10.1017/S1464793106007007)
Warton, Weber 2002 Biometrical Journal 44(2):161-174 (10.1002/1521-4036(200203)44:2<161::AID-BIMJ161>3.0.CO;2-N)
Xiao, White, Hooten, Durham 2011 Ecology 92(10):1887-1894 (10.1890/11-0538.1)
## Related tutorials
- [Errors in variables and Deming regression](../errors-in-variables-deming-regression/) - the general two-error line fit that the SMA is a special case of.
- [Allometry and log-log regression in R](../allometry-and-log-log-regression/) - getting onto the log scale and back off it again.
- [Measurement error and regression dilution](../measurement-error-regression-dilution/) - why the least squares slope attenuates, and by how much.
- [Testing isometry and comparing slopes](../testing-isometry-and-comparing-slopes/) - inference once the line is chosen.