| Title: | Multilevel Latent Profile, Class and Transition Models |
|---|---|
| Description: | Provides functions for fitting multilevel latent profile analysis, latent class, and latent transition models for continuous, categorical, and mixed-type variables. Models are estimated by expectation-maximization, handle missing data, and provide standard and robust standard errors. Model selection is supported by information criteria, entropy, classification diagnostics, and a parametric bootstrap likelihood ratio test. Covariates can be related to latent class membership using three-step methods with correction for classification error. Latent transition models estimate transitions between latent profiles across ordered occasions and are compatible with the 'tna' package for network and sequence analysis. The multilevel mixture formulation follows Vermunt (2003) <doi:10.1111/j.0081-1750.2003.t01-1-00131.x>. |
| Authors: | Mohammed Saqr [aut, cre, cph] (ORCID: <https://orcid.org/0000-0001-5881-3109>), Sonsoles López-Pernas [aut] (ORCID: <https://orcid.org/0000-0002-9621-1392>) |
| Maintainer: | Mohammed Saqr <[email protected]> |
| License: | GPL-3 |
| Version: | 0.9.23 |
| Built: | 2026-10-07 09:31:24 UTC |
| Source: | https://github.com/mohsaqr/latents |
as.data.frame() returns the primary table of a latents result: the
measurement parameters of a fitted model, the candidates of an enumeration,
the test of a bootstrap, and so on. It is the same table as get_results(x);
get_results() with what = returns the others.
## S3 method for class 'multilpa' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_start' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_covariates' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_additive' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_covariates' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_bootstrap_lrt' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_bootstrap_lrt' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_table' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_transitions' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_transitions' as.data.frame(x, row.names = NULL, optional = FALSE, ...)## S3 method for class 'multilpa' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_start' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_covariates' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_additive' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_covariates' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_bootstrap_lrt' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_bootstrap_lrt' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_table' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_transitions' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'summary_multilpa_transitions' as.data.frame(x, row.names = NULL, optional = FALSE, ...)
x |
An object returned by a latents function, or the |
row.names |
Passed to |
optional |
Ignored; present for compatibility with |
... |
Must be empty. An argument here raises |
A base data.frame, one row per parameter, candidate or test
depending on the object.
get_results(), latents-print, latents-summary.
fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) head(as.data.frame(fit))fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) head(as.data.frame(fit))
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.
bootstrap_lrt( null_model, alternative_model, data = NULL, iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL )bootstrap_lrt( null_model, alternative_model, data = NULL, iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL )
null_model |
Smaller, converged |
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 |
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.
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.
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).
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) comparisonset.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
An enumeration keeps every candidate it fitted. This returns one of them, so that a caller who wants to plot, summarise or test a particular candidate names it by its class counts instead of indexing the grid by position.
candidate_fit(x, n_profiles, n_group_classes = 1L, model = NULL)candidate_fit(x, n_profiles, n_group_classes = 1L, model = NULL)
x |
An |
n_profiles |
Number of individual profiles identifying the candidate. |
n_group_classes |
Number of group classes identifying the candidate. |
model |
The covariance model identifying the candidate, needed when
|
The fitted model for that cell of the grid: an object of class
multilpa, exactly as multilpa() returned it, with every verb of this
package available on it.
latents_unknown_candidate when the requested class counts are not in
the grid, and latents_failed_candidate when they are in the grid but
that fit raised an error, so no model exists to return. Neither situation
returns NULL, because a NULL would flow silently into whatever the
caller did next.
set.seed(1) d <- data.frame(g = rep(1:10, each = 10), y = rnorm(100)) candidates <- enumerate_classes(d, "y", "g", n_profiles = 1:2, n_group_classes = 1, n_starts = 2, seed = 1) candidate_fit(candidates, n_profiles = 2, n_group_classes = 1, model = "VVI")set.seed(1) d <- data.frame(g = rep(1:10, each = 10), y = rnorm(100)) candidates <- enumerate_classes(d, "y", "g", n_profiles = 1:2, n_group_classes = 1, n_starts = 2, seed = 1) candidate_fit(candidates, n_profiles = 2, n_group_classes = 1, model = "VVI")
One row per model with its log likelihood, number of parameters,
information criteria, classification quality and convergence, so
alternative specifications (for example trajectory classes alone against
a growth mixture model) are compared without assembling tables by hand.
delta_bic is each model's BIC above the smallest, and bic_weight the
Schwarz weight, exp(-delta/2) normalized over the models compared (Wagenmakers
and Farrell 2004): the approximate posterior probability of each model if
one of them is true, with equal prior probabilities.
compare_models(...)compare_models(...)
... |
Two or more fits from |
Information criteria are comparable only on the same data: every model must be fitted to the same outcome, persons (or groups) and observations, or the comparison is refused.
A base data.frame of class latents_comparison with one row per
model: model, n_classes, random, n_parameters, log_likelihood,
aic, bic, sabic, icl, entropy, smallest_share, delta_bic,
bic_weight, converged. Raises latents_bad_argument for fits it
cannot compare and latents_incomparable_models for fits to different
data.
Wagenmakers, E.-J., & Farrell, S. (2004). AIC model selection using Akaike weights. Psychonomic Bulletin & Review, 11, 192–196.
# Fixed trajectories against growth curves with a random intercept, # for the first 60 students few_students <- subset(growth_scores, student <= 60) trajectories <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", seed = 1) growth <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) compare_models(trajectories = trajectories, growth = growth)# Fixed trajectories against growth curves with a random intercept, # for the first 60 students few_students <- subset(growth_scores, student <= 60) trajectories <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", seed = 1) growth <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) compare_models(trajectories = trajectories, growth = growth)
A simulated dataset with the structure this package exists for, and all of it in one place: enrolments nested in students, ordered within each student, with a covariate that predicts which pattern an enrolment follows.
course_engagementcourse_engagement
A data frame with 1422 rows (106 students, 32 courses, up to 15 enrolments each) and 11 columns:
Integer student identifier, 1 to 106. The nesting unit;
pass it as id.
Integer course identifier, 1 to 32. Which course the enrolment was in.
Integer, the position of this course in the student's own
order, 1 upwards. Pass it as time. Students who took fewer courses have
shorter sequences, so the panel is ragged.
Numeric, course page views on the log1p(events) scale, standardized
within course.
Numeric, lecture videos viewed on the log1p(events) scale, standardized
within course.
Numeric, forum posts read on the log1p(events) scale, standardized
within course.
Numeric, forum posts written on the log1p(events) scale, standardized
within course.
Numeric, days the student was active in the course as
on the log1p(events) scale, standardized within course. It is generated from the four click measures and then
blurred, because a student is recorded present on a day precisely because
they clicked something, and a day is a coarse unit – one tick however
much happened inside it. The indicators are therefore locally dependent
on purpose: attendance correlates 0.51 to 0.67 with the click measures
overall and 0.16 to 0.26 within a single profile, which is more
association than a shared profile alone would produce. See the note on
local dependence below.
Numeric, the grade the student earned in the course
before this one, standardised across the cohort. At a student's first
course an entry grade stands in its place. It is a predictor of
engagement here, not an outcome of it: pass it as profile_covariates
to multilpa(profile_covariates = ), or as the predictor to
r3step().
Factor, "disengaged" or "engaged", the pattern the
row was generated from. This is the truth behind the data, not something
a study would observe; it is shipped so a fitted model can be checked
against what produced it. It is deliberately not called profile:
get_results(fit, "assignments") adds a column of that name, and a
dataset already
carrying it would make the verb refuse to join its own output.
Factor, "committed" or "wavering", the kind of
student the row's student was generated as. The truth behind the group
classes, shipped for the same reason.
Each row is one student's enrolment in one course. A student takes up to
fifteen courses, and sequence orders them, so the same rows support a
cross-sectional two-level model (id = "student"), a latent transition model
(time = "sequence"), and a membership-covariate model
(profile_covariates = "previous_grade").
Two kinds of student were generated. Both contain both patterns of engagement; they differ in the mix, which is the two-level quantity this package estimates. A committed student is engaged in about four enrolments in five, a wavering student in about one in four. Pooled across the sample the engaged share describes neither kind, which is the point: recovering the split is what a two-level model does and a pooled one cannot.
A latent profile model with a diagonal residual covariance assumes the
indicators are independent given the profile. attendance is built from the
click measures, so for these data that assumption is false, and the package
finds it: get_results(fit, "residuals") reports a largest residual of 0.21
p = 1.7e-15, and covariance_model = "full" improves the log likelihood
from -7618.5 to -7494.7 for twenty more parameters, which every information
criterion prefers.
This is deliberate. A bundled dataset that satisfies local independence
exactly can only ever show those verbs agreeing that nothing is wrong; here
the diagnostic has a real violation to find, its cause is known, and the fix
can be seen to work. One consequence is worth knowing before it surprises
you: under a diagonal model enumerate_classes() keeps preferring more
classes, because extra classes absorb residual association the model is not
allowed to express directly.
The five activity indicators are standardized within course: each value is
that enrolment's distance from its own course's mean, in that course's
standard deviations. Every indicator therefore has mean zero and standard
deviation one within every course, and a browse of 1.2 reads as "1.2
standard deviations more active than the average enrolment in that course".
This is the scale a learning-analytics reader meets, because activity volume
is not comparable across courses: a course with weekly quizzes generates more
clicks than a seminar whatever its students are like, so an analyst compares a
student with their coursemates rather than with the whole cohort.
In this simulation the courses do not in fact differ in activity volume, so
the standardization sets the scale rather than removing a course effect. It is
stated here rather than left implicit because a reader who assumed otherwise
would misread what the course column does.
The indicators are simulated directly on the log1p(events) scale, the scale
of a logged activity export; no raw counts are generated. They are
standardized on that scale, not as counts.
Standardizing is affine within a course, so it leaves the within-course skew
exactly as it found it, and raw learning-analytics counts are strongly
right-skewed: on an unlogged draft of these data the skew alone looked like an
extra class, exactly as a floor or ceiling effect does. The log removes the
skew; the standardization puts the courses on a common footing afterwards.
Simulated by data-raw/course-engagement.R, seed 2026. The
simulation is calibrated to the shape of a real learning-analytics export
– its scales, its spreads, its correlations and how persistent engagement
is across a student's courses – but only those aggregate constants were
used. No row, identifier or value of any real student is present, and the
source is not distributed.
vignette("lpa", package = "latents") for the analysis these data are used in,
multilpa() for the two-level model, lta() for the
sequence, and multilpa(profile_covariates = ) for previous_grade.
fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) as.data.frame(fit) get_results(fit, what = "profile_probabilities") # The generating truth ships alongside, so recovery can be checked directly. get_results(fit, what = "assignments", data = course_engagement, truth = "engagement")fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) as.data.frame(fit) get_results(fit, what = "profile_probabilities") # The generating truth ships alongside, so recovery can be checked directly. get_results(fit, what = "assignments", data = course_engagement, truth = "engagement")
One row per variable, with the summaries you would look at before fitting and the one that decides whether a two-level model has anything to find: the intraclass correlation, the share of each variable's variance that lies between groups rather than within them.
descriptives(x, ...) ## S3 method for class 'data.frame' descriptives(x, vars = NULL, id = NULL, by = NULL, ...) ## S3 method for class 'multilpa' descriptives(x, by = NULL, ...) ## S3 method for class 'multilpa_transitions' descriptives(x, by = NULL, ...) ## S3 method for class 'multilpa_covariates' descriptives(x, by = NULL, ...)descriptives(x, ...) ## S3 method for class 'data.frame' descriptives(x, vars = NULL, id = NULL, by = NULL, ...) ## S3 method for class 'multilpa' descriptives(x, by = NULL, ...) ## S3 method for class 'multilpa_transitions' descriptives(x, by = NULL, ...) ## S3 method for class 'multilpa_covariates' descriptives(x, by = NULL, ...)
x |
A data frame, or a fitted model of this package. A fit carries the
columns it was built from, so |
... |
Passed between methods. |
vars |
Character vector of column names to describe. Repeated names
are described once. For a data frame, defaults to every numeric column
that is not |
id |
Optional. The column identifying the groups rows are nested in.
Supplying it adds |
by |
Optional. |
An ICC near zero suggests little between-group mean variation in that variable. It does not rule out group classes that differ in other variables, profile prevalence, or other features of their distributions.
A base data.frame, one row per variable, or one row per variable
and by level, with the columns
variablecharacter: the column described.
ninteger: observations with a value.
n_missinginteger: observations without one.
mean,sd,min,max
numeric, and NA_real_ for a variable
that is not numeric.
n_distinctinteger: distinct observed values, which is the useful summary where a mean is not.
n_groups,icc
present only when id is known. icc is the
one-way random-effects estimate, and may be slightly negative when the
between-group mean square falls below the within-group one; that is the
estimator reporting no group structure, not an error. Rows with a
missing group ID do not contribute to either statistic.
A by column comes first, named after what it splits on and holding that
level's own value, NA for the stratum of rows whose stratifier is
missing. For every variable, n + n_missing summed over the strata equals
the number of input rows; that invariant is asserted before the table is
returned. Describing by a class the model assigned keeps that assignment
out of the described frame, so an indicator of the caller's own that is
named profile or group_class is summarized as itself, not replaced by
the assignment. A by whose name is one of the summary's own columns
cannot be told apart from them and raises latents_bad_data.
Bliese, P. D. (2000). Within-group agreement, non-independence, and reliability. In K. J. Klein & S. W. J. Kozlowski (Eds.), Multilevel theory, research, and methods in organizations.
multilpa() to fit, diagnostics() for the after-the-fit
counterpart.
descriptives( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student" ) # A missing stratifier is a stratum of its own, not a silent loss and not # missingness invented in a variable that has none. descriptives(data.frame(cohort = c("A", "A", "B", NA), score = 1:4), vars = "score", by = "cohort") fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) descriptives(fit) descriptives(fit, by = "profile")descriptives( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student" ) # A missing stratifier is a stratum of its own, not a silent loss and not # missingness invented in a variable that has none. descriptives(data.frame(cohort = c("A", "A", "B", NA), score = 1:4), vars = "score", by = "cohort") fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) descriptives(fit) descriptives(fit, by = "profile")
summary() reports what a model estimated. This reports whether to believe
it: how sharply the posterior separates the classes, how big each class
actually is, how confidently each unit was assigned, and whether the
within-profile independence the model assumes survives contact with the data.
diagnostics(x, data = NULL, plots = TRUE, by = c("profile", "overall"), ...) ## S3 method for class 'multilpa_diagnostics' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_diagnostics' print(x, ...) ## S3 method for class 'multilpa_diagnostics' plot(x, ...)diagnostics(x, data = NULL, plots = TRUE, by = c("profile", "overall"), ...) ## S3 method for class 'multilpa_diagnostics' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'multilpa_diagnostics' print(x, ...) ## S3 method for class 'multilpa_diagnostics' plot(x, ...)
x |
A fitted model of this package. |
data |
Optional. The data the model was fitted to. A fit carries the columns it was built from, so this is only needed to override them. |
plots |
|
by |
|
... |
For |
row.names, optional
|
Passed to the base generic; |
It gathers the four classification tables of get_results() — "entropy",
"classification", "average_posteriors" and "residuals" — and prints
one line of reading per diagnostic rather than four differently shaped
tables. The tables themselves come back from get_results() on the result, or
on the fit.
An object of class multilpa_diagnostics. Read its tables with
get_results(result, what = ), which offers "entropy", "classification",
"average_posteriors", "residuals" and "all", and never with $. A
model family that has no bivariate residuals leaves that table out of
"all", and asking for it by name raises latents_no_group_classes.
print() returns the object invisibly, having printed one line per
diagnostic: relative entropy, smallest class, lowest average posterior and
largest residual, at each level the fit has. plot() returns every
classification plot as a latents_plots list of ggplot objects – the
effective profile sizes, the posterior probability of each case's assigned
profile, each case's relative entropy, and the average posterior
probability matrix – that draws all four when printed.
as.data.frame() returns the entropy table, the primary one.
get_results() for these tables and every other one, descriptives()
for the before-the-fit counterpart, and summary() for what the model
estimated rather than whether to trust it.
fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) quality <- diagnostics(fit) quality get_results(quality, what = "classification") get_results(diagnostics(fit, by = "overall"), what = "residuals")fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) quality <- diagnostics(fit) quality get_results(quality, what = "classification") get_results(diagnostics(fit, by = "overall"), what = "residuals")
Fits every requested combination and retains errors and warnings alongside successful fits. Information criteria are descriptive: neither the smallest BIC nor high entropy guarantees the correct number of classes. Unconverged, boundary, and unreplicated fits are reported, not silently selected.
enumerate_classes( data, vars, id, n_profiles = 1:4, n_group_classes = 1:3, model = "basic", seed = NULL, family = "profiles", between_variance = c("varying", "equal"), time = NULL, ... )enumerate_classes( data, vars, id, n_profiles = 1:4, n_group_classes = 1:3, model = "basic", seed = NULL, family = "profiles", between_variance = c("varying", "equal"), time = NULL, ... )
data |
Data frame. |
vars |
Continuous indicator names. |
id |
The column that identifies the groups the observations are
nested in (students in schools, reports in students). |
n_profiles |
Positive integer profile counts to try. |
n_group_classes |
Positive integer group-class counts to try. With
|
model |
The covariance structures to cross with the class counts.
|
seed |
Optional reproducible seed for each fit. |
family |
|
between_variance |
For group-class families: the between-variance
restrictions to cross, |
time |
The occasion column: enumerate latent transition models
( |
... |
Further arguments to |
An object of class multilpa_enumeration. Read it with the verbs
that describe it rather than by reaching into it: as.data.frame() gives
one row per candidate model with every criterion and diagnostic,
summary() gives one row per information criterion naming the candidate
that minimises it, plot() draws one criterion across the grid, and
candidate_fit() returns the fitted model for one cell of the grid.
latents_bad_argument when model names an unknown structure, when it is
given together with another way of setting the structure, or when it is
given although every indicator is categorical.
Celeux, G., & Govaert, G. (1995). Gaussian parsimonious clustering models. Pattern Recognition, 28(5), 781–793.
candidate_fit() to take one fitted model out of the grid,
summary.multilpa_enumeration() for the criterion-by-criterion comparison.
set.seed(1) d <- data.frame(g = rep(1:10, each = 10), y = rnorm(100)) candidates <- enumerate_classes(d, "y", "g", n_profiles = 1:2, n_group_classes = 1, n_starts = 2, seed = 1) as.data.frame(candidates) summary(candidates) # Single level, crossing class counts with two named structures: single <- enumerate_lpa(iris, c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"), n_profiles = 1:3, model = c("VVV", "EEE"), n_starts = 2, seed = 1) summary(single)set.seed(1) d <- data.frame(g = rep(1:10, each = 10), y = rnorm(100)) candidates <- enumerate_classes(d, "y", "g", n_profiles = 1:2, n_group_classes = 1, n_starts = 2, seed = 1) as.data.frame(candidates) summary(candidates) # Single level, crossing class counts with two named structures: single <- enumerate_lpa(iris, c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"), n_profiles = 1:3, model = c("VVV", "EEE"), n_starts = 2, seed = 1) summary(single)
Fits single-level latent class models with each number of classes in
n_classes and returns them in one table for comparison by information
criteria, entropy and the diagnostics of each fit. Every indicator is
categorical unless named in ordinal or count, as in lca(). It is
enumerate_classes() with id = NULL,
and it returns the same object; the number of classes is its n_profiles
column.
enumerate_lca(data, vars, n_classes = 1:4, ...)enumerate_lca(data, vars, n_classes = 1:4, ...)
data |
A data frame with one row per observation. |
vars |
Names of the categorical indicator columns. |
n_classes |
Positive whole numbers of classes to compare. |
... |
Further arguments for |
For observations nested in groups, use enumerate_classes() with the
grouping column as id and the indicators as categorical.
An object of class multilpa_enumeration, as enumerate_classes()
returns: as.data.frame() gives one row per candidate, summary() the
candidate each criterion prefers, plot() the criteria against the
number of classes, and candidate_fit() one fitted candidate, named by
its n_profiles.
latents_bad_argument when id, n_group_classes, categorical or
model is passed.
lca() to fit one model; enumerate_lpa() for continuous
indicators.
models <- enumerate_lca(student_esm, c("time_with_friends", "on_social_media", "sports"), n_classes = 1:3, n_starts = 2, seed = 1) summary(models)models <- enumerate_lca(student_esm, c("time_with_friends", "on_social_media", "sports"), n_classes = 1:3, n_starts = 2, seed = 1) summary(models)
Fits single-level latent profile models with each number of profiles in
n_profiles under each covariance structure in model, and returns them
in one table for comparison by information criteria, entropy and the
diagnostics of each fit. It is enumerate_classes() with id = NULL, and
it returns the same object.
enumerate_lpa(data, vars, n_profiles = 1:4, model = "basic", ...)enumerate_lpa(data, vars, n_profiles = 1:4, model = "basic", ...)
data |
A data frame with one row per observation. |
vars |
Names of the indicator columns. |
n_profiles |
Positive whole numbers of profiles to compare. |
model |
The covariance structures: |
... |
Further arguments for |
For observations nested in groups, use enumerate_classes() with the
grouping column as id, which also crosses the number of group classes.
An object of class multilpa_enumeration, as enumerate_classes()
returns: as.data.frame() gives one row per candidate, summary() the
candidate each criterion prefers, plot() the criteria against the
number of profiles, and candidate_fit() one fitted candidate.
latents_bad_argument when id or n_group_classes is passed, or when
model names an unknown structure.
Celeux, G., & Govaert, G. (1995). Gaussian parsimonious clustering models. Pattern Recognition, 28(5), 781–793.
lpa() to fit one model; enumerate_lca() for categorical
indicators; bootstrap_lrt() to test one number of profiles against
another.
models <- enumerate_lpa(srl, c("cognitive_strategies", "intrinsic_value", "self_efficacy", "self_regulation", "test_anxiety"), n_profiles = 1:3, n_starts = 2, seed = 1) summary(models) plot(models)models <- enumerate_lpa(srl, c("cognitive_strategies", "intrinsic_value", "self_efficacy", "self_regulation", "test_anxiety"), n_profiles = 1:3, n_starts = 2, seed = 1) summary(models) plot(models)
Fits mixture_regression() for every requested number of classes (and of group
classes, for the two-level model) and returns their fit statistics in one
table. Optionally adds a parametric bootstrap likelihood-ratio test of
k - 1 against k classes (McLachlan 1987; Nylund, Asparouhov and Muthen
2007): outcomes are simulated from the k - 1 class fit, both models are
refitted to every replicate, and the p-value is the share of replicate
statistics at least as large as the observed one, (1 + b) / (1 + B).
enumerate_regressions( formula, data, n_classes = 1:4, n_group_classes = 1L, bootstrap = 0L, bootstrap_starts = 3L, seed = NULL, ... ) ## S3 method for class 'latents_regression_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_regression_enumeration' get_results( x, what = c("fit", "model"), n_classes = NULL, n_group_classes = 1L, ... ) ## S3 method for class 'latents_regression_enumeration' print(x, digits = 4L, ...)enumerate_regressions( formula, data, n_classes = 1:4, n_group_classes = 1L, bootstrap = 0L, bootstrap_starts = 3L, seed = NULL, ... ) ## S3 method for class 'latents_regression_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_regression_enumeration' get_results( x, what = c("fit", "model"), n_classes = NULL, n_group_classes = 1L, ... ) ## S3 method for class 'latents_regression_enumeration' print(x, digits = 4L, ...)
formula |
A model formula |
data |
A data frame. |
n_classes |
Integer vector of class counts to fit. |
n_group_classes |
Integer vector of group-class counts (two-level
model; needs |
bootstrap |
Number of bootstrap replicates for the likelihood-ratio
test; |
bootstrap_starts |
Random starts for each bootstrap refit. |
seed |
|
... |
Further arguments to |
x |
A |
row.names, optional
|
Unused; part of the generic. |
what |
|
digits |
Significant digits. |
An object of class latents_regression_enumeration. Its table (from
as.data.frame() or get_results(x, "fit")) has one row per model:
n_classes, n_group_classes, log_likelihood, n_parameters,
aic, bic, bic_rows, sabic, icl, entropy, smallest_share,
converged, n_best_replicated, best_bic (the minimum-BIC row), and,
with bootstrap > 0, blrt_statistic, blrt_p_value and
blrt_replicates (the successful replicates) and blrt_flagged (the
failed, unconverged, separated or reversed-likelihood replicates). The
p-value is NA if any requested replicate fails validation. get_results(x, "model", n_classes = , n_group_classes = ) returns one fitted model.
latents_bad_argument for an empty or invalid grid. A model that cannot
be fitted at some count (for example every start degenerates) gets a row
of NA with its condition message in note, rather than stopping the
comparison.
McLachlan, G. J. (1987). On bootstrapping the likelihood ratio test statistic for the number of components in a normal mixture. Applied Statistics, 36, 318–324.
Nylund, K. L., Asparouhov, T., & Muthen, B. O. (2007). Deciding on the number of classes in latent class analysis and growth mixture modeling: A Monte Carlo simulation study. Structural Equation Modeling, 14, 535–569.
classes <- enumerate_regressions(score ~ hours, data = study_hours, n_classes = 1:3, n_starts = 3, seed = 1) classes with_test <- enumerate_regressions(score ~ hours, data = study_hours, n_classes = 1:2, n_starts = 3, seed = 1, bootstrap = 19) as.data.frame(with_test)classes <- enumerate_regressions(score ~ hours, data = study_hours, n_classes = 1:3, n_starts = 3, seed = 1) classes with_test <- enumerate_regressions(score ~ hours, data = study_hours, n_classes = 1:2, n_starts = 3, seed = 1, bootstrap = 19) as.data.frame(with_test)
Estimates the measurement model once, on its own, and then estimates the group-class structure with that measurement held fixed. This is the staged workflow used when the measurement solution is meant to be decided before the grouping question is asked, so that adding group classes cannot move the profiles they are meant to describe.
fit_staged( data, vars, id, n_profiles, n_group_classes = 2L, measurement = NULL, variance_model = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, missing = c("error", "fiml"), covariance_model = c("diagonal", "full"), categorical = character(), min_probability = 1e-10, time = NULL )fit_staged( data, vars, id, n_profiles, n_group_classes = 2L, measurement = NULL, variance_model = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, missing = c("error", "fiml"), covariance_model = c("diagonal", "full"), categorical = character(), min_probability = 1e-10, time = NULL )
data |
A data frame containing indicators and a group identifier. |
vars |
Unique character vector of continuous indicator column names. |
id |
Name of the observed group identifier column. Character, factor, or
numeric identifiers are supported; missing identifiers are not. Required
here: this model has a second level by construction, so it has no
single-level form and does not take |
n_profiles |
Positive integer number of individual profiles. |
n_group_classes |
Number of latent group classes to estimate in the second stage. Must be at least two; one would leave nothing to estimate. |
measurement |
Optional fitted |
variance_model |
Either |
n_starts |
Number of EM starts, used for both stages. |
max_iter |
Nonnegative integer maximum number of EM updates per start.
|
tol |
Positive relative log-likelihood tolerance. Convergence requires
absolute change no greater than |
min_variance |
Positive lower bound on each variance, or each covariance eigenvalue for full covariance, in squared input units. This defines a constrained maximum-likelihood problem. Bound-active estimates are explicitly reported and generate a warning. |
seed |
Optional random seed: any whole number |
missing |
|
covariance_model |
|
categorical |
Character vector naming indicators to treat as categorical. Each is modelled by unrestricted, profile-specific response probabilities over its observed categories, which is the latent class measurement model. Binary, ordinal and unordered indicators are all handled by the same unrestricted parameterization; numeric, integer, logical, character and factor columns are accepted. Indicators not named here stay Gaussian, so naming a subset fits a mixed-mode model. |
min_probability |
Positive lower bound on every categorical response
probability, defining a constrained maximum-likelihood problem in the same
way |
time |
Optional name of a column giving each observation's position
within its group, such as a wave, occasion or course number. The model does
not use it; it is stored so that |
The second stage treats the measurement solution as known, so its
Wald standard errors (parameter_inference()'s default), information
criteria and likelihood-ratio comparisons are conditional on that solution
and are narrower than they would be if the measurement had been estimated
jointly. parameter_inference(fit, method = "bootstrap") removes that
conditioning for the standard errors and intervals: every resample of the
groups is staged again, measurement first, so the replicates carry the
first stage's sampling variability into the second stage, and the table
also reports the measurement parameters with their own errors. A
measurement supplied by the caller is re-estimated on each resample
with the same options, since the resample is new data. The conditioning
of the criteria and likelihood comparisons is a property of staging
itself, and it is the reason both parameter counts are reported:
compare staged fits with one another using n_parameters, and compare a
staged fit with a jointly estimated one using
n_parameters_with_measurement, remembering that the staged likelihood is
not the joint maximum and the comparison is descriptive.
A multilpa object for the second stage, so that every accessor,
diagnostic and method works on it unchanged. Its measurement parameters
are exactly the first stage's. It additionally carries fixed, naming the
held blocks; n_parameters, counting only the parameters this stage
estimated; and n_parameters_with_measurement, which adds the held
measurement back. Use get_results() with what = "stages" for a tidy
two-row summary of both stages.
multilpa() with fixed for finer control over which blocks are
held, and starting_values() with what = "measurement" for the values a
stage hands on.
set.seed(7) example_data <- data.frame( school = rep(seq_len(30), each = 8), score_a = stats::rnorm(240), score_b = stats::rnorm(240) ) staged <- fit_staged(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) get_results(staged, what = "stages") get_results(staged, what = "profile_probabilities")set.seed(7) example_data <- data.frame( school = rep(seq_len(30), each = 8), score_a = stats::rnorm(240), score_b = stats::rnorm(240) ) staged <- fit_staged(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) get_results(staged, what = "stages") get_results(staged, what = "profile_probabilities")
Builds one tna::tna() model per latent group class of a fitted transition
model, collected into the group_tna object tna's grouped verbs expect.
A class row with no expected outgoing moves keeps the fit's unestimated
transition probabilities and raises latents_empty_transition_row.
get_group_tna(x, ...) ## S3 method for class 'multilpa_transitions' get_group_tna(x, label = "Group class", ...) ## S3 method for class 'multilpa_lta' get_group_tna(x, ...) ## S3 method for class 'multilpa' get_group_tna(x, label = "Group class", ...) ## S3 method for class 'multilpa_covariates' get_group_tna(x, label = "Group class", ...)get_group_tna(x, ...) ## S3 method for class 'multilpa_transitions' get_group_tna(x, label = "Group class", ...) ## S3 method for class 'multilpa_lta' get_group_tna(x, ...) ## S3 method for class 'multilpa' get_group_tna(x, label = "Group class", ...) ## S3 method for class 'multilpa_covariates' get_group_tna(x, label = "Group class", ...)
x |
A fitted model from |
... |
Passed to |
label |
What the classes are called in tna's output. |
An object of class group_tna, one tna model per latent class:
centralities() returns one tidy table with a group column,
compare() contrasts two classes, and plot() draws them together.
get_tna() for one network over the whole sample.
if (requireNamespace("tna", quietly = TRUE)) { activity <- c("browse", "lectures", "forum_read") moves <- lta(course_engagement, vars = activity, id = "student", time = "sequence", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) get_group_tna(moves) }if (requireNamespace("tna", quietly = TRUE)) { activity <- c("browse", "lectures", "forum_read") moves <- lta(course_engagement, vars = activity, id = "student", time = "sequence", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) get_group_tna(moves) }
One verb returns every tidy table this package computes, named by what.
The tables were once eleven separate verbs and seven as.data.frame()
methods with their own what catalogues; a reader had to know which verb
owned which table before they could ask for it. Here the object decides
what it can offer and what names it, so the same call shape reads a
measurement model, a classification diagnostic and a transition matrix.
get_results(x, what = NULL, ...) ## Default S3 method: get_results(x, what = NULL, ...) ## S3 method for class 'multilpa' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_covariates' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_transitions' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_enumeration' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_bootstrap_lrt' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_start' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_diagnostics' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_covariates' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_transitions' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_enumeration' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_bootstrap_lrt' get_results(x, what = NULL, ...)get_results(x, what = NULL, ...) ## Default S3 method: get_results(x, what = NULL, ...) ## S3 method for class 'multilpa' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_covariates' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_transitions' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_enumeration' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_bootstrap_lrt' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_start' get_results(x, what = NULL, ...) ## S3 method for class 'multilpa_diagnostics' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_covariates' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_transitions' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_enumeration' get_results(x, what = NULL, ...) ## S3 method for class 'summary_multilpa_bootstrap_lrt' get_results(x, what = NULL, ...)
x |
A fitted model of this package, an enumeration grid, a bootstrap comparison, a starting-value set, a classification-diagnostics object, or the summary of any of them. |
what |
Which table to return, or |
... |
Arguments for the requested table. Each table accepts only the
arguments listed for it below, and any other name raises
|
A base data.frame for a single what, whose columns are given
under Tables, or a named list of such data frames for what = "all".
Every fitted model offers these:
"profiles"The Gaussian measurement model, one row per profile
and continuous indicator: profile, indicator, mean, variance,
standard_deviation. Zero rows when every indicator is categorical.
Takes scale, and data, which adds mean_standard_error and
variance_standard_error.
"responses"The categorical measurement model, one row per
profile, categorical indicator and category: profile, indicator,
category, probability, threshold. The threshold is
qlogis(P(y <= category)) and is NA_real_ for each indicator's final
category, where the cumulative probability is one. Takes data, which
adds probability_standard_error.
"covariances"The within-profile residual covariance matrices,
one row per profile and ordered pair of continuous indicators:
profile, indicator, indicator_2, covariance. The diagonal
parameterization carries no covariance array but does state a covariance
matrix – the variances on the diagonal and exact zeros off it – and it
is reported explicitly, so one shape answers both parameterizations.
"counts"Effective class memberships, one row per class at each
level the fit has: level, class, effective_count (the summed
posteriors) and effective_proportion. These differ from the modal
counts in "classification" whenever entropy is below one.
"model"One row describing the whole fit: its dimensions,
likelihood, parameter counts, aic, bic_groups, bic_individual and
its convergence and boundary diagnostics. The columns are those of the
family: a covariate fit reports its predictor counts, because padding
those onto a two-level fit would leave the common case mostly NA.
Comparing fits
across families is what "information_criteria" is for, and that table
does have one column set for every family.
"posteriors"One row per individual and profile: row, group,
profile, posterior, modal. Takes format.
"assignments"One row per observation, the columns the fit was
built from (or those of data) followed by profile, group_class
where the model has one, uncertainty, and one posterior_profile_*
column per profile. uncertainty is one minus the posterior of the
assigned profile, which is what modal assignment discards. Takes data
and truth; truth returns a recovery cross-tabulation instead,
described below.
"classification"Classification quality, one row per class and
level: level, class, n_modal, proportion_modal, estimated_n,
estimated_proportion, average_posterior,
odds_correct_classification. Takes level. Under sampling weights
estimated_n and estimated_proportion (and so the odds of correct
classification) are weighted posterior totals; the modal and average
posterior columns describe the units supplied.
"average_posteriors"For the units assigned to each class, their
mean posterior of belonging to every class: level, assigned_class,
class, n_assigned, average_posterior. Each assigned class's rows
sum to one. Conditions on the assigned class, where
"classification_errors" conditions on the true one; the two are
different numbers, not two spellings of one table. Takes level.
"classification_errors"The probability that a unit truly in one
class is assigned to another: level, true_class, assigned_class,
probability, summing to one within each true_class. Takes level.
On a membership-covariate fit this is a marginal summary over the fitted
units, not a classification-error adjustment conditional on covariates.
A fit with a noise component raises latents_unsupported_noise, as
three_step() does, and "all" and summary() leave the table out.
"bch_weights"The BCH inverse-error weights, one row per unit and
class: level, unit, assigned_class, class, weight. Takes
level; weight a regression with one level, not with "both". On a
membership-covariate fit these invert the marginal error matrix and are
not a conditional three-step correction. Refused for a fit with a noise
component, like "classification_errors".
"entropy"One row per level: level, n_classes, n_units,
entropy_sum, relative_entropy.
"residuals"Bivariate residuals for every indicator pair, testing
the within-profile independence the measurement model assumes: profile,
indicator_1, indicator_2, kind, observed, expected, residual,
effective_n, statistic, df, p_value, p_adjusted. Takes data,
by and adjust.
"information_criteria"Every likelihood-penalty criterion the
package computes. Takes format and definitions.
"sequences"The assignments in occasion order, one row per
observation: group, group_class, time, profile. Takes format.
Raises latents_no_time for a fit made without time.
"sequence_summary"One row per group class, summarizing how much
data it contributes: group_class, groups, observations,
mean_length, median_length, shortest, longest, complete,
gaps. Raises latents_no_time for a fit made without time.
"starts"One row per EM start: start, log_likelihood,
converged, iterations, error and, where the family records it,
boundary.
"data"The columns of the original data the model was fitted to,
one row per observation in input order: the identifier, the occasion
where there is one, and the indicators under their original names.
Columns a model never saw are not here, which is why three_step() and
r3step() still take data.
Every family with discrete group classes adds:
"group_posteriors"One row per observed group and group class:
group, group_size, log_likelihood, group_class, posterior,
modal. Takes format.
And each family adds its own:
"profile_probabilities", "stages"
A multilpa fit.
"profile_probabilities" has one row per group class and profile:
group_class, profile, probability, group_class_probability.
"stages" has one row per estimation stage: stage, group_classes,
fixed, log_likelihood, parameters, parameters_with_measurement,
converged. An ordinary fit has a single "joint" row and a
fit_staged() result has two; fixed is NA_character_ for a stage
that estimated every block, never a sentinel such as "none".
"coefficients"A covariate fit. One row per estimated membership
coefficient: level ("profile" or "group"), outcome, term,
parameter (always "logit"), estimate. The standard errors are not
here; parameter_inference() reports them.
"transitions", "initial", "sequence_lengths"
A transition
fit. "transitions" has one row per group class and ordered pair of
profiles, with group_class, from, to, probability,
expected_count, stable, estimated and
group_class_probability; it takes estimated and
stable to restrict it. "initial" has one row per group class and
profile: group_class, profile, probability, prevalence,
group_class_probability. "sequence_lengths" has one row per group:
group, group_class, occasions, observations, complete.
"candidates", "criteria"
An enumeration grid.
"criteria" has one row per information criterion, naming the candidate
that minimises it. "candidates" has one row per candidate model,
with its class counts, its covariance model, log likelihood,
parameter count, every criterion
under both sample-size conventions, both entropies, and the convergence,
boundary, replication, warning and error diagnostics. Failed candidates
are retained with NA estimates and their error text.
"test", "replicates"
A bootstrap comparison. "test" is the
one-row result; "replicates" has one row per simulated dataset.
"covariances"A starting-value set. One row per profile and
ordered pair of continuous indicators: profile, indicator,
indicator_2, covariance.
"all" returnsA named list of every table the object can
produce, in catalogue order, built from the same definitions a single
what uses, so the two can never disagree. It takes no further arguments:
every table is built with its defaults, and supplying anything else raises
latents_bad_argument. A table this particular object cannot produce is
left out rather than erroring – a sequence table for a fit made without
time, bivariate residuals for a family with no discrete group classes –
so the names of the list say what was available. Only the classed
refusals that mean this object has no such table are skipped; anything
else propagates.
These reach the requested table through ..., and each table takes
only the ones listed for it under Tables above.
dataA data frame with one row per observation of the fit, in the
order it was fitted in. NULL uses the columns the fit carries. For
"profiles" and "responses" it triggers parameter_inference() and
adds a standard error beside every estimate.
scaleFor "profiles", "raw" reports the estimates in input units
and "standardized" divides each indicator's deviation from its grand
mean by that indicator's observed standard deviation, which is what
plot(x, scale = "standardized") draws. Every standard error beside an
estimate is divided by the same constant, so the whole row is on one
scale.
formatFor the posterior tables and "sequences", "long" gives one
row per unit and class and "wide" one row per unit with one column per
class. For "information_criteria", "wide" is the one-row reporting
shape and "long" gives one row per criterion and sample-size convention.
levelWhich level of classification to report: "individuals",
"groups" or "both". NULL, the default, gives "both" for a fit that
has discrete group classes and "individuals" for one that does not,
which is the same rule diagnostics() follows.
truthFor "assignments", one or more column names of data holding
a known label to check the assignments against. See Recovery below.
byFor "residuals", "profile" assesses each profile separately
and "overall" pools them.
adjustFor "residuals", the multiplicity correction applied across
the indicator pairs.
definitionsFor "information_criteria" in long format, TRUE adds
the formula and the reference for each criterion.
estimated, stable
For "transitions", TRUE or FALSE restricts the
table to the rows that were estimated from observed occupancy, or to the
diagonal. NULL keeps every row.
what = "assignments" with truth returns a
cross-tabulation of the model's labels against known ones instead of the
per-observation table: one row per class and truth value, with columns
assignment (which model label the column was compared against),
class, truth (the column name), value, n and proportion, the
share of the units carrying that truth value that were assigned to that
class. Several truth columns may be named at once and are stacked.
Each truth column is compared against the level it describes, which is
read off the data rather than guessed: a column that takes one value
within every group is a property of the group and is compared against
group_class; a column that varies inside any group cannot be a
group-level label and is compared against profile. The assignment
column records which comparison was made, so the choice is never silent.
Group-level recovery counts each group once, even when groups have
different numbers of observations. Unused factor levels are omitted,
because their within-truth proportions are undefined.
Every column is compared against profile for a fit with no discrete
group classes.
The labels are the model's own and carry no order, so a recovery table whose mass sits off the diagonal is a relabelling, not a failure.
Let q be the number of free parameters,
n the chosen sample size, and EN the classification entropy of the
level matching that convention. The criteria are deviance = -2L,
aic = -2L + 2q, bic = -2L + q log(n),
sabic = -2L + q log((n + 2) / 24), caic = -2L + q (log(n) + 1),
awe = -2(L - EN) + 2q (1.5 + log(n)), icl = -2L + q log(n) + 2 EN,
kic = -2L + 3(q + 1), and clc = -2L + 2 EN. Note that clc uses the
entropy sum, as icl and awe here do; tidyLPA reports a CLC built
from relative entropy instead, which is bounded by one and so penalizes
almost nothing, and the two numbers are not comparable.
Every criterion that depends on a sample size is reported under both
multilevel conventions, "groups" and "individuals", and uses
group-level posteriors under the first and individual-level posteriors
under the second. That is a stated per-level choice, not a unique
multilevel definition: use one convention consistently across compared
candidates. deviance, aic and kic depend on no sample size and carry
convention = NA_character_. clc depends on none either, but its
convention selects which level's classification uncertainty it penalizes,
so it is reported once per convention. Individual sample sizes exclude
rows with no observed indicators. Where the group classification entropy
is undefined, group-level awe, icl and clc are NA.
The odds of correct classification for
class k is (p / (1 - p)) / (r / (1 - r)), where p is the average
posterior in the assigned class and r is the model-estimated class
proportion. Values near one indicate classification no better than the
class proportion alone; values of five or more are conventionally read as
adequate separation. It is NA_real_ wherever it is undefined, that is
whenever average_posterior is missing, or it or estimated_proportion
is zero or one, so a single-class solution always reports NA_real_.
average_posterior is itself NA_real_ for a class with no modal
members, where the mean is taken over nothing and is undefined rather than
zero. Modal assignment discards classification uncertainty, which is why
n_modal and estimated_n differ whenever entropy is below one.
relative_entropy is 1 - EN / (n log K), on the zero-to-one scale, so
higher is sharper; entropy_sum is the raw EN the entropy-penalized
criteria use, so lower is sharper. relative_entropy is NA_real_ when a
level has a single class, where it is undefined rather than perfect.
A fit with individual profiles but no discrete group classes returns
individuals only for level = "both", and raises
latents_no_group_classes for level = "groups".
The measurement model assumes the indicators
are independent within a profile. "residuals" tests that pair by pair,
weighting every observation by the posterior the fit holds for it, so a
frame in any other row order would pair each observation with someone
else's posterior; the alignment is checked rather than assumed. A large
residual is evidence of local dependence between two indicators, which the
enumeration grid will otherwise absorb by asking for an extra profile. The
p-values are approximate, and p_adjusted applies the multiplicity
correction named by adjust across the pairs.
Schwarz, G. (1978). Estimating the dimension of a model. Annals of Statistics, 6, 461–464. Sclove, S. L. (1987). Application of model-selection criteria to some problems in multivariate analysis. Psychometrika, 52, 333–343. Bozdogan, H. (1987). Model selection and Akaike's information criterion. Psychometrika, 52, 345–370. Banfield, J. D., & Raftery, A. E. (1993). Model-based Gaussian and non-Gaussian clustering. Biometrics, 49, 803–821. Biernacki, C., & Govaert, G. (1997). Using the classification likelihood to choose the number of clusters. Computing Science and Statistics, 29, 451–457. Cavanaugh, J. E. (1999). A large-sample model selection criterion based on Kullback's symmetric divergence. Statistics and Probability Letters, 42, 333–343. Biernacki, C., Celeux, G., & Govaert, G. (2000). Assessing a mixture model for clustering with the integrated completed likelihood. IEEE Transactions on Pattern Analysis and Machine Intelligence, 22, 719–725. Bolck, A., Croon, M., & Hagenaars, J. (2004). Estimating latent structure models with categorical variables. Political Analysis, 12, 3–27. Nagin, D. S. (2005). Group-Based Modeling of Development. Harvard University Press. Vermunt, J. K. (2010). Latent class modeling with covariates: two improved three-step approaches. Political Analysis, 18, 450–469.
as.data.frame(), which returns the primary table alone,
summary(), which prints every table, and diagnostics() for the
classification tables gathered with a condensed reading.
fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) get_results(fit) get_results(fit, what = "profile_probabilities") get_results(fit, what = "entropy") names(get_results(fit, what = "all")) # `engagement` and `student_type` are the kinds each row was simulated from, # which the model never saw. Both recovery checks are one call. get_results(fit, what = "assignments", data = course_engagement, truth = c("engagement", "student_type"))fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) get_results(fit) get_results(fit, what = "profile_probabilities") get_results(fit, what = "entropy") names(get_results(fit, what = "all")) # `engagement` and `student_type` are the kinds each row was simulated from, # which the model never saw. Both recovery checks are one call. get_results(fit, what = "assignments", data = course_engagement, truth = c("engagement", "student_type"))
The result of enumerate_classes(family = ) with group-class families:
one fitted candidate per family, between-variance restriction and number
of group classes.
## S3 method for class 'latents_family_enumeration' get_results( x, what = c("candidates", "best"), criterion = c("bic", "aic", "bic_individual"), ... ) ## S3 method for class 'latents_family_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_family_enumeration' print(x, ...) ## S3 method for class 'latents_family_enumeration' summary(object, ...)## S3 method for class 'latents_family_enumeration' get_results( x, what = c("candidates", "best"), criterion = c("bic", "aic", "bic_individual"), ... ) ## S3 method for class 'latents_family_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_family_enumeration' print(x, ...) ## S3 method for class 'latents_family_enumeration' summary(object, ...)
x |
A |
what |
|
criterion |
|
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
object |
A |
A base data.frame, one row per candidate: model (the code
candidate_fit() takes), family, between_variance,
n_group_classes, log_likelihood, n_parameters, aic, bic,
bic_individual, relative entropy, smallest_class,
min_effective_groups, converged, boundary, n_best_replicated,
and the candidate's warnings and error. With one group class every
family is the same model, so those rows share a likelihood.
set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) grid <- enumerate_classes(ratings, "climate", "team", family = c("additive", "dispersion"), n_group_classes = 1:2, n_starts = 3, seed = 1) get_results(grid) get_results(grid, "best")set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) grid <- enumerate_classes(ratings, "climate", "team", family = c("additive", "dispersion"), n_group_classes = 1:2, n_starts = 3, seed = 1) get_results(grid) get_results(grid, "best")
Every table of a mixture_regression() fit with random = is a base
data.frame with one row per observation unit of that table.
## S3 method for class 'latents_growth_mixture' get_results( x, what = "coefficients", level = 0.95, vcov_type = NULL, time = NULL, data = NULL, truth = NULL, ... ) ## S3 method for class 'latents_growth_mixture' as.data.frame( x, row.names = NULL, optional = FALSE, what = "coefficients", ... ) ## S3 method for class 'latents_growth_mixture' coef(object, ...) ## S3 method for class 'latents_growth_mixture' vcov(object, type = NULL, ...) ## S3 method for class 'latents_growth_mixture' confint(object, parm, level = 0.95, ...) ## S3 method for class 'latents_growth_mixture' logLik(object, ...) ## S3 method for class 'latents_growth_mixture' nobs(object, ...)## S3 method for class 'latents_growth_mixture' get_results( x, what = "coefficients", level = 0.95, vcov_type = NULL, time = NULL, data = NULL, truth = NULL, ... ) ## S3 method for class 'latents_growth_mixture' as.data.frame( x, row.names = NULL, optional = FALSE, what = "coefficients", ... ) ## S3 method for class 'latents_growth_mixture' coef(object, ...) ## S3 method for class 'latents_growth_mixture' vcov(object, type = NULL, ...) ## S3 method for class 'latents_growth_mixture' confint(object, parm, level = 0.95, ...) ## S3 method for class 'latents_growth_mixture' logLik(object, ...) ## S3 method for class 'latents_growth_mixture' nobs(object, ...)
x |
A |
what |
Which table: |
level |
Confidence level of the intervals. |
vcov_type |
|
time |
For |
data, truth
|
For |
... |
Unused. |
row.names, optional
|
Unused; for the generic. |
object |
A |
type |
As |
parm |
Unused. |
A base data.frame (or, for "all", a named list of them).
few_students <- subset(growth_scores, student <= 60) small_fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) get_results(small_fit, "classes") get_results(small_fit, "random") # All 300 students, with random intercepts and slopes fit <- mixture_regression(score ~ wave, growth_scores, n_classes = 3, id = "student", class_level = "group", random = "wave", random_covariance = "equal", n_starts = 3, seed = 1) get_results(fit, "classes") get_results(fit, "random") get_results(fit, "recovery", data = growth_scores, truth = "trajectory")few_students <- subset(growth_scores, student <= 60) small_fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) get_results(small_fit, "classes") get_results(small_fit, "random") # All 300 students, with random intercepts and slopes fit <- mixture_regression(score ~ wave, growth_scores, n_classes = 3, id = "student", class_level = "group", random = "wave", random_covariance = "equal", n_starts = 3, seed = 1) get_results(fit, "classes") get_results(fit, "random") get_results(fit, "recovery", data = growth_scores, truth = "trajectory")
Tables of a mixture-of-regressions fit
## S3 method for class 'latents_mixture_regression' get_results( x, what = "coefficients", level = 0.95, vcov_type = NULL, data = NULL, truth = NULL, by = c("class", "group_class"), ... ) ## S3 method for class 'latents_mixture_regression' as.data.frame( x, row.names = NULL, optional = FALSE, what = "coefficients", level = 0.95, vcov_type = NULL, ... ) ## S3 method for class 'latents_mixture_regression' coef(object, ...) ## S3 method for class 'latents_mixture_regression' vcov(object, type = NULL, ...) ## S3 method for class 'latents_mixture_regression' confint(object, parm, level = 0.95, ...) ## S3 method for class 'latents_mixture_regression' logLik(object, ...) ## S3 method for class 'latents_mixture_regression' nobs(object, ...)## S3 method for class 'latents_mixture_regression' get_results( x, what = "coefficients", level = 0.95, vcov_type = NULL, data = NULL, truth = NULL, by = c("class", "group_class"), ... ) ## S3 method for class 'latents_mixture_regression' as.data.frame( x, row.names = NULL, optional = FALSE, what = "coefficients", level = 0.95, vcov_type = NULL, ... ) ## S3 method for class 'latents_mixture_regression' coef(object, ...) ## S3 method for class 'latents_mixture_regression' vcov(object, type = NULL, ...) ## S3 method for class 'latents_mixture_regression' confint(object, parm, level = 0.95, ...) ## S3 method for class 'latents_mixture_regression' logLik(object, ...) ## S3 method for class 'latents_mixture_regression' nobs(object, ...)
x |
A fit from |
what |
The table to return; see Tables. |
level |
Confidence level for the intervals. |
vcov_type |
|
data, truth
|
For |
by |
For |
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
object |
A fit from |
type |
Covariance type for |
parm |
Parameter names or positions; all by default. |
A base data.frame as described under Tables, or a named list
of them for what = "all".
coefficientsOne row per class and regression term: class
("common" for a shared term), term, estimate, std_error,
statistic (Wald z), p_value, conf_low, conf_high,
p_adjusted (Benjamini-Hochberg across the non-intercept rows;
NA for intercepts) and, for the binomial and Poisson families,
exp_estimate (odds or rate ratio). For the ordinal family each
class's thresholds lead its rows as terms "threshold:<lower>|<upper>"
(delta-method errors; no p_adjusted or exp_estimate), and
exp_estimate of a slope is the cumulative odds ratio of a higher
category.
classesOne row per class: share (mean posterior), count
(summed posterior), n_assigned (modal assignment), mean_posterior
(average posterior of the units assigned to it); sigma and
sigma_std_error for the Gaussian family; prior and
prior_std_error when membership has no covariates. Units are groups
under class_level = "group".
membershipOne row per multinomial-logit coefficient: model
("class" or "group_class"), class, term, reference, the Wald
columns and odds_ratio. In the two-level model the class intercepts
are per group class, (Intercept):group_class_h.
group_classesTwo-level model: one row per group class and
class, with the model-implied class probability, the group_share,
n_groups_assigned, and std_error of the probability when
membership has no covariates.
fitOne row: log likelihood, parameter count, aic, bic
(penalized by the number of independent units: rows, or groups when
id defines them), bic_rows (always by rows, as flexmix reports it),
sabic, icl, relative entropy (and group_entropy), the smallest
class share, convergence and start replication.
assignmentsOne row per data row used: row (its position in
the supplied data), the id column, modal class, its posterior,
and one probability_class_k column per class; two-level fits add
group_class and group_posterior.
groupsOne row per group (class_level = "group" or the
two-level model): its size, modal class or group class, and
posterior probabilities.
fittedOne row per data row: observed, the posterior-weighted
fitted mean, fitted_modal (the modal class's mean), residual
and class.
startsOne row per start: its log likelihood, convergence,
iterations, stage ("screened": stopped after the 50-iteration
screening because better starts existed; "completed": run to
convergence or max_iter), whether it degenerated, and which one was
selected. n_best_replicated counts completed starts only.
classificationOne row per assigned (modal) class and class:
the mean_posterior probability of class among the units assigned to
assigned, and n_assigned. Its diagonal is each class's average
posterior probability; values near one mean well-separated classes.
recoveryNeeds data and truth: the modal classes
cross-tabulated against a known classification, one row per assigned
class and true value, with the count n and its share of the
assigned class. Group-level under class_level = "group" or
by = "group_class", where truth must be constant within groups.
fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) get_results(fit, "coefficients") get_results(fit, "fit") get_results(fit, "coefficients", vcov_type = "robust") get_results(fit, "recovery", data = study_hours, truth = "strategy")fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) get_results(fit, "coefficients") get_results(fit, "fit") get_results(fit, "coefficients", vcov_type = "robust") get_results(fit, "recovery", data = study_hours, truth = "strategy")
Tables of a pooled multiply imputed fit
## S3 method for class 'latents_pooled' get_results(x, what = c("estimates", "imputations", "fits"), ...) ## S3 method for class 'latents_pooled' as.data.frame(x, row.names = NULL, optional = FALSE, what = "estimates", ...)## S3 method for class 'latents_pooled' get_results(x, what = c("estimates", "imputations", "fits"), ...) ## S3 method for class 'latents_pooled' as.data.frame(x, row.names = NULL, optional = FALSE, what = "estimates", ...)
x |
A |
what |
|
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
A base data.frame; see pool_imputations() for the columns.
set.seed(1) # Two stand-in completed data sets. With real missing covariates, pass the # `mids` object mice::mice() returns instead. completed <- lapply(1:2, function(i) { within(subset(course_engagement, student <= 40), previous_grade <- previous_grade + rnorm(length(previous_grade), sd = 0.1)) }) pooled <- pool_imputations(completed, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 1, profile_covariates = "previous_grade", n_starts = 2, seed = 1) get_results(pooled, "imputations")set.seed(1) # Two stand-in completed data sets. With real missing covariates, pass the # `mids` object mice::mice() returns instead. completed <- lapply(1:2, function(i) { within(subset(course_engagement, student <= 40), previous_grade <- previous_grade + rnorm(length(previous_grade), sd = 0.1)) }) pooled <- pool_imputations(completed, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 1, profile_covariates = "previous_grade", n_starts = 2, seed = 1) get_results(pooled, "imputations")
The result of enumerate_classes(..., time = ): one transition model per
number of profiles and group classes (and covariance structure, when
model names several).
## S3 method for class 'latents_transition_enumeration' get_results( x, what = c("candidates", "best"), criterion = c("bic", "aic", "bic_individual"), ... ) ## S3 method for class 'latents_transition_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_transition_enumeration' print(x, ...) ## S3 method for class 'latents_transition_enumeration' summary(object, ...)## S3 method for class 'latents_transition_enumeration' get_results( x, what = c("candidates", "best"), criterion = c("bic", "aic", "bic_individual"), ... ) ## S3 method for class 'latents_transition_enumeration' as.data.frame(x, row.names = NULL, optional = FALSE, ...) ## S3 method for class 'latents_transition_enumeration' print(x, ...) ## S3 method for class 'latents_transition_enumeration' summary(object, ...)
x |
A |
what |
|
criterion |
|
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
object |
A |
A base data.frame, one row per candidate: n_profiles,
n_group_classes, model, log_likelihood, n_parameters, aic,
bic, bic_individual, relative profile entropy, converged,
boundary, n_best_replicated, warnings and error.
# The first 50 students keep the example quick few_students <- subset(course_engagement, student <= 50) grid <- enumerate_classes(few_students, c("browse", "lectures"), "student", time = "sequence", n_profiles = 2:3, n_group_classes = 1, n_starts = 2, seed = 1) get_results(grid) get_results(grid, "best")# The first 50 students keep the example quick few_students <- subset(course_engagement, student <= 50) grid <- enumerate_classes(few_students, c("browse", "lectures"), "student", time = "sequence", n_profiles = 2:3, n_group_classes = 1, n_starts = 2, seed = 1) get_results(grid) get_results(grid, "best")
Tidy tables of a fit from multilpa(family = "additive"). Every table is a
base data.frame; the fit itself never needs to be reached into.
## S3 method for class 'multilpa_additive' get_results( x, what = "parameters", level = 0.95, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, data = NULL, truth = NULL, ... ) ## S3 method for class 'multilpa_additive' as.data.frame(x, row.names = NULL, optional = FALSE, what = "parameters", ...) ## S3 method for class 'multilpa_additive' coef(object, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa_additive' vcov( object, type = c("observed", "robust", "opg"), scale = c("natural", "unconstrained"), ... ) ## S3 method for class 'multilpa_additive' confint(object, parm, level = 0.95, type = c("observed", "robust", "opg"), ...) ## S3 method for class 'multilpa_additive' logLik(object, ...) ## S3 method for class 'multilpa_additive' nobs(object, ...)## S3 method for class 'multilpa_additive' get_results( x, what = "parameters", level = 0.95, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, data = NULL, truth = NULL, ... ) ## S3 method for class 'multilpa_additive' as.data.frame(x, row.names = NULL, optional = FALSE, what = "parameters", ...) ## S3 method for class 'multilpa_additive' coef(object, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa_additive' vcov( object, type = c("observed", "robust", "opg"), scale = c("natural", "unconstrained"), ... ) ## S3 method for class 'multilpa_additive' confint(object, parm, level = 0.95, type = c("observed", "robust", "opg"), ...) ## S3 method for class 'multilpa_additive' logLik(object, ...) ## S3 method for class 'multilpa_additive' nobs(object, ...)
x |
A |
what |
Which table; |
level |
Confidence level of the intervals in |
vcov_type |
|
adjust |
p-value adjustment across the class means, one of
stats::p.adjust.methods; |
data, truth
|
For |
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
object |
A |
scale |
|
type |
Covariance type, as |
parm |
Natural parameter names or positions; all by default. |
A base data.frame, or a named list of them for what = "all".
parametersOne row per natural parameter: level ("between"
for class means and between-group variances, "within" for the shared
within-group variances, "group_class" for class weights),
group_class ("shared" for a parameter common to all classes),
indicator (NA for weights), parameter ("mean", "variance",
"weight"), estimate, standard_error, statistic, p_value,
p_adjusted, conf_low, conf_high. Only means carry a Wald test.
Intervals for variances are formed on the log scale and for weights on
the logit scale. When inference is refused (boundary, unconverged,
singular information) the standard-error columns are NA and a
latents_no_standard_errors message says why.
group_classesOne row per group class: weight, count
(summed posterior), n_assigned (modal), mean_posterior among the
groups assigned to it, and effective_groups: the number of perfectly
classified groups that would estimate the class weight as precisely as
this fit does, w^2 (1 - w) / Var(w). It equals the class's expected
group count when classes are perfectly separated and falls when a
class is small or poorly separated. Below 50, intervals for class
parameters are flagged with a latents_weak_class warning (threshold
and its simulation evaluation: validation/ADDITIVE_SIMULATION.md in
the source repository). NA for one class or when inference is refused.
groupsOne row per group (named by the id column): size n,
modal group_class, its posterior, and probability_group_class_h.
interceptsOne row per group and indicator: n, the observed
group average, and the posterior mean and variance of the group's
intercept, mixed over its class posterior (so the variance includes
uncertainty about class membership). Conditional on the estimated
parameters; not a statement about their sampling uncertainty.
conditional_interceptsAs intercepts, one row per group,
indicator and group class, conditional on that class, with the class
posterior.
assignmentsOne row per data row: row, the id column, the
group's modal group_class and posterior, and
assignment_level = "group": every row inherits its group's class.
classificationOne row per assigned and group class: the
mean_posterior of group_class among groups assigned to assigned.
fitOne row: log likelihood, n_parameters, aic, bic
(penalized by the number of groups), bic_individual (by rows),
n_groups, n_obs, relative entropy over groups, the smallest class
weight, min_effective_groups, converged, boundary, kkt and start
replication.
recoveryNeeds data and truth: one row per modal group
class and known value, with the number of groups n and their share
of the assigned class. Not part of what = "all".
startsOne row per start: likelihood, convergence, iterations, number of between variances held at zero, error message of a failed start, and which start was selected.
set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240), support = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) fit <- multilpa(ratings, c("climate", "support"), "team", n_group_classes = 2, family = "additive", n_starts = 3, seed = 1) get_results(fit) get_results(fit, "group_classes") get_results(fit, "parameters", vcov_type = "robust")set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240), support = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) fit <- multilpa(ratings, c("climate", "support"), "team", n_group_classes = 2, family = "additive", n_starts = 3, seed = 1) get_results(fit) get_results(fit, "group_classes") get_results(fit, "parameters", vcov_type = "robust")
Tidy tables of a fit from multilpa(family = "restricted_cross_level") or
multilpa(family = "full_cross_level").
## S3 method for class 'multilpa_cross_level' get_results(x, what = "profiles", ...) ## S3 method for class 'multilpa_cross_level' as.data.frame(x, row.names = NULL, optional = FALSE, what = "profiles", ...) ## S3 method for class 'multilpa_cross_level' print(x, ...) ## S3 method for class 'multilpa_cross_level' summary(object, ...) ## S3 method for class 'multilpa_cross_level' logLik(object, ...) ## S3 method for class 'multilpa_cross_level' nobs(object, ...)## S3 method for class 'multilpa_cross_level' get_results(x, what = "profiles", ...) ## S3 method for class 'multilpa_cross_level' as.data.frame(x, row.names = NULL, optional = FALSE, what = "profiles", ...) ## S3 method for class 'multilpa_cross_level' print(x, ...) ## S3 method for class 'multilpa_cross_level' summary(object, ...) ## S3 method for class 'multilpa_cross_level' logLik(object, ...) ## S3 method for class 'multilpa_cross_level' nobs(object, ...)
x |
A |
what |
Which table; |
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
object |
A |
A base data.frame, or a named list of them for what = "all".
profilesOne row per individual profile and indicator: level = "individual", profile, indicator, mean, variance; then one row
per group class and indicator for the group means: level = "group",
the class in profile, and the class's mean and variance of the
group means.
group_classesOne row per group class: weight, count
(summed posterior) and n_assigned.
compositionOne row per group class and profile: the share of the class's members in each profile. Model-implied for the full family; for the restricted family a posterior-weighted description, since its prevalences do not depend on the class.
groupsOne row per group: size, modal group class, its posterior and every class probability.
assignmentsOne row per data row: its modal profile and
posterior, and its group's modal group_class (inherited).
fitOne row: family, log likelihood, n_parameters, aic,
bic (by groups), bic_individual (by rows), sizes, convergence and
boundary. The likelihood is the working likelihood of the manifest
specification (the group means are computed from the same ratings), so
it is comparable only between cross-level fits of the same data.
startsThe start records (full family) or the two parts' likelihoods (restricted family).
set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240, sample(c(-1, 1), 240, replace = TRUE), 0.6)) fit <- multilpa(ratings, "climate", "team", n_profiles = 2, n_group_classes = 2, family = "full_cross_level", n_starts = 3, seed = 1) get_results(fit) get_results(fit, "composition")set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240, sample(c(-1, 1), 240, replace = TRUE), 0.6)) fit <- multilpa(ratings, "climate", "team", n_profiles = 2, n_group_classes = 2, family = "full_cross_level", n_starts = 3, seed = 1) get_results(fit) get_results(fit, "composition")
The result of lta() with transitions = "occasion",
transition_covariates, initial_covariates or measurement = "occasion".
## S3 method for class 'multilpa_lta' get_results( x, what = "transitions", level = 0.95, vcov_type = c("observed", "robust", "opg"), ... ) ## S3 method for class 'multilpa_lta' as.data.frame(x, row.names = NULL, optional = FALSE, what = "transitions", ...) ## S3 method for class 'multilpa_lta' print(x, ...) ## S3 method for class 'multilpa_lta' summary(object, ...) ## S3 method for class 'multilpa_lta' coef(object, ...) ## S3 method for class 'multilpa_lta' vcov(object, type = c("observed", "robust", "opg"), ...) ## S3 method for class 'multilpa_lta' logLik(object, ...) ## S3 method for class 'multilpa_lta' nobs(object, ...)## S3 method for class 'multilpa_lta' get_results( x, what = "transitions", level = 0.95, vcov_type = c("observed", "robust", "opg"), ... ) ## S3 method for class 'multilpa_lta' as.data.frame(x, row.names = NULL, optional = FALSE, what = "transitions", ...) ## S3 method for class 'multilpa_lta' print(x, ...) ## S3 method for class 'multilpa_lta' summary(object, ...) ## S3 method for class 'multilpa_lta' coef(object, ...) ## S3 method for class 'multilpa_lta' vcov(object, type = c("observed", "robust", "opg"), ...) ## S3 method for class 'multilpa_lta' logLik(object, ...) ## S3 method for class 'multilpa_lta' nobs(object, ...)
x |
A |
what |
Which table; |
level |
Confidence level of the coefficient intervals. |
vcov_type |
|
... |
Unused. |
row.names, optional
|
Unused; part of the generic. |
object |
A |
type |
Covariance type, as |
A base data.frame, or a named list of them for what = "all".
transitionsModel-implied transition probabilities, one row per
group class, origin from and destination to; with occasion-varying
or covariate-dependent transitions, one set per destination
occasion, averaged over the groups observed there.
transition_coefficientsOne row per group class, move from ->
to and design term: the log odds of that move rather than staying
in from, its standard error, Wald statistic, p-value, interval and
odds_ratio. Terms are (Intercept) or occasion_t and each
covariate column.
second_order_transitions, second_order_coefficients
With
order = 2: the probability of moving to given the profiles at the
two previous occasions (previous, from), averaged over the groups
observed from the third occasion on, and its logit coefficients
(reference: staying in from). The transitions tables then describe
the first move only.
initial, initial_coefficients
The initial distribution (averaged over groups) and its logit coefficients, reference: the last profile.
profilesMeans and variances per profile and indicator, per
occasion when measurement is occasion-specific.
group_classes, assignments, fit, starts
As for other fits.
# Does the grade in the previous course shift the chance of switching # engagement profile? `previous_grade` changes from course to course. fit <- lta(course_engagement, c("browse", "lectures"), "student", n_profiles = 2, time = "sequence", transition_covariates = "previous_grade", n_starts = 2, seed = 1) get_results(fit, "transition_coefficients") get_results(fit, "transitions")# Does the grade in the previous course shift the chance of switching # engagement profile? `previous_grade` changes from course to course. fit <- lta(course_engagement, c("browse", "lectures"), "student", n_profiles = 2, time = "sequence", transition_covariates = "previous_grade", n_starts = 2, seed = 1) get_results(fit, "transition_coefficients") get_results(fit, "transitions")
Builds one tna::tna() model from a fitted transition model, aggregated
over every latent group class.
get_tna(x, ...) ## S3 method for class 'multilpa_transitions' get_tna(x, ...) ## S3 method for class 'multilpa_lta' get_tna(x, ...) ## S3 method for class 'multilpa' get_tna(x, ...) ## S3 method for class 'multilpa_covariates' get_tna(x, ...)get_tna(x, ...) ## S3 method for class 'multilpa_transitions' get_tna(x, ...) ## S3 method for class 'multilpa_lta' get_tna(x, ...) ## S3 method for class 'multilpa' get_tna(x, ...) ## S3 method for class 'multilpa_covariates' get_tna(x, ...)
x |
A fitted model from |
... |
Passed to |
The aggregate is formed from the expected transition counts, summed across classes and then normalised, rather than by averaging the class transition matrices. The two differ: averaging weights each class by how probable it is, and summing counts weights it by how much transition mass it actually contributes, which is what a marginal transition probability means. A class holding a tenth of the students but a fifth of the observed moves counts for the latter. If a profile has no expected outgoing moves in any class, its transition row has no count-based estimate; the network uses the fitted class rows averaged by class probability and warns that the row is unestimated. It never interprets absent moves as a certain self-transition.
A general lta() fit – with occasion-varying or covariate-dependent
transitions, a second order, movers and stayers, or ordinal, count or
weighted data – has no single transition matrix, and is refused with
latents_unsupported_tna; its transitions are in
get_results(x, "transitions").
For a multilpa() or multilca() fit made with time =, there is
no estimated transition matrix: the network is built by tna::tna() from
each group's sequence of modal profiles in time order, the observed
transitions between the profiles the observations were assigned to.
get_group_tna() splits those sequences by each group's modal group class.
A fit made without time has no order and is refused.
An object of class tna, as tna::tna() returns: every verb of that
package applies to it, including centralities(), communities(),
cliques() and plot().
get_group_tna() for one network per latent class.
if (requireNamespace("tna", quietly = TRUE)) { activity <- c("browse", "lectures", "forum_read") moves <- lta(course_engagement, vars = activity, id = "student", time = "sequence", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) get_tna(moves) }if (requireNamespace("tna", quietly = TRUE)) { activity <- c("browse", "lectures", "forum_read") moves <- lta(course_engagement, vars = activity, id = "student", time = "sequence", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) get_tna(moves) }
A simulated multilevel longitudinal dataset made for the multilevel growth
mixture model. 600 students in 40 schools (15 per school) sit a reading
test on five occasions (wave 0 to 4). Two kinds of student follow two
trajectories: improving (starts near 48, gains 3 points a wave) and
plateauing (starts near 56, gains 0.3). Students vary around their
trajectory (random intercept SD 3, random slope SD 0.6, correlation -0.2;
residual SD 2). Schools are of two types: in supportive schools 80% of
students improve, in struggling schools 25% do, and a school's support
programme (standardized, measured once) makes it more likely to be
supportive. About 8% of tests were missed at random.
growth_schoolsgrowth_schools
A data frame with 2780 rows (600 students, 40 schools) and 7 columns:
Integer school identifier, 1 to 40. Pass it as cluster.
Integer student identifier, 1 to 600. Pass it as id.
Integer test occasion, 0 to 4. The time variable.
Reading score.
Standardized school support programme, constant within school.
Factor, "supportive" or "struggling": the school's
generating type. Not for fitting; kept to check recovery.
Factor, "improving" or "plateauing": the student's
generating kind. Not for fitting; kept to check recovery.
The school types are what a multilevel growth mixture model's group
classes recover: mixture_regression(..., cluster = "school", n_group_classes = 2), with group_membership = "programme".
Simulated by data-raw/growth-schools.R in the source repository.
mixture_regression(), get_results.latents_growth_mixture().
descriptives(growth_schools, c("score", "programme")) fit <- mixture_regression(score ~ wave, growth_schools, n_classes = 2, id = "student", class_level = "group", random = "wave", random_covariance = "equal", cluster = "school", n_group_classes = 2, group_membership = "programme", n_starts = 2, seed = 1) get_results(fit, "group_classes") get_results(fit, "recovery", data = growth_schools, truth = "trajectory")descriptives(growth_schools, c("score", "programme")) fit <- mixture_regression(score ~ wave, growth_schools, n_classes = 2, id = "student", class_level = "group", random = "wave", random_covariance = "equal", cluster = "school", n_group_classes = 2, group_membership = "programme", n_starts = 2, seed = 1) get_results(fit, "group_classes") get_results(fit, "recovery", data = growth_schools, truth = "trajectory")
A simulated longitudinal dataset made for growth models. 300 students sit
an achievement test on up to seven occasions (wave 0 to 6). Three kinds of
student follow three trajectories: improving (starts near 48, gains 3.5
points a wave), stable (starts near 62, gains 0.5) and declining
(starts near 58, loses 2.5). Within each kind, students vary around the
trajectory: each has their own start and rate of change (random intercept
SD 4, random slope SD 0.8, correlation -0.3, the same spread in every kind),
with residual SD 3. Motivation, measured once, makes a student more likely
to be improving and less likely to be declining. About 12% of tests were
missed at random, so the panel is unbalanced.
growth_scoresgrowth_scores
A data frame with 1844 rows (300 students) and 5 columns:
Integer student identifier, 1 to 300. Pass it as id.
Integer test occasion, 0 to 6. The time variable.
Achievement score.
Standardized motivation, constant within student.
Factor, "improving", "stable" or "declining":
the student's generating kind. Not for fitting; kept to check
recovery.
Because students vary within their kind, a trajectory model without random effects needs more classes than the three that generated the data; a growth mixture model with random intercepts and slopes recovers three.
Simulated by data-raw/growth-scores.R in the source repository.
mixture_regression(), compare_models().
descriptives(growth_scores, c("score", "motivation")) fit <- mixture_regression(score ~ wave, growth_scores, n_classes = 3, id = "student", class_level = "group", random = "wave", random_covariance = "equal", n_starts = 4, seed = 1) get_results(fit, "recovery", data = growth_scores, truth = "trajectory")descriptives(growth_scores, c("score", "motivation")) fit <- mixture_regression(score ~ wave, growth_scores, n_classes = 3, id = "student", class_level = "group", random = "wave", random_covariance = "equal", n_starts = 4, seed = 1) get_results(fit, "recovery", data = growth_scores, truth = "trajectory")
Every error a caller might reasonably want to handle carries a class, so it can be caught by what went wrong rather than by matching message text, which changes between versions. This page is the contract: the classes below are stable, the messages are not.
Nothing; this page documents conditions rather than a function.
latents_bad_dataThe data frame, the indicator columns or the
group column break the input contract: too few rows, a duplicated or
absent column name, an indicator that is not a numeric vector, an
indicator that is constant or wholly unobserved, missing or non-finite
values under missing = "error", or a missing group identifier.
latents_bad_argumentA scalar argument is outside its contract:
n_profiles, n_group_classes, n_starts, max_iter, tol,
min_variance, or a seed or seeds that is not a whole number
set.seed() accepts.
latents_bad_timetime is missing, or repeats a position within
a group, so the ordering it is supposed to supply does not exist.
latents_bad_categoricalA categorical indicator cannot be
encoded, or min_probability leaves no room for its number of
categories.
latents_bad_startSupplied starting values are missing a block the model needs, do not have the shape it requires, or carry labels that name an indicator or a category this fit does not have.
latents_bad_fixedA fixed request names a block the model does
not have, or one that start does not supply, or start is absent.
latents_bad_stageA measurement fit offered to fit_staged()
has already estimated multiple group classes, or was fitted to different
indicators, profiles or measurement options than the stage requested.
latents_bad_transitionA transition model was asked for with fewer than two profiles, or with no group observed at two occasions.
latents_bad_inference_dataThe data passed to a verb alongside
a fit do not reproduce the fit: a different number of rows, different
values, or a different group ordering. Raised for a plain multilpa fit and for a
covariate fit alike.
latents_no_indicator_dataA fit carries no stored indicators, so the request cannot be answered without the original data.
latents_incomplete_fitA fit is missing fields the request
needs, usually because it was made by an older version. Raised instead
of letting a list subset by an absent name yield a NULL element keyed
NA, which would leave a hole in the result.
latents_unidentifiedThe requested model cannot be identified from these data: more profiles than distinct observed indicator rows, more group classes than groups, or more than one group class with only one profile or only singleton groups.
latents_all_starts_failedEvery EM start raised, so no fit was produced. The message carries the distinct start errors.
latents_empty_profileA profile has no observed responses for an indicator, so its response probabilities are not identified; or a group class has no effective membership, so the profile prevalence within it is not defined.
latents_too_few_groupsFewer independent groups than the
quantities being reported, so the covariance cannot be formed. Raised by
robust parameter_inference(), and by three_step() and r3step(),
whose cluster-robust contributions sum to zero at the estimate and so
span at most one dimension fewer than the number of groups.
latents_inseparable_classesClasses cannot be separated well enough for the requested three-step correction.
latents_no_convergeA fit required by the request did not
converge, and using it would report an unconverged estimate as final.
Raised for a plain multilpa fit and for a covariate fit alike.
latents_boundary_fitA variance or a response probability sits at its bound, or a mixing probability is zero, so the parameter is on the edge of its space and the asymptotic normal approximation Wald inference relies on does not hold. The standard error is refused rather than reported.
latents_singular_informationThe observed information is not positive definite, or is numerically singular, so it cannot be inverted and no standard error can be formed.
latents_unknown_candidateThe requested profile and group-class combination was never enumerated.
latents_failed_candidateAn enumerated candidate could not be fitted. The message carries that candidate's error.
latents_no_group_classesA verb that needs discrete group classes was called on a model that has none. Every model family this version fits has them, so this is a guard against a future family rather than a refusal the current surface can produce.
latents_bad_nestingTwo models are not nested in the way the
comparison requires, including a bootstrap_lrt() pair whose held
measurement blocks differ, are held at different values, or are held in
only one of the two models.
latents_incomparable_modelsTwo models were fitted to different data, or differ in covariance structure or centering mode where the comparison requires them to match.
latents_reversed_likelihoodThe model with more parameters has the lower likelihood, so at least one fit is at a local optimum and the comparison is meaningless.
latents_bad_outcomeA distal outcome given to three_step()
is not of a type the requested method can handle, varies within a group
when a group-level outcome is requested, or is a measurement indicator
that already helped define the classes.
latents_bad_covariateA predictor given to r3step() varies
within a group when group-level membership is predicted, makes the
design rank deficient, or is a measurement indicator that already
helped define the classes. The counterpart of latents_bad_outcome.
multilpa_indicator_reusedA measurement indicator was offered
as an external variable, to three_step() as an outcome or to
r3step() as a predictor. Raised alongside latents_bad_outcome or
latents_bad_covariate, so one handler catches the mistake from
either verb.
latents_bad_scoresScore contributions could not be formed for the robust sandwich.
These name capabilities the package does not have. They are raised in place of returning a number that would be wrong.
latents_unsupported_inferenceStandard errors are not available
for this particular fit. Raised for a covariate fit with categorical
indicators, where no score is implemented for the response
probabilities, for fits made by an older version, for a fit recording
a covariance structure code this version does not know, and by
parameter_inference(method = "bootstrap") for a fit holding a
measurement block, whose held values came from a fit these data do not
resample, and by parameter_inference() for a fit made with prior,
which sits at a posterior mode rather than a likelihood maximum.
latents_unsupported_priormultilpa(prior = ) was asked for a
combination the conjugate prior is not defined for: the VEE, EVE, VVE
or EVV covariance structure (mclust defines none), categorical
indicators, a fixed block, membership covariates, or missing
indicator values.
latents_unsupported_noisemultilpa(noise = TRUE) was asked
for more than one group class, categorical indicators, a fixed block,
membership covariates or missing indicator values; or a verb that does
not yet account for a noise component — parameter_inference(),
three_step(), r3step(), bootstrap_lrt(), starting_values(),
bivariate residuals, fit_staged() or a posterior plot — was given a
fit that has one.
latents_unsupported_structuremultilpa() was asked for a
covariance structure other than EEI, VVI, EEE or VVV together with
profile_covariates or group_covariates, which fit those four only.
latents_bad_weightsweights does not name a column, or its
values are missing, negative, non-finite, vary within a unit, or are
positive for fewer than two units.
latents_unsupported_weightsA weighted fit was given to a verb or
option that does not account for sampling weights: bootstrap_lrt(),
enumerate_regressions(bootstrap = ), three_step(), r3step(),
prior or noise; or a non-sandwich vcov_type was asked of one.
latents_unsupported_indicatorordinal or count indicators were
combined with an option that does not take them yet: membership
covariates, start, fixed, prior or noise.
latents_pooling_failedAn imputation's fit or inference failed
in pool_imputations(), or the imputations' fits report different
parameters. Rubin's rules need every imputation, so none is dropped; the
message names the imputation and carries the original reason.
latents_bootstrap_failedFewer than two resamples produced a
usable fit, so there is nothing to read a spread from. Raised by
parameter_inference(method = "bootstrap"); the message carries the
first reason a resample gave.
latents_no_free_parametersThe fit holds every parameter it has,
so there is no free coordinate to report a standard error for. Raised by
parameter_inference() and vcov() on a fully held fit.
latents_held_parameterA named parameter was held fixed by this
fit, so it has no sampling distribution and no confidence interval.
Raised by confint() when parm names a held coordinate.
latents_unsupported_sensitivitysensitivity() was called on a
model family whose refit needs arguments the shared refit does not carry,
whose profile labels cannot yet be aligned between two fits, or a
directly fixed fit whose original free starts cannot be replayed.
latents_unsupported_three_stepthree_step() or r3step() was
called on a membership-covariate fit. Its classification errors depend
on the fitted predictors, but the correction here uses one unconditional
error matrix.
latents_unsupported_bootstrapbootstrap_lrt() was called on a
person-centred fit. Group baselines were removed before estimation and
no distribution for them is available to simulate raw data under the
null model.
latents_unsupported_tnaget_tna() or get_group_tna() was
called on a general transition fit (a lta() fit with occasion-varying
or covariate-dependent transitions, a second order, movers and stayers,
or ordinal, count or weighted data), which has no single transition
matrix to hand over; its transitions are in
get_results(x, "transitions").
latents_no_plotplot(x, what = "all") was called on an object
whose plot method names no views to draw.
latents_no_timeA sequence verb was called on a fit made without
time, so it carries no ordering.
latents_missing_packageA verb needs a package listed in
Suggests that is not installed. Raised by get_tna() and
get_group_tna() when the tna package is absent; the message names
the package and how to install it.
latents_no_continuous, latents_no_categorical
The requested panel needs indicators of a kind this fit does not have.
latents_nothing_to_describedescriptives() was given no
variables to describe: either vars named none, or the frame has no
numeric column outside id and by to fall back on.
latents_nothing_to_plotThe requested panel has no rows to draw.
latents_unknown_category, latents_unknown_criterion
A named category or information criterion does not exist in this fit.
latents_bad_scaleAn unrecognised plotting scale was requested.
A fit that is returned but qualified warns rather than errors, so that a simulation loop can catch the qualification it cares about and let the others through.
latents_failed_startsSome, but not all, EM starts raised. The
surviving starts are in get_results(fit, "starts").
latents_not_identifiedmixture_regression() with a binary outcome and one
trial per class assignment: a mixture of Bernoulli regressions is not
identified there.
latents_missing_datamixture_regression() with missing = "error" met a
missing value in a variable the model uses.
latents_rows_droppedWarning. mixture_regression() with
missing = "omit" dropped incomplete rows; the message states how
many.
latents_no_valid_startEvery mixture_regression() start degenerated: a
class emptied or its variance collapsed.
latents_degenerate_startWarning. Some mixture_regression() starts
degenerated and were set aside.
latents_separationWarning. A mixture_regression() logit or log-link
coefficient diverged (perfect separation); it and its standard error
are unreliable.
latents_em_decreaseThe mixture_regression() EM likelihood decreased
beyond roundoff. This indicates a defect and should be reported.
latents_unconvergedThe best start had not converged when
max_iter was reached, so the returned estimate is not a maximum.
latents_boundaryA variance, or a covariance eigenvalue, sits at
min_variance, so the fit is bound-active and its standard errors are
not valid at that parameter.
latents_small_classesA profile or group class has effective membership below one, so it is supported by less than one observation.
latents_weak_classmultilpa(family = "additive") only: a
group class is supported by fewer than 50 effective groups (see
effective_groups in get_results.multilpa_additive()), because it is
small, poorly separated, or both. Standard errors are still reported,
but intervals for class means and weights can be miscalibrated.
latents_failed_replicatesSome bootstrap replicates failed
validation, so the bootstrap p-value is NA.
latents_single_levelA message, not a warning: id = NULL was
passed, so the fit has one observation per unit and no second level.
Raised by multilpa() on every such fit and once by an
enumerate_classes() grid; suppressMessages() or a
latents_single_level handler silences it.
latents_bootstrap_droppedSome resamples did not produce a usable
fit and were left out of the interval. Raised by
parameter_inference(method = "bootstrap"), naming how many, so the
count the interval rests on is never quietly smaller than iter.
latents_sensitivity_droppedOne or more seeds did not produce a
fit in sensitivity(). Their rows are NA rather than absent, and the
warning names how many failed and the first reason, so the table is never
quietly shorter than seeds.
latents_extreme_coefficientsA membership logit coefficient is large enough that the class is close to separated, so the estimate is driven by scaling, a sparse class or separation rather than by the data.
latents_empty_transition_rowA profile is never occupied before a
final occasion, so its transition row is uniform by construction rather
than estimated. get_results(fit, "transitions", estimated = FALSE)
lists them. The transition-network handoff also warns when it includes
such a row, because it cannot be read as evidence of persistence.
latents_unverified_alignmentA supplied data frame has too
little fitted information to establish row order, for example only a
repeated group identifier. The result is returned on the caller's
assurance that the rows are in fitting order.
set.seed(1) example_data <- data.frame( school = rep(seq_len(8), each = 6), score_a = stats::rnorm(48), score_b = stats::rnorm(48) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1) # A sequence verb on a fit made without `time` is catchable by class. tryCatch(get_results(fit, "sequences"), latents_no_time = function(condition) { "this fit carries no ordering" })set.seed(1) example_data <- data.frame( school = rep(seq_len(8), each = 6), score_a = stats::rnorm(48), score_b = stats::rnorm(48) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1) # A sequence verb on a fit made without `time` is catchable by class. tryCatch(get_results(fit, "sequences"), latents_no_time = function(condition) { "this fit carries no ordering" })
Fits a single-level latent class model: each observation belongs to one of
n_classes unobserved classes, and within a class the discrete
indicators are independent. Categorical indicators have unrestricted
response probabilities; ordinal and count in ... choose the
structured measurement models described by multilpa(). It
is the model multilca() fits with id = NULL, and it returns the same
object, so every accessor, plot and inference verb of the package applies
to it. In the results the classes are labelled profile_1, profile_2,
and so on, as throughout the package.
lca(data, vars, n_classes, ...)lca(data, vars, n_classes, ...)
data |
A data frame with one row per observation. |
vars |
Names of the discrete indicator columns. Categories are taken
from the data; |
n_classes |
Number of classes, a positive whole number. |
... |
Further arguments for |
For observations nested in groups, use multilca() with the grouping
column as id, which adds latent classes of groups above the classes of
observations.
A fitted multilpa object, as multilca() returns.
latents_bad_argument when id or n_group_classes is passed; the
conditions multilca() raises otherwise.
Lazarsfeld, P. F., & Henry, N. W. (1968). Latent Structure Analysis. Houghton Mifflin.
lpa() for continuous indicators; multilca() for the
two-level model; enumerate_lca() to compare numbers of classes.
fit <- lca(student_esm, c("time_with_friends", "on_social_media", "tv_video_games", "sports"), n_classes = 2, n_starts = 3, seed = 1) fit plot(fit, what = "heatmap")fit <- lca(student_esm, c("time_with_friends", "on_social_media", "tv_video_games", "sports"), n_classes = 2, n_starts = 3, seed = 1) fit plot(fit, what = "heatmap")
Fits a single-level latent profile model: a finite mixture of multivariate
normal distributions, in which each observation belongs to one of
n_profiles unobserved profiles and each profile has its own mean on every
indicator. It is the model multilpa() fits with id = NULL, and it
returns the same object, so every accessor, plot and inference verb of the
package applies to it.
lpa(data, vars, n_profiles, ...)lpa(data, vars, n_profiles, ...)
data |
A data frame with one row per observation. |
vars |
Names of the indicator columns. Numeric columns are continuous
indicators; name categorical ones in |
n_profiles |
Number of profiles, a positive whole number. |
... |
Further arguments for |
For observations nested in groups (students in schools, occasions in
persons), use multilpa() with the grouping column as id, which adds
latent classes of groups above the profiles.
A fitted multilpa object, as multilpa() returns.
latents_bad_argument when id or n_group_classes is passed; the
conditions multilpa() raises otherwise.
Oberski, D. (2016). Mixture models: Latent profile and latent class analysis. In J. Robertson & M. Kaptein (Eds.), Modern Statistical Methods for HCI (pp. 275–287). Springer.
lca() for categorical indicators; multilpa() for the two-level
model; enumerate_lpa() to compare numbers of profiles.
fit <- lpa(srl, c("cognitive_strategies", "intrinsic_value", "self_efficacy", "self_regulation", "test_anxiety"), n_profiles = 2, variance_model = "equal", covariance_model = "full", n_starts = 3, seed = 1) fit plot(fit, what = "raincloud")fit <- lpa(srl, c("cognitive_strategies", "intrinsic_value", "self_efficacy", "self_regulation", "test_anxiety"), n_profiles = 2, variance_model = "equal", covariance_model = "full", n_starts = 3, seed = 1) fit plot(fit, what = "raincloud")
Fits latent profiles to repeated observations of the same group and
estimates the probabilities of moving between them from one occasion to the
next. The measurement model is that of multilpa() – Gaussian, categorical
or mixed indicators, invariant across occasions – so profiles keep the same
meaning at every occasion and a change of profile is a change of state
rather than a change of definition. With more than one group class, each
class carries its own initial distribution and its own transition matrix,
which is how groups that differ in their dynamics are separated from groups
that differ only in where they start.
lta( data, vars, id, n_profiles, time, n_group_classes = 1L, variance_model = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, missing = c("error", "fiml"), covariance_model = c("diagonal", "full"), categorical = character(), min_probability = 1e-10, occasions = c("observed", "grid"), select_start = c("likelihood", "converged"), transitions = c("homogeneous", "occasion"), transition_covariates = character(), initial_covariates = character(), measurement = c("invariant", "occasion"), order = 1L, model = NULL, mover_stayer = FALSE, weights = NULL, ordinal = character(), count = character(), count_model = c("poisson", "negative_binomial"), count_dispersion = c("varying", "equal") )lta( data, vars, id, n_profiles, time, n_group_classes = 1L, variance_model = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, missing = c("error", "fiml"), covariance_model = c("diagonal", "full"), categorical = character(), min_probability = 1e-10, occasions = c("observed", "grid"), select_start = c("likelihood", "converged"), transitions = c("homogeneous", "occasion"), transition_covariates = character(), initial_covariates = character(), measurement = c("invariant", "occasion"), order = 1L, model = NULL, mover_stayer = FALSE, weights = NULL, ordinal = character(), count = character(), count_model = c("poisson", "negative_binomial"), count_dispersion = c("varying", "equal") )
data |
A data frame containing indicators and a group identifier. |
vars |
Unique character vector of continuous indicator column names. |
id |
Name of the observed group identifier column. Character, factor, or
numeric identifiers are supported; missing identifiers are not. Required
here: this model has a second level by construction, so it has no
single-level form and does not take |
n_profiles |
Integer number of individual profiles, at least two. |
time |
Name of the column giving each observation's occasion within its group. Required: this model is defined by the ordering. Values must be complete and unique within each group. |
n_group_classes |
Positive integer number of latent group classes. One gives an ordinary single-level latent transition model; more than one fits a mixture of transition models over groups. |
variance_model |
Either |
n_starts |
Positive integer number of EM starts. The first is
deterministic: Ward's hierarchical clustering of the standardized
indicators (on at most 2000 evenly spaced rows), each cluster described by
its own means and spread, which is the start mclust's name refers to and
finds solutions whose profiles differ in shape. The rest are k-means from
random centres. When |
max_iter |
Nonnegative integer maximum number of EM updates per start.
|
tol |
Positive relative log-likelihood tolerance. Convergence requires
absolute change no greater than |
min_variance |
Positive lower bound on each variance, or each covariance eigenvalue for full covariance, in squared input units. This defines a constrained maximum-likelihood problem. Bound-active estimates are explicitly reported and generate a warning. |
seed |
Optional random seed: any whole number |
missing |
|
covariance_model |
|
categorical |
Character vector naming indicators to treat as categorical. Each is modelled by unrestricted, profile-specific response probabilities over its observed categories, which is the latent class measurement model. Binary, ordinal and unordered indicators are all handled by the same unrestricted parameterization; numeric, integer, logical, character and factor columns are accepted. Indicators not named here stay Gaussian, so naming a subset fits a mixed-mode model. |
min_probability |
Positive lower bound on every categorical response
probability, defining a constrained maximum-likelihood problem in the same
way |
occasions |
How a group's occasions are placed on the transition grid.
|
select_start |
Which start the fit reports. |
transitions |
|
transition_covariates |
Names of columns whose values shift the transition probabilities through a multinomial logit per origin profile: the log odds of moving to each other profile rather than staying. A column may change over occasions; the value at the destination occasion is used. Factors are expanded to indicator columns. |
initial_covariates |
Names of columns that shift the initial profile distribution (a multinomial logit, reference: the last profile), read at each group's first occasion. |
measurement |
|
order |
|
model |
A covariance structure as |
mover_stayer |
|
weights |
|
ordinal, count, count_model, count_dispersion
|
Ordinal and count
indicators, as in |
This is latent transition analysis (LTA); with more than one group class it
is a mixture over transition patterns, so the classes are trajectories rather
than states. multilpa() is the cross-sectional counterpart.
By default transitions are first order and homogeneous over
occasions: the probability of moving from one profile to another does not
depend on the occasion or on earlier profiles. Measurement parameters are
shared across occasions and across group classes. A profile that no group occupies before
its final occasion leaves its transition row without information; the row
is then uniform by construction rather than estimated. Such a row is warned
about when the model is fitted, is flagged by the estimated column of
the transition table and is listed by
get_results(fit, "transitions", estimated = FALSE).
Profile labels are arbitrary and
are not comparable across fits without alignment.
max_iter = 0 updates nothing: the starting values are evaluated and
returned with the expectation they imply, and the fit reports
converged = FALSE after zero iterations. Every reported quantity,
including expected_count and the implied profile prevalence, is then
read off that single expectation.
A multilpa_transitions object containing means, variances,
optional covariances and response_probabilities,
initial_probabilities (group classes by profiles),
transition_probabilities (profiles by profiles by group classes, rows
indexing the profile moved from), group_probabilities, posterior
matrices, classifications, log likelihood, information criteria, restart
diagnostics and convergence history. Use get_results() for the tidy
transition table and for the other
tables. No standard
errors, likelihood-ratio tests or guarantees of global optimality are
given for this model family.
With transitions = "occasion", transition_covariates,
initial_covariates, measurement = "occasion" or order = 2 the
result is a multilpa_lta object instead, read with
get_results.multilpa_lta(): transition and initial logit coefficients
with Wald standard errors (observed, robust or OPG, from analytic
scores), model-implied transition probabilities per occasion, profiles
per occasion. Missing indicators (missing = "fiml") and covariance
structures are supported; diagonal fits are finished by a quasi-Newton
search on the exact likelihood, others by EM.
Agreement with Mplus (User's Guide examples 8.13, 8.14; occasion-specific
thresholds), depmixS4 and LMest is recorded in
equivalence/lta-extensions/ of the source repository.
Collins, L. M., & Lanza, S. T. (2010). Latent class and latent transition analysis. Wiley.
Vermunt, J. K. (2003). Multilevel latent class models. Sociological Methodology, 33, 213–239. doi:10.1111/j.0081-1750.2003.t01-1-00131.x.
get_results() for the fitted transition probabilities and for the
assignments in order, and multilpa() for the
cross-sectional model this one shares its measurement parameters with.
# Students take their courses in their own order, which `sequence` records, # so the same enrolments that fit a cross-sectional model fit a transition # one. Engagement mostly persists from one course to the next. fit <- lta( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, time = "sequence", n_starts = 2, seed = 1 ) get_results(fit, what = "transitions") summary(fit)# Students take their courses in their own order, which `sequence` records, # so the same enrolments that fit a cross-sectional model fit a transition # one. Engagement mostly persists from one course to the next. fit <- lta( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, time = "sequence", n_starts = 2, seed = 1 ) get_results(fit, what = "transitions") summary(fit)
Fits a regression whose coefficients differ across latent classes: the
data are assumed to come from n_classes subpopulations, each with its own
regression of the outcome on the predictors, and the model estimates the
regressions, the class sizes and every row's posterior class membership at
once. This is clusterwise, latent class or mixture regression (DeSarbo and
Cron 1988; Wedel and DeSarbo 1995).
mixture_regression( formula, data, n_classes, family = c("gaussian", "binomial", "poisson", "negative_binomial", "ordinal"), id = NULL, class_level = c("observation", "group"), n_group_classes = 1L, common = NULL, membership = ~1, group_membership = ~1, variance = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, missing = c("error", "omit"), select_start = c("likelihood", "converged"), vcov_type = c("observed", "robust", "opg", "none"), weights = NULL, random = NULL, random_covariance = c("varying", "equal", "proportional"), random_diagonal = FALSE, cluster = NULL )mixture_regression( formula, data, n_classes, family = c("gaussian", "binomial", "poisson", "negative_binomial", "ordinal"), id = NULL, class_level = c("observation", "group"), n_group_classes = 1L, common = NULL, membership = ~1, group_membership = ~1, variance = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, missing = c("error", "omit"), select_start = c("likelihood", "converged"), vcov_type = c("observed", "robust", "opg", "none"), weights = NULL, random = NULL, random_covariance = c("varying", "equal", "proportional"), random_diagonal = FALSE, cluster = NULL )
formula |
A model formula |
data |
A data frame. |
n_classes |
Number of regression classes. |
family |
|
id |
|
class_level |
|
n_group_classes |
Number of second-level group classes, for
|
common |
|
membership |
Covariates predicting class membership (a multinomial
logit, the first class as the reference): variable names such as
|
group_membership |
Group-level covariates predicting the group class, for the two-level model, as names or a one-sided formula. Must be constant within groups. |
variance |
|
n_starts |
Number of random starts, besides one start built from the residuals of a pooled regression. Every start runs 50 EM iterations; the better half (at least two) continue to convergence. |
max_iter |
Maximum EM iterations per start. |
tol |
Relative convergence tolerance on the log likelihood. |
min_variance |
Floor for a Gaussian residual variance, relative to the outcome's variance. A class that reaches it is fitting too few rows exactly; such starts are set aside. |
seed |
|
missing |
|
select_start |
|
vcov_type |
Covariance of the estimates stored with the fit:
|
weights |
|
random |
|
random_covariance |
How the random-effect covariance differs across
classes: |
random_diagonal |
|
cluster |
|
An object of class latents_mixture_regression, or
latents_growth_mixture when random is given. Read it with
as.data.frame() (the coefficient table) or get_results() (every
table, by name), and use predict(), plot(), summary(), coef(),
vcov(), confint(), logLik() and nobs() on it. The tables are
described on get_results.latents_mixture_regression().
class_level and n_group_classes choose one of three likelihoods.
id = NULL)Every row is its own unit and has its own class.
class_level = "group"Every row of a group (id) belongs to the
same class, so a class is a kind of group: the regression mixture for
repeated measures, flexmix(y ~ x | id). Each group's evidence is the
product of its rows' densities.
class_level = "observation" with id and n_group_classes >= 2
Rows keep their own class, and a second-level group class shifts how
probable each regression class is within its groups (Vermunt 2003).
With n_group_classes = 1 the groups only enter the "robust" standard
errors, which are then clustered on id.
With repeated measures of persons (id, class_level = "group"), a time
predictor in formula gives every class its own trajectory: latent class
growth analysis (Nagin 2005). random adds random effects within
classes, so persons scatter around their class trajectory: the growth
mixture model (Verbeke and Lesaffre 1996; Muthen and Shedden 1999). Within
class k, person i's outcomes are
with Z built from random. The likelihood is exact (Gaussian, no
numerical integration); estimation is EM with the random effects as missing
data, finished by quasi-Newton with analytic scores. Such a fit has class
latents_growth_mixture; its tables, plots and inference are described on
get_results.latents_growth_mixture() and
plot.latents_growth_mixture(). Random effects need the gaussian family,
id and class_level = "group".
With cluster, persons are nested in clusters (students in schools) and
the multilevel growth mixture model is fitted: each cluster belongs to a
group class h, and the trajectory-class logits of its persons have a
group-class-specific intercept and shared covariate slopes,
(Asparouhov and Muthen 2008; Vermunt 2003). group_membership names
cluster-level covariates of the group class. The group classes are read
with get_results(fit, "group_classes") and "clusters".
"gaussian" (identity link, a residual standard deviation per class, or
one shared under variance = "equal"), "binomial" (logit link; the
outcome is 0/1, logical, a two-level factor whose second level is the
success, or cbind(successes, failures)), "poisson" (log link;
offset() terms in the formula are honoured) and "negative_binomial"
(log link, NB2: variance mu + alpha mu^2 with a dispersion alpha per class,
or one shared under variance = "equal", for overdispersed counts; a
dispersion estimated at zero is the Poisson limit and raises
latents_boundary).
"ordinal" fits a cumulative-logit (proportional-odds) regression within
each class (McCullagh 1980),
with ordered thresholds
per class in place of the intercept, and the parametrization of
MASS::polr(): a positive slope moves the class towards higher
categories. The outcome is an ordered factor, a factor (its levels taken
in order) or whole-number categories; the categories are those observed,
at least two. common terms share their slopes across classes, and
offset() is honoured; the formula's intercept is replaced by the
thresholds, so it must not be removed. The thresholds appear in the
coefficient table as terms "threshold:<lower>|<upper>", and
exp_estimate of a slope is the cumulative odds ratio of a higher
category. The class "mean" of an ordinal outcome (fitted values,
predict(), trajectories) is the expected category score
, the categories scored 1 to C in
order; predict(type = "probabilities") gives the category probabilities.
With one outcome per class assignment the classes are identified by how
continuous predictors shift the category probabilities: a mixture with no
predictors, or with two categories (then the logistic mixture of
"binomial"), is refused with latents_not_identified unless
class_level = "group" gives each class assignment several outcomes.
A mixture of Bernoulli regressions with one trial per unit is not
identified – any mixture of Bernoullis is again a Bernoulli – so a binary
outcome requires class_level = "group" (several rows per class
assignment) or binomial counts with more than one trial per row. The
request is refused otherwise.
latents_bad_argument for an inconsistent specification;
latents_bad_data for an outcome outside the family's support or a
covariate that varies within a group where it may not;
latents_not_identified for a binary outcome with one trial per class
assignment, or an ordinal mixture with one outcome per class assignment
and two categories or no predictors; latents_missing_data for missing values under
missing = "error"; latents_rows_dropped (warning) under
missing = "omit"; latents_no_valid_start when every start
degenerated; latents_unconverged (warning) when the selected start did
not converge; latents_degenerate_start (warning) when some starts
degenerated; latents_separation (warning) when a coefficient diverged.
Asparouhov, T., & Muthen, B. (2008). Multilevel mixture models. In G. R. Hancock & K. M. Samuelsen (Eds.), Advances in latent variable mixture models (pp. 27–51). Information Age.
McCullagh, P. (1980). Regression models for ordinal data. Journal of the Royal Statistical Society B, 42, 109–142.
Muthen, B., & Shedden, K. (1999). Finite mixture modeling with mixture outcomes using the EM algorithm. Biometrics, 55, 463–469.
Nagin, D. S. (2005). Group-based modeling of development. Harvard University Press.
Proust-Lima, C., Philipps, V., & Liquet, B. (2017). Estimation of extended mixed models using latent classes and latent processes: the R package lcmm. Journal of Statistical Software, 78(2), 1–56.
Verbeke, G., & Lesaffre, E. (1996). A linear mixed-effects model with heterogeneity in the random-effects population. Journal of the American Statistical Association, 91, 217–221.
DeSarbo, W. S., & Cron, W. L. (1988). A maximum likelihood methodology for clusterwise linear regression. Journal of Classification, 5, 249–282.
Wedel, M., & DeSarbo, W. S. (1995). A mixture likelihood approach for generalized linear models. Journal of Classification, 12, 21–55.
Follmann, D. A., & Lambert, D. (1991). Identifiability of finite mixtures of logistic regression models. Journal of Statistical Planning and Inference, 27, 375–381.
Vermunt, J. K. (2003). Multilevel latent class models. Sociological Methodology, 33, 213–239.
Leisch, F. (2004). FlexMix: A general framework for finite mixture models and latent class regression in R. Journal of Statistical Software, 11(8).
enumerate_regressions() to compare numbers of classes.
fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) fit as.data.frame(fit) get_results(fit, "classes") # One class per student, several rows per student: by_student <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, id = "student", class_level = "group", n_starts = 3, seed = 1) get_results(by_student, "fit") # Students nested in schools, each school in one of two group classes: schools <- mixture_regression(score ~ wave, growth_schools, n_classes = 2, id = "student", class_level = "group", random = "wave", random_covariance = "equal", cluster = "school", n_group_classes = 2, n_starts = 2, seed = 1) get_results(schools, "group_classes")fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) fit as.data.frame(fit) get_results(fit, "classes") # One class per student, several rows per student: by_student <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, id = "student", class_level = "group", n_starts = 3, seed = 1) get_results(by_student, "fit") # Students nested in schools, each school in one of two group classes: schools <- mixture_regression(score ~ wave, growth_schools, n_classes = 2, id = "student", class_level = "group", random = "wave", random_covariance = "equal", cluster = "school", n_group_classes = 2, n_starts = 2, seed = 1) get_results(schools, "group_classes")
Two-level latent class analysis for categorical indicators: multilpa()
with discrete indicators in vars treated as categorical unless named
in ordinal or count through .... Each profile is described by
unrestricted response probabilities for categorical items and the
corresponding structured parameters for ordinal and count items,
and the group classes differ in how probable each profile is for their
observations. For models that combine categorical and continuous
indicators, call multilpa() and name the categorical ones in its
categorical argument.
multilca(data, vars, id, n_profiles, n_group_classes = 2L, ...)multilca(data, vars, id, n_profiles, n_group_classes = 2L, ...)
data |
A data frame with one row per observation. |
vars |
Names of the categorical indicator columns. Numeric, integer, logical, character and factor columns are accepted; factor levels keep their declared order and other types are sorted. |
id |
Name of the group identifier column, or |
n_profiles |
Number of observation-level latent classes (profiles). |
n_group_classes |
Number of group-level latent classes. Defaults to 2,
or to 1 when |
... |
Further arguments to |
A fitted model of class multilpa (or multilpa_covariates when
membership covariates are given), exactly as multilpa() returns it with
the corresponding discrete indicator types. Categorical response
probabilities are in
get_results(fit, "responses"), one row per profile, item and category.
latents_bad_argument when categorical is supplied, since its
columns are derived from vars, ordinal and count here. Every condition of
multilpa() can also be raised.
multilpa() for continuous and mixed indicators, and
vignette("lca", package = "latents").
activities <- c("time_with_friends", "on_social_media", "tv_video_games") fit <- multilca(subset(student_esm, day <= 1), vars = activities, id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 1, seed = 1) get_results(fit, "responses") get_results(fit, "profile_probabilities")activities <- c("time_with_friends", "on_social_media", "tv_video_games") fit <- multilca(subset(student_esm, day <= 1), vars = activities, id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 1, seed = 1) get_results(fit, "responses") get_results(fit, "profile_probabilities")
Fits individual Gaussian profiles nested within observed groups. A discrete latent group class determines the profile proportions. Profile means and variances are shared across group classes (measurement invariance), and indicators are independent conditional on individual profile membership by default. Full residual covariance and observed-data maximum likelihood for missing indicators are available.
multilpa( data, vars, id, n_profiles, n_group_classes = 2L, profile_covariates = character(), group_covariates = character(), variance_model = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, start = NULL, missing = c("error", "fiml"), profile_slopes = c("shared", "group_class"), covariance_model = c("diagonal", "full"), categorical = character(), min_probability = 1e-10, time = NULL, fixed = character(), centering = c("none", "person", "grand"), volume = NULL, shape = NULL, orientation = NULL, model = NULL, select_start = c("likelihood", "converged"), prior = NULL, noise = FALSE, acceleration = c("squarem", "none"), family = c("profiles", "additive", "dispersion", "additive_dispersion", "restricted_cross_level", "full_cross_level"), between_variance = c("varying", "equal"), weights = NULL, ordinal = character(), count = character(), count_model = c("poisson", "negative_binomial"), count_dispersion = c("varying", "equal") )multilpa( data, vars, id, n_profiles, n_group_classes = 2L, profile_covariates = character(), group_covariates = character(), variance_model = c("varying", "equal"), n_starts = 10L, max_iter = 1000L, tol = 1e-08, min_variance = 1e-06, seed = NULL, start = NULL, missing = c("error", "fiml"), profile_slopes = c("shared", "group_class"), covariance_model = c("diagonal", "full"), categorical = character(), min_probability = 1e-10, time = NULL, fixed = character(), centering = c("none", "person", "grand"), volume = NULL, shape = NULL, orientation = NULL, model = NULL, select_start = c("likelihood", "converged"), prior = NULL, noise = FALSE, acceleration = c("squarem", "none"), family = c("profiles", "additive", "dispersion", "additive_dispersion", "restricted_cross_level", "full_cross_level"), between_variance = c("varying", "equal"), weights = NULL, ordinal = character(), count = character(), count_model = c("poisson", "negative_binomial"), count_dispersion = c("varying", "equal") )
data |
A data frame containing indicators and a group identifier. |
||||||||||||||||||||||||
vars |
Unique character vector of continuous indicator column names. |
||||||||||||||||||||||||
id |
Name of the observed group identifier column. Character, factor, or numeric identifiers are supported; missing identifiers are not.
|
||||||||||||||||||||||||
n_profiles |
Positive integer number of individual profiles. |
||||||||||||||||||||||||
n_group_classes |
Positive integer number of latent group classes. With
|
||||||||||||||||||||||||
profile_covariates, group_covariates
|
Names of numeric columns of
|
||||||||||||||||||||||||
variance_model |
Either |
||||||||||||||||||||||||
n_starts |
Positive integer number of EM starts. The first is
deterministic: Ward's hierarchical clustering of the standardized
indicators (on at most 2000 evenly spaced rows), each cluster described by
its own means and spread, which is the start mclust's name refers to and
finds solutions whose profiles differ in shape. The rest are k-means from
random centres. When |
||||||||||||||||||||||||
max_iter |
Nonnegative integer maximum number of EM updates per start.
|
||||||||||||||||||||||||
tol |
Positive relative log-likelihood tolerance. Convergence requires
absolute change no greater than |
||||||||||||||||||||||||
min_variance |
Positive lower bound on each variance, or each covariance eigenvalue for full covariance, in squared input units. This defines a constrained maximum-likelihood problem. Bound-active estimates are explicitly reported and generate a warning. |
||||||||||||||||||||||||
seed |
Optional random seed: any whole number |
||||||||||||||||||||||||
start |
Optional list of |
||||||||||||||||||||||||
missing |
|
||||||||||||||||||||||||
profile_slopes |
|
||||||||||||||||||||||||
covariance_model |
|
||||||||||||||||||||||||
categorical |
Character vector naming indicators to treat as categorical. Each is modelled by unrestricted, profile-specific response probabilities over its observed categories, which is the latent class measurement model. Binary, ordinal and unordered indicators are all handled by the same unrestricted parameterization; numeric, integer, logical, character and factor columns are accepted. Indicators not named here stay Gaussian, so naming a subset fits a mixed-mode model. |
||||||||||||||||||||||||
min_probability |
Positive lower bound on every categorical response
probability, defining a constrained maximum-likelihood problem in the same
way |
||||||||||||||||||||||||
time |
Optional name of a column giving each observation's position
within its group, such as a wave, occasion or course number. The model does
not use it; it is stored so that |
||||||||||||||||||||||||
fixed |
Character vector naming measurement blocks to hold at the
values |
||||||||||||||||||||||||
centering |
How to centre the continuous indicators before fitting.
|
||||||||||||||||||||||||
volume, shape, orientation
|
The covariance structure, in the three
pieces it is made of. Each profile's covariance decomposes as
The estimates are those of Celeux and Govaert (1995); the two models with
a shared orientation and a free shape, EVE and VVE, have no closed form
and use the minorize-maximize step of Browne and McNicholas (2014).
Parameter counts match Anything other than EEI, VVI, EEE or VVV is maximized across every profile
at once, so it cannot be combined with a held |
||||||||||||||||||||||||
model |
The covariance structure named by its mclust code, one of
|
||||||||||||||||||||||||
select_start |
Which start the fit reports. |
||||||||||||||||||||||||
prior |
|
||||||||||||||||||||||||
noise |
|
||||||||||||||||||||||||
acceleration |
|
||||||||||||||||||||||||
family |
The model family.
These families take only Two cross-level families estimate individual profiles and group classes from the same indicators, following the manifest-aggregation specification of Houle et al. (2026, supplement): each group's means of the indicators are between-level indicators of its group class, with class-specific means and variances.
These take |
||||||||||||||||||||||||
between_variance |
For the additive and additive-dispersion families:
|
||||||||||||||||||||||||
weights |
|
||||||||||||||||||||||||
ordinal |
Names of indicators in |
||||||||||||||||||||||||
count |
Names of indicators in |
||||||||||||||||||||||||
count_model |
|
||||||||||||||||||||||||
count_dispersion |
For |
Infinite and constant observed indicators are rejected. Each
indicator must have at least two distinct observed values. No rows are
silently dropped. In FIML mode, fully missing individuals contribute no
direct measurement likelihood but receive posterior probabilities from
their group's information. Observation counts retain these individuals;
the individual-level BIC excludes them through n_informative, while the
group-level BIC uses all observed groups. Missing patterns can prevent parameter
identification; no general identification guarantee is made. Initialization
alone uses indicator-mean filling. EM uses conditional Gaussian sufficient
statistics and observed marginal densities. More than one group class requires more than one profile
and at least one group with multiple individuals; these checks are necessary
but do not establish identification. By default the highest finite
likelihood across starts is returned, even if that start did not converge;
select_start = "converged" prefers a converged start, and converged and
starts show which was chosen. Profile and group-class labels are arbitrary.
An multilpa object containing means, variances, optional
covariances (indicators by indicators by profiles),
profile_probabilities, group_probabilities, posterior matrices,
classifications, log likelihood, information criteria, restart diagnostics,
and convergence history. Individual rows retain their input order; groups
retain first-occurrence order. bic and bic_groups use the observed group
count; bic_individual uses n_informative, the number of rows carrying at
least one observed indicator. These are alternative
conventions, not interchangeable criteria. boundary identifies variance
bounds; small_classes flags effective memberships below one. The fit
itself carries no standard errors: parameter_inference() computes them
from the fit and the data it was fitted to, and bootstrap_lrt() tests
nested models. No guarantee of global optimality is
given, whatever n_starts is used.
Read the tidy form with as.data.frame(); what selects which table.
Vermunt, J. K. (2003). Multilevel latent class models. Sociological Methodology, 33, 213–239. doi:10.1111/j.0081-1750.2003.t01-1-00131.x.
Banfield, J. D., & Raftery, A. E. (1993). Model-based Gaussian and non-Gaussian clustering. Biometrics, 49, 803–821. doi:10.2307/2532201.
Houle, S. A., Morin, A. J. S., & Harvey, J.-F. (2026). Multilevel latent profile analyses: A comprehensive guide. Organizational Research Methods. doi:10.1177/10944281261469432.
Skinner, C. J. (1989). Domain means, regression and multivariate analysis. In C. J. Skinner, D. Holt, & T. M. F. Smith (Eds.), Analysis of complex surveys (pp. 59–87). Wiley.
Asparouhov, T. (2005). Sampling weights in latent variable modeling. Structural Equation Modeling, 12, 411–434. doi:10.1207/s15328007sem1203_4.
Fraley, C., & Raftery, A. E. (2007). Bayesian regularization for normal mixture estimation and model-based clustering. Journal of Classification, 24, 155–181. doi:10.1007/s00357-007-0004-5.
multilca() for a model in which every indicator is categorical.
# Course sessions nested in students: engagement profiles of sessions, and # classes of students that differ in how often they are engaged. activity <- c("browse", "lectures", "forum_read", "forum_post", "attendance") fit <- multilpa(course_engagement, activity, "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 42) summary(fit) as.data.frame(fit) get_results(fit, what = "profile_probabilities")# Course sessions nested in students: engagement profiles of sessions, and # classes of students that differ in how often they are engaged. activity <- c("browse", "lectures", "forum_read", "forum_post", "attendance") fit <- multilpa(course_engagement, activity, "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 42) summary(fit) as.data.frame(fit) get_results(fit, what = "profile_probabilities")
Standard errors, tests and intervals for every free parameter of
multilpa(profile_covariates = ), at all three levels at once: the
measurement model, the
profile logits and the group-class logits. Without them a membership
coefficient cannot be reported, because nothing distinguishes a real effect
from separation.
Computes ordinary maximum-likelihood standard errors from the inverse observed Hessian and delta-method Wald confidence intervals. These are local, asymptotic estimates conditional on the selected numbers of classes. They are not robust sandwich errors and do not account for model selection. Mixture boundary solutions and unidentified Hessians do not admit this calculation.
## S3 method for class 'multilpa_additive' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), ... ) ## S3 method for class 'multilpa_covariates' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa_cross_level' parameter_inference(x, ...) parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa_lta' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") )## S3 method for class 'multilpa_additive' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), ... ) ## S3 method for class 'multilpa_covariates' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa_cross_level' parameter_inference(x, ...) parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa_lta' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") )
x |
A converged |
data |
Optional. The data frame the model was fitted to; when omitted it is rebuilt from the indicators, identifiers and occasions the fit stores, which round-trip exactly. Supplying it is the stronger check that the caller still holds that frame. A supplied frame is checked exactly against the stored indicators and identifiers; for an older fit without stored indicators, the likelihood provides a weaker consistency check. |
level |
Confidence level strictly between zero and one. |
step |
Positive finite-difference step for the analytic score derivative. |
vcov_type |
|
adjust |
Multiplicity correction applied to |
method |
How uncertainty is measured. |
... |
Unused by most methods; accepted so every method shares the generic's signature. |
iter |
Number of resamples when |
n_starts, max_iter, tol
|
Passed to each bootstrap refit. Each replicate
is a fresh fit, so |
seed |
Optional seed for the resampling: any whole number |
boundary |
What to do when categorical response probabilities sit on
their lower bound, |
Class-membership coefficients are on the multinomial logit scale with the final profile and the final group class as references, so a coefficient is a log odds against that reference. The tests are Wald tests and inherit the usual caveat: they are unreliable for a coefficient driven to the boundary by separation, which the size of the estimate and its standard error together will reveal.
For a general transition fit (an lta() fit with covariate,
occasion-varying or second-order transitions), the table has one row per
transition, initial and second-order logit: block, group_class,
from, to, term, estimate, standard_error, statistic,
p_value, p_adjusted, conf_low, conf_high (and previous for a
second-order fit). method = "bootstrap" resamples persons, refits, and
matches every replicate's profiles (on the measurement) and group classes
to the original's, rebasing the initial logits when the reference profile
moves; it needs data, and works where Wald inference is refused (for
example full covariance structures). Transition inference holds nothing
at a bound: with no bound active boundary = "fix" gives the same table
as "error", and a fit with an active bound raises latents_boundary_fit
under either.
A base data.frame with one row per free parameter and the same
columns, in the same order, that parameter_inference() returns for every
other fitted class: level ("measurement", "profile" or "group"),
outcome (which profile or group class the coefficient predicts, or which
profile a measurement parameter belongs to), term, parameter
("mean", "variance", "covariance", "response" or
"coefficient"), estimate,
standard_error, statistic, p_value, p_adjusted, conf_low and
conf_high. Variances and covariances are reported in their natural units,
with standard errors carried through the delta method from the log and
log-Cholesky coordinates on which they are estimated. Categorical
indicators contribute one "response" row per profile, indicator and
category, named indicator:category: the probability of that category,
with its standard error carried from the multinomial logits it is
estimated on, and no Wald test, as for the covariate-free model.
A base data.frame with one row per reported parameter and the
columns level ("measurement", "profile" or "group"), outcome,
term, parameter, estimate, standard_error, statistic, p_value,
p_adjusted, conf_low and conf_high. Estimates are on the natural
scale: variances in their own units, probabilities as probabilities. A
parameter whose null value is on the boundary of its space – a variance, a
probability, a residual variance on the covariance diagonal – carries NA
for statistic, p_value and p_adjusted, because a Wald test against
that boundary is not a question worth asking; the interval still is. All
class probabilities are reported; their sum constraints make the natural
covariance singular. Wald intervals for probabilities are formed on the
logit scale and for variances (and covariance diagonals) on the log
scale, then mapped back with the same standard error, so they stay inside
the parameter's range and widen towards the far side of a bound; every
other interval is estimate +/- z * standard_error. confint() returns
the same intervals. A probability at or below 1e-6 is treated as on its
bound (refused, or held by boundary = "fix").
A fit made with fixed reports only the parameters it estimated: a held
measurement block is a constant of this likelihood, so it contributes no
row here and no row or column to the information matrix. The table is on
the natural scale, where every class probability is reported, so it has one
row per estimated natural coefficient: n_parameters rows plus one for
each set of probabilities whose reference category the estimation scale
drops. The held values still enter the likelihood at
the values they were held at, so every standard error, interval and p-value
is conditional on that measurement solution and does not propagate its
uncertainty. See fit_staged() for what that conditioning means. For a
fit_staged() result, method = "bootstrap" does propagate it: each
resample refits both stages, and the table has a row for every
parameter, the measurement included (attribute stages_resampled is 2).
With method = "bootstrap" the table has the same columns and the same
rows, estimate is still the fitted value, standard_error is the
standard deviation of the replicates and conf_low/conf_high are the
percentile interval. statistic, p_value and p_adjusted are NA
throughout: putting a normal approximation back on top of the replicates
the interval was read from is the assumption this path exists to avoid, so
the interval is the inference. Its attributes are covariance (the
covariance of the replicates, which vcov() returns), level, method,
iter, n_valid, replicates (the kept replicates, one row each),
messages (why a resample was dropped, NA where it was not),
structure and fixed; covariance_unconstrained is NULL, because the
estimation-scale chart is what this path does without.
Diagnostics of the fit as a whole travel as attributes rather than as
columns repeated down every row: covariance and covariance_unconstrained
(natural and estimation-scale covariance of the estimates), hessian,
gradient, scaled_score, condition_ratio, level, step, vcov_type,
fixed (the measurement blocks this fit held, character() when none) and
adjust. When vcov_type is "robust", group_scores holds the
groups-by-parameters score matrix and scaling_correction the MLR scaling
correction factor tr(A^-1 B) / q, used for scaled likelihood-ratio
difference tests.
latents_no_converge for an unconverged fit, latents_boundary_fit when
a variance, a response probability or a mixing probability sits at its
bound, latents_singular_information when the observed information cannot
be inverted, latents_no_free_parameters when fixed held every
parameter, latents_bad_inference_data when a supplied data does not
reproduce the fit, and latents_too_few_groups for vcov_type = "robust"
with fewer independent groups than reported parameters. A fit whose score
is still far from zero is reported with a latents_unconverged warning
rather than refused. A fit made with multilpa(prior = ) is refused by
the Wald path with latents_unsupported_inference (it sits at a
posterior mode, where the likelihood's score is not zero), and a fit with
a noise component by either path with latents_unsupported_noise.
method = "bootstrap" adds latents_unsupported_inference for a fit that
holds a measurement block through multilpa(fixed = ) — the held values
came from another fit and resampling these data does not resample them;
a fit_staged() result is bootstrapped by staging again instead — and
latents_bootstrap_failed when fewer than two resamples produced a usable
fit, whose message carries the first reason one gave. Resamples that fail
or do not converge are dropped with a latents_bootstrap_dropped warning
naming how many, rather than being silently left out of the count.
# Two separated profiles, membership driven by `x` and by the group's class. set.seed(5) school <- rep(seq_len(16), each = 8) high_class <- rep(rep(c(FALSE, TRUE), length.out = 16), each = 8) x <- rnorm(128) profile <- ifelse( runif(128) < plogis(-1 + 2 * high_class + 0.8 * x), 2L, 1L ) example_data <- data.frame( school = school, x = x, y1 = rnorm(128, ifelse(profile == 2L, 2, -2), 0.7), y2 = rnorm(128, ifelse(profile == 2L, 1.5, -1.5), 0.7) ) fit <- multilpa(example_data, c("y1", "y2"), "school", n_profiles = 2, n_group_classes = 2, profile_covariates = "x", n_starts = 2, seed = 1) parameter_inference(fit, example_data) set.seed(42) example_data <- data.frame( school = rep(seq_len(10), each = 10), score_a = rnorm(100), score_b = rnorm(100) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1) parameter_inference(fit) # Many tests in one table: name the correction, do not apply one by stealth. parameter_inference(fit, adjust = "BH") # A structure that constrains the shape across profiles (VEI) is charted in # its own free coordinates, so it has Wald standard errors too. shaped <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1, volume = "varying", shape = "equal", orientation = "axis") parameter_inference(shaped) # The bootstrap resamples schools and refits inside the same family. parameter_inference(shaped, method = "bootstrap", iter = 10, n_starts = 1, seed = 1)# Two separated profiles, membership driven by `x` and by the group's class. set.seed(5) school <- rep(seq_len(16), each = 8) high_class <- rep(rep(c(FALSE, TRUE), length.out = 16), each = 8) x <- rnorm(128) profile <- ifelse( runif(128) < plogis(-1 + 2 * high_class + 0.8 * x), 2L, 1L ) example_data <- data.frame( school = school, x = x, y1 = rnorm(128, ifelse(profile == 2L, 2, -2), 0.7), y2 = rnorm(128, ifelse(profile == 2L, 1.5, -1.5), 0.7) ) fit <- multilpa(example_data, c("y1", "y2"), "school", n_profiles = 2, n_group_classes = 2, profile_covariates = "x", n_starts = 2, seed = 1) parameter_inference(fit, example_data) set.seed(42) example_data <- data.frame( school = rep(seq_len(10), each = 10), score_a = rnorm(100), score_b = rnorm(100) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1) parameter_inference(fit) # Many tests in one table: name the correction, do not apply one by stealth. parameter_inference(fit, adjust = "BH") # A structure that constrains the shape across profiles (VEI) is charted in # its own free coordinates, so it has Wald standard errors too. shaped <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1, volume = "varying", shape = "equal", orientation = "axis") parameter_inference(shaped) # The bootstrap resamples schools and refits inside the same family. parameter_inference(shaped, method = "bootstrap", iter = 10, n_starts = 1, seed = 1)
Standard errors, tests and intervals for every parameter of an lta()
fit: the measurement model, the group-class shares, each group class's
initial profile distribution and its transition matrix. The scores follow
the Fisher identity (each is an expected count from the forward-backward
pass minus the count the fitted probabilities imply), and the observed
information is the numerical Jacobian of that analytic score.
## S3 method for class 'multilpa_transitions' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa_transitions' vcov( object, data = NULL, step = 1e-04, vcov_type = c("observed", "robust", "opg"), scale = c("natural", "unconstrained"), ... ) ## S3 method for class 'multilpa_transitions' confint(object, parm, level = 0.95, data = NULL, ...)## S3 method for class 'multilpa_transitions' parameter_inference( x, data = NULL, level = 0.95, step = 1e-04, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods, method = c("wald", "bootstrap"), iter = 199L, n_starts = 10L, max_iter = 1000L, tol = 1e-08, seed = NULL, boundary = c("error", "fix") ) ## S3 method for class 'multilpa_transitions' vcov( object, data = NULL, step = 1e-04, vcov_type = c("observed", "robust", "opg"), scale = c("natural", "unconstrained"), ... ) ## S3 method for class 'multilpa_transitions' confint(object, parm, level = 0.95, data = NULL, ...)
x |
A fitted |
data |
Optional. The data frame the model was fitted to. The fit stores its indicators, codes, groups and occasions, so it is not needed; when given it must reproduce them. |
level |
Confidence level of the intervals. |
step |
Finite-difference step for the observed information. |
vcov_type |
|
adjust |
Multiplicity correction for |
method |
|
iter, n_starts, max_iter, tol, seed
|
Bootstrap resamples, the controls of each refit, and an optional seed (the caller's random state is restored). |
boundary |
Accepted for the generic. Transition inference holds
nothing at a bound: with no bound active |
object |
A fitted |
scale |
|
... |
Unused. |
parm |
Parameters to report, as names or positions of the natural coefficients; all by default. |
A base data.frame with one row per natural parameter and the
columns of parameter_inference(): level, outcome, term,
parameter, estimate, standard_error, statistic, p_value,
p_adjusted, conf_low and conf_high, named as coef() names them.
An initial probability (parameter = "initial_probability", level = "profile") has outcome the profile and term the group class; a
transition probability ("transition_probability") has outcome the
profile moved to and term group_class_h:profile_j, the group class
and the profile moved from. Probabilities and variances carry no Wald
test. Attributes covariance (natural scale),
covariance_unconstrained, vcov_type and level.
latents_no_converge for an unconverged fit; latents_boundary_fit when
a variance, a response, initial or transition probability, or a
group-class share sits at its bound, or a transition row was never
informed (a profile no group occupies before its last occasion);
latents_singular_information when the information cannot be inverted;
latents_bad_inference_data when a supplied data does not reproduce
the fit; latents_too_few_groups for robust or OPG errors with no more
groups than parameters; latents_bad_argument for method = "bootstrap"
without data.
fit <- lta(subset(course_engagement, student <= 40), c("browse", "lectures", "forum_read"), "student", n_profiles = 2, time = "sequence", n_starts = 2, seed = 1) parameter_inference(fit)fit <- lta(subset(course_engagement, student <= 40), c("browse", "lectures", "forum_read"), "student", n_profiles = 2, time = "sequence", n_starts = 2, seed = 1) parameter_inference(fit)
Draws information criteria against the number of profiles, one panel per
criterion and one line per covariance model and number of group classes. It
is the view plot(x) draws for an enumeration, as a function of its own so
that its arguments are listed (and completed by an editor) without going
through what =. A candidate that failed to converge is marked with a cross
on its panel's floor, so a gap in a line reads as a failure and not as a
missing candidate.
plot_enumeration( x, criterion = c("aic", "bic_groups", "bic_individual", "icl_individual"), combine = TRUE, labels = TRUE, mark_minimum = TRUE, main = NULL, subtitle = NULL )plot_enumeration( x, criterion = c("aic", "bic_groups", "bic_individual", "icl_individual"), combine = TRUE, labels = TRUE, mark_minimum = TRUE, main = NULL, subtitle = NULL )
x |
A |
criterion |
One or more criterion columns of |
combine |
|
labels |
|
mark_minimum |
|
main, subtitle
|
Title and subtitle. |
A ggplot object, one panel per criterion; with combine = FALSE
and several criteria, a latents_plots list of ggplot objects, one per
criterion, named by its criterion column.
latents_unknown_criterion for a criterion that is not an
information criterion of the grid; latents_nothing_to_plot when no
converged candidate has a finite value.
plot.multilpa_enumeration() for the profile tree,
summary.multilpa_enumeration() for the candidate each criterion prefers.
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(7) example_data <- data.frame(score_a = rnorm(120), score_b = rnorm(120)) candidates <- enumerate_lpa(example_data, c("score_a", "score_b"), n_profiles = 1:3, n_starts = 2) plot_enumeration(candidates) plot_enumeration(candidates, criterion = "all") }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(7) example_data <- data.frame(score_a = rnorm(120), score_b = rnorm(120)) candidates <- enumerate_lpa(example_data, c("score_a", "score_b"), n_profiles = 1:3, n_starts = 2) plot_enumeration(candidates) plot_enumeration(candidates, criterion = "all") }
Returns the catalogue of what = values accepted by the plot() methods,
with the group each belongs to, so the available views can be listed rather
than recalled from a help page.
plot_views()plot_views()
A base data.frame with one row per plot type and the columns
type (the value to pass as what), group ("measurement",
"structure", "diagnostics", "selection" or "every"), and
description.
plot_views()plot_views()
Each model's AIC, BIC and sample-size adjusted BIC, lowest best; the model with the smallest BIC is marked.
## S3 method for class 'latents_comparison' plot(x, main = NULL, subtitle = NULL, ...)## S3 method for class 'latents_comparison' plot(x, main = NULL, subtitle = NULL, ...)
x |
A |
main, subtitle
|
Title and subtitle. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
few_students <- subset(growth_scores, student <= 60) trajectories <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", seed = 1) growth <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) comparison <- compare_models(trajectories = trajectories, growth = growth) if (requireNamespace("ggplot2", quietly = TRUE)) plot(comparison)few_students <- subset(growth_scores, student <= 60) trajectories <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", seed = 1) growth <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) comparison <- compare_models(trajectories = trajectories, growth = growth) if (requireNamespace("ggplot2", quietly = TRUE)) plot(comparison)
One line per model code, criterion against the number of group classes; models are told apart by colour, point shape and line type.
## S3 method for class 'latents_family_enumeration' plot( x, criterion = c("bic", "aic", "bic_individual"), main = NULL, subtitle = NULL, ... )## S3 method for class 'latents_family_enumeration' plot( x, criterion = c("bic", "aic", "bic_individual"), main = NULL, subtitle = NULL, ... )
x |
A |
criterion |
|
main, subtitle
|
Optional title and subtitle. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) grid <- enumerate_classes(ratings, "climate", "team", family = c("additive", "dispersion"), n_group_classes = 1:2, n_starts = 3, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(grid)set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) grid <- enumerate_classes(ratings, "climate", "team", family = c("additive", "dispersion"), n_group_classes = 1:2, n_starts = 3, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(grid)
Plot a growth mixture model
## S3 method for class 'latents_growth_mixture' plot( x, what = c("trajectories", "individuals", "coefficients", "random", "classification", "posteriors"), time = NULL, facet = FALSE, persons = 12L, max_persons = 120L, spread = 0.8, level = 0.95, main = NULL, subtitle = NULL, ... )## S3 method for class 'latents_growth_mixture' plot( x, what = c("trajectories", "individuals", "coefficients", "random", "classification", "posteriors"), time = NULL, facet = FALSE, persons = 12L, max_persons = 120L, spread = 0.8, level = 0.95, main = NULL, subtitle = NULL, ... )
x |
A |
what |
|
time |
The numeric variable to draw along; by default the first
numeric variable of |
facet |
For |
persons |
For |
max_persons |
For |
spread |
For |
level |
Confidence level of the bands and intervals. |
main, subtitle
|
Title and subtitle; defaults describe the view. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
few_students <- subset(growth_scores, student <= 60) fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(fit)few_students <- subset(growth_scores, student <= 60) fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(fit)
A fit that classifies persons (id with class_level = "group") is a
trajectory model, and draws the trajectory views of
plot.latents_growth_mixture() (ggplot2): "trajectories" (the
default), "individuals", "coefficients" and "classification". Any
other fit draws base-graphics views: "fitted" (the default),
"coefficients" and "posteriors".
## S3 method for class 'latents_mixture_regression' plot( x, what = NULL, predictor = NULL, level = 0.95, main = NULL, subtitle = NULL, time = NULL, facet = FALSE, persons = 12L, max_persons = 120L, ... )## S3 method for class 'latents_mixture_regression' plot( x, what = NULL, predictor = NULL, level = 0.95, main = NULL, subtitle = NULL, time = NULL, facet = FALSE, persons = 12L, max_persons = 120L, ... )
x |
A fit from |
what |
For a trajectory model: |
predictor |
For |
level |
Confidence level of intervals and bands. |
main |
Plot title; |
subtitle |
For a trajectory model, the subtitle; |
time, facet, persons, max_persons
|
For a trajectory model, as in
|
... |
Unused. |
For a trajectory model, a ggplot object (raises
latents_missing_package without ggplot2); otherwise x, invisibly,
called for its plot.
fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) plot(fit) plot(fit, what = "coefficients")fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) plot(fit) plot(fit, what = "coefficients")
One row per parameter: each imputation's aligned estimate as an open circle and the pooled estimate as a filled circle with its interval, so the between-imputation spread is visible next to the total uncertainty.
## S3 method for class 'latents_pooled' plot(x, level = c("profile", "group", "measurement"), main = NULL, ...)## S3 method for class 'latents_pooled' plot(x, level = c("profile", "group", "measurement"), main = NULL, ...)
x |
A |
level |
Which parameter level to show: |
main |
Plot title; |
... |
Unused. |
x, invisibly. Called for the side effect of drawing.
set.seed(1) completed <- lapply(1:2, function(i) { within(subset(course_engagement, student <= 40), previous_grade <- previous_grade + rnorm(length(previous_grade), sd = 0.1)) }) pooled <- pool_imputations(completed, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 1, profile_covariates = "previous_grade", n_starts = 2, seed = 1) plot(pooled)set.seed(1) completed <- lapply(1:2, function(i) { within(subset(course_engagement, student <= 40), previous_grade <- previous_grade + rnorm(length(previous_grade), sd = 0.1)) }) pooled <- pool_imputations(completed, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 1, profile_covariates = "previous_grade", n_starts = 2, seed = 1) plot(pooled)
The criterion against the number of profiles, one line per number of group classes (and structure), told apart by colour, shape and line type.
## S3 method for class 'latents_transition_enumeration' plot( x, criterion = c("bic", "aic", "bic_individual"), main = NULL, subtitle = NULL, ... )## S3 method for class 'latents_transition_enumeration' plot( x, criterion = c("bic", "aic", "bic_individual"), main = NULL, subtitle = NULL, ... )
x |
A |
criterion |
|
main, subtitle
|
Optional title and subtitle. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
few_students <- subset(course_engagement, student <= 50) grid <- enumerate_classes(few_students, c("browse", "lectures"), "student", time = "sequence", n_profiles = 2:3, n_group_classes = 1, n_starts = 2, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(grid)few_students <- subset(course_engagement, student <= 50) grid <- enumerate_classes(few_students, c("browse", "lectures"), "student", time = "sequence", n_profiles = 2:3, n_group_classes = 1, n_starts = 2, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(grid)
Draws one view of a fit as a ggplot object, which can be printed, saved
with ggplot2::ggsave() or styled further with + ggplot2::theme().
Every view shares one profile order (largest first), one profile share (the
posterior share every table reports) and one set of Okabe-Ito colours
paired with point shapes, so a profile looks the same in every view.
Series are labelled directly rather than through a legend.
## S3 method for class 'multilpa' plot( x, what = c("profiles", "bars", "heatmap", "raincloud", "parallel", "pairs", "responses", "probabilities", "sequences", "sizes", "entropy", "posteriors", "avepp", "all"), data = NULL, scale = c("raw", "standardized"), category = "last", labels = TRUE, intervals = TRUE, cell_labels = TRUE, main = NULL, subtitle = NULL, statistic = c("mean", "median", "mode"), ... )## S3 method for class 'multilpa' plot( x, what = c("profiles", "bars", "heatmap", "raincloud", "parallel", "pairs", "responses", "probabilities", "sequences", "sizes", "entropy", "posteriors", "avepp", "all"), data = NULL, scale = c("raw", "standardized"), category = "last", labels = TRUE, intervals = TRUE, cell_labels = TRUE, main = NULL, subtitle = NULL, statistic = c("mean", "median", "mode"), ... )
x |
A fitted |
what |
Which view to draw. The measurement model: The two-level structure: Classification:
|
data |
Optional. The data frame the model was fitted to. A fit carries the columns it was built from, so intervals are drawn without it; pass it only to compute them from a different frame. |
scale |
For the mean views and |
category |
For |
labels |
|
intervals |
For |
cell_labels |
For |
main, subtitle
|
Title and subtitle. |
statistic |
For |
... |
Nothing further is accepted; an unknown argument raises an error
of class |
The plots need the ggplot2 package, which latents suggests rather than
requires; without it a plot is refused with an error of class
latents_missing_package.
"profiles" and "bars" draw 95% Wald intervals when the fit has
standard errors. Profile numbers are arbitrary, so two fits must have
their labels aligned before their plots are compared. With
scale = "standardized" the standard deviations are the observed
indicator standard deviations, not the within-profile ones, so the values
are comparable across indicators but are not effect sizes.
A ggplot object; for what = "all", a latents_plots list of them,
named by view, that draws every one when printed.
latents_missing_package without ggplot2;
latents_no_continuous, latents_no_categorical, latents_no_time and
latents_nothing_to_plot when the fit lacks what a view needs;
latents_bad_argument for an argument the method does not use.
Zappia, L. and Oshlack, A. (2018). Clustering trees: a visualization for evaluating clusterings at multiple resolutions. GigaScience, 7(7), giy083.
plot_views() for the catalogue of views.
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(7) example_data <- data.frame( school = rep(seq_len(12), each = 10), score_a = rnorm(120), score_b = rnorm(120) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2) plot(fit) plot(fit, scale = "standardized") plot(fit, what = "bars") plot(fit, what = "pairs") plot(fit, what = "avepp") }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(7) example_data <- data.frame( school = rep(seq_len(12), each = 10), score_a = rnorm(120), score_b = rnorm(120) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2) plot(fit) plot(fit, scale = "standardized") plot(fit, what = "bars") plot(fit, what = "pairs") plot(fit, what = "avepp") }
"means" shows each group class's mean per indicator with its Wald
interval (when the fit supports inference). "variances" sets the shared
within-group variance beside each between-group variance. "intercepts"
shows every group's posterior intercept mean, marked by its modal class,
with the class means over them. Classes are distinguished by colour and by
point shape.
## S3 method for class 'multilpa_additive' plot( x, what = c("means", "variances", "intercepts"), level = 0.95, main = NULL, subtitle = NULL, ... )## S3 method for class 'multilpa_additive' plot( x, what = c("means", "variances", "intercepts"), level = 0.95, main = NULL, subtitle = NULL, ... )
x |
A |
what |
Which view. |
level |
Confidence level of the intervals. |
main, subtitle
|
Optional title and subtitle. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240), support = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) fit <- multilpa(ratings, c("climate", "support"), "team", n_group_classes = 2, family = "additive", n_starts = 3, seed = 1) plot(fit) plot(fit, what = "intercepts") }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240), support = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240)) fit <- multilpa(ratings, c("climate", "support"), "team", n_group_classes = 2, family = "additive", n_starts = 3, seed = 1) plot(fit) plot(fit, what = "intercepts") }
Draws the replicate likelihood-ratio statistics as a histogram with the observed statistic marked, so that the p-value can be read as a tail area of the distribution that was actually simulated. Invalid replicates carry no statistic and are counted in the subtitle rather than dropped silently.
## S3 method for class 'multilpa_bootstrap_lrt' plot( x, main = NULL, subtitle = NULL, palette = NULL, style = .multilpa_style(), ... )## S3 method for class 'multilpa_bootstrap_lrt' plot( x, main = NULL, subtitle = NULL, palette = NULL, style = .multilpa_style(), ... )
x |
An |
main, subtitle
|
Panel title and secondary line, or |
palette |
A vector of at least two colours: the histogram fill and the
observed-statistic marker. |
style |
A list of visual constants, as built by |
... |
Further named visual constants, merged into |
The input, invisibly. Called for the side effect of drawing.
latents_nothing_to_plot when no replicate produced a finite statistic.
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) plot(comparison)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) plot(comparison)
Draws the parts of a covariate fit that are still fixed quantities: the
measurement model, the assignments and the classification diagnostics, as
plot.multilpa() draws them. Profile prevalence cannot be drawn: a
covariate model has no single prevalence vector, because prevalence varies
with each unit's covariates, so asking for it is refused rather than
answered with an average that no unit has.
## S3 method for class 'multilpa_covariates' plot( x, what = c("profiles", "bars", "heatmap", "raincloud", "parallel", "pairs", "responses", "sequences", "sizes", "entropy", "posteriors", "avepp", "all"), data = NULL, scale = c("raw", "standardized"), category = "last", labels = TRUE, intervals = TRUE, cell_labels = TRUE, main = NULL, subtitle = NULL, statistic = c("mean", "median", "mode"), ... )## S3 method for class 'multilpa_covariates' plot( x, what = c("profiles", "bars", "heatmap", "raincloud", "parallel", "pairs", "responses", "sequences", "sizes", "entropy", "posteriors", "avepp", "all"), data = NULL, scale = c("raw", "standardized"), category = "last", labels = TRUE, intervals = TRUE, cell_labels = TRUE, main = NULL, subtitle = NULL, statistic = c("mean", "median", "mode"), ... )
x |
A fitted |
what |
The view; every view of |
data, scale, category, labels, intervals, cell_labels, main, subtitle, statistic, ...
|
As in |
A ggplot object; for what = "all", a latents_plots list.
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(5) school <- rep(seq_len(16), each = 8) high_class <- rep(rep(c(FALSE, TRUE), length.out = 16), each = 8) x <- rnorm(128) profile <- ifelse( runif(128) < plogis(-1 + 2 * high_class + 0.8 * x), 2L, 1L ) example_data <- data.frame( school = school, x = x, y1 = rnorm(128, ifelse(profile == 2L, 2, -2), 0.7), y2 = rnorm(128, ifelse(profile == 2L, 1.5, -1.5), 0.7) ) fit <- multilpa(example_data, c("y1", "y2"), "school", n_profiles = 2, n_group_classes = 2, profile_covariates = "x", n_starts = 2) plot(fit) }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(5) school <- rep(seq_len(16), each = 8) high_class <- rep(rep(c(FALSE, TRUE), length.out = 16), each = 8) x <- rnorm(128) profile <- ifelse( runif(128) < plogis(-1 + 2 * high_class + 0.8 * x), 2L, 1L ) example_data <- data.frame( school = school, x = x, y1 = rnorm(128, ifelse(profile == 2L, 2, -2), 0.7), y2 = rnorm(128, ifelse(profile == 2L, 1.5, -1.5), 0.7) ) fit <- multilpa(example_data, c("y1", "y2"), "school", n_profiles = 2, n_group_classes = 2, profile_covariates = "x", n_starts = 2) plot(fit) }
"profiles" shows the individual profiles' means per indicator;
"group_means" the group classes' means of the group means;
"composition" the profile shares within each group class. Profiles and
classes are told apart by colour and point shape.
## S3 method for class 'multilpa_cross_level' plot( x, what = c("profiles", "group_means", "composition"), main = NULL, subtitle = NULL, ... )## S3 method for class 'multilpa_cross_level' plot( x, what = c("profiles", "group_means", "composition"), main = NULL, subtitle = NULL, ... )
x |
A |
what |
Which view. |
main, subtitle
|
Optional title and subtitle. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240, sample(c(-1, 1), 240, replace = TRUE), 0.6)) fit <- multilpa(ratings, "climate", "team", n_profiles = 2, n_group_classes = 2, family = "full_cross_level", n_starts = 3, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(fit)set.seed(1) ratings <- data.frame( team = rep(seq_len(40), each = 6), climate = rep(rnorm(40, rep(c(-1, 1), each = 20), 0.5), each = 6) + rnorm(240, sample(c(-1, 1), 240, replace = TRUE), 0.6)) fit <- multilpa(ratings, "climate", "team", n_profiles = 2, n_group_classes = 2, family = "full_cross_level", n_starts = 3, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(fit)
what = "enumeration" draws information criteria against the number of
profiles, one panel per criterion and one line per covariance model and
number of group classes. A candidate that failed to converge is marked with
a cross on its panel's floor, so a gap in a line reads as a failure and not
as a missing candidate. what = "tree" draws how profiles split as more are
added: each row is one solution divided into its profiles by posterior
share, and bands carry the posterior mass shared by a profile and one in the
next solution, coloured by lineage. A band that forks is a profile split in
two; bands that merge or cross are cases reshuffled.
## S3 method for class 'multilpa_enumeration' plot( x, what = c("enumeration", "tree"), criterion = c("aic", "bic_groups", "bic_individual", "icl_individual"), combine = TRUE, labels = TRUE, mark_minimum = TRUE, main = NULL, subtitle = NULL, ... )## S3 method for class 'multilpa_enumeration' plot( x, what = c("enumeration", "tree"), criterion = c("aic", "bic_groups", "bic_individual", "icl_individual"), combine = TRUE, labels = TRUE, mark_minimum = TRUE, main = NULL, subtitle = NULL, ... )
x |
A |
what |
|
criterion |
For |
combine |
|
labels |
|
mark_minimum |
|
main, subtitle
|
Title and subtitle. |
... |
Nothing further is accepted; an unknown argument raises an error
of class |
A ggplot object, or with combine = FALSE and several criteria a
latents_plots list of them, one per criterion.
latents_unknown_criterion for a criterion that is not a
column of the grid; latents_nothing_to_plot when no converged candidate
has a finite value, or when a tree has fewer than two numbers of profiles
for every model.
Zappia, L. and Oshlack, A. (2018). Clustering trees: a visualization for evaluating clusterings at multiple resolutions. GigaScience, 7(7), giy083.
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(7) example_data <- data.frame(score_a = rnorm(120), score_b = rnorm(120)) candidates <- enumerate_lpa(example_data, c("score_a", "score_b"), n_profiles = 1:3, n_starts = 2) plot(candidates) plot(candidates, what = "tree") }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(7) example_data <- data.frame(score_a = rnorm(120), score_b = rnorm(120)) candidates <- enumerate_lpa(example_data, c("score_a", "score_b"), n_profiles = 1:3, n_starts = 2) plot(candidates) plot(candidates, what = "tree") }
One panel per group class (and occasion, when transitions vary): a tile per origin and destination, labelled with its probability.
## S3 method for class 'multilpa_lta' plot(x, main = NULL, subtitle = NULL, ...)## S3 method for class 'multilpa_lta' plot(x, main = NULL, subtitle = NULL, ...)
x |
A |
main, subtitle
|
Optional title and subtitle. |
... |
Unused. |
A ggplot object. Raises latents_missing_package without ggplot2.
fit <- lta(course_engagement, c("browse", "lectures"), "student", n_profiles = 2, time = "sequence", transition_covariates = "previous_grade", n_starts = 2, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(fit)fit <- lta(course_engagement, c("browse", "lectures"), "student", n_profiles = 2, time = "sequence", transition_covariates = "previous_grade", n_starts = 2, seed = 1) if (requireNamespace("ggplot2", quietly = TRUE)) plot(fit)
The measurement model is the one multilpa() fits, so every measurement and
classification view it draws is available here. what = "transitions" is the
view this family adds: the estimated transition matrix, one panel per group
class, with a row that has no data support labelled in parentheses because
it is uniform by construction rather than estimated.
## S3 method for class 'multilpa_transitions' plot( x, what = c("transitions", "profiles", "bars", "heatmap", "responses", "sequences", "sizes", "entropy", "posteriors", "avepp", "all"), data = NULL, scale = c("raw", "standardized"), category = "last", labels = TRUE, cell_labels = TRUE, main = NULL, subtitle = NULL, ... )## S3 method for class 'multilpa_transitions' plot( x, what = c("transitions", "profiles", "bars", "heatmap", "responses", "sequences", "sizes", "entropy", "posteriors", "avepp", "all"), data = NULL, scale = c("raw", "standardized"), category = "last", labels = TRUE, cell_labels = TRUE, main = NULL, subtitle = NULL, ... )
x |
A fitted |
what |
The view to draw. |
data |
Optional. Accepted for consistency with |
scale |
|
category |
For |
labels |
Whether to label series directly. |
cell_labels |
For |
main, subtitle
|
Title and subtitle. |
... |
Nothing further is accepted; an unknown argument raises an error
of class |
A ggplot object; for what = "all", a latents_plots list.
get_tna() and get_group_tna() to draw the transitions as a
network instead, and plot.multilpa() for the same views on a
cross-sectional fit.
if (requireNamespace("ggplot2", quietly = TRUE)) { fit <- lta(subset(course_engagement, student <= 40), c("browse", "lectures", "forum_read"), "student", n_profiles = 2, time = "sequence", n_starts = 2) plot(fit, what = "transitions") plot(fit, what = "profiles") }if (requireNamespace("ggplot2", quietly = TRUE)) { fit <- lta(subset(course_engagement, student <= 40), c("browse", "lectures", "forum_read"), "student", n_profiles = 2, time = "sequence", n_starts = 2) plot(fit, what = "transitions") plot(fit, what = "profiles") }
Fits multilpa() to every completed data set, aligns each fit's profile and
group-class labels to the first one, and pools the estimates with Rubin's
rules. This is the route for missing covariates, which
missing = "fiml" cannot integrate out: the model conditions on
covariates, so it has no distribution for them to integrate over. Impute
them first (for example with the mice package, including the indicators and
the group identifier in the imputation model), then pass the completed data
here.
pool_imputations( imputed, vars, id, n_profiles, ..., level = 0.95, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods )pool_imputations( imputed, vars, id, n_profiles, ..., level = 0.95, vcov_type = c("observed", "robust", "opg"), adjust = .multilpa_p_adjust_methods )
imputed |
The completed data sets: a list of at least two data frames
with the same columns and rows in the same order, or a |
vars, id, n_profiles
|
As in |
... |
Further arguments for |
level |
Confidence level of the pooled intervals. |
vcov_type, adjust
|
As in |
Pooling is on the reported (natural) scale: for each parameter the pooled
estimate is the mean over imputations, and its variance is the mean
within-imputation variance plus (1 + 1/m) times the between-imputation
variance. Degrees of freedom follow Rubin (1987) with a large complete-data
sample, which is what maximum-likelihood inference assumes.
Pooled intervals are symmetric t intervals on this scale; unlike the
transformed Wald intervals of parameter_inference(), they can extend
outside the parameter space for probabilities or variances.
An object of class latents_pooled. as.data.frame() and
get_results(x, "estimates") give one row per parameter, with the columns
of parameter_inference() (level, outcome, term, parameter,
estimate, standard_error, statistic, p_value, p_adjusted,
conf_low, conf_high) plus df (Rubin's degrees of freedom),
within and between (the two variance components), riv (the
relative increase in variance due to missingness) and fmi (the fraction
of missing information). statistic and p_value are NA where
parameter_inference() reports no test (variances and probabilities).
get_results(x, "imputations") gives each imputation's aligned estimates,
one row per imputation and parameter, and get_results(x, "fits") one row
per imputation with its log likelihood, convergence and the label
permutation that aligned it.
Rubin's rules are only as good as the imputations. The covariate and the
indicators are related through the latent classes, so an imputation model
that is linear in the indicators (mice's defaults) misses that structure:
in validation/pool-imputations-coverage.R (a membership slope with 30%
of its covariate missing at random given the indicators, 200 replications,
ten imputations) 95% intervals covered 0.925 under predictive mean
matching and 0.935 under Bayesian linear regression, against 0.995 when the
covariate was approximately drawn from its conditional distribution using
known generating parameters. That oracle arm omits uncertainty in the
imputation parameters and is not a validation of proper multiple
imputation or nominal interval coverage. The study is one design, not a
general guarantee for any method. Use an imputation model that accounts for
the latent structure and clustering, includes the indicators and relevant
nonlinearities, and propagates parameter uncertainty. Imputing separately
within assigned classes alone ignores classification uncertainty.
Complete-case coverage was 0.955 in that design, but selecting on the
indicators changes their within-class distributions; the usual logistic
case-control slope argument does not guarantee unbiased slopes when the
outcome is latent and its measurement model is estimated.
latents_bad_data when imputed is not a list of at least two data frames
with identical columns and row counts; latents_pooling_failed when an
imputation's fit or inference fails (its message names the imputation and
carries the original reason), since Rubin's rules need every imputation;
latents_missing_package for a mids object without mice installed.
Warnings from the individual fits are passed on as they are raised.
Rubin, D. B. (1987). Multiple Imputation for Nonresponse in Surveys. Wiley.
Meng, X.-L. (1994). Multiple-imputation inferences with uncongenial sources of input. Statistical Science, 9(4), 538–558.
van Buuren, S. (2018). Flexible Imputation of Missing Data (2nd ed.), section 5.2. Chapman & Hall/CRC.
set.seed(1) # Two stand-in completed data sets. With real missing covariates, pass the # `mids` object mice::mice() returns instead. completed <- lapply(1:2, function(i) { within(subset(course_engagement, student <= 40), previous_grade <- previous_grade + rnorm(length(previous_grade), sd = 0.1)) }) pooled <- pool_imputations(completed, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 1, profile_covariates = "previous_grade", n_starts = 2, seed = 1) pooled get_results(pooled, "fits")set.seed(1) # Two stand-in completed data sets. With real missing covariates, pass the # `mids` object mice::mice() returns instead. completed <- lapply(1:2, function(i) { within(subset(course_engagement, student <= 40), previous_grade <- previous_grade + rnorm(length(previous_grade), sd = 0.1)) }) pooled <- pool_imputations(completed, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 1, profile_covariates = "previous_grade", n_starts = 2, seed = 1) pooled get_results(pooled, "fits")
Predictions for new persons are not implemented yet. The fitted class
trajectories and each fitted person's own predicted curve are tables of
get_results.latents_growth_mixture(): "trajectories" and
"individual".
## S3 method for class 'latents_growth_mixture' predict(object, ...)## S3 method for class 'latents_growth_mixture' predict(object, ...)
object |
A |
... |
Unused. |
Never returns; raises latents_unsupported_prediction.
few_students <- subset(growth_scores, student <= 60) fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) # Prediction is refused; get_results(fit, "trajectories") has the curves try(predict(fit))few_students <- subset(growth_scores, student <= 60) fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) # Prediction is refused; get_results(fit, "trajectories") has the curves try(predict(fit))
Predict from a mixture-of-regressions fit
## S3 method for class 'latents_mixture_regression' predict( object, newdata = NULL, type = c("response", "class_response", "posterior", "probabilities"), ... )## S3 method for class 'latents_mixture_regression' predict( object, newdata = NULL, type = c("response", "class_response", "posterior", "probabilities"), ... )
object |
A fit from |
newdata |
|
type |
|
... |
Unused. |
A base data.frame. For "response": row and fitted. For
"class_response": row, class, prior and fitted, one row per
data row and class. For "posterior": the same columns as
get_results(fit, "assignments"). For "probabilities": row,
class, category, prior and probability, one row per data row,
class and category. Raises latents_bad_argument for
"probabilities" with another family.
fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) new_students <- data.frame(hours = c(2, 10)) predict(fit, new_students, type = "class_response")fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) new_students <- data.frame(hours = c(2, 10)) predict(fit, new_students, type = "class_response")
Evaluates the fitted mixture on new rows: the E-step of the fit, at its estimates, applied to data it has not seen. Rows go through the fit's own preparation – the same categorical levels, the same centering, the same missing-data handling – so a prediction on the training data reproduces the fit's own posteriors exactly.
## S3 method for class 'multilpa' predict(object, newdata = NULL, type = c("class", "posterior", "density"), ...)## S3 method for class 'multilpa' predict(object, newdata = NULL, type = c("class", "posterior", "density"), ...)
object |
A fit from |
newdata |
|
type |
|
... |
Unused. |
A base data.frame with one row per row of newdata: row (its
position in newdata), the id column for a two-level fit, and
for "class": profile (0 is the noise component of a
noise = TRUE fit) and its posterior, and for two-level fits
group_class and group_posterior;
for "posterior": those columns and one probability_profile_k
column per profile (and probability_noise);
for "density": log_density.
A two-level model classifies a row using its group: the group class is
inferred from all of that group's rows, and it shifts the row's profile
probabilities. The groups in newdata (the fit's id column) are treated
as new groups, each classified from its own rows alone, which is the case
of applying a model to a new cohort. A single-level fit, or a fit given
id = NULL, treats every row as its own unit.
latents_bad_data when newdata lacks an indicator or the id column,
has a missing value the fit cannot handle (missing = "error"), a
category the fit never saw, or a non-numeric continuous indicator.
activity <- c("browse", "lectures", "forum_read") training <- subset(course_engagement, student <= 60) fit <- multilpa(training, activity, id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) new_students <- subset(course_engagement, student > 100) head(predict(fit, new_students)) head(predict(fit, new_students, type = "density"))activity <- c("browse", "lectures", "forum_read") training <- subset(course_engagement, student <= 60) fit <- multilpa(training, activity, id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) new_students <- subset(course_engagement, student > 100) head(predict(fit, new_students)) head(predict(fit, new_students, type = "density"))
Print methods for the objects the package returns: fitted models,
enumerations, bootstrap tests, covariate and transition models, pooled
imputations, their summaries, result tables and plot lists. Each prints a
compact overview; get_results() returns the tables themselves as data
frames.
## S3 method for class 'multilpa_enumeration' print(x, ...) ## S3 method for class 'multilpa_start' print(x, ...) ## S3 method for class 'summary_multilpa_additive' print(x, digits = 4L, ...) ## S3 method for class 'multilpa_additive' print(x, ...) ## S3 method for class 'multilpa_covariates' print(x, rows = 20L, ...) ## S3 method for class 'summary_multilpa_covariates' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'summary_multilpa_enumeration' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'multilpa_bootstrap_lrt' print(x, ...) ## S3 method for class 'summary_multilpa_bootstrap_lrt' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'latents_growth_mixture' print(x, digits = 4L, ...) ## S3 method for class 'summary_latents_growth_mixture' print(x, digits = 4L, ...) ## S3 method for class 'latents_table' print(x, n = 20L, ...) ## S3 method for class 'multilpa' print(x, rows = 20L, ...) ## S3 method for class 'summary_multilpa' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'latents_mixture_regression' print(x, digits = 4L, ...) ## S3 method for class 'summary_latents_mixture_regression' print(x, digits = 4L, ...) ## S3 method for class 'latents_plots' print(x, ...) ## S3 method for class 'latents_pooled' print(x, digits = 4L, rows = 20L, ...) ## S3 method for class 'multilpa_transitions' print(x, rows = 20L, ...) ## S3 method for class 'summary_multilpa_transitions' print(x, digits = 4L, rows = 10L, ...)## S3 method for class 'multilpa_enumeration' print(x, ...) ## S3 method for class 'multilpa_start' print(x, ...) ## S3 method for class 'summary_multilpa_additive' print(x, digits = 4L, ...) ## S3 method for class 'multilpa_additive' print(x, ...) ## S3 method for class 'multilpa_covariates' print(x, rows = 20L, ...) ## S3 method for class 'summary_multilpa_covariates' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'summary_multilpa_enumeration' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'multilpa_bootstrap_lrt' print(x, ...) ## S3 method for class 'summary_multilpa_bootstrap_lrt' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'latents_growth_mixture' print(x, digits = 4L, ...) ## S3 method for class 'summary_latents_growth_mixture' print(x, digits = 4L, ...) ## S3 method for class 'latents_table' print(x, n = 20L, ...) ## S3 method for class 'multilpa' print(x, rows = 20L, ...) ## S3 method for class 'summary_multilpa' print(x, digits = 4L, rows = 10L, ...) ## S3 method for class 'latents_mixture_regression' print(x, digits = 4L, ...) ## S3 method for class 'summary_latents_mixture_regression' print(x, digits = 4L, ...) ## S3 method for class 'latents_plots' print(x, ...) ## S3 method for class 'latents_pooled' print(x, digits = 4L, rows = 20L, ...) ## S3 method for class 'multilpa_transitions' print(x, rows = 20L, ...) ## S3 method for class 'summary_multilpa_transitions' print(x, digits = 4L, rows = 10L, ...)
x |
An object returned by a latents function, or the |
... |
Passed to the underlying data frame printing, or ignored. |
digits |
Number of significant digits printed. |
rows |
How many rows of each table to print. A longer table is shown to
that depth, with its remaining row count and the |
n |
The most rows of a result table printed. |
x, invisibly. Called for the side effect of printing.
get_results() for the tables, latents-summary and
latents-as-data-frame.
fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) print(fit, rows = 3)fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) print(fit, rows = 3)
Requests maximum a posteriori (MAP) estimation of the Gaussian profile means
and covariances under the conjugate prior of Fraley and Raftery (2007),
exactly as mclust::priorControl() does for mclust::Mclust(). Pass the
result as multilpa(prior = ). The prior keeps covariances away from
singularity, which is what lets a many-profile or many-indicator model be
fitted where maximum likelihood degenerates.
prior_control(shrinkage = 0.01, mean = NULL, dof = NULL, scale = NULL) ## S3 method for class 'latents_prior' print(x, ...)prior_control(shrinkage = 0.01, mean = NULL, dof = NULL, scale = NULL) ## S3 method for class 'latents_prior' print(x, ...)
shrinkage |
Non-negative prior precision factor for the means, |
mean |
Optional prior mean, one value per continuous indicator, in the
indicators' own units. Naming one needs a positive |
dof |
Optional prior degrees of freedom, a single number. |
scale |
Optional prior scale: a single positive number for an
axis-parallel or spherical structure, or a positive-definite |
x |
A |
... |
Ignored. |
Every argument left NULL takes mclust::defaultPrior()'s value, computed
from the indicators being fitted: the prior mean is their column means, the
degrees of freedom are d + 2 for d indicators, and the scale is
(1/G)^(2/d) times the sample covariance (ellipsoidal structures) or times
the average sample variance (axis-parallel and spherical structures), for
G profiles.
An object of class latents_prior: a list with shrinkage, mean,
dof and scale, the last three NULL where the default is to be
computed from the data.
print() returns x invisibly.
Raises latents_bad_argument for a negative or non-finite shrinkage, a
mean with a zero shrinkage, or a non-positive dof or scale.
Fraley, C., & Raftery, A. E. (2007). Bayesian regularization for normal mixture estimation and model-based clustering. Journal of Classification, 24, 155–181. doi:10.1007/s00357-007-0004-5
multilpa(), whose prior argument takes this.
prior_control() prior_control(shrinkage = 0)prior_control() prior_control(shrinkage = 0)
The R3STEP approach. A multinomial logit of class membership on covariates, fitted after the measurement model rather than alongside it, but treating the assigned class as an error-prone indicator of the true one with the error rates held fixed at what step one found. Regressing the modal class directly instead attenuates every coefficient, because some units are in the wrong class and the covariate cannot explain why.
r3step( x, data, covariates, level = c("individuals", "groups"), ci_level = 0.95, vcov_type = c("robust", "observed"), adjust = c("BH", "holm", "hochberg", "hommel", "bonferroni", "BY", "none"), by_group_class = FALSE )r3step( x, data, covariates, level = c("individuals", "groups"), ci_level = 0.95, vcov_type = c("robust", "observed"), adjust = c("BH", "holm", "hochberg", "hommel", "bonferroni", "BY", "none"), by_group_class = FALSE )
x |
A covariate-free fitted |
data |
The data frame carrying the covariates, in the fit's row order. Shared fit columns are checked row by row; a frame with too little identifying information warns that alignment cannot be verified. |
covariates |
Character vector of numeric columns not used as measurement
indicators in |
level |
|
ci_level |
Confidence level for the intervals. |
vcov_type |
|
adjust |
Multiplicity correction applied across the covariate terms,
passed to |
by_group_class |
|
The error matrix is held fixed rather than estimated jointly, which
is what makes this a three-step method and what keeps the covariates from
reshaping the classes.
On a lta() fit, level = "individuals" models the marginal profile at
each occasion. It does not regress initial-state or transition
probabilities on the covariates. vcov_type = "robust" clusters the
occasion scores by group; "observed" treats them as independent.
Over 60 replications of a two-profile design with a true log-odds slope of 1.2 and intercept -0.3, this recovered the slope with a bias of -0.002 and the intercept with a bias of +0.006, where a logistic regression on the modal class was biased by -0.163 on the slope. The correction costs some precision: the standard deviation of the slope across replications was 0.127 against 0.100 for the naive fit. The nominal 95% intervals covered the true slope in 95% of those replications.
That coverage was measured where the profiles separate well. The error
matrix is still treated as known rather than estimated, so intervals should
be expected to run narrow where classification is poorer; check
get_results(x, "classification_errors") before relying on them.
A base data.frame with one row per non-reference class and term,
and the columns level, outcome, term, estimate, standard_error,
statistic, p_value, p_adjusted, conf_low and conf_high.
p_value is uncorrected and p_adjusted carries the correction named
by adjust, which is also recorded in the result's adjust attribute;
it is NA on the intercept rows, which are not part of the tested family.
A coefficient with zero standard error has an undefined Wald statistic and
p-value, reported as NA_real_ rather than infinity or zero.
Coefficients are log odds against the final class, which is the reference,
matching multilpa(profile_covariates = ); that class is named in the
result's
reference_class attribute, and the variance that was used in its
vcov_type attribute. With by_group_class = TRUE the intercept rows are
one per group class, named group_class_1, group_class_2, ..., followed
by the shared slopes and, at level = "groups", the group-class logits
against the last group class; the result also carries by_group_class and
the maximised log_likelihood as attributes.
Vermunt, J. K. (2010). Latent class modeling with covariates: two improved three-step approaches. Political Analysis, 18, 450–469. Asparouhov, T., & Muthen, B. (2014). Auxiliary variables in mixture modeling: three-step approaches using Mplus. Structural Equation Modeling, 21, 329–341.
three_step() for a distal outcome, and
multilpa(profile_covariates = ) for the
one-step alternative that estimates everything jointly.
set.seed(21) g <- rep(seq_len(50), each = 12) x <- rnorm(600) truth <- 1L + as.integer(runif(600) < plogis(-0.3 + 1.2 * x)) example_data <- data.frame( g = g, x = x, a = rnorm(600, ifelse(truth == 2L, 1.2, -1.2)), b = rnorm(600, ifelse(truth == 2L, 1.2, -1.2)) ) fit <- multilpa(example_data, c("a", "b"), "g", n_profiles = 2, n_group_classes = 1, n_starts = 4, seed = 1) r3step(fit, example_data, "x")set.seed(21) g <- rep(seq_len(50), each = 12) x <- rnorm(600) truth <- 1L + as.integer(runif(600) < plogis(-0.3 + 1.2 * x)) example_data <- data.frame( g = g, x = x, a = rnorm(600, ifelse(truth == 2L, 1.2, -1.2)), b = rnorm(600, ifelse(truth == 2L, 1.2, -1.2)) ) fit <- multilpa(example_data, c("a", "b"), "g", n_profiles = 2, n_group_classes = 1, n_starts = 4, seed = 1) r3step(fit, example_data, "x")
Prints the estimates, then the classification diagnostics, then draws every
plot the fit supports. A first look, not a substitute for the verbs: each
section is what summary(), diagnostics() and plot() return, and the
tables are reachable from those rather than from here.
report( x, data = NULL, plots = TRUE, by = c("profile", "overall"), rows = 10L, ... )report( x, data = NULL, plots = TRUE, by = c("profile", "overall"), rows = 10L, ... )
x |
A fitted model of this package. |
data |
Optional. The data the model was fitted to; a fit carries the columns it was built from. |
plots |
|
by |
Passed to |
rows |
How many rows of each of the summary's tables to print, passed
to |
... |
Nothing further is accepted. An argument this function cannot
forward raises an error of class |
The fitted model, invisibly. Called for the printing and drawing.
summary(), which is every table the fit can
produce, then descriptives(), then the condensed reading of
diagnostics(), then every plot view the fit supports. Each section is
what that verb returns, and the tables are reached from get_results()
rather than from here.
summary(), diagnostics(), descriptives(),
plot_views().
fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) report(fit, plots = FALSE)fit <- multilpa( course_engagement, vars = c("browse", "lectures", "forum_read", "forum_post", "attendance"), id = "student", n_profiles = 2, n_group_classes = 2, n_starts = 4, seed = 1 ) report(fit, plots = FALSE)
A latent profile model is fitted by EM from random starts, so what comes back
is a local maximum reached from one particular set of them. n_starts
reports how many starts within a single seed's stream reached the best
likelihood; it cannot say whether a different stream would have found a
different mode. This verb refits the model under each of several seeds and
reports what changed.
sensitivity( x, data = NULL, seeds = 1:10, n_starts = NULL, max_iter = NULL, tol = NULL, tolerance = NULL )sensitivity( x, data = NULL, seeds = 1:10, n_starts = NULL, max_iter = NULL, tol = NULL, tolerance = NULL )
x |
A fitted |
data |
Optional. The data the model was fitted to, in its original row order. A fit carries the columns it was built from, so this is only needed to override them. Shared identifier and indicator columns are checked against the fit before assignment agreement is calculated. |
seeds |
Seeds to refit under: at least two distinct whole numbers of
either sign, as |
n_starts |
Starts per refit. Defaults to the number the reference fit used, so each seed gets the same search effort it did. |
max_iter, tol
|
Passed to each refit; default to the reference fit's. |
tolerance |
Two log likelihoods within this of each other count as the same optimum. Defaults to the reference fit's own replication tolerance, which is what it used to decide whether its starts agreed. |
Three things are worth reading off the result. Whether every seed reached the
same maximised log likelihood, which is what optimum counts. Whether the
seeds that reached it converged. And how many observations were assigned to a
different profile, which is what agreement measures, after the arbitrary
profile labels of each refit have been matched to the reference fit's – two
fits of the same mixture can be identical and still number their profiles
differently, so comparing labels directly would report disagreement that is
not there.
A seed whose refit fails contributes a row with NA estimates rather than
being dropped silently, and a latents_sensitivity_dropped warning names
how many failed, so the table is never quietly shorter than seeds.
A base data.frame, one row per seed, with columns seed,
log_likelihood, converged, iterations, optimum (1 for the best
maximum found across the seeds, 2 for the next distinct one, and so on),
best (whether this seed reached optimum 1) and agreement (the
proportion of observations given the same profile as the reference fit,
after label alignment). A failed refit has NA throughout except seed.
Hipp, J. R., & Bauer, D. J. (2006). Local solutions in the estimation of growth mixture models. Psychological Methods, 11, 36–53.
enumerate_classes() to vary the number of classes rather than the
seed, and parameter_inference() for uncertainty about the parameters of a
single solution.
set.seed(7) example_data <- data.frame( school = rep(seq_len(12), each = 10), score_a = c(rnorm(60, -1.5), rnorm(60, 1.5)), score_b = c(rnorm(60, -1), rnorm(60, 1)) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 3, seed = 1) sensitivity(fit, seeds = 1:3, n_starts = 3)set.seed(7) example_data <- data.frame( school = rep(seq_len(12), each = 10), score_a = c(rnorm(60, -1.5), rnorm(60, 1.5)), score_b = c(rnorm(60, -1), rnorm(60, 1)) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 3, seed = 1) sensitivity(fit, seeds = 1:3, n_starts = 3)
Draws new outcomes on the fitted design: each person's class from their membership probabilities, their random effects from that class's covariance, and their outcomes around their own trajectory with the class's residual variance.
## S3 method for class 'latents_growth_mixture' simulate(object, nsim = 1, seed = NULL, ...)## S3 method for class 'latents_growth_mixture' simulate(object, nsim = 1, seed = NULL, ...)
object |
A |
nsim |
Number of simulated outcome vectors. |
seed |
|
... |
Unused. |
A base data.frame with one row per fitted row and one column per
simulation, sim_1, sim_2, ...
few_students <- subset(growth_scores, student <= 60) small_fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) head(simulate(small_fit, nsim = 2, seed = 1)) # All 300 students, with random intercepts and slopes fit <- mixture_regression(score ~ wave, growth_scores, n_classes = 3, id = "student", class_level = "group", random = "wave", random_covariance = "equal", n_starts = 2, seed = 1) head(simulate(fit, nsim = 2, seed = 1))few_students <- subset(growth_scores, student <= 60) small_fit <- mixture_regression(score ~ wave, few_students, n_classes = 2, id = "student", class_level = "group", random = "intercept", seed = 1) head(simulate(small_fit, nsim = 2, seed = 1)) # All 300 students, with random intercepts and slopes fit <- mixture_regression(score ~ wave, growth_scores, n_classes = 3, id = "student", class_level = "group", random = "wave", random_covariance = "equal", n_starts = 2, seed = 1) head(simulate(fit, nsim = 2, seed = 1))
Draws class memberships from the fitted mixing model (group classes first
in the two-level model, one class per group under class_level = "group"), then outcomes from each row's class regression, at the fitted
rows' predictors.
## S3 method for class 'latents_mixture_regression' simulate(object, nsim = 1, seed = NULL, ...)## S3 method for class 'latents_mixture_regression' simulate(object, nsim = 1, seed = NULL, ...)
object |
A fit from |
nsim |
Number of simulated outcome vectors. |
seed |
|
... |
Unused. |
A base data.frame with one row per fitted row and one column per
simulation, sim_1, sim_2, ...; for a binomial fit with more than one
trial the columns hold success counts, and for an ordinal fit the
fitted categories in the outcome's own type (a factor with the fitted
levels, or the category numbers).
fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) head(simulate(fit, nsim = 2, seed = 1))fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) head(simulate(fit, nsim = 2, seed = 1))
Scores on five constructs of the Motivated Strategies for Learning Questionnaire (MSLQ) for 300 simulated respondents. Each score is the mean of the construct's Likert items, on a scale from 1 to 7. The responses were generated by a large language model (GPT), not collected from students: they come from a study of how language models answer psychological surveys, and are used here because they are a well-structured, openly shareable cross-sectional example. They should not be read as evidence about students.
srlsrl
A data frame with 300 rows and 5 columns:
cognitive_strategiescognitive strategy use
intrinsic_valueintrinsic value of the course
self_efficacyself-efficacy
self_regulationself-regulation
test_anxietytest anxiety
The five constructs are strongly correlated (about 0.8, except test anxiety), which makes the data a good illustration of how the choice of covariance structure shapes a latent profile analysis.
The SRL_GPT data of the psychnets package, with descriptive
column names. Saqr, M. (2025). Delving into the psychology of machines:
Exploring the structure of self-regulated learning via LLM-generated
survey responses. Computers in Human Behavior, 173, 108769.
doi:10.1016/j.chb.2025.108769
descriptives(srl, names(srl))descriptives(srl, names(srl))
Returns the starting-value list multilpa() accepts, taken from a fitted
model or from any object that already carries the parameter blocks, such as a
retained reference solution. This exists so that callers never assemble the
list by hand, and so that starting values are validated where they are built
rather than deep inside the fitting loop.
starting_values( x, covariance = c("auto", "drop", "keep"), what = c("all", "measurement") )starting_values( x, covariance = c("auto", "drop", "keep"), what = c("all", "measurement") )
x |
A fitted |
covariance |
|
what |
|
An object of class multilpa_start: the list multilpa() accepts as
start, with elements means, variances, profile_probabilities, and
group_probabilities, plus covariances when the full-covariance
parameterization is returned and response_probabilities when the object
carries a categorical measurement model. what = "measurement" drops
profile_probabilities and group_probabilities. The Gaussian blocks are
unnamed and indexed by position, matching what multilpa() expects of
start; read them with as.data.frame(), which labels the positions,
rather than out of the list. response_probabilities is the exception: the
list keeps each indicator's name and each block keeps its category names,
because those labels are what lets multilpa() attach a block to the item
it was estimated for rather than to whichever item happens to sit in that
position. A fit that names its categorical indicators in another order, or
that has other categories, therefore refuses the start with
latents_bad_start instead of reporting a likelihood for a measurement
model it never held. The list payload is unchanged by the class, so a
multilpa_start can be passed straight back as start.
get_results() for the values as tidy tables.
set.seed(7) example_data <- data.frame( school = rep(seq_len(12), each = 10), score_a = rnorm(120), score_b = rnorm(120) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1) starting_values(fit) as.data.frame(starting_values(fit)) refit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 1, start = starting_values(fit)) logLik(refit)set.seed(7) example_data <- data.frame( school = rep(seq_len(12), each = 10), score_a = rnorm(120), score_b = rnorm(120) ) fit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 2, seed = 1) starting_values(fit) as.data.frame(starting_values(fit)) refit <- multilpa(example_data, c("score_a", "score_b"), "school", n_profiles = 2, n_group_classes = 1, n_starts = 1, start = starting_values(fit)) logLik(refit)
Experience-sampling data on what university students did between prompts when they were not studying. Prompts are nested within students, so the data suit a two-level latent class analysis: each prompt belongs to an activity profile, and students differ in how often their prompts fall in each profile.
student_esmstudent_esm
A data frame with 2582 rows (100 students) and 15 columns:
Integer student identifier, 1 to 100. The nesting unit;
pass it as id.
Integer study day, 0 to 13.
Integer prompt of the day, 1 to 5 among the prompts kept.
Factor, "no" or "yes": spent time with
friends since the previous prompt.
Factor, "no" or "yes": used social media.
Factor, "no" or "yes": watched TV or played
video games.
Factor, "no" or "yes": listened to music.
Factor, "no" or "yes": did sports.
Factor, "no" or "yes": went for a walk.
Factor, "no" or "yes": read.
Factor, "no" or "yes": worked in a part-time
job.
Integer rating of feeling happy, 1 to 7.
Integer rating of feeling relaxed, 1 to 7.
Integer rating of feeling worried, 1 to 7.
Integer rating of feeling exhausted, 1 to 7.
The students answered six prompts a day for fourteen days. At a prompt where the student had not studied since the previous prompt, the questionnaire asked which of eight leisure activities they had done. The study asked these items only at non-study prompts, so the dataset contains those prompts alone. Each row is one answered non-study prompt with all eight activity items and all four affect ratings observed.
The dataset is a subset of the original study: 100 of its students, drawn
with a fixed seed from the students with at least 15 such prompts, with every
such prompt they answered. Each student contributes 15 to 51 prompts (median
24). The eight activity items are categorical indicators; the four affect
ratings are continuous-scored and can be combined with them in a mixed
measurement model. worried has a floor: 49.7% of its ratings are 1, so a
profile of low-worry prompts can have zero variance on it and reach the
min_variance bound.
openESM dataset 0062, deposited by Neubauer and Schmiedek at
Zenodo, doi:10.5281/zenodo.17347974, under the Creative Commons
Attribution 4.0 licence (CC-BY 4.0), and retrieved through the openESM
database (https://openesmdata.org). The subset is produced by
data-raw/student-esm.R in the source repository; identifiers are
renumbered and the values are otherwise unchanged.
Neubauer, A. B., & Schmiedek, F. (2024). Approaching academic adjustment on multiple time scales. Zeitschrift für Erziehungswissenschaft, 27, 147–168. doi:10.1007/s11618-023-01182-8
multilpa() with categorical = for the two-level latent class
model.
head(student_esm) activities <- c("time_with_friends", "on_social_media", "tv_video_games", "listened_music", "sports", "walking", "reading", "part_time_job") first_week <- subset(student_esm, day <= 6) lca <- multilpa(first_week, vars = activities, id = "student", n_profiles = 2, n_group_classes = 2, categorical = activities, n_starts = 3, seed = 1) get_results(lca, what = "responses") get_results(lca, what = "profile_probabilities")head(student_esm) activities <- c("time_with_friends", "on_social_media", "tv_video_games", "listened_music", "sports", "walking", "reading", "part_time_job") first_week <- subset(student_esm, day <= 6) lca <- multilpa(first_week, vars = activities, id = "student", n_profiles = 2, n_group_classes = 2, categorical = activities, n_starts = 3, seed = 1) get_results(lca, what = "responses") get_results(lca, what = "profile_probabilities")
A simulated dataset for mixture_regression(), built so that every nesting of the
mixture regression has a model that generated some of its columns, and the
truth is kept for checking recovery.
study_hoursstudy_hours
A data frame with 900 rows (150 students by 6 weeks) and 10 columns:
Integer student identifier, 1 to 150.
Integer study week, 1 to 6.
Hours studied that week, 0 to 12.
Hours slept the night before the quiz.
Standardized motivation, constant within student.
Quiz score.
Integer 0/1: passed the weekly check.
Count of questions the student asked that week.
Factor, "deep" or "surface": the week's generating
strategy. Not for fitting; kept to check recovery.
Factor, "steady" or "erratic": the student's
generating kind. Not for fitting; kept to check recovery.
150 students each report six study weeks. In every week a student studies
with one of two strategies, and the strategy decides how hours become a quiz
score: a deep week gains about 4.5 points per hour from a base of 35, a
surface week under one point per hour from a base of 55. Students are of two
kinds that differ in their mix of strategies – steady students study
deeply in about 85% of weeks, erratic ones in about 25% – and higher
motivation makes a student more likely to be steady. More sleep makes a
deep week more likely.
So score ~ hours is a two-class mixture at the level of the week
(class_level = "observation"), whose class shares differ by student (the
two-level model, id = "student", n_group_classes = 2). passed depends
on the student's kind, not the week's strategy, so it is a mixture at the
level of the student (class_level = "group"). questions is a count whose
Poisson regression depends on the week's strategy.
Simulated by data-raw/study-hours.R in the source repository.
mixture_regression(), enumerate_regressions().
fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) get_results(fit, "coefficients")fit <- mixture_regression(score ~ hours, data = study_hours, n_classes = 2, n_starts = 3, seed = 1) get_results(fit, "coefficients")
Summary methods for fitted models, enumerations, bootstrap tests, covariate and transition models, mixture regressions and pooled imputations. A summary collects the model's main tables and diagnostics; printing it shows each table to a set depth.
## S3 method for class 'multilpa_additive' summary(object, level = 0.95, vcov_type = c("observed", "robust", "opg"), ...) ## S3 method for class 'multilpa_covariates' summary(object, ...) ## S3 method for class 'multilpa_enumeration' summary(object, ...) ## S3 method for class 'multilpa_bootstrap_lrt' summary(object, ...) ## S3 method for class 'latents_growth_mixture' summary(object, level = 0.95, vcov_type = NULL, ...) ## S3 method for class 'multilpa' summary(object, ...) ## S3 method for class 'latents_mixture_regression' summary(object, level = 0.95, vcov_type = NULL, ...) ## S3 method for class 'latents_pooled' summary(object, ...) ## S3 method for class 'multilpa_transitions' summary(object, ...)## S3 method for class 'multilpa_additive' summary(object, level = 0.95, vcov_type = c("observed", "robust", "opg"), ...) ## S3 method for class 'multilpa_covariates' summary(object, ...) ## S3 method for class 'multilpa_enumeration' summary(object, ...) ## S3 method for class 'multilpa_bootstrap_lrt' summary(object, ...) ## S3 method for class 'latents_growth_mixture' summary(object, level = 0.95, vcov_type = NULL, ...) ## S3 method for class 'multilpa' summary(object, ...) ## S3 method for class 'latents_mixture_regression' summary(object, level = 0.95, vcov_type = NULL, ...) ## S3 method for class 'latents_pooled' summary(object, ...) ## S3 method for class 'multilpa_transitions' summary(object, ...)
object |
An object returned by a latents function. |
level |
Confidence level for the reported intervals, strictly between zero and one. |
vcov_type |
|
... |
Ignored; present for compatibility with |
An object of class summary_<class> holding the summarised tables.
Print it, or read any table with get_results() or as.data.frame().
latents-print, latents-as-data-frame, get_results().
fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) print(summary(fit), rows = 3)fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) print(summary(fit), rows = 3)
The three-step approach: the measurement model is fitted first, units are assigned to classes, and only then is the outcome brought in, corrected for the fact that some assignments are wrong. Doing it in one step instead lets the outcome pull the classes toward itself; doing it naively by modal class instead attenuates every difference. This does neither.
three_step( x, data, outcome, level = c("individuals", "groups"), method = c("bch", "proportional", "modal"), ci_level = 0.95, contrast = c("none", "pairs"), adjust = c("BH", "holm", "hochberg", "hommel", "bonferroni", "BY", "none"), vcov_type = c("cluster", "independent") )three_step( x, data, outcome, level = c("individuals", "groups"), method = c("bch", "proportional", "modal"), ci_level = 0.95, contrast = c("none", "pairs"), adjust = c("BH", "holm", "hochberg", "hommel", "bonferroni", "BY", "none"), vcov_type = c("cluster", "independent") )
x |
A covariate-free fitted |
data |
The data frame carrying the outcome, in the fit's row order. Shared fit columns are checked row by row; a frame with too little identifying information warns that alignment cannot be verified. |
outcome |
Name of a numeric outcome column not used as a measurement
indicator in |
level |
|
method |
|
ci_level |
Confidence level for the intervals. |
contrast |
|
adjust |
Multiplicity correction applied across the pairwise
differences, passed to |
vcov_type |
|
The correction assumes the outcome is independent of the assigned
class given the true one, which is what makes a three-step method valid.
On a lta() fit, level = "individuals" uses one row per occasion and
relates the outcome to the profile at that occasion. The default
group-clustered variance accounts for repeated occasions within a group.
This is a marginal profile-outcome analysis; it does not estimate an
outcome effect on transitions or a sequence-level outcome model.
A cluster-robust variance is the sum of one outer product per independent
unit, and those contributions sum to zero at the estimate, so it has rank
at most one less than the number of units. With a single group it is
exactly zero and with as many groups as classes it is singular. Both are
refused with latents_too_few_groups rather than reported as a very
small standard error; vcov_type = "independent" is the labelled way to
ask for the unclustered variance instead.
The error matrix is treated as known rather than estimated, so the intervals
are optimistic and the amount is worth stating. Over 60 replications of a
two-profile design with 8% misclassification and a true class difference of
10, "bch" recovered that difference with a bias of -0.13 against -1.28 for
"modal" and -1.82 for "proportional", and its nominal 95% intervals
covered the truth 85% of the time. Treat the point estimate as close to
unbiased and the interval as somewhat too narrow; the other two methods are
biased enough that their intervals covered the truth in none of the 60.
A base data.frame. For contrast = "none" it has one row per class
and the columns level, method, class, estimate, standard_error,
conf_low, conf_high and effective_n. It carries no statistic or
p_value, unlike r3step(): an outcome mean has no meaningful null value
to be tested against, and a p-value for "this class's mean is zero" would
answer a question nobody asked. The comparison that does have a null is the
difference between two classes, and that is what contrast = "pairs"
returns: one row per pair with the columns level, method, class,
reference_class, estimate (the mean of class minus the mean of
reference_class), standard_error, statistic, p_value,
p_adjusted, conf_low and conf_high, matching r3step().
A contrast with zero standard error has no defined test statistic or
p-value; those columns are NA_real_ rather than a spurious finite test.
Standard errors are cluster-robust in both shapes under the default
vcov_type = "cluster", taking the fit's groups as the independent units,
so they remain honest when the outcome is measured on observations nested
inside those groups; vcov_type = "independent" gives the unclustered
variance instead. The variance that was used is recorded in the result's
vcov_type attribute, and for contrast = "pairs" the correction applied
to p_adjusted is recorded in its adjust attribute. A
contrast = "none" table tests nothing, so it carries no adjust
attribute.
Vermunt, J. K. (2010). Latent class modeling with covariates: two improved three-step approaches. Political Analysis, 18, 450–469. Bakk, Z., & Vermunt, J. K. (2016). Robustness of stepwise latent class modeling with continuous distal outcomes. Structural Equation Modeling, 23, 20–31.
set.seed(11) group <- rep(seq_len(40), each = 10) truth <- 1L + as.integer(runif(400) > 0.5) example_data <- data.frame( g = group, a = rnorm(400, ifelse(truth == 2L, 1.1, -1.1)), b = rnorm(400, ifelse(truth == 2L, 1.1, -1.1)), y = rnorm(400, ifelse(truth == 2L, 10, 0)) ) fit <- multilpa(example_data, c("a", "b"), "g", n_profiles = 2, n_group_classes = 1, n_starts = 4, seed = 1) three_step(fit, example_data, "y") three_step(fit, example_data, "y", contrast = "pairs")set.seed(11) group <- rep(seq_len(40), each = 10) truth <- 1L + as.integer(runif(400) > 0.5) example_data <- data.frame( g = group, a = rnorm(400, ifelse(truth == 2L, 1.1, -1.1)), b = rnorm(400, ifelse(truth == 2L, 1.1, -1.1)), y = rnorm(400, ifelse(truth == 2L, 10, 0)) ) fit <- multilpa(example_data, c("a", "b"), "g", n_profiles = 2, n_group_classes = 1, n_starts = 4, seed = 1) three_step(fit, example_data, "y") three_step(fit, example_data, "y", contrast = "pairs")
coef(), vcov(), confint(), logLik() and nobs() for multilevel
profile and class models (multilpa()), covariate models, latent transition
models and, for vcov(), pooled imputations. Mixture regressions, growth
mixtures and the additive, cross-level and general transition families
document the same methods with their get_results() page.
## S3 method for class 'multilpa_covariates' vcov( object, data = NULL, step = 1e-04, vcov_type = c("observed", "robust", "opg"), scale = c("natural", "unconstrained"), boundary = c("error", "fix"), ... ) ## S3 method for class 'multilpa_covariates' coef(object, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa_covariates' confint(object, parm, level = 0.95, data = NULL, ...) ## S3 method for class 'multilpa_covariates' logLik(object, ...) ## S3 method for class 'multilpa_covariates' nobs(object, ...) ## S3 method for class 'multilpa' coef(object, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa' vcov(object, data = NULL, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa' confint(object, parm, level = 0.95, data = NULL, ...) ## S3 method for class 'multilpa' logLik(object, ...) ## S3 method for class 'multilpa' nobs(object, ...) ## S3 method for class 'latents_pooled' vcov(object, ...) ## S3 method for class 'multilpa_transitions' logLik(object, ...) ## S3 method for class 'multilpa_transitions' nobs(object, ...) ## S3 method for class 'multilpa_transitions' coef(object, ...)## S3 method for class 'multilpa_covariates' vcov( object, data = NULL, step = 1e-04, vcov_type = c("observed", "robust", "opg"), scale = c("natural", "unconstrained"), boundary = c("error", "fix"), ... ) ## S3 method for class 'multilpa_covariates' coef(object, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa_covariates' confint(object, parm, level = 0.95, data = NULL, ...) ## S3 method for class 'multilpa_covariates' logLik(object, ...) ## S3 method for class 'multilpa_covariates' nobs(object, ...) ## S3 method for class 'multilpa' coef(object, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa' vcov(object, data = NULL, scale = c("natural", "unconstrained"), ...) ## S3 method for class 'multilpa' confint(object, parm, level = 0.95, data = NULL, ...) ## S3 method for class 'multilpa' logLik(object, ...) ## S3 method for class 'multilpa' nobs(object, ...) ## S3 method for class 'latents_pooled' vcov(object, ...) ## S3 method for class 'multilpa_transitions' logLik(object, ...) ## S3 method for class 'multilpa_transitions' nobs(object, ...) ## S3 method for class 'multilpa_transitions' coef(object, ...)
object |
A fitted model: from |
data |
Optional. The data frame the model was fitted to. When omitted it is rebuilt from what the fit stores (indicators, identifiers, occasions and designs), which round-trips exactly; supplying it is the stronger check that the caller still holds that frame. |
step |
Finite-difference step for the observed information (covariate models). |
vcov_type |
|
scale |
|
boundary |
|
... |
For |
parm |
Optional coefficient names or indices; defaults to every
estimated coefficient. Naming a coefficient that |
level |
Confidence level strictly between zero and one. |
coef(): a named numeric vector. vcov(): a named symmetric matrix
over the same parameters. confint(): a matrix with one row per parameter
and the lower and upper bounds as columns. logLik(): a "logLik" object
with df and nobs attributes. nobs(): the number of groups, the
independent units of the likelihood.
parameter_inference() for the tidy table of estimates, standard
errors and intervals.
fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) coef(fit) logLik(fit) nobs(fit)fit <- multilpa(course_engagement, c("browse", "lectures", "forum_read"), "student", n_profiles = 2, n_group_classes = 2, n_starts = 2, seed = 1) coef(fit) logLik(fit) nobs(fit)