AdvancedIndependently reviewed, not yet spot-checked by a human

Sensitivity analysis

A request to add a sensitivity analysis is among the most common referee comments, and it asks for something more specific than most authors supply. This page runs sixteen individually defensible analyses on one dataset so the spread of estimates becomes visible, then permutes the exposure to measure what it costs to pick a setting after seeing the results.

What a request for a sensitivity analysis is actually asking for

A sensitivity analysis swaps out one analysis decision that could reasonably have gone the other way, reruns, and asks whether the conclusion turns over.

What it varies is an analysis decision. Every word there rules something out, and the three things it rules out get mixed in constantly:

What it variesWhat that is calledWhich page
Analysis decisions (model specification, covariates, outcome definition, inclusion rules)Sensitivity analysisThis page
Data points (which observation, which study is driving the result)Influence analysisInfluence diagnostics
Populations (in whom the effect is larger)Subgroup analysisMultiplicity
Significance thresholds (how to judge after many tests)Multiplicity adjustmentMultiplicity

The last row is the one most often confused, so to be explicit: a sensitivity analysis does not need a multiplicity adjustment. It does not ask whether one of these is real; it asks whether the same conclusion survives a change of setting. Adjusting it answers a question about significance when the question was about stability.

The dataset, and what it cannot be used to say

This page uses survival::rotterdam, 2,982 records from a breast cancer registry, of whom 339 received hormonal therapy. The dataset has no missing values at all (0 cells), so the very common sensitivity axis of swapping one missing-data method for another does not exist here; the four axes below are something else.

Sixteen defensible analyses

Four axes, two options each, every combination:

AxisOption AOption BWhy both are defensible
Outcome definitionRecurrence-free survivalOverall survivalBoth are standard endpoints in the breast cancer literature
Covariate setAge, tumour size, grade, node countPlus menopausal status and hormone receptorBoth are done
Node count codingAs recordedCapped at tenThe right tail is long and capping is a common treatment
Follow-up lengthCompleteTruncated at five yearsFive years is the conventional window in breast cancer

The grid is a full factorial rather than a hand-picked string of settings, because only a factorial design lets the spread be attributed to an axis. A string of settings yields a scatter of points, and which decision is driving the result is the question worth asking.

A two-panel specification curve. Upper panel: the horizontal axis holds the 16 analysis settings ordered by hazard ratio, the vertical axis is the hazard ratio on a log scale, and each setting is drawn as a point estimate with a 95% confidence interval. The leftmost point is 0.695 and the rightmost is 0.995, with a horizontal line at the null value of 1. Position 12 is marked with a pale vertical band and its point drawn as a red filled square for the pre-specified main analysis, whose interval crosses the null. Lower panel: an eight-row matrix of settings, two rows per axis, where a dark dot marks which option a setting used; red dots mark the main analysis choices on all four axes. The dark dots in the capped node-count row cluster in the left half.
One question, one dataset. The hazard ratio runs from 0.695 to 0.995; 7 settings reach statistical significance and 9 do not. The lower panel lets each line be traced back to the four decisions that produced it.Plotting script figures/scripts/B8-07-sensitivity-analysis.R
RankOutcomeCovariatesNodesFollow-upHazard ratio95% CIp
1recurrence-free survivalcore plus twocapped at tentruncated at five years0.6950.585–0.826< 0.001
2recurrence-free survivalcorecapped at tentruncated at five years0.7170.604–0.851< 0.001
3recurrence-free survivalcore plus twocapped at tencomplete0.7330.627–0.856< 0.001
4recurrence-free survivalcorecapped at tencomplete0.7430.636–0.867< 0.001
5overall survivalcore plus twocapped at tentruncated at five years0.7570.613–0.934= 0.009
6overall survivalcore plus twocapped at tencomplete0.7860.659–0.937= 0.007
7overall survivalcorecapped at tencomplete0.8090.680–0.964= 0.018
8overall survivalcorecapped at tentruncated at five years0.8230.669–1.013= 0.066
9recurrence-free survivalcore plus twoas recordedtruncated at five years0.8470.715–1.002= 0.053
10recurrence-free survivalcoreas recordedtruncated at five years0.8700.736–1.028= 0.103
11recurrence-free survivalcore plus twoas recordedcomplete0.8740.751–1.018= 0.083
12 (main)recurrence-free survivalcoreas recordedcomplete0.8840.760–1.028= 0.111
13overall survivalcore plus twoas recordedtruncated at five years0.9250.753–1.138= 0.462
14overall survivalcore plus twoas recordedcomplete0.9320.784–1.108= 0.423
15overall survivalcoreas recordedcomplete0.9540.803–1.133= 0.590
16overall survivalcoreas recordedtruncated at five years0.9950.811–1.220= 0.960

