Conditional logistic regression
Why matched data cannot be fitted with ordinary logistic regression, what strata() actually conditions away, what happens when you put the matching variables back into the model (nothing happens, and nothing tells you), and how the 1:1 case relates to McNemar's test — which is four different numbers, not one.
What this model is for
Matching is a design-stage device for dealing with confounding. For every case you pick one or more controls who are identical or close on named variables — age, sex, year of presentation, same hospital — so that those variables cannot produce a difference between the groups by construction.
Matching is a decision that charges interest, though. Once the data are matched, people inside a matched set are no longer independent of one another, and ordinary logistic regression assumes every observation is independent. Feeding matched data to glm(..., family = binomial) throws the matching away entirely.
Conditional logistic regression instead compares only within a matched set, never across sets. Technically it conditions each set’s own intercept — “how likely anybody in this set was to be a case at all” — out of the likelihood, which is where the name comes from.
A matched set with controls contributes the probability that, of everyone in the set, it was this particular person who turned out to be the case:
That one expression explains three separate things further down this page, so it is worth memorising its shape: the numerator holds only the case, the denominator holds the whole set, and the set’s own identity appears nowhere in it.
The example on this page
datasets::infert is a matched case-control study of secondary infertility. It ships with base R, so there is nothing to install: 248 rows in 83 matched sets, exactly one case per set, matched on age, parity and education. The exposures are the number of spontaneous abortions (spontaneous) and the number of induced abortions (induced).
The same dataset carries the case-control studies chapter. That chapter is about the design; this page is only about the model.
library(survival)
data(infert)
# The correct model: the stratum IS the matched set
cond <- clogit(case ~ spontaneous + induced + strata(stratum), data = infert)
summary(cond)
# The wrong model: identical right-hand side, matching simply discarded
naive <- glm(case ~ spontaneous + induced, data = infert, family = binomial)
exp(cbind(OR = coef(naive), confint.default(naive)))
# Put a matching variable back in and watch what happens
clogit(case ~ spontaneous + induced + age + parity + strata(stratum), data = infert)Verified with R 4.6.0 and survival 3.8.6. clogit() lives in the survival package and calls coxph() internally, which is why its output looks like a survival analysis. That is the implementation, not a typo.
import numpy as np
import statsmodels.api as sm
from statsmodels.discrete.conditional_models import ConditionalLogit
inf = sm.datasets.get_rdataset("infert").data
X = inf[["spontaneous", "induced"]]
cond = ConditionalLogit(inf["case"], X, groups=inf["stratum"]).fit()
print(cond.summary())
print(np.exp(cond.params)) # conditional ORs
# The version that ignores the matching, for contrast
naive = sm.Logit(inf["case"], sm.add_constant(X)).fit()
print(np.exp(naive.params))statsmodels has ConditionalLogit and its estimates agree with R, but the diagnostics and downstream tooling are far thinner than survival's. Matched analyses are better done in R.
What happens when the matching is ignored
The two models carry identical right-hand sides. The only difference is whether strata(stratum) is present. That is what makes the comparison clean: any difference can be attributed to honouring the matching, and not to having added a covariate.
figures/scripts/B2-10-conditional-logistic.R| Variable | Conditional OR | Matching ignored | Ratio | Conditional SE | SE, matching ignored |
|---|---|---|---|---|---|
| Spontaneous abortions, per one more | 7.285 | 3.311 | 2.20 | 0.352 | 0.212 |
| Induced abortions, per one more | 4.092 | 1.519 | 2.69 | 0.361 | 0.206 |
The OR for spontaneous abortions falls from 7.285 to 3.311 — more than halved, and in the direction of 1. Induced abortions move the same way (4.092 to 1.519). The direction is not an accident: matching makes cases and controls resemble one another on the matching variables, so pooling them as if they were independent samples dilutes the association between exposure and outcome.
What strata() actually does
Go back to that likelihood. Read it as a contest: every member of the set is given a score of , and the expression asks for the probability that, with the scores distributed that way, the case is the one who was picked.
If everybody in a set has identical exposures, everybody has the same score, and that probability is no matter what is. Such a set contributes a constant to the likelihood, so its contribution to the estimate is 0 — not “a little”.
This is directly measurable. Under the main model on this page (spontaneous + induced, both counts), a set contributes nothing only when every member shares the same pair of counts. Of the 83 matched sets, 7 qualify; the remaining 76 sets (228 rows) are what actually drives the estimate.
Delete those 7 sets entirely and refit:
| Variable | Coefficient, all 83 sets | Coefficient, 76 sets only | SE, all sets | SE, those sets only |
|---|---|---|---|---|
spontaneous | 1.985876 | 1.985876 | 0.3524 | 0.3524 |
induced | 1.409012 | 1.409012 | 0.3607 | 0.3607 |
The largest discrepancy is 1.1e-15 for the coefficients and 5.6e-16 for the standard errors — floating-point machine precision, not a real difference. Those 7 sets never entered the answer.
The figure below does not show the main model. It shows a simplified version that can be drawn: clogit(case ~ spont_any + strata(stratum)). That differs from the main model in two ways — the exposure is collapsed to binary (ever versus never had a spontaneous abortion), and induced is not in the model at all. The condition is looser, so more sets qualify as identical: 28 identical within themselves and 55 varying. The principle is the same; this version is merely drawable (a box is filled or it is not), whereas counts are not. Under that simplified model, all sets and the varying sets alone both give a coefficient of 1.653873.
figures/scripts/B2-10-conditional-logistic.RMatching variables must not go back into the model
This is the easiest mistake to make in a matched analysis and the hardest to catch yourself. Here is what happens when a matching variable is added back:
| Added to the model | Its own coefficient | Its standard error | Coefficient on spontaneous | Fitted log-likelihood | What R said |
|---|---|---|---|---|---|
age | NA | 0 | 1.98587552 | -64.20223692 | nothing at all |
parity | NA | 0 | 1.98587552 | -64.20223692 | nothing at all |
education | NA | 0 | 1.98587552 | -64.20223692 | nothing at all |
age + parity + education | NA | 0 | 1.98587552 | -64.20223692 | nothing at all |
With no matching variable added, the coefficient on spontaneous is 1.98587552 and the log-likelihood is -64.20223692. Every row of the table above is bit-identical to that: the largest change is 0 in the coefficient and 0 in the log-likelihood. Not “close”. Zero.
The reason is that likelihood again. A matching variable is constant within a set, so its term in appears in the numerator and the denominator at the same time and cancels. It has no way to change anything, and the data contain no information with which to estimate it.
1:1 matching and its relationship to McNemar
When the matching is 1:1 (one control per case) and the exposure is binary, conditional logistic regression collapses to something you can compute by hand.
Cross-tabulate each pair by whether the case was exposed against whether the control was exposed:
| Control exposed | Control not exposed | |
|---|---|---|
| Case exposed | 17 (concordant) | 38 (b) |
| Case not exposed | 8 (c) | 20 (concordant) |
The 37 pairs on the diagonal share an exposure and, by the previous section, contribute nothing. The remaining 46 discordant pairs are the entire information content, and the conditional OR is exactly the ratio of those two cells:
In this dataset 38 divided by 8 is 4.750, and the OR reported by clogit() is 4.750. They differ by 1.8e-8, which is the optimiser’s convergence tolerance rather than a real difference.
Read the other way round, this is also why McNemar alone is usually not enough. It takes one binary exposure, cannot adjust for anything else, and handles neither 1:M matching nor a continuous exposure. Conditional logistic regression does all of those, and in the special case where McNemar applies it returns the same answer.
Common misuses
| Misuse | Why it is wrong |
|---|---|
| Fitting matched data with ordinary logistic regression | Members of a set are not independent; the estimate moves towards 1 and the p-value will not warn you |
| Using a change in the p-value to detect a dropped stratum term | The p-value can move either way; only the design documentation is reliable |
| Putting a matching variable in as a covariate | It is constant within a set, so the coefficient returns NA with no warning whatsoever |
Removing strata() because that column came back NA | That reverts to the biased model; the fix is to take the matching variable out |
Reporting without checking coef() for NA | It is the only place that tells you a variable was discarded |
| Assuming that more sets and tighter matching are always better | Overmatching makes the exposure constant within sets, and those sets contribute zero |
| Quoting the row count as the sample size when discussing power | The usable information is the number of sets that vary within themselves |
| Writing “conditional logistic regression equals McNemar” without naming the test | Only the uncorrected McNemar equals the score test, and correction is the default |
| Stopping at McNemar | It cannot adjust for covariates and handles neither 1:M matching nor continuous exposures |
| Reading an OR from a matched case-control study as an RR | The denominators were chosen by the investigator; see case-control studies |
| Splitting 1:M data down to 1:1 so that McNemar can be used | That discards the other controls; clogit() handles 1:M directly |
| Adjusting for the matching variables “again, to be safe” at the analysis stage | It is not safer; it just puts an inexplicable empty column in the report |
Reproducing every number on this page
/opt/homebrew/bin/Rscript figures/scripts/B2-10-conditional-logistic.RRead 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 same matched data give different odds ratios from conditional logistic regression and from ordinary logistic regression that ignores the matching. What does ignoring it do?
Show the answer and why
Correct answer: It gives 7.29 - the conditional model's value, and ignoring the matching drags the estimate to less than half of it
The conditional model gives 7.29 and ordinary logistic regression 3.31, losing more than half. Ignoring the matching is not merely adjusting for less: the matching variables determine who was selected as a control, so ignoring them ignores the selection mechanism, and the estimate is pulled towards one. 4.09 is a different term in the conditional model (per induced abortion), not the same contrast. In a matched study the matching is part of the design, and the analysis has to follow it.
In 7 of the 83 matched sets every member has the same exposure. What do those sets contribute to the conditional logistic estimate?
Show the answer and why
Correct answer: Those 7 sets contribute nothing - the conditional likelihood reduces to a constant for them
The 7 sets with no within-set variation add only a constant to the conditional likelihood, which is the same as not being there - conditional logistic regression runs on the 76 sets that do vary. 83 is the total, and using it as the effective sample size overstates how many parameters these data can carry. This is why "a couple of hundred records" is not a statement about effective sample size in a matched study: what counts is discordant sets, not rows.
Once each set is reduced to one case and one control, which pairs does McNemar's test actually use?
Show the answer and why
Correct answer: The discordant pairs, 46 of them - only pairs whose members differ in exposure carry the estimate
McNemar's test, and the paired odds ratio, run entirely on the 46 discordant pairs. However many concordant pairs there are - 37 here - they leave the estimate untouched and only inflate the apparent sample size; power depends on the discordant count, not on the 83 pairs in total. When a matched study reports a large n, the question to ask is how many of those pairs were discordant.
Chapters that use this method
Sources and licences
This page is original writing