Univariable screening table (with optional multivariable merge)
Source:R/regression_uv.R
table_regression_uv.RdFits one model per candidate predictor (the univariable screen)
and renders them as a single table with one row block per
predictor. With multivariable = TRUE (default), the full model
containing all predictors is merged side by side under
"Univariable" / "Multivariable" column groups – the standard
presentation of applied epidemiology (the
gtsummary::tbl_uvregression() + tbl_merge() workflow).
Arguments
- data
A data frame.
- outcome
The outcome column (unquoted name, tidyselect). For
method = "coxph", aSurv(time, status)expression evaluated indata(thetbl_uvregressionconvention).- predictors
Candidate predictor columns (tidyselect, e.g.
c(age, sex, education)orwhere(is.numeric)). The outcome column(s) are dropped from the selection automatically.- method
"lm"(default),"glm", or"coxph"(requires thesurvivalpackage; estimates render as HRs withexponentiate = TRUE).- family
A stats::family for
method = "glm", in any of the three formsstats::glm()accepts: a family object (binomial()), its name ("binomial"), or the bare constructor (binomial). Defaultbinomial(), somethod = "glm"alone is the logistic screen; supplyingfamilywithoutmethodselects the glm screen directly (a family can only mean that). Refused formethod = "coxph", andgaussian()with the identity link is refused too: usemethod = "lm"for the linear screen. Withmethod = "lm", any non-gaussianfamilyis refused the same way (usemethod = "glm"), and a suppliedgaussian()is ignored with a classed warning – the linear screen already fits it.- multivariable
Logical, default
TRUE: merge the full model (all predictors together) as a second column group.- complete_cases
Logical, default
FALSE.TRUErestricts ALL models to the rows complete on outcome + every predictor (common-sample comparison); the reduction is disclosed in the table note.- show_columns
Passed to
table_regression(). Defaultc("n", "b", "ci", "p")– thetbl_uvregressioncolumn set;"n"is the per-predictor sample size. The multivariable group carries noNcolumn (its singlenis a fit-statistics row, as in the reference layouts). For binary outcomes, add"n_events"for outcome event counts asevents/Nper factor level (each column group counts on its own estimation sample). Formethod = "lm", add"r2"(and/or"adj_r2") for the share of outcome variance each predictor explains on its own – see Variance explained below. Formethod = "coxph", the RMST / risk-difference families ("rmst","risk_diff", ...) work with an explicit numerictau/at_timeshared by every column: each univariable fit runs its ownboot_n-replicate bootstrap, and the multivariable group reports the covariate-adjusted estimand from the full fit.tau = "minmax"is refused (per-fit horizons would make the column incomparable across predictors).- show_intercept
Display the
(Intercept)rows? DefaultFALSE– the opposite oftable_regression()'s default, because each univariable fit carries its own nuisance intercept. See Intercepts.- title
Table title;
NULL(default) builds"Univariable and multivariable <type> regression: <outcome>".- ...
Passed to
table_regression()(exponentiate,vcov,cluster,p_adjust,digits,labels,output, ...).nestedis not meaningful for a screen and is refused.clustermust be a single vector with one value per row ofdata; it is aligned to each fit's own estimation sample automatically.
Value
See table_regression() (same output contract).
Sample sizes
By default each univariable model is fit on its own complete
cases, so N varies across predictors – that is what the N
column discloses (shown on the first row of each block), and a
table note states it whenever the Ns differ. The multivariable
model is fit on the complete cases of all its variables (its
n appears in the fit-statistics rows). Pass
complete_cases = TRUE to restrict every model – univariable and
multivariable – to the common complete-case sample.
Variance explained
show_columns = c("n", "b", "ci", "p", "r2") adds an
\(R^2\) column to the screen (method = "lm"): each
predictor block reports its own model's \(R^2\), on the
first row of the block like N. It answers what a coefficient and
its interval cannot – how much of the outcome the predictor
accounts for – and often shows that a firmly established
association still explains a small share of the variance. Add
"adj_r2" for the adjusted form. On the multivariable side the
\(R^2\) is one number for the whole model, so it stays in
the fit-statistics rows (where it is shown by default) instead of
being repeated down a column. Not available for method = "glm"
or "coxph": outside least squares only competing
pseudo-\(R^2\) measures exist, and spicy asks you to name
the one you want (show_fit_stats = "pseudo_r2_mcfadden").
Multiplicity
p_adjust (passed through to table_regression()) treats the
whole univariable screen as ONE family (all screened coefficients
together); the multivariable model is its own family, as in any
multi-model table.
Why the default screen is linear
The default method = "lm" fits the linear screen: R's canonical
model, and – when the outcome is continuous – the estimand with
the most direct reading. If the outcome looks binary under this
default, the screen proceeds as a linear probability model and
says so in a classed warning: LPM coefficients are probability
differences, comparable across models and samples in a way that
odds ratios are not (Mood, 2010), but the model's built-in
heteroskedasticity calls for vcov = "HC3". A two-level factor
(or logical) outcome is coded 0/1 on its second level – the glm
convention – and the warning names the modeled probability; an
outcome with more observed levels is refused (a multinomial
outcome has no linear screen). The classical
epidemiological screen is one argument away: method = "glm"
(with the default family = binomial()) gives the logistic
screen, and supplying any family selects the glm screen
directly.
Intercepts
Hidden by default on both sides (each univariable fit has its own
nuisance intercept), matching gtsummary::tbl_regression()'s
intercept = FALSE default. Pass show_intercept = TRUE to
display them: each univariable block then opens with its own fit's
(Intercept) row, and the multivariable model shows its intercept
as in any table_regression() table.
References
Batra, N. et al. (Eds.) (2021). The Epidemiologist R Handbook, Univariate and multivariable regression. https://epirhandbook.com/en/new_pages/regression.html
Mood, C. (2010). Logistic regression: Why we cannot do what we think we can do, and what we can do about it. European Sociological Review, 26(1), 67-82. doi:10.1093/esr/jcp006
Sjoberg, D.D., Whiting, K., Curry, M., Lavery, J.A., & Larmarange, J. (2021). Reproducible summary tables with the gtsummary package. The R Journal, 13(1), 570-580.
Examples
# \donttest{
table_regression_uv(
sochealth,
outcome = smoking,
predictors = c(age, sex, education),
family = binomial(),
exponentiate = TRUE
)
#> Ordered factor(s) detected. Polynomial contrasts (the R default for `ordered()`) decompose the factor into orthogonal trend components: `.L` = linear, `.Q` = quadratic, `.C` = cubic, `^k` = degree k. Coefficients are trends across the ordered levels, NOT per-level effects against a reference.
#> ℹ To display per-level (treatment) effects, refit with `factor(x, ordered = FALSE)` or set `options(contrasts = c("contr.treatment", "contr.treatment"))`.
#> This message is displayed once per session.
#> Univariable and multivariable logistic regression: smoking
#>
#> Univariable Multivariable
#> ─────────────────────────────── ─────────────────────
#> Variable │ N OR 95% CI p OR 95% CI
#> ─────────────────┼────────────────────────────────────────────────────────
#> age │ 1175 1.01 [1.00, 1.01] .292 1.01 [1.00, 1.02]
#> sex: │
#> Female (ref.) │ – – – – – –
#> Male │ 1175 0.95 [0.72, 1.26] .713 0.96 [0.73, 1.28]
#> education: │
#> .L │ 1175 0.53 [0.40, 0.70] <.001 0.53 [0.40, 0.69]
#> .Q │ 1.03 [0.81, 1.29] .832 1.02 [0.81, 1.29]
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#> n │ 1175
#> R² (McFadden) │ 0.02
#> R² (Nagelkerke) │ 0.03
#> AIC │ 1200.9
#>
#> Multi…
#> ──────
#> Variable │ p (OR)
#> ─────────────────┼────────
#> age │ .214
#> sex: │
#> Female (ref.) │ –
#> Male │ .800
#> education: │
#> .L │ <.001
#> .Q │ .852
#>
#> Note. Logistic regression models.
#> Std. errors: classical (Fisher information).
#> OR = odds ratio.
#> Coefficients exponentiated and displayed as OR; CI bounds exponentiated.
#> Ordered factor `education`: polynomial trends (.L = linear, .Q = quadratic).
# Linear screen with the share of variance each predictor
# explains on its own (the multivariable model reports its own
# R-squared in the fit-statistics rows).
table_regression_uv(
sochealth,
outcome = wellbeing_score,
predictors = c(age, sex, bmi),
show_columns = c("n", "b", "ci", "p", "r2")
)
#> Univariable and multivariable linear regression: wellbeing_score
#>
#> Univariable Multiv…
#> ──────────────────────────────────────── ───────
#> Variable │ N B 95% CI p R² B
#> ─────────────────┼───────────────────────────────────────────────────
#> age │ 1200 0.04 [-0.02, 0.10] .177 0.00 0.07
#> sex: │
#> Female (ref.) │ – – – – – –
#> Male │ 1200 3.89 [ 2.13, 5.64] <.001 0.02 4.27
#> bmi │ 1188 -0.60 [-0.83, -0.36] <.001 0.02 -0.67
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#> n │ 1188
#> R² │ 0.04
#> Adj. R² │ 0.04
#>
#> Multivariable
#> ─────────────────────
#> Variable │ 95% CI (B) p (B)
#> ─────────────────┼───────────────────────
#> age │ [ 0.01, 0.13] .023
#> sex: │
#> Female (ref.) │ – –
#> Male │ [ 2.52, 6.02] <.001
#> bmi │ [-0.91, -0.44] <.001
#>
#> Note. Linear regression models.
#> Std. errors: classical (OLS).
#> Each univariable model is fit on its own complete cases; N varies by predictor (1188-1200).
# }