The table in one sentence: this dataset gives no single answer to whether there is an association. The analysis setting gives the answer.

The axis that drives the curve is the one no methods section mentions

Averaging each axis over the other three and taking the difference gives how far that axis moves the estimate on the log scale:

AxisSwing
Node count coding0.184
Outcome definition0.093
Covariate set0.037
Follow-up length0.015

The dominant axis is whether node count is capped. The lower panel of the figure shows it directly: the dark dots in that row sit almost entirely in the left half.

This is worth pausing on. A methods section states the endpoint and lists the covariates — the two axes a reader can see, ranking second and third here. First place goes to a data-preparation decision that never appears at all.

The main analysis sits mid-curve, and that is the point

The pre-specified main analysis takes the least-processed option on each of the four axes: recurrence-free survival, core covariates, node count as recorded, follow-up complete.

Its hazard ratio is 0.884 (95% CI 0.760–1.028, p = 0.111), not statistically significant, at rank 12 of sixteen. On the same curve, 7 settings are significant.

The main analysis is neither the most conservative nor the most significant setting. Its standing does not come from where it sits on the curve; it comes from having been fixed before the curve existed. Were standing to come from position, then:

  • picking the most significant setting could be written up as the specification with the clearest signal
  • picking the most conservative one could be written up as the cautious approach

Both sentences are available, and both would be written after seeing the results. Picking a conservative setting after the fact and calling it caution is the same act as picking a significant one.

A robust result is an empty phrase

The sentence appears in papers constantly and carries no information on its own: it does not say which perturbations were run, what each one estimated, or whether any of them changed the conclusion.

Three things belong in the report:

  1. Which perturbations were run, listed individually, including the ones that changed nothing
  2. The effect estimate and interval under each, not just a p value, not a balance table, and certainly not a single sentence
  3. Whether any of them turned the conclusion over, and if so which, and why the main analysis is still the main analysis

Robust does not mean correct

The curve above is what a result that is not robust looks like. The opposite case deserves more caution: sixteen settings all reaching significance would still not establish that the conclusion is correct.

The reason is visible in this page’s own data. Confounding by indication is present in all sixteen settings, and swapping the outcome definition, the covariates, the coding, or the time window does not touch it. A tidy, uniform curve can be tilted as a whole in one direction.

Influence diagnostics makes the same point for meta-analysis: a pooled analysis can be perfectly robust to dropping any single study while every included study shares one systematic bias — because that bias does not disappear when one study leaves.

A sensitivity analysis rules out only what it actually perturbed. About the assumptions it left alone, it has said nothing.

What picking the most significant setting costs, measured

The claim above is that picking afterwards and fixing in advance are different things. How different can be measured.

The method: shuffle the exposure column, which makes the true association exactly zero, and rerun the same 16 settings. Repeat 4,000 times (seed 20260826), recording two numbers each time — the smallest p value, and the p value of the pre-specified setting.

Two horizontal bars comparing false positive proportions. The upper red bar is for reporting only the most significant of the sixteen settings, with a length corresponding to 17.9%; the lower blue bar is for reporting a single pre-specified setting, at 6.5%. A vertical dashed line marks the nominal 5% level and is labelled nominal 5% above the plot. The red bar extends far past the dashed line and the blue bar exceeds it slightly. The horizontal axis is scaled in percentages.
4,000 permutations, with the exposure shuffled so there is no association to find. Both arms run the same analyses; the only difference is which one gets reported.Plotting script figures/scripts/B8-07-sensitivity-analysis.R
  • Reporting only the most significant setting: 17.9% of runs claim significance
  • Reporting only the pre-specified setting: 6.5%

The gap is 11.4 percentage points. It needs no inference to read, because the exposure was shuffled, so every significant result above is a false positive.

Same settings, same dataset, same true association of zero. The only thing that changes is which result gets written up. That is why pre-specification is not an administrative formality.

