Course outline

R Programming / Evaluation, ownership, and reproducible results / Lesson 13

Formulas, contrasts, and generalized models

Inspect coding and response scale before interpreting a model coefficient or prediction.

About 4 min readContent reviewed
On this page

Lesson goals

  • Expand formula terms and identify contrast columns.
  • Distinguish a GLM family from its link function.
  • Validate grouped-binomial counts and prediction scale.

Formula syntax is a modeling language

In a formula, a*b includes a, b, and their interaction a:b; it does not simply compute an elementwise product. I(a*b) requests ordinary arithmetic inside a term. Removing an intercept changes the coding and interpretation of other columns. Transforming a response, specifying a link, and adding an offset are also different operations. Build and inspect a tiny model matrix before interpreting a complex specification. Its columns connect the symbolic formula to the actual numeric predictor, and reveal whether the intended main effects, interactions, and reference levels entered the calculation.

Contrasts define comparisons between categories

The first fixture declares a two-level factor and an explicit treatment contrast. The control row contains intercept one and indicator zero; the treated row contains two ones. Under this coding, a linear-model intercept refers to the control conditional mean and the indicator coefficient represents a difference on the modelโ€™s linear predictor scale. Other contrast choices can represent different comparisons without changing the fitted function space in a full-rank equivalent model. Changing coding changes coefficient meaning, so preserve the contrast contract when sharing a fit or constructing new-data matrices.

r Illustrative base-R script; R runtime not available during review
d <- data.frame(group = factor(c("control", "treated", "control"),
                               levels = c("control", "treated")))
X <- model.matrix(~ group, data = d,
                  contrasts.arg = list(group = contr.treatment(c("control", "treated"), base = 1)))
cat("columns=", ncol(X), "\n", sep = "")
cat("control row=", paste(X[1, ], collapse = ","), "\n", sep = "")
cat("treated row=", paste(X[2, ], collapse = ","), "\n", sep = "")
Expected output
columns=2
control row=1,0
treated row=1,1

An explicit treatment contrast gives an intercept and a treated indicator; the reference category is declared rather than inherited from global options.

A generalized model separates family and link

A generalized linear model combines a linear predictor with a response distribution family and a link relating the conditional mean to that predictor. A binomial-logit model expresses log odds linearly; predict with type="response" returns probabilities while the link scale remains log odds. A Poisson model commonly uses a log link for nonnegative counts, but exposure, overdispersion, and dependence need separate attention. A convenient family name does not validate those assumptions. State the observational unit, mean target, variance structure, and exposure interpretation before choosing a model from its output range alone.

Grouped responses preserve their denominators

The second fixture supplies success and failure counts as a two-column response. Its intercept-only estimate pools three successes out of eight trials, producing probability 0.375 and log odds log(3/5). Treating the two proportions as equally weighted observations would ignore their denominators when trial totals differ. Validate nonnegative counts, total trials, response ordering, and missingness explicitly. Separation or sparse categories can make some logistic fits difficult or nonidentifiable; convergence flags and extreme coefficients require investigation. This synthetic intercept fit is a small interface example, not evidence that a complex real model is well specified.

r Illustrative base-R script; R runtime not available during review
d <- data.frame(success = c(2, 1), failure = c(2, 3))
fit <- glm(cbind(success, failure) ~ 1, data = d,
           family = binomial(link = "logit"), na.action = na.fail)
cat(sprintf("logit=%.6f\n", unname(coef(fit)[1])))
p <- predict(fit, type = "response")
cat("probabilities=", paste(sprintf("%.3f", p), collapse = ","), "\n", sep = "")
Expected output
logit=-0.510826
probabilities=0.375,0.375

The intercept-only grouped-binomial fit pools three successes in eight trials; response predictions differ from the linear predictor scale.

Predictions retain the entire modeling contract

Keep factor levels, contrasts, offsets, transformations, and units alongside the fitted object. An offset is a known contribution with coefficient fixed by design, not an ordinary predictor whose effect is estimated. When using transformations learned from data, preserve their training-only parameters rather than recomputing them from new observations. Inspect warnings, convergence status, rank, and relevant residual behavior before interpreting a coefficient. Report uncertainty and limitations according to the design. The expected results here are analytically derived; no formula parser, contrast builder, GLM optimizer, or prediction routine was executed in R during review.

Common errors and fixes

A coefficient is read as a probability difference in logistic regression

Why it happens
The coefficient is on the log-odds linear predictor scale.
How to fix it
Transform predictions under the declared link and state the comparison.
Prevent it next time
Label every displayed prediction with its scale.

A model changes after a factor reference is reordered

Why it happens
The contrast columns changed their meaning.
How to fix it
Preserve or explicitly redefine levels and contrasts.
Prevent it next time
Inspect a tiny model matrix before comparing coefficients.

Proportions with different trial totals receive equal weight accidentally

Why it happens
The grouped-binomial denominator was discarded.
How to fix it
Supply success/failure counts or an equivalent documented weighted representation.
Prevent it next time
Validate both outcome counts and total trials.

Check your understanding

1. Expand y ~ a*b and explain how it differs from y ~ I(a*b).

Show worked solution

The first formula includes a, b, and their interaction a:b, plus the ordinary default intercept. The second includes the computed arithmetic product as one term with an intercept. For numeric variables the resulting columns generally differ; inspect model.matrix rather than relying on the visual similarity of the expressions.

2. Two groups have one success in two trials and nine successes in ten trials. What is the pooled intercept-only probability?

Show worked solution

The pooled estimate is ten successes divided by twelve trials, or 5/6. The equal-weight mean of the two proportions is (0.5+0.9)/2=0.7 and answers a different weighting question. Keep denominators visible when defining the response.

Key takeaways

  • Formula operators and arithmetic operators have different roles.
  • Coding and link scale determine coefficient interpretation.
  • Grouped outcomes require their trial counts.

Official references