AdvancedIndependently reviewed, not yet spot-checked by a human

Variable selection for prediction models

Picking predictors for a prediction model has nothing to do with causal structure — the only question is whether a variable makes the prediction better. Three common strategies are run on one breast cancer cohort; this dataset cannot tell their external performance apart (the three point estimates differ in the third decimal place, and no uncertainty is estimated here). The difference that does matter lies elsewhere - the same algorithm, on resamples of the same patients, hands back dozens of different "final models". A closing section covers how to read a machine-learning prediction paper - what variable importance and SHAP do and do not say, and why not one item on the acceptance list changes.

Change the question and the whole rulebook changes

Every regression in the B2 and B6 families is answering an aetiological question: does this exposure have an effect on this outcome. In that kind of model, “should this variable go in” is a question about causal structure — a confounder must go in, a mediator must stay out, and a collider creates bias if you include it. The criteria live in the cohort chapter and on the DAG page.

A prediction model asks something else entirely: what is this person’s risk? The same decision now runs on completely different criteria:

Aetiological modelPrediction model
Should this variable go inDepends on where it sits in the causal diagramDepends on whether it makes the prediction more accurate
MediatorsIncluding one blocks part of the effect you are estimatingAllowed, as long as it is available at deployment
CollinearityThe target coefficient becomes unstable, so it is a problemNot a problem, as long as the predictions are stable
How to read the coefficientsEffect sizes, expected to carry a causal readingDo not read them; they are only weights
What counts as successUnbiased estimation, assumptions satisfiedHow it performs on people it has never seen

Three strategies, one dataset

survival::rotterdam is the development cohort (2982 patients, 1713 events) and survival::gbsg is the external validation set (686 patients). There are 8 candidate variables: age, menopausal status, tumour size, grade, node count, PgR, ER and hormonal treatment.

The three approaches you will meet most often in published papers:

  1. Full model approach — specify all 8 in advance and keep every one, whatever its p-value.
  2. Stepwise regression — start from the full model and delete backwards using AIC.
  3. Univariable screening — fit each variable on its own first, and only let the ones with p < 0.05 into the multivariable model.
StrategyVariables keptDevelopment C-indexExternal C-indexExternal calibration slope
Full model (prespecified)80.6700.6620.723
Backward stepwise (AIC)60.6690.6610.721
Univariable screening (p < 0.05)70.6690.6620.722

Stepwise regression’s problem is not that it selects badly, it is that it selects unstably

The model diagnostics page already demonstrated one half of this with 300 simulations: throw 10 pure noise variables at stepAIC() and it keeps 1.81 of them on average, with at least one “significant” random variable surviving in 48.0% of runs. That demonstration is not repeated here.

This page asks a different question: same algorithm, same patients, one new resample — will it give you the same model?

Resample the development cohort 200 times, run the identical backward AIC on each resample, and record how often each variable survives:

Bar chart. The horizontal axis lists the eight candidate variables and the vertical axis is the percentage of resamples in which backward AIC kept each one. With the full sample, tumour size, grade and node count sit at essentially one hundred percent, while age, menopausal status, PgR, ER and hormonal treatment fall between roughly twenty and eighty percent. When each resample is cut to 200 patients, every variable except log ER is kept less often — log ER rises slightly from 26.5% to 32.0% — and the bars converge toward the middle, with node count the exception to that convergence: it stays near the top at 99.0%.
The same development cohort resampled 200 times, running the identical backward AIC on each resample. Dark bars use every patient; light bars draw 200 patients each time. A bar near 100% means the variable survives reliably; a bar at mid-height means whether it reaches the final model is essentially a coin toss.Plotting script figures/scripts/B5-01-variable-selection.R
VariableUnivariable pIn the final model on the full data?Selected in resamples (all patients)Selected in resamples (200 patients)
age< 0.001yes62%32%
menopausal< 0.001no40%22%
tumour size< 0.001yes100%79%
grade 3< 0.001yes100%56%
nodes< 0.001yes100%99%
log PgR< 0.001yes76%31%
log ER0.773no27%32%
hormonal tx< 0.001yes63%26%

Across those 200 resamples, backward AIC produced 25 different models, and the most frequent one accounted for only 22% of the runs. Cut each resample to two hundred patients — much closer to the size of most single-centre studies — and it becomes 69 different models, with the most common one accounting for 12%.