What to look for when reading a paper

  1. Where the sensitivity analyses live. A table in the appendix listing an effect estimate per setting and a sentence in the discussion saying the result was robust are entirely different things.
  2. Whether there are effect estimates, or only p values and balance tables. With only p values, you cannot see whether the conclusion would turn over.
  3. Whether the main and sensitivity roles were fixed in writing. Look for pre-specified, or according to the protocol, and for a registration number.
  4. Whether the listed perturbations are only the ones that changed nothing. On paper, a complete set and a filtered set are indistinguishable.
  5. What was not perturbed. This usually matters more than what was — see the section above.

Run it yourself

library(survival)

d0 <- rotterdam
d0$rfs_time  <- pmin(d0$rtime, d0$dtime)
d0$rfs       <- as.integer(d0$recur == 1 | d0$death == 1)
d0$nodes_cap <- pmin(d0$nodes, 10)
d0$lpgr      <- log(d0$pgr + 1)

grid <- expand.grid(
  outcome  = c("rfs", "os"),
  covars   = c("core", "plus"),
  nodesf   = c("raw", "cap"),
  followup = c("full", "trunc5"),
  stringsAsFactors = FALSE
)

fit_one <- function(d, g) {
  if (g$followup == "trunc5") {
    tt <- 5 * 365.25
    d$rfs <- ifelse(d$rfs_time > tt, 0L, d$rfs); d$rfs_time <- pmin(d$rfs_time, tt)
    d$death <- ifelse(d$dtime > tt, 0L, d$death); d$dtime <- pmin(d$dtime, tt)
  }
  nod <- if (g$nodesf == "raw") "nodes" else "nodes_cap"
  cov <- if (g$covars == "core") c("age", "size", "grade", nod)
         else c("age", "size", "grade", nod, "meno", "lpgr")
  lhs <- if (g$outcome == "rfs") "Surv(rfs_time, rfs)" else "Surv(dtime, death)"
  f <- as.formula(paste(lhs, "~ hormon +", paste(cov, collapse = " + ")))
  summary(coxph(f, data = d))$coefficients["hormon", c("coef", "Pr(>|z|)")]
}

res <- t(sapply(seq_len(nrow(grid)), function(i) fit_one(d0, as.list(grid[i, ]))))
grid$hr <- exp(res[, 1]); grid$p <- res[, 2]
print(grid[order(grid$hr), ], row.names = FALSE)

Verified on R 4.6.0. Only the sixteen-setting part runs below; the permutation needs 4,000 iterations and lives in the full script.

Common misuses

MisuseWhy it is wrong
Writing only that the sensitivity analyses showed the result was robustWithout which perturbations, what each estimated, and whether any turned over, the sentence says nothing
Reporting only p values for the sensitivity analysesYou cannot see whether the conclusion would turn over, which is what they are for
Reporting propensity score sensitivity analyses as balance tablesA balance table describes the matching, not whether the estimate moved
Choosing which settings to run after seeing the resultsThat is selective reporting, and on paper it looks like pre-specification
Adopting the most conservative setting as the main analysis afterwardsThe same act as adopting the most significant one, pointed the other way
Reading all sensitivity analyses passed as the conclusion being correctIt rules out only what was perturbed; a shared bias survives every setting
Applying a multiplicity adjustment to sensitivity analysesThey ask about stability, not about significance
Treating leave-one-out as the whole of sensitivity analysisThat is influence analysis, varying data points rather than analysis decisions
Leaving the main and sensitivity roles unfixed in the manuscriptA reader cannot tell which one was decided in advance
Perturbing only the axes that leave the conclusion intactA filtered set is indistinguishable from a complete one

Rerun every number on this page

/opt/homebrew/bin/Rscript figures/scripts/B8-07-sensitivity-analysis.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.

One dataset, one question, sixteen individually defensible analyses. What is the hazard ratio at the end nearest the null, and what is that number for?

Show the answer and why

Correct answer: 0.995, while the other end sits clearly further from the null. Both ends are defensible analyses, so the quantity to read is the width itself, not one point chosen from inside it

0.995 is the setting closest to the null, and the other end is 0.695 — one dataset, one question, and a defensible reason for every setting in between. What this question trains is reading the whole curve as a quantity: its width says how sensitive the conclusion is to analysis decisions, and the fact that it crosses the null says that whether there is an association here is settled by the setting rather than by the data. 0.884 is the pre-specified main analysis, which sits mid-curve rather than at the right-hand end. 0.695 is the opposite end, and calling it the end nearest the null reverses the direction of the entire curve; its p value is indeed the smallest, but that follows from it being furthest from the null, not nearest to it.

