Skip to contents

Simulates indicators under the null model while preserving observed group sizes, refits both models, and compares their likelihood differences. Models must differ by exactly one individual profile or one group class, with the other count, covariance structure and centering mode fixed. Grand-mean centering is repeated in every simulated refit. Person-centred fits are refused because this model does not specify a generative distribution for the group baselines removed by that transformation. This is a native parametric bootstrap, not an implementation of Mplus TECH14. It does not use a chi-square reference distribution. Any failed/nonconverged or reversed replicate makes the p-value NA, avoiding silent deletion of difficult fits.

Usage

bootstrap_lrt(
  null_model,
  alternative_model,
  data = NULL,
  iter = 199L,
  n_starts = 10L,
  max_iter = 1000L,
  tol = 1e-08,
  seed = NULL
)

Arguments

null_model

Smaller, converged multilpa() model, with or without membership covariates.

alternative_model

Larger model fitted to exactly the same data.

data

Optional. The data both models were fitted to, used to verify both fitted likelihoods; when omitted it is rebuilt from what the null model stores.

iter

Number of simulated datasets (at least two; use many for inference).

n_starts

Number of starts for each simulated fit.

max_iter

Maximum EM iterations for each simulated fit.

tol

Relative likelihood convergence tolerance.

seed

Optional seed: any whole number set.seed() accepts. The caller's random-number state is restored on exit.

Value

An object of class multilpa_bootstrap_lrt, carrying the observed statistic, the finite-simulation corrected p-value, its Monte Carlo standard error, the measurement blocks held fixed in every fit, and one record per replicate. Read it with the verbs that describe it: as.data.frame() gives the single-row test result, get_results(what = "replicates") one row per simulated replicate, summary() the test beside the replicate diagnostics, and plot() the simulated null distribution with the observed statistic marked. Inspect failed starts, boundary flags and likelihood replication before interpreting results.

Details

Fits made with missing = "fiml" are supported when both models were: every replicate is given the observed data's missing cells before it is refitted, preserving the pattern of available measurements. Calibration requires missingness independent of the indicators and latent classes, conditional on any fixed covariates. Carrying a fixed mask does not reproduce a general MAR mechanism that depends on observed indicators, or an MNAR mechanism. FIML estimation under MAR does not by itself validate this bootstrap under MAR; the retained size study covers MCAR only.

Membership-covariate fits (multilpa(profile_covariates = , group_covariates = )) are supported: the covariates are held at their observed values, group classes are drawn from the fitted group logits and profiles from each row's profile logits, as the model conditions on the covariates. Both models must use the same profile covariates, group covariates and slope specification, except that a one-group-class null, which cannot carry group covariates or slopes by group class, may be compared with an alternative that adds them. When data is omitted it is rebuilt from the alternative, whose columns include the null's. A covariate replicate counts as valid when its likelihood has converged even if a membership logit is still drifting, as it does for an empty or separated class in an over-fitted alternative: the statistic reads only the maximized likelihood, which such a fit has reached. The replicate table's logits_settled column records whether both refits' logits had also converged. The original models must be fully converged.

Models fitted with fixed, including those from fit_staged(), are supported: every replicate is refitted with the same blocks held at the same values, so the simulated statistics compare the same two constrained models as the observed statistic does. The p-value is then conditional on that measurement solution, which is treated as known and whose own uncertainty is not propagated. A constrained pair whose nesting cannot be established is refused rather than given a p-value.

Conditions

latents_bad_nesting when only one of the two models holds measurement blocks fixed, when they hold different blocks, when they hold the same blocks at different values, when they hold a measurement fixed while differing in the number of profiles (the held blocks then have different shapes, so the null is not a restriction of the alternative), or when the alternative does not estimate more free parameters than the null. latents_failed_replicates is warned when some replicate fails validation, and the p-value is NA. latents_unsupported_bootstrap refuses person-centred fits, for which this simulator has no group-baseline distribution. latents_unsupported_prior refuses fits estimated with a prior: their likelihoods are evaluated at posterior modes, not ML estimates. latents_incomparable_models when the two models differ in their observations, grouping, covariance structure, centering or missing-data handling; latents_bad_data when the data have missing indicators and the models were not fitted with missing = "fiml"; latents_bad_nesting also when two covariate models use different covariates or slope specifications (other than a one-group-class null).

Examples

set.seed(1)
example_data <- data.frame(
  school = rep(seq_len(10), each = 10),
  score = rnorm(100, rep(c(-2, 2), each = 50))
)
smaller <- multilpa(example_data, "score", "school", n_profiles = 1,
                    n_group_classes = 1, n_starts = 2, seed = 1)
larger <- multilpa(example_data, "score", "school", n_profiles = 2,
                   n_group_classes = 1, n_starts = 2, seed = 1)
# `iter` is small so the example runs quickly; use many more for inference.
comparison <- bootstrap_lrt(smaller, larger, iter = 9, n_starts = 1,
                            max_iter = 2000, tol = 1e-6, seed = 1)
comparison
#> Parametric bootstrap likelihood-ratio comparison
#> Null: 1 profile; alternative: 2 profiles
#> Observed statistic: 44.198110
#> p-value: 0.1 (Monte Carlo SE 0.0949) from 9 of 9 valid replicates