Univariable screening has a fault stepwise regression does not

Univariable screening — fit each variable alone, admit only those with p < 0.05 — is even more common than stepwise regression, because it looks more conservative. It is not.

Look at menopausal status in the table above: its univariable p-value is far below 0.001, so it passes the screen and enters the multivariable model — but backward AIC deletes it. The reason is that it almost duplicates age: once you adjust for age, there is nothing left of it.

The reverse case is the dangerous one: a variable can carry no univariable signal at all and still matter after adjustment. Univariable screening throws it away before it ever gets adjusted, and no number in the final model records that this happened.

So how should you choose?

The current advice (TRIPOD+AI, the methodological literature from Riley and colleagues) comes down to one sentence: fix the candidate variables and the model form before you see any results, then put all of them in.

ApproachWhen it makes senseWatch out for
Prespecify from domain knowledge and keep everything (full model approach)The default. Candidates come from existing models, guidelines, and what is actually obtainable in clinicThe number of candidates has to clear the sample size requirement first
Let penalised regression decide the weightsMany candidates, sample size not generousThe variables lasso drops are just as unstable — see the shrinkage page
Cut on availabilityAlways applicable, and it should be done firstThis is not a statistical decision, so it introduces no optimism
Stepwise regressionAlmost neverIf you really use it, the whole selection procedure has to go inside the resampling loop — see the internal validation page

Run it yourself

library(survival); library(MASS)
data(cancer, package = "survival")

rot <- rotterdam
rot$rfs_time  <- pmin(rot$rtime, rot$dtime)
rot$rfs_event <- as.integer(rot$recur == 1 | rot$death == 1)
rot$size_mm   <- c("<=20" = 15, "20-50" = 35, ">50" = 60)[as.character(rot$size)]
rot$grade3    <- as.integer(rot$grade >= 3)
rot$log_pgr   <- log1p(rot$pgr); rot$log_er <- log1p(rot$er)

cand <- c("age", "meno", "size_mm", "grade3", "nodes", "log_pgr", "log_er", "hormon")
f <- as.formula(paste("Surv(rfs_time, rfs_event) ~", paste(cand, collapse = " + ")))

full <- coxph(f, data = rot)          # full model approach: this one line
step <- stepAIC(full, direction = "backward", trace = 0)

# Univariable screening: note what it is asking -- association without adjustment
sapply(cand, function(v)
  summary(coxph(as.formula(paste("Surv(rfs_time, rfs_event) ~", v)),
                data = rot))$coefficients[1, "Pr(>|z|)"])

# Stability: resample the same data and watch what it picks each time
tally <- setNames(numeric(length(cand)), cand)
for (b in 1:200) {
  d  <- rot[sample(nrow(rot), replace = TRUE), ]
  ff <- f; environment(ff) <- environment()   # without this line the whole loop fails silently
  st <- stepAIC(coxph(ff, data = d), direction = "backward", trace = 0)
  tally[names(coef(st))] <- tally[names(coef(st))] + 1
}
round(100 * tally / 200)

Verified with R 4.6.0 + survival 3.8.6 + MASS 7.3.65. ⚠️ stepAIC() re-evaluates the model call in the frame that called it, so the data object inside a loop has to be visible to the formula; the line environment(ff) <- environment() in the figure script is what handles this, and without it every resample fails silently.

When the paper uses a machine-learning model

“XGBoost predicts AKI”, “random forest predicts sepsis” — papers like these are now common in clinical informatics and critical care, and a medical student usually meets one for the first time at a journal club. This section teaches how to read them, not how to fit them. Swapping the model for a forest or a boosted ensemble is a separate skill tree, but not one item on the acceptance list changes.

In order: variable importance, impurity bias, SHAP, and the one thing that has not changed.

One, variable importance is not a regression coefficient. It has no direction, no confidence interval, and it is not an effect size. A variable ranked first only means it came first under whatever importance definition the package used — permutation importance means that shuffling it degrades prediction the most, while the default in tree packages is often the impurity-based version of point two, and the two can rank the same variables differently. Either way it does not tell you whether risk goes up or down, or by how much, and it cannot be cited as “this factor raises risk by so much”. When a Discussion turns an importance ranking into a list of risk factors, it is treating two entirely different quantities as one.

