Skip to contents

Estimates the proportional change in variance (PCV) sequentially by fitting intermediate (partially-adjusted) models, adding each predictor one-by-one. The step-specific PCV is the change in between-stratum variance contributed by a predictor given the variables already in the model. Because the steps are sequential it is order-dependent: it reflects each variable's marginal, model-dependent change, not an order-invariant “unique” contribution.

Usage

stepwise_pcv(
  data,
  outcome,
  vars,
  engine = "lme4",
  family = "gaussian",
  context = NULL,
  sampling_weights = NULL,
  estimation = c("fitted", "ML")
)

Arguments

data

Data frame with observations. Ensure make_strata was run first so the stratum variable exists.

outcome

Character string; the dependent variable.

vars

Character vector; predictors (strata groupings & covariates) to add sequentially to the model.

engine

Modeling engine ("lme4", "brms", "wemix", or "ordinal"). Default is "lme4"; switches to "wemix" automatically when sampling_weights is supplied, and to "ordinal" for an ordinal family or ordered-factor outcome.

family

Error distribution and link function. Default is "gaussian".

context

Optional character vector naming one or more higher-level context columns in data (e.g. "school", "region"), forwarded to every step's fit_maihda call so that each model carries a crossed contextual random intercept, (1 | context), alongside the intersectional stratum effect – the stepwise analogue of maihda(context = ) (the literature's contextual cross-classified MAIHDA). The reported Step_PCV / Total_PCV are then the between-stratum PCV net of the context (the context intercept is held in the null model and every step, so the stratum variance is isolated from it), and an extra Context_Variance column reports the between-context variance held at each step. Context columns join the complete-case filter so every step shares one analytic sample. A context variable may not be a stratum dimension, "stratum" itself, or one of vars. Supported by the lme4 and brms engines only (wemix and ordinal fit no crossed random effects). See fit_maihda for the full contextual-model semantics. Default NULL (no context).

sampling_weights

Optional name of a sampling-weight column for design-weighted stepwise fits; see fit_maihda. The weight column joins the complete-case filter so every step uses the same analytic sample.

estimation

Variance-estimation basis for the between-stratum variances compared across steps, "fitted" (default) or "ML"; see calculate_pcv. Affects Gaussian lmer fits only.

Value

A data.frame (class maihda_stepwise) showing the sequential models, the between-stratum variance at each step, and both the step-specific and total PCV. For a binary (binomial/Bernoulli) outcome it also carries the discriminatory-accuracy trajectory: AUC (the C-statistic of each step's model – step 0 is the strata-only discriminatory accuracy), Step_AUC and Total_AUC (the absolute change in AUC, delta-AUC, versus the previous step and versus the null), and MOR (the Median Odds Ratio, logit link only). These columns are absent for non-binary outcomes. When context is supplied, a Context_Variance column reports the between-context variance held at each step (for a brms fit the posterior median of each context's variance, summed over the contexts; Variance is a posterior median too). For a binary outcome the discriminatory-accuracy trajectory is still reported alongside it: the AUC and MOR are the intersectional-scope (between-stratum) quantities – the concordance of the fixed effects plus the stratum interaction, excluding the context random effect – so they carry the same net-of-context scope as the Step_PCV / Total_PCV columns, exactly as summary.maihda_model reports the intersectional-scope AUC for a contextual fit.

The variance-estimation basis chosen by estimation is recorded as an "estimation" attribute on the returned table; the basis actually used is recorded as "estimation_used" ("mixed" when estimation = "ML" but a step's ML refit was skipped at the boundary, leaving it on REML). Both are shown by print(). An "ml_variances" attribute is TRUE when the compared variances are maximum-likelihood estimates (glmer, wemix or ordinal fits, or lmer fits refitted by maximum likelihood); print() notes it under the default estimation, as calculate_pcv does.

Step_PCV is NA for any step whose preceding model has a between-stratum variance at the singularity boundary: that variance is the step's denominator, and at the boundary it carries only optimizer noise, so the ratio is undefined rather than small. Those steps are listed in an "undefined_step_pcv" attribute and noted by print(). Total_PCV is unaffected, since it divides by the null model's variance.

Details

All models are fit on the complete cases for outcome, stratum, and all variables in vars so that each sequential variance comparison uses the same analytic sample.

For a binary (or ordinal) outcome the sequential between-stratum variances live on the latent scale, whose level-1 variance is fixed by the link: a step that adds an individual-level variable (one varying within strata) rescales the latent metric itself, so its Step_PCV mixes explained variance with rescaling and can be understated or negative on that account – see the “Latent-scale families and rescaling” note in calculate_pcv. Steps that add a stratum-constant dimension are largely unaffected.

For a binary outcome the table additionally tracks discriminatory accuracy (Merlo et al. 2016): AUC is each model's C-statistic and Step_AUC / Total_AUC are its absolute change (delta-AUC), in contrast to the proportional Step_PCV / Total_PCV. The MOR is reported for the logit link (NA otherwise) and is a monotone transform of the between-stratum variance already in Variance. For a design-weighted fit (sampling_weights) the AUC is the design-weighted (population) C-statistic. Reuses maihda_discriminatory_accuracy on each step's fitted model, so no additional models are fit. Note that adding a stratum-defining dimension (one already encoded by the strata) typically leaves the AUC essentially unchanged: it re-partitions the between-stratum variance (so the PCV and MOR move) but not the per-stratum predicted ranking the rank-based AUC depends on. The AUC trajectory is therefore most informative for individual-level covariates that vary within strata.

References

Merlo, J., Wagner, P., Ghith, N., & Leckie, G. (2016). An original stepwise multilevel logistic regression analysis of discriminatory accuracy: the case of neighbourhoods and health. PLOS ONE, 11(4), e0153778.

Examples

# \donttest{
strata_result <- make_strata(maihda_sim_data, c("gender", "race"))
stepwise_pcv(strata_result$data, "health_outcome", c("gender", "race", "age"))
#>  Step      Model        Added_Variable Variance Step_PCV Total_PCV
#>     0 Null Model None (Intercept only)   26.715   0.0000    0.0000
#>     1    Model 1                gender   30.863  -0.1553   -0.1553
#>     2    Model 2                  race    2.346   0.9240    0.9122
#>     3    Model 3                   age    3.032  -0.2922    0.8865
#> 
#> Variance basis: as fitted (REML for Gaussian lmer, matching summary())
#> 

# Contextual cross-classified stepwise PCV: strata crossed with a higher-level
# context (country). Step_PCV / Total_PCV are then net of the country intercept,
# and a Context_Variance column reports the between-country variance per step.
cc <- make_strata(maihda_country_data, c("gender", "ses"))
stepwise_pcv(cc$data, "math", c("gender", "ses"), context = "country")
#>  Step      Model        Added_Variable Variance Context_Variance Step_PCV
#>     0 Null Model None (Intercept only)  915.232             1137   0.0000
#>     1    Model 1                gender 1114.595             1136  -0.2178
#>     2    Model 2                   ses    1.988             1128   0.9982
#>  Total_PCV
#>     0.0000
#>    -0.2178
#>     0.9978
#> 
#> Step_PCV / Total_PCV are the between-stratum PCV NET OF the context random
#> intercept held in every model; Context_Variance is that (summed)
#> between-context variance at each step.
#> 
#> Variance basis: as fitted (REML for Gaussian lmer, matching summary())
#> 
# }