Skip to content
Statistical RethinkingStatistical Rethinking

11.1 Ordered categorical outcomes

Ordered categorical outcomes, such as Likert scales, have a natural order but no fixed spacing. Cumulative link models assume that a latent continuous variable is cut into categories by thresholds.

11.1.1 The cumulative link model

The idea: there is a latent continuous variable ϕi\phi_i that determines the response. The observed category yiy_i is determined by where ϕi\phi_i falls relative to cutpoints α1<α2<⋯<αK−1\alpha_1 < \alpha_2 < \cdots < \alpha_{K-1}:

log⁡Pr⁡(yi≤k)1−Pr⁡(yi≤k)=αk−ϕi\log\frac{\Pr(y_i \leq k)}{1 - \Pr(y_i \leq k)} = \alpha_k - \phi_i

where ϕi=βxi\phi_i = \beta x_i is the linear predictor. The cutpoints αk\alpha_k are estimated from the data.

# Ordered categorical (cumulative link) model
# Simulate Likert-scale data (1-7 response)

set.seed(42)
n <- 300

# Latent continuous variable

x <- runif(n, 0, 10)
phi <- -1 + 0.3 * x + rnorm(n, sd = 1)

# Cutpoints for 7 categories (6 cutpoints)

cutpoints <- c(-2, -1, 0, 1, 2, 3)

# Convert to ordered categories

y <- cut(phi, breaks = c(-Inf, cutpoints, Inf), labels = 1:7)
y <- as.numeric(as.character(y))

cat("Ordered categorical data:\n")
cat(" Categories:", paste(sort(unique(y)), collapse = ", "), "\n")
cat(" Distribution:", paste(table(y), collapse = ", "), "\n")

# Fit ordered logistic regression using MASS::polr

library(MASS)
m <- polr(factor(y) ~ x, method = "logistic")

cat("\nOrdered logistic regression:\n")
cat(" Coefficient:", round(coef(m)[1], 3), "\n")
cat(" Cutpoints:", round(m$zeta, 3), "\n")

# Predicted probabilities at x = 2, 5, 8

cat("\nPredicted probabilities:\n")
for (x_val in c(2, 5, 8)) {
pred <- predict(m, data.frame(x = x_val), type = "prob")
cat(sprintf(" x = %d: P(y=1)=%.3f P(y=4)=%.3f P(y=7)=%.3f\n",
x_val, pred[1], pred[4], pred[7]))
}
Click Run to execute this code in your browser with WebR

11.1.2 Interpreting the model

The coefficient β\beta tells us how the latent variable shifts per unit increase in xx. A positive β\beta means higher xx shifts the distribution toward higher categories.

# Visualize the cumulative link model
set.seed(42)
n <- 300
x <- runif(n, 0, 10)
phi <- -1 + 0.3 * x + rnorm(n, sd = 1)
cutpoints <- c(-2, -1, 0, 1, 2, 3)
y <- as.numeric(cut(phi, breaks = c(-Inf, cutpoints, Inf), labels = 1:7))

library(MASS)
m <- polr(factor(y) ~ x, method = "logistic")

# Plot predicted category probabilities vs x

x_seq <- seq(0, 10, length.out = 200)
pred_matrix <- predict(m, data.frame(x = x_seq), type = "prob")

# Stacked area plot

par(mfrow = c(1, 1))
colors <- rainbow(7)
plot(NULL, xlim = c(0, 10), ylim = c(0, 1),
xlab = "x", ylab = "Probability",
main = "Cumulative link: category probabilities vs x")

for (k in 1:7) {
if (k == 1) {
polygon(c(x_seq, rev(x_seq)), c(pred_matrix[, k], rep(0, length(x_seq))),
col = adjustcolor(colors[k], 0.7), border = NA)
} else {
polygon(c(x_seq, rev(x_seq)),
c(rowSums(pred_matrix[, 1:k]), rev(rowSums(pred_matrix[, 1:(k-1)]))),
col = adjustcolor(colors[k], 0.7), border = NA)
}
}
legend("topright", legend = paste("y =", 1:7), fill = adjustcolor(colors, 0.7),
ncol = 4, cex = 0.8)

cat("As x increases, probability shifts toward higher categories.\n")
cat("The cutpoints determine where the shifts occur.\n")
Click Run to execute this code in your browser with WebR
Rethinking

Ordered categories are not intervals. A Likert scale from 1 to 7 looks like numbers, but the distance between 1 and 2 is not necessarily the same as the distance between 6 and 7. The cumulative link model respects this: it assumes only the order, not the spacing. The cutpoints are estimated from the data, so the model learns where the boundaries are. This is more honest than treating ordered categories as continuous numbers, which assumes equal spacing — an assumption that is rarely justified.

Summary of Section 11.1

  • Ordered categorical outcomes have order but no fixed spacing.
  • Cumulative link model: a latent variable is cut into categories by thresholds.
  • log⁡Pr⁡(y≤k)1−Pr⁡(y≤k)=αk−ϕi\log\frac{\Pr(y \leq k)}{1 - \Pr(y \leq k)} = \alpha_k - \phi_i where ϕi=βxi\phi_i = \beta x_i.
  • The coefficient β\beta shifts the latent variable; cutpoints αk\alpha_k are estimated.
  • Use MASS::polr() for ordered logistic regression in R.