Two, impurity-based importance is systematically biased towards high-cardinality variables. A tree picks a split at every node, and a variable with more distinct values offers more places to split, so it more easily finds a cut that looks useful on noise alone. Continuous variables and many-levelled categorical ones (admitting specialty, drug codes) therefore rise systematically, and binary variables sink systematically — neither of which has anything to do with whether they are actually useful. Permutation importance, or recomputing on held-out data, reduces this but does not remove it.

Three, SHAP is local attribution, not a causal effect. It answers “for this patient, how far did this variable push the model’s prediction away from the baseline” — a decomposition that takes the fitted model as given. If the association the model learned came from confounding, selection or reverse causation, SHAP will faithfully attribute that association, and will draw it very persuasively. A beautiful SHAP plot is not causal evidence; it is the model describing itself.

Four, the acceptance criteria are unchanged. Discrimination, calibration, the decision curve, and external validation — every one of them, none optional. And the one machine-learning papers most often skip is calibration: tree ensembles shrink their predictions towards the middle by construction, a calibration slope above 1 is a common finding, and a paper that reports only the AUC will never show you that.

Four questions to ask when reading a paper

  1. Were the candidate variables prespecified, or chosen from the data? If the Methods do not say “prespecified”, assume they came from the data.
  2. Was anything revalidated after selection? Selecting variables spends degrees of freedom; the p-values, confidence intervals and C-index computed afterwards are all optimistic.
  3. Will every variable in the model actually be available at deployment? This is the one place a reader can catch data leakage.
  4. Are the discarded variables reported? A paper that lists only the final model leaves the reader no way to judge how marginal the deletions were.

Common misuses

MisuseWhy it is wrong
Selecting with stepwise regression, then reporting p-values and CIs as usualSelection is itself multiple comparison; one new resample gives a different variable set
Saying “the variables in the final model are independent risk factors”They are predictive weights, not causal effects — and they change with the subsample
Dropping a variable because its univariable p-value is not significantSome variables only contribute after adjustment, and the loss leaves no trace in any number
Treating prediction model selection like aetiological selectionThe criteria are entirely different: causal position versus predictive contribution
Deleting variables out of concern about collinearityA prediction model only needs stable predictions; collinearity is not a problem
Including a variable that will not be available at deploymentData leakage; every statistical check passes and only deployment reveals it
Screening candidates without checking them against the sample sizeBackwards order — clear the sample size requirement first
Justifying deletions on the grounds that “the model is more parsimonious”Parsimony is not the acceptance criterion; predictive performance is

Reproducing every number on this page

/opt/homebrew/bin/Rscript figures/scripts/B5-01-variable-selection.R

Read the figure

The answer comes from the same statistical output that produced this page's figures, not from a number typed in beside them.

The three variable-selection strategies give calibration slopes of 0.723, 0.721 and 0.722 in the same external cohort. How should that set of numbers be read?

Show the answer and why

Correct answer: All three sit well below 1 and differ only in the third decimal, so 0.723 says the same thing the other two do: the model stretches the external cohort's risk differences too far apart

A calibration slope below 1 means the predicted risk differences are too extreme - high-risk people pushed too high, low-risk people pressed too low. That is not the same as overestimating overall: an overall shift lives in the intercept, and moving every prediction down changes the mean without touching the slope. As for which of 0.721, 0.722 and 0.723 is larger, each is a single point estimate computed once in the same external cohort, and this page does not estimate their uncertainty, so a ranking in the third decimal cannot choose a strategy. What the three agree on is a calibration problem the three strategies share, not evidence that one beats another.

Age has a univariable p value far below one in a thousand, yet backward AIC does not keep it in every resample. What do those two facts together tell you?

Show the answer and why

Correct answer: Age is kept in 62.0 percent of the resamples; an unadjusted p value and survival through AIC ask different questions, and the two can disagree

The contrast between 62.0 and 100.0 is the point: tumour size survives every single resample, age barely six times in ten. Age has a strong univariable association, but it overlaps with menopausal status and others, and what is left after adjustment is not enough to make AIC pay for the parameter every time. Reading the gap as too few resamples does not hold either - tumour size reaches 100.0 in the very same resamples, and noise does not single out age. Menopausal status at 40.0 is indeed less stable still, but that does not make age stable; all a reader sees in the paper is which variables entered the final model, never how narrowly each of them did.

