Guided analysis
Logistic regression odds ratios
A binary outcome, a set of covariates, and a Table 3 of odds ratios: unadjusted and adjusted side by side, a forest plot on a log axis, and continuous covariates reported per a clinically meaningful step such as age per 10 years.
Adjust for what else is going on
A group comparison tells you whether an outcome happened more often in one group. Logistic regression answers the next question: by how much, after accounting for the other things that differ between patients? Each covariate gets an odds ratio — the relative odds of the outcome per increment (for a number) or versus a reference level (for a category).
Unadjusted odds ratios come from one model per covariate. Adjusted odds ratios come from a single joint model, so each one is the effect of that variable with the others held fixed. Most of the gap between the two columns here is the confounding that the adjustment removed — when a treatment is given more often to sicker patients, the unadjusted column carries their extra risk and the adjusted column does not.
What an odds ratio is, and is not
Odds are events divided by non-events: 30 complications in 100 patients is a risk of 30% but odds of 30/70 = 0.43. An odds ratio compares those odds between two groups.
An odds ratio is not a risk ratio, and it is always the further of the two from 1. When the outcome is uncommon (under roughly 10%) the two are close enough that people read an OR as a relative risk; when the outcome is common — as in the example below, where 28% of patients had a complication — an OR of 0.50 corresponds to about a 42% reduction in risk, not the 50% that "half" suggests. Report it as an odds ratio and say so.
One more quirk: an adjusted odds ratio differs from the unadjusted one even when there is nothing to correct. Add a covariate that predicts the outcome and the odds ratio for everything else moves further from 1, whether or not that covariate has any relation to the exposure — statisticians call this non-collapsibility, and it is a property of the odds ratio itself, not evidence that bias was removed. So read a gap between the two columns as confounding only when the covariate you added is plausibly related to both the exposure and the outcome.
Is logistic regression appropriate?
Use it when each row is one independent participant, the outcome is binary (event / no event), and you have the baseline covariates you want to adjust for. The tool needs at least 10 patients in the smaller outcome group before it will fit a model, and it drops rows with a missing value in any column you use.
It checks for separation (a covariate that predicts the outcome almost perfectly, which makes odds ratios meaningless), for multicollinearity among numeric covariates (two variables carrying the same information), and it reports the model's overall discrimination as a C-statistic — measured on the very patients the model was fitted to, so it flatters the model and would be lower in new patients. Each of those is advisory — none of them blocks a result or changes a number. The tool does not fit penalized (Firth), matched, or mixed models; if separation is flagged, seek statistical review rather than reporting the affected odds ratios.
How to read the result
- OR < 1: lower odds of the outcome. OR > 1: higher.
- A numeric covariate's OR is per increment — set the increment (for example per 10 years of age) so that one step is clinically meaningful.
- A category's OR is versus its reference level, shown as "1 (reference)".
- A 95% CI that crosses 1 means the effect is not statistically resolved. It does not mean there is no effect.
- Adjusted odds ratios are adjusted only for the covariates you put in the model. Anything you did not measure could still be confounding the result.
With fewer than about 10 events per model term, adjusted estimates become unstable — the tool warns you when that happens.
Example output
| Characteristic | Unadjusted OR (95% CI) | Adjusted OR (95% CI) |
|---|---|---|
| arm (reference: Standard care) | ||
| New treatment | 1.02 (0.63–1.66, p=0.932) | 0.50 (0.28–0.91, p=0.023) |
| age (per 10 units) | 1.56 (1.19–2.05, p=0.001) | 1.69 (1.26–2.26, p<0.001) |
| stage (reference: I) | ||
| II | 1.85 (1.01–3.38, p=0.045) | 2.01 (1.07–3.78, p=0.030) |
| III | 2.61 (1.43–4.77, p=0.002) | 3.43 (1.73–6.79, p<0.001) |
The text Figura produced for this example, ready to paste into a methods section:
Characteristic Unadjusted OR (95% CI, p) Adjusted OR (95% CI, p) arm (reference: Standard care) New treatment 1.02 (0.63–1.66, p=0.932) 0.50 (0.28–0.91, p=0.023) age (per 10 units) 1.56 (1.19–2.05, p=0.001) 1.69 (1.26–2.26, p<0.001) stage (reference: I) II 1.85 (1.01–3.38, p=0.045) 2.01 (1.07–3.78, p=0.030) III 2.61 (1.43–4.77, p=0.002) 3.43 (1.73–6.79, p<0.001) Multivariable logistic regression (n = 320, 91 events) adjusted for arm, age, stage. Unadjusted odds ratios are from single-covariate models; adjusted odds ratios are from the joint model. Overall model discrimination: apparent (in-sample) C-statistic = 0.68. 13 observation(s) were flagged as influential (Cook's distance > 4/n); inspect them for data-entry errors. Analyses were performed with Figura (Saha, 2026; https://figurastats.org), which runs R with the ggplot2 package in the browser.
Sample data
Download sample.csv — 320 rows, columns arm, age, stage, complication. A frozen synthetic dataset generated by a script in the repository's data-raw/ folder; nothing in it is a real patient.
How to cite
Journals ask for a software statement. The methods text Figura generates already ends with one; this is the same attribution in reference form.
Saha S. Figura: clinical manuscript figures and statistics in the browser. 2026. https://figurastats.org
@misc{figura2026,
author = {Saha, Sandeep},
title = {Figura: clinical manuscript figures and statistics in the browser},
year = {2026},
url = {https://figurastats.org},
note = {Accessed <date>}
}