Each of the four axes moves the estimate by some amount (swing on the log scale). Which axis drives the curve?

Show the answer and why

Correct answer: 0.184, the coding of node count. As recorded or capped at ten is a decision almost no paper spells out in its methods section

The node-coding swing is 0.184, roughly twice the second-placed axis, and there is a mechanism behind it: node count is the strongest prognostic factor in this dataset and is also related to who received treatment (worse prognosis, more likely treated), so how it enters the model decides how much of that confounding is removed — capping swaps the functional form of a strong confounder. The outcome definition comes second at 0.093, and the reasoning that swapping endpoints must be the largest available change does not hold here: recurrence-free and overall survival are highly correlated in breast cancer, so swapping them changes the event set while leaving the covariate structure alone. The covariate set is third at 0.037, and having the most room to move the estimate is precisely backwards — adding two variables only weakly related to treatment removes little further confounding. The lesson is that a methods section shows the endpoint and the covariate list while the spread is driven by the data-preparation decision nobody thinks needs explaining.

Where does the pre-specified main analysis fall among the sixteen settings ranked by hazard ratio, and what does that position mean?

Show the answer and why

Correct answer: Rank 12, mid-curve. It is neither the most conservative nor the most significant setting, and its standing rests entirely on having been fixed in advance

The main analysis lands at rank 12, mid-curve and slightly to the right. It does not reach statistical significance, while seven settings on the same curve do — and if the main analysis drew its standing from its position, this page would have no grounds for refusing those seven. Rank 16 is the end nearest the null, and equating the main analysis with the most conservative setting licenses an author to pick a conservative one after the fact and call it caution, which is the same act as picking the most significant one. Rank 1 is the end furthest from the null, and the claim that a main analysis should carry the clearest signal is the standard phrasing of selective reporting. Pre-specification has force precisely because it happens before this curve exists.

Shuffle the exposure so the true association is exactly zero, then rerun the same sixteen settings. If only the most significant one is reported, what proportion of runs claims significance?

Show the answer and why

Correct answer: 0.179. The exposure has been shuffled, so every significant result inside that proportion is a false positive

0.179 is the false positive proportion when only the most significant of the sixteen settings is reported. It needs no inference to read, because the exposure was shuffled: the null is built in rather than assumed. 0.065 is the pre-specified arm from the same simulation, and calling it the cherry-picked rate erases the gap between the two, which is the only thing this figure exists to show. 0.114 is the difference between them, a gap rather than a rate.

The permutation ran four thousand times. What is the Monte Carlo standard error of the false positive proportions it reports, and what does that number limit?

Show the answer and why

Correct answer: 0.0034. Differences finer than this magnitude are beyond what the simulation can resolve, so what it supports is the large gap between the two arms, not the decimals separating either bar from the nominal level

0.0034 is the Monte Carlo standard error near the nominal level, which is this simulation's resolution. It decides which differences may be read: the gap between the arms is 0.1140, more than thirty times it and entirely solid, while a few thousandths between a bar and the nominal level deserve far more caution. 0.0653 is the observed pre-specified proportion, and calling it the standard error would make the resolution as large as the signal, leaving the figure unable to show anything at all. 0.1140 is the gap between the arms, and calling it the standard error does the opposite — it condemns the one solid finding here as noise. Whether enough runs were done is settled by this number, not by intuition.

How many of the sixteen settings reach statistical significance, and what can and cannot be concluded from that count?

Show the answer and why

Correct answer: 7. The conclusion is not robust to changing an analysis decision; but in the other direction, even sixteen out of sixteen would not establish that it is correct

Seven settings reach significance and nine do not, so this conclusion is not robust to a change of analysis decision. The half worth remembering runs the other way: robust does not mean correct. Treatment in this dataset was given by indication, that confounding is present in all sixteen settings, and changing a setting cannot remove it — a curve where every point is significant can still be tilted as a whole in one direction. Reading uniform significance as a passed check is the misreading this page exists to block. 12 is the main analysis's rank on the curve rather than a count of significant settings; and even were it the count, reporting the significant settings because the main analysis understated the effect is the definition of selective reporting.

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.