The development cohort is resampled 200 times and the identical backward AIC run on each resample, recording the final model every time. What is the important output of that experiment?

Show the answer and why

Correct answer: The same people and the same code, differing only in the resample, produced 25 distinct final models - that sentence describes one draw, not a finding

The full-sample version produced 25 distinct models, not one, so the claim that it converges on a single model is refuted by the same output; the two-hundred-per-draw version is worse still at 69. Nor does resampling confirm the model fitted to the complete data: it asks what the same algorithm picks when it meets another batch of people from the same source, and the answer is usually something else. What inherits that instability is every later sentence that presupposes membership of the final model - this is an independent risk factor, this retains predictive value after adjustment.

When each resample is cut from the whole cohort down to two hundred people, most variables are selected less often while nodes barely moves. What does that mean?

Show the answer and why

Correct answer: Nodes still reaches 99.0 percent in the small samples, a signal strong enough to survive the shrinkage; the decline elsewhere says sample size also decides which variables reach the final model

99.0 and 100.0 are all but identical, while grade drops from 100.0 to 55.5 - same algorithm, subsets of the same people, differing only in how many are seen each time. Selection frequency measures whether a variable's signal is strong enough to clear the AIC threshold reliably, which is decided jointly by signal strength and sample size, not by how much the variable matters clinically. So reading 55.5 as grade mattering less in a smaller population restates a power problem as biology; and pointing at the two near-ceiling figures for nodes to conclude that sample size is irrelevant picks the one variable that is unaffected to speak for the whole table.

Menopausal status has a univariable p value far below one in a thousand, so it passes univariable screening; backward AIC then drops it from the model. What is the sensible reading?

Show the answer and why

Correct answer: Menopausal status is kept in only 40.0 percent of the resamples; it overlaps almost entirely with age, so once age is adjusted for little independent contribution is left

40.0 does not contradict the univariable p value: univariable analysis looks at menopausal status against the outcome on its own, and age and menopausal status measure very nearly the same thing, so once both are in the model there is little extra for menopausal status to explain. Age at 62.0 is higher, but that is not a verdict on which one is the genuine risk factor - selection frequency is statistical stability, not causal standing, and this page's model was never fitted to estimate effects. log ER at 26.5 is indeed lower, but it is dropped for a different reason entirely: it has no univariable association at all. And AIC does not delete variables in order of selection frequency; at every step it asks whether the parameter buys enough improvement in the likelihood to be worth its cost.

The three strategies retain different numbers of variables in the complete development data, yet their external C-index values differ in the third decimal place. What does that comparison support?

Show the answer and why

Correct answer: Backward AIC keeps 6 variables without external performance dropping, which says that when events are plentiful relative to parameters the deleted variables were contributing little

This development cohort gives every parameter a couple of hundred events, and under those conditions stepwise deletion usually removes variables that were contributing little, so external performance shows no difference - move to data where events are tight and the same deletions start removing things that matter. The second option reads a failure to detect a difference this once as prespecification being unnecessary, but prespecification is not guarding this C-index; it is guarding against the problem in the section above, where the same algorithm returns a different set of variables on the next draw. The third is more direct: a variable count in the middle is not an acceptance criterion of any kind. Predictive performance is, and predictive performance cannot separate these three here.

Watch next

What Are Clinical Prediction Rules?
ENTerry Shaneyfelt· 10 minEstablishes what a prediction rule looks like in clinical use before we argue about which variables belong inside one.
AI 臨床研究實戰 EP7|預測 vs 分類、Data Leakage、Propensity Score
繁中Colon & Code· 10 minIn Mandarin. The first half is exactly the point this page opens with — a prediction question and a causal question are not the same question.
Key Steps and Common Pitfalls in Clinical Prediction Model Research
ENRichard_D_Riley· 57 minThe full hour, with variable selection as one segment of it. Worth watching once you want to see the whole B5 family as a single workflow.

Sources and licences

This page is original writing

Report a content problem

The statistics on this site are written by AI and reviewed by AI; a human only spot-checks. What you can see may be what we cannot.

The more specific, the more fixable — e.g. which sentence disagrees with which textbook or paper.

Needed only if you want a reply; reports without it are still read.

Sent along with your report

These are attached automatically. You can drop any of them.