| Title: | A General Framework for Latent Class and Profile Analysis |
|---|---|
| Description: | Provides a unified framework for finite-mixture latent variable models, including latent class analysis (LCA), latent profile analysis (LPA), latent class/profile analysis with covariates, and latent transition analysis (LTA), within one consistent interface. Estimation methods include the expectation-maximization (EM) algorithm; neural network estimation, which requires 'Python' and its dependent libraries; integration with 'Mplus', which requires an installed copy of 'Mplus'; and stochastic EM (SEM) through the optional 'flexmix', 'Rmixmod', and 'RMixtComp' backends. 'flexmix' and the default 'Rmixmod' path perform configurable warm-up trajectories and promote the best candidates to full SEM replications. 'Rmixmod' additionally exposes its native strategy interface, including chained SEM-to-EM estimation, whereas 'RMixtComp' exposes its native SEM and Gibbs controls without the external warm-up stage. Model assessment includes the Akaike information criterion (AIC), Bayesian information criterion (BIC), Schwarz information criterion (SIC), consistent AIC (CAIC), approximate weight of evidence (AWE), sample-size-adjusted BIC (SABIC), entropy, and average posterior probabilities. Model-comparison procedures include the ordinary likelihood-ratio test, the Mplus TECH11 Vuong-Lo-Mendell-Rubin and adjusted Lo-Mendell-Rubin tests, and fixed-replicate or sequential parametric bootstrap likelihood-ratio tests. Standard errors can be estimated by nonparametric bootstrap, numerical observed information, or analytic observed information based on Louis' identity. Classification-error-adjusted maximum-likelihood and Bolck-Croon-Hagenaars three-step methods support covariates predicting latent membership, initial-status and transition regressions, and latent classes or states predicting continuous and categorical external observed dependent variables. Simulation, posterior-probability, classification-error, extraction, summary, plotting, model-adjustment, and update utilities are also provided for reproducible workflows. |
| Authors: | Haijiang Qin [aut, cre, cph] (ORCID: <https://orcid.org/0009-0000-6721-5653>), Lei Guo [aut, cph] (ORCID: <https://orcid.org/0000-0002-8273-3587>) |
| Maintainer: | Haijiang Qin <[email protected]> |
| License: | GPL-3 |
| Version: | 1.0.4 |
| Built: | 2026-09-22 11:21:46 UTC |
| Source: | https://github.com/cran/LCPA |
This function reorders the latent classes/profiles of object2 to best match those in object1
by minimizing the total assignment cost based on posterior class membership (MAP classification).
It uses the Linear Sum Assignment Problem (LSAP) solver to find the optimal one-to-one mapping between latent classes.
Useful for comparing or averaging models across replications, initializations, or algorithms where class labels may be permuted.
adjust.model(object1, object2)adjust.model(object1, object2)
object1 |
An object of class |
object2 |
An object of class |
The alignment is performed by:
Computing Maximum A Posteriori (MAP) classification matrices for both models.
Calculating a distance matrix between classes (typically Euclidean distance between binary MAP vectors).
Solving the Linear Sum Assignment Problem (LSAP) via solve_LSAP to find the permutation minimizing total mismatch cost.
Reordering all class-specific components of object2 according to this optimal assignment.
A modified version of object2, with all parameters and posterior probabilities reordered
to best match the latent class structure of object1. The returned object retains its original class
("LCA" or "LPA") and includes aligned:
Prior probabilities (P.Z)
Posterior probabilities (P.Z.Xn)
MAP classifications (Z)
Profile/Class-specific parameters:
For "LPA": means, covs
For "LCA": par, probability
All relevant dimnames and names are synchronized with object1
Both models must have identical numbers of observations (), latent classes (), and indicators ().
Designed for use after fitting multiple models (e.g., different random starts) to ensure consistent class labeling.
Does not modify object1; only returns a reordered object2.
## Not run: # need Mplus and Python library(LCPA) set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 3) # Fit two models with different random seeds fit1 <- LCA(data.obj$response, L = 3, method = "Mplus", nrep = 1) fit2 <- LCA(data.obj$response, L = 3, method = "NNE", nrep = 1) # Align fit2 to fit1's class ordering fit2_aligned <- adjust.model(fit1, fit2) # Compare prior probabilities before and after print("Before alignment:") print(fit2$params$P.Z) print("After alignment:") print(fit2_aligned$params$P.Z) ## End(Not run)## Not run: # need Mplus and Python library(LCPA) set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 3) # Fit two models with different random seeds fit1 <- LCA(data.obj$response, L = 3, method = "Mplus", nrep = 1) fit2 <- LCA(data.obj$response, L = 3, method = "NNE", nrep = 1) # Align fit2 to fit1's class ordering fit2_aligned <- adjust.model(fit1, fit2) # Compare prior probabilities before and after print("Before alignment:") print(fit2$params$P.Z) print("After alignment:") print(fit2_aligned$params$P.Z) ## End(Not run)
Standardizes polytomous response data by converting raw category values to consecutive integers starting from 0. Records original category values for potential reverse transformation. Handles varying numbers of response categories across indicators.
adjust.response(response)adjust.response(response)
response |
A matrix or data frame containing response data where:
Non-numeric columns will be coerced to numeric with warning. |
The function processes each indicator column independently:
Extracts unique response values and sorts them in ascending order
Maps smallest value to 0, second smallest to 1, etc.
Records original values in poly.orig for possible reverse transformation
Handles indicators with different numbers of categories through NA-padding
Missing values (NA) in input are preserved as NA in output.
A named list containing:
poly.orig matrix. Original sorted category values for each indicator.
Rows correspond to indicators, columns to category positions. Empty cells filled with NA.
poly.valueInteger vector of length . Number of unique response categories per indicator.
poly.maxScalar integer. Maximum number of categories across all indicators, i.e., .
response matrix. Adjusted response data where original values are replaced by
zero-based category indices (0 to for categories).
# Simulate response data with 3 indicators and varying categories set.seed(123) resp <- data.frame( indicator1 = sample(1:3, 10, replace = TRUE), indicator2 = sample(c(0, 5, 10), 10, replace = TRUE), indicator3 = sample(1:2, 10, replace = TRUE) ) # Apply adjustment adjusted <- adjust.response(resp) # Inspect results str(adjusted) print(adjusted$poly.orig) # Original category values print(adjusted$response) # Standardized responses# Simulate response data with 3 indicators and varying categories set.seed(123) resp <- data.frame( indicator1 = sample(1:3, 10, replace = TRUE), indicator2 = sample(c(0, 5, 10), 10, replace = TRUE), indicator3 = sample(1:2, 10, replace = TRUE) ) # Apply adjustment adjusted <- adjust.response(resp) # Inspect results str(adjusted) print(adjusted$poly.orig) # Original category values print(adjusted$response) # Standardized responses
Checks whether each column in the response matrix contains exactly the number
of unique response categories specified in poly.value. Handles edge cases
where all indicators have identical category counts efficiently.
check.response(response, poly.value)check.response(response, poly.value)
response |
A numeric matrix of dimension
Each cell contains the observed response value for a subject on an indicator. |
poly.value |
An integer vector of length |
Logical value indicating validation status:
TRUE if either:
All columns have identical numbers of unique values (regardless of poly.value specification)
Each column's unique value count matches its corresponding poly.value entry
FALSE if any column's unique value count mismatches its specified poly.value
(when columns have varying category counts)
This function contains a specific behavior: When all indicators have identical numbers of
unique response categories, it returns TRUE immediately without validating against
poly.value. This may lead to unexpected results if poly.value contains
inconsistent expectations. Users should ensure poly.value accurately reflects
their measurement model.
# Valid case: Matching category counts resp_matrix <- matrix(c(1,1,2,2, 1,2,3,1), ncol = 2) check.response(resp_matrix, poly.value = c(2, 3)) # Returns TRUE # Invalid case: Mismatched category counts check.response(resp_matrix, poly.value = c(2, 2)) # Returns FALSE # Special case: Uniform category counts bypass poly.value check uniform_resp <- matrix(rep(1:2, each = 4), ncol = 2) check.response(uniform_resp, poly.value = c(2, 5)) # Returns TRUE (bypass behavior)# Valid case: Matching category counts resp_matrix <- matrix(c(1,1,2,2, 1,2,3,1), ncol = 2) check.response(resp_matrix, poly.value = c(2, 3)) # Returns TRUE # Invalid case: Mismatched category counts check.response(resp_matrix, poly.value = c(2, 2)) # Returns FALSE # Special case: Uniform category counts bypass poly.value check uniform_resp <- matrix(rep(1:2, each = 4), ncol = 2) check.response(uniform_resp, poly.value = c(2, 5)) # Returns TRUE (bypass behavior)
Compares two latent class/profile models using multiple fit indices, likelihood ratio tests, and classification metrics. The models may have any class counts, including equal class counts.
compare.model(object1, object2, nrep.bootstrap = 0)compare.model(object1, object2, nrep.bootstrap = 0)
object1 |
An object of class |
object2 |
An object of class |
nrep.bootstrap |
Integer specifying the number of bootstrap replications for the parametric
bootstrap likelihood ratio test (BLRT). Default is |
This function performs comprehensive model comparison between two LCA/LPA models. Key features include:
Automatically orders models by parameter count (smaller model first)
Computes multiple fit indices via get.fit.index
Calculates classification quality metrics (entropy, average posterior probabilities)
Performs three types of likelihood ratio tests:
Standard LRT, see LRT.test
VLMR and adjusted LMR tests, see LRT.test.VLMR
Parametric bootstrap LRT (computationally intensive but robust), see LRT.test.Bootstrap
Computes a Bayes factor approximation from the difference in
SIC = -0.5 * BIC
Important requirements:
Both models must be of the same type (LCA or LPA)
The three likelihood-ratio procedures accept any pair of class counts, including equal class counts. The VLMR component requires models fitted to the same observations in the same order; LPA models must also use the same covariance constraint.
nrep.bootstrap > 0 requires significant computational resources
An object of class compare.model containing:
NNamed vector of sample sizes for the ordered models.
INamed vector of indicator counts for the ordered models.
LNamed vector of latent class/profile counts for the ordered models.
nparNamed vector with number of free parameters for each model
entropyNamed vector with entropy values (classification accuracy measure) for each model
AvePPList containing average posterior probabilities per latent class/profile
fit.indexList of get.fit.index objects for both models
BFBayes Factor for model comparison (based on SIC)
LRT.objLikelihood ratio test (LRT) results
LRT.VLMR.objMplus TECH11 VLMR and adjusted LMR test results
LRT.Bootstrap.objBootstrap LRT results (if nrep.bootstrap > 0)
callThe matched function call
argumentsList containing the original arguments passed to the function
LCA, LPA, get.fit.index,
extract, LRT.test, LRT.test.VLMR
library(LCPA) set.seed(123) data.obj <- sim.LPA(N = 500, I = 5, L = 3, constraint = "V0") response <- data.obj$response # need Mplus # Compare 3-class vs 4-class LPA models object1 <- LPA(response, L = 3, method = "Mplus", constraint = "V0") object2 <- LPA(response, L = 4, method = "Mplus", constraint = "V0") compare.model.obj <- compare.model(object1, object2) print(compare.model.obj)library(LCPA) set.seed(123) data.obj <- sim.LPA(N = 500, I = 5, L = 3, constraint = "V0") response <- data.obj$response # need Mplus # Compare 3-class vs 4-class LPA models object1 <- LPA(response, L = 3, method = "Mplus", constraint = "V0") object2 <- LPA(response, L = 4, method = "Mplus", constraint = "V0") compare.model.obj <- compare.model(object1, object2) print(compare.model.obj)
A generic S3 extractor function designed to retrieve internal components from various model and simulation objects
produced by the LCPA package. This function provides a consistent interface across different classes,
allowing users to access estimated parameters, fit statistics, simulation truths, standard errors, and more.
extract(object, what, ...) ## S3 method for class 'LCA' extract(object, what, ...) ## S3 method for class 'LPA' extract(object, what, ...) ## S3 method for class 'LCPA' extract(object, what, ...) ## S3 method for class 'LTA' extract(object, what, ...) ## S3 method for class 'sim.LCA' extract(object, what, ...) ## S3 method for class 'sim.LPA' extract(object, what, ...) ## S3 method for class 'sim.LTA' extract(object, what, ...) ## S3 method for class 'fit.index' extract(object, what, ...) ## S3 method for class 'compare.model' extract(object, what, ...) ## S3 method for class 'SE' extract(object, what, ...)extract(object, what, ...) ## S3 method for class 'LCA' extract(object, what, ...) ## S3 method for class 'LPA' extract(object, what, ...) ## S3 method for class 'LCPA' extract(object, what, ...) ## S3 method for class 'LTA' extract(object, what, ...) ## S3 method for class 'sim.LCA' extract(object, what, ...) ## S3 method for class 'sim.LPA' extract(object, what, ...) ## S3 method for class 'sim.LTA' extract(object, what, ...) ## S3 method for class 'fit.index' extract(object, what, ...) ## S3 method for class 'compare.model' extract(object, what, ...) ## S3 method for class 'SE' extract(object, what, ...)
object |
An object of one of the following classes:
|
what |
One non-missing, non-empty character string specifying the
component to extract. Valid choices depend on the class of |
... |
Reserved for S3 method compatibility; no additional arguments are used. |
This function supports extraction from ten primary object classes. Below are available components for each:
LCALatent Class Analysis model results. Available components:
paramsList containing all estimated model parameters.
par3D array () of conditional response probabilities.
P.ZVector of length with latent class prior probabilities.
category.levelsList of the ordered observed response categories for each indicator.
nparNumber of free parameters in the model.
Log.LikLog-likelihood of the final model.
AICAkaike Information Criterion.
BICBayesian Information Criterion.
best_BICBest BIC value across replication runs (if nrep > 1).
P.Z.Xn matrix of posterior class probabilities.
ZVector of length with MAP-classified latent class memberships.
probabilityList of formatted conditional probability matrices per item.
Log.Lik.historyVector tracking log-likelihood at each EM iteration.
Log.Lik.nrepVector of log-likelihoods from each replication run.
modelBackend model object for method="NNE", method="Mplus", method="flexmix", method="Rmixmod", or method="RMixtComp". The latter three are SEM fits only.
callThe original function call used for model estimation.
argumentsList containing all input arguments passed to the LCA function.
LPALatent Profile Analysis model results. Available components:
paramsList containing all estimated model parameters.
means matrix of estimated mean vectors for each profile.
covs array of estimated covariance matrices.
P.ZVector of length with profile prior probabilities.
nparNumber of free parameters (depends on constraint).
Log.LikLog-likelihood of the final model.
AICAkaike Information Criterion.
BICBayesian Information Criterion.
best_BICBest BIC value across replication runs (if nrep > 1).
P.Z.Xn matrix of posterior profile probabilities.
ZVector of length with MAP-classified profile memberships.
Log.Lik.historyVector tracking log-likelihood at each EM iteration.
Log.Lik.nrepVector of log-likelihoods from each replication run.
modelBackend model object (neural network, Mplus, flexmix SEM, Rmixmod SEM, or RMixtComp SEM).
callThe original function call used for model estimation.
argumentsList containing all input arguments passed to the LPA function.
constraintCovariance structure constraints applied during estimation (from original arguments).
LCPALatent Class/Profile Analysis (with covariates). Available components:
betaInitial class coefficients (p1 x L matrix).
beta.seStandard errors for beta.
beta.Z.staZ-statistics for beta.
beta.p.value.tail1One-tailed p-values for beta.
beta.p.value.tail2Two-tailed p-values for beta.
P.Z.XnPosterior probabilities (N x L).
P.ZPrior proportions (length L).
ZModal class assignments (length N).
nparNumber of free parameters.
Log.LikLog-likelihood.
AICAIC.
BICBIC.
vcovVariance-covariance matrix of the free Step 3 coefficients.
informationObserved information matrix for method.SE="Numeric" or "Analytic"; otherwise NULL.
SE.diagnosticsDiagnostics for the selected standard-error method.
bound.diagnosticsIndices of Step 3 coefficients at or near an optimization bound.
iterationsOptimization iterations in Step 3.
convergedLogical indicator of successful NLopt termination.
Log.Lik.historyStep 3 log-likelihood history.
paramsStep 1 model parameters (LCA/LPA output).
callFunction call.
argumentsInput arguments list.
LTALatent Transition Analysis model results. Available components:
betaInitial class coefficients (p1 x L matrix).
gammaTransition coefficients (nested list).
step1.poolLogical indicating whether Step 1 used the row-bound responses from all time points.
beta.seStandard errors for beta.
gamma.seStandard errors for gamma.
beta.Z.staZ-statistics for beta.
gamma.Z.staZ-statistics for gamma.
beta.p.value.tail1One-tailed p-values for beta.
gamma.p.value.tail1One-tailed p-values for gamma.
beta.p.value.tail2Two-tailed p-values for beta.
gamma.p.value.tail2Two-tailed p-values for gamma.
P.Z.XnsList of posterior probabilities per time (each N x L).
P.ZsList of prior proportions per time (each length L).
ZsList of modal class assignments per time (each length N).
nparNumber of free parameters.
Log.LikLog-likelihood.
AICAIC.
BICBIC.
vcovVariance-covariance matrix of the free Step 3 coefficients.
informationObserved information matrix for method.SE="Numeric" or "Analytic"; otherwise NULL.
SE.diagnosticsDiagnostics for the selected standard-error method.
bound.diagnosticsIndices of Step 3 coefficients at or near an optimization bound.
iterationsOptimization iterations in Step 3.
convergedLogical indicator of successful NLopt termination.
Log.Lik.historyStep 3 log-likelihood history.
paramsStep 1 model parameters (LCA/LPA output).
callFunction call.
argumentsInput arguments list.
sim.LCASimulated Latent Class Analysis data. Available components:
responseInteger matrix () of simulated categorical observations.
parArray () of true class-specific category probabilities.
ZInteger vector (length ) of true latent class assignments.
P.ZNumeric vector (length ) of true class proportions.
poly.valueInteger vector (length ) specifying categories per variable.
P.Z.XnBinary matrix () of true class membership indicators.
callThe original function call used for simulation.
argumentsList containing all input arguments passed to sim.LCA.
sim.LPASimulated Latent Profile Analysis data. Available components:
responseNumeric matrix () of simulated continuous observations.
means matrix of true class-specific means.
covs array of true class-specific covariance matrices.
P.Z.Xn matrix of true class membership indicators.
P.ZNumeric vector (length ) of true class proportions.
ZInteger vector (length ) of true profile assignments.
constraintOriginal constraint specification passed to sim.LPA.
callThe original function call used for simulation.
argumentsList containing all input arguments passed to sim.LPA.
sim.LTASimulated Latent Transition Analysis data. Available components:
responsesList of response matrices per time point.
ZsList of true latent class assignments per time.
P.ZsList of true class proportions per time.
parTrue conditional probabilities (for categorical items).
meansTrue profile means (for continuous variables).
covsTrue covariance matrices per class and time.
poly.valueCategories per variable (for categorical items).
rateTransition rate matrix or structure.
covariatesSimulated covariate matrix.
betaTrue initial class coefficients.
gammaTrue transition coefficients.
ref.classReference class for the true regression coefficients.
callOriginal simulation function call.
argumentsInput arguments used in simulation.
fit.indexModel fit indices object. Available components:
NSample size used to compute sample-size-dependent indices.
nparNumber of free parameters in the model.
Log.LikLog-likelihood of the model.
-2LLDeviance statistic (-2 times log-likelihood).
AICAkaike Information Criterion.
BICBayesian Information Criterion.
SICSchwarz information criterion on the log-likelihood scale ().
CAICConsistent AIC.
AWEApproximate Weight of Evidence.
SABICSample-Size Adjusted BIC (alternative formulation).
callOriginal function call that generated the fit indices.
argumentsList containing input arguments (includes original model object).
compare.modelModel comparison results. Available components:
N, I, L
Named vectors giving sample size, indicator count, and class count for each model.
nparNamed numeric vector with free parameters for each model (model1, model2).
entropyNamed numeric vector with entropy values for each model.
AvePPList of average posterior probabilities per class/profile for each model.
fit.indexList of get.fit.index objects for both models.
BFBayes Factor comparing models (based on SIC differences).
LRT.objStandard likelihood ratio test results (requires nested models).
LRT.VLMR.objMplus TECH11 VLMR and adjusted LMR test results.
LRT.Bootstrap.objParametric bootstrap likelihood ratio test results (if nrep.bootstrap > 0).
callThe matched function call used for comparison.
argumentsList containing original input arguments (object1, object2, nrep.bootstrap).
SEStandard error estimation results. Available components:
seList containing standard errors for parameters (components depend on model type).
vcovVariance-covariance matrix for method="Obs" or "Louis"; NULL for bootstrap.
hessianObserved information matrix for method="Obs" or "Louis"; NULL for bootstrap.
diagnosticsMethod-specific diagnostic information (e.g., estimation method).
callFunction call that generated the object.
argumentsInput arguments used in estimation.
meansStandard errors for profile means (LPA models only — accessed via se list).
covsStandard errors for covariance parameters (LPA models only — accessed via se list).
P.ZStandard errors for class proportions (both LCA/LPA — accessed via se list).
parStandard errors for conditional probabilities (LCA models only — accessed via se list).
The requested component. Return type varies depending on what and the class of object.
If an invalid what is provided, an informative error is thrown listing valid options.
extract(LCA): Extract fields from a LCA object
extract(LPA): Extract fields from a LPA object
extract(LCPA): Extract fields from a LCPA object
extract(LTA): Extract fields from a LTA object
extract(sim.LCA): Extract fields from a sim.LCA object
extract(sim.LPA): Extract fields from a sim.LPA object
extract(sim.LTA): Extract fields from a sim.LTA object
extract(fit.index): Extractor method for fit.index objects
extract(compare.model): Extract fields from a compare.model object
extract(SE): Extract fields from a SE object
For LCA, LPA, LCPA, and LTA objects, components reflect estimated parameters.
For sim.LCA, sim.LPA, and sim.LTA objects, components reflect true data-generating parameters.
In SE objects:
Top-level components like vcov and hessian are available when method = "Obs" or "Louis".
Requesting them under Bootstrap triggers a warning and returns NULL.
Parameter-specific SEs (e.g., means, par) are stored within the se list.
You can extract them directly by name (e.g., extract(se_obj, "means")).
Attempting to extract unavailable parameter SEs (e.g., par from an LPA model) triggers an error with available options.
For fit.index and compare.model objects, valid components are dynamically determined from the object’s names.
All methods ignore additional arguments (...).
set.seed(123) # Simulate LPA data: 500 observations, 3 continuous variables, 2 latent profiles # Constraint "E0": Equal variances across classes, zero covariances data.obj <- sim.LPA(N = 500, I = 3, L = 2, constraint = "E0") # Extract the simulated response matrix (N x I) for model fitting response <- extract(data.obj, "response") # Extract the TRUE covariance matrices (I x I x L array) extract(data.obj, "covs") # Fit an LPA model to the simulated data using the SAME constraint ("E0") fit_E0 <- LPA(response, L = 2, constraint = "E0") # Extract the ESTIMATED covariance matrices from the fitted model extract(fit_E0, "covs") # Simulate LCA data: 30 observations, 5 categorical items, 3 latent classes sim_data <- sim.LCA(N = 30, I = 5, L = 3) # Extract the TRUE conditional probability array extract(sim_data, "par")set.seed(123) # Simulate LPA data: 500 observations, 3 continuous variables, 2 latent profiles # Constraint "E0": Equal variances across classes, zero covariances data.obj <- sim.LPA(N = 500, I = 3, L = 2, constraint = "E0") # Extract the simulated response matrix (N x I) for model fitting response <- extract(data.obj, "response") # Extract the TRUE covariance matrices (I x I x L array) extract(data.obj, "covs") # Fit an LPA model to the simulated data using the SAME constraint ("E0") fit_E0 <- LPA(response, L = 2, constraint = "E0") # Extract the ESTIMATED covariance matrices from the fitted model extract(fit_E0, "covs") # Simulate LCA data: 30 observations, 5 categorical items, 3 latent classes sim_data <- sim.LCA(N = 30, I = 5, L = 3) # Extract the TRUE conditional probability array extract(sim_data, "par")
Computes the average posterior probability for the most likely class assignment
in latent class/profile analysis. This metric quantifies classification precision.
The total average posterior probability (Nylund-Gibson & Choi, 2018) indicate adequate classification quality.
get.AvePP(object)get.AvePP(object)
object |
An object of class
|
A matrix with the following structure:
Rows: Represent each latent class (1 to L) and a final "Total" row.
Columns: Represent each latent class (1 to L) and a final "Total" column.
Diagonal elements : Average posterior probability for observations assigned to class .
That is,
where is the number of participants assigned to class
, and .
Off-diagonal elements (): Average posterior probability of class
among observations assigned to class . Useful for assessing classification confusion.
Bottom-right corner : Overall average posterior probability across all observations,
Classification quality is considered acceptable if (Nylund-Gibson & Choi, 2018).
Nylund-Gibson, K., & Choi, A. Y. (2018). Ten frequently asked questions about latent class analysis. Translational Issues in Psychological Science, 4(4), 440–461. doi:10.1037/tps0000176
# Example with simulated data set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 2, IQ=0.9) response <- data.obj$response # Fit 2-class model with EM algorithm fit.em <- LCA(response, L = 2, method = "EM", nrep = 10) AvePP_value <- get.AvePP(fit.em) print(AvePP_value)# Example with simulated data set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 2, IQ=0.9) response <- data.obj$response # Fit 2-class model with EM algorithm fit.em <- LCA(response, L = 2, method = "EM", nrep = 10) AvePP_value <- get.AvePP(fit.em) print(AvePP_value)
Computes classification error probability (CEP) matrices for bias-corrected three-step latent class/profile and latent transition models with covariates.
get.CEP(P.Z.Xns, CEP.time.cross = TRUE)get.CEP(P.Z.Xns, CEP.time.cross = TRUE)
P.Z.Xns |
A list of length
All matrices are expected to have identical dimensions and to be ordered chronologically. |
CEP.time.cross |
Logical. If |
The CEP matrix describes the relationship between the latent class and the modal class assignment obtained from posterior class-membership probabilities.
For participant at time , the modal class assignment is
defined as
For time point , the CEP matrix is estimated as
Equivalently, defining the posterior-weighted class prevalence as
the estimator can be written as
The resulting matrix is oriented as
so that, in the Step-3 likelihood, the probability associated with an observed
modal assignment under candidate latent class
is
obtained from
In other words, the column corresponding to the participant's observed modal assignment is selected, and the entries across rows give the corresponding classification probabilities under the candidate latent classes.
If CEP.time.cross = FALSE, the above estimator is computed separately for
each time point.
If CEP.time.cross = TRUE, the numerator and denominator are pooled across
time points before normalization:
Therefore, the pooled CEP is not the simple arithmetic mean of the time-specific CEP matrices. Rather, it is equivalent to a class-specific posterior-mass-weighted combination of the time-specific matrices.
Using a common pooled CEP across time assumes that the classification-error mechanism
is sufficiently stable across time for a common CEP matrix to be appropriate. This classification-error invariance assumption is conceptually related to, but is not equivalent to, longitudinal measurement invariance.
A named list of length . Each element is an
CEP matrix whose:
rows index the latent ("true") class;
columns index the modal ("assigned" or "predicted") class;
entry estimates
.
Thus, each row is a conditional probability distribution over modal assignments given a latent class and sums to 1 up to numerical precision. Column sums are not constrained to equal 1.
When CEP.time.cross = TRUE, all elements of the returned list contain
the same pooled CEP matrix.
List elements are named "t1", "t2", ..., "tT".
The function assumes complete posterior-probability matrices; missing values are not explicitly handled.
All elements of P.Z.Xns are expected to have the same numbers
of individuals and latent classes.
Posterior-probability rows are expected to sum to 1; this condition is not explicitly checked by the function.
Modal assignment is obtained using which.max(). If two or more
classes have exactly equal maximum posterior probabilities,
which.max() selects the first maximum.
If a latent class has zero posterior-weighted prevalence, the CEP for that class is undefined and division by zero may occur.
Liang, Q., de la Torre, J., & Law, N. (2023). Latent transition cognitive diagnosis model with covariates: A three-step approach. Journal of Educational and Behavioral Statistics, 48(6), 690–718. doi:10.3102/10769986231163320
# Simulate posterior probabilities for two time points, # three latent classes, and 100 individuals set.seed(123) N <- 100 L <- 3 times <- 2 P.Z.Xns <- replicate( times, t(apply( matrix(runif(N * L), N, L), 1, function(x) x / sum(x) )), simplify = FALSE ) # Time-specific CEP matrices cep_time_specific <- get.CEP( P.Z.Xns, CEP.time.cross = FALSE ) # Pooled CEP matrix used at all time points cep_pooled <- get.CEP( P.Z.Xns, CEP.time.cross = TRUE ) # Under the [true class, assigned class] orientation, # rows should sum to one rowSums(cep_time_specific[[1]])# Simulate posterior probabilities for two time points, # three latent classes, and 100 individuals set.seed(123) N <- 100 L <- 3 times <- 2 P.Z.Xns <- replicate( times, t(apply( matrix(runif(N * L), N, L), 1, function(x) x / sum(x) )), simplify = FALSE ) # Time-specific CEP matrices cep_time_specific <- get.CEP( P.Z.Xns, CEP.time.cross = FALSE ) # Pooled CEP matrix used at all time points cep_pooled <- get.CEP( P.Z.Xns, CEP.time.cross = TRUE ) # Under the [true class, assigned class] orientation, # rows should sum to one rowSums(cep_time_specific[[1]])
Computes the relative entropy statistic to evaluate classification quality in Latent Class Analysis (LCA) or Latent Profile Analysis (LPA) models. Entropy measures how accurately cases are assigned to latent classes based on posterior probabilities, with values closer to 1 indicating better separation between classes.
get.entropy(object)get.entropy(object)
object |
An object of class
|
A numeric value between 0 and 1 representing the relative entropy (Nylund-Gibson et al., 2018; Clark et al., 2013):
1.0: Perfect classification (each case belongs exclusively to one class)
0.8-1.0: Good classification quality
0.6-0.8: Moderate classification quality
< 0.6: Poor classification quality (consider model simplification)
Calculated using the formula:
where:
= Sample size
= Number of latent classes
= Posterior probability of participant
belonging to class
Values should be interpreted alongside other diagnostics (BIC, bootstrapped LRT) as high entropy alone doesn't guarantee model validity. Low entropy may indicate:
Overly complex model (too many classes)
Poorly measured latent constructs
Violation of local independence assumption
Clark, S. L., Muthén, B., Kaprio, J., D'Onofrio, B. M., Viken, R., & Rose, R. J. (2013). Models and strategies for factor mixture analysis: An example concerning the structure underlying psychological disorders. Structural Equation Modeling: A Multidisciplinary Journal, 20(4), 681–703. doi:10.1080/10705511.2013.824786
Nylund-Gibson, K., & Choi, A. Y. (2018). Ten frequently asked questions about latent class analysis. Translational Issues in Psychological Science, 4(4), 440–461. doi:10.1037/tps0000176
# Example with simulated data set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 2, IQ=0.9) response <- data.obj$response # Fit 2-class model with EM algorithm fit.em <- LCA(response, L = 2, method = "EM", nrep = 10) entropy_value <- get.entropy(fit.em) cat("Classification entropy:", round(entropy_value, 3), "\n")# Example with simulated data set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 2, IQ=0.9) response <- data.obj$response # Fit 2-class model with EM algorithm fit.em <- LCA(response, L = 2, method = "EM", nrep = 10) entropy_value <- get.entropy(fit.em) cat("Classification entropy:", round(entropy_value, 3), "\n")
Computes a comprehensive set of model fit indices for objects returned by
LCA or LPA. These indices balance model
fit (log-likelihood) with model complexity (number of parameters) to facilitate
model selection. All indices are derived from the observed-data log-likelihood
and parameter count.
get.fit.index(object)get.fit.index(object)
object |
An object of class
|
An object of class "fit.index" containing:
Number of observations used to fit the model
Number of free parameters in the model
Log-likelihood of the model:
Deviance statistic:
where is the prior probability of class ,
and is participant 's indicator vector.
Akaike Information Criterion:
,
where = number of free parameters. Lower values indicate better fit.
Bayesian Information Criterion:
,
where = sample size. Incorporates stronger penalty for complexity than AIC.
Schwarz information criterion on the log-likelihood scale:
.
Equivalent to . Often used in latent class modeling.
Consistent AIC:
.
Consistent version of AIC that converges to true model as .
Approximate Weight of Evidence:
.
Penalizes complexity more heavily than CAIC.
Sample-Size Adjusted BIC:
.
Recommended for latent class/profile analysis with moderate sample sizes.
List containing the fitted model as object.
Matched function call.
# Fit LPA model set.seed(123) data.obj <- sim.LPA(N = 100, I = 3, L = 2, constraint = "E0") fit <- LPA(data.obj$response, L = 2, constraint = "VV", method = "EM") # Compute fit indices fit_indices <- get.fit.index(fit) fit_indices extract(fit_indices, "SABIC")# Fit LPA model set.seed(123) data.obj <- sim.LPA(N = 100, I = 3, L = 2, constraint = "E0") fit <- LPA(data.obj$response, L = 2, constraint = "VV", method = "EM") # Compute fit indices fit_indices <- get.fit.index(fit) fit_indices extract(fit_indices, "SABIC")
Computes the log-likelihood of observed categorical data under a Latent Class Analysis (LCA) model given class probabilities and conditional response probabilities. The calculation assumes local independence of responses conditional on latent class membership.
get.Log.Lik.LCA(response, par, P.Z)get.Log.Lik.LCA(response, par, P.Z)
response |
A numeric matrix of dimension
|
par |
A 3-dimensional array of dimension
|
P.Z |
A numeric vector of length
|
The log-likelihood calculation follows these steps:
Response Standardization:
Original responses are converted to 0-based integers
using adjust.response.
For example, original values {1,2,5} become {0,1,2}
(ordered and relabeled sequentially).
Class-Conditional Likelihood Contribution:
For each participant and class , local independence gives
where is the standardized response value, and probabilities
are taken from par[l, i, x_{ni}+1].
Participant-Level Marginal Likelihood Contribution:
For each participant , combine class-specific likelihoods weighted
by class probabilities:
Total Observed-Data Log-Likelihood: Sum the log marginal contributions across participants:
A single numeric value equal to the total observed-data
log-likelihood defined below.
Computes the log-likelihood of observed continuous data under a Latent Profile Analysis (LPA) model with multivariate normal distributions within each latent profile.
get.Log.Lik.LPA(response, means, covs, P.Z)get.Log.Lik.LPA(response, means, covs, P.Z)
response |
A numeric matrix of dimension |
means |
A matrix of dimension |
covs |
An array of dimension |
P.Z |
A numeric vector of length
|
The log-likelihood calculation follows these steps:
Class-Conditional Likelihood Contribution:
For participant in profile , the multivariate normal log
density is
Participant-Level Marginal Likelihood Contribution:
The marginal density for participant is
Total Observed-Data Log-Likelihood:
A single numeric value equal to the total observed-data
log-likelihood defined below.
Computes the observed-data log-likelihood for a Latent Transition Analysis (LTA) model
using the three-step approach with measurement error correction. The likelihood integrates over
all possible latent class paths while incorporating classification uncertainty via
Classification Error Probability (CEP) matrices. This function is designed to work with
parameters estimated from the LTA function.
get.Log.Lik.LTA( params, CEP, P.Z.Xns, Zs, covariates, ref.class, covariates.time.cross = FALSE )get.Log.Lik.LTA( params, CEP, P.Z.Xns, Zs, covariates, ref.class, covariates.time.cross = FALSE )
params |
A named
|
CEP |
A
where |
P.Z.Xns |
A
the posterior probability of participant |
Zs |
A |
covariates |
A |
ref.class |
Integer between 1 and |
covariates.time.cross |
Logical. If |
The log-likelihood calculation follows these steps:
Initial class probabilities (time 1):
For participant , compute using multinomial logit with covariates
:
where for reference class
represented in params.
Transition Probabilities (times ):
For transition from class at time to class at time :
where for every origin
class and reference destination .
Scaled forward recursion: Initialize
and recursively compute
This forward recursion marginalizes exactly over all latent paths without
explicitly constructing the paths.
Analytic score: Optimization uses the corresponding scaled backward recursion to obtain state and transition posterior probabilities for the exact multinomial-logit score.
A single numeric value representing the total observed-data log-likelihood:
where is a latent class path and
Zs[[t]][n] stores the modal assignment .
When no covariates are included:
Initial probabilities reduce to (multinomial probabilities)
Transition probabilities reduce to
(time-specific Markov transition probabilities)
LTA for three-step LTA estimation,
get.CEP for CEP matrix computation
Computes the total number of free parameters in an LCA model based on the number of categories per observed variable and the number of latent classes. This follows standard LCA parameterization with local independence assumption.
get.npar.LCA(poly.value, L)get.npar.LCA(poly.value, L)
poly.value |
A numeric vector of length |
L |
Integer specifying the number of latent classes. |
Parameter count derivation:
Conditional response probabilities: parameters
Independent class proportions: parameters (since )
For each observed variable with categories:
Each latent class requires conditional probabilities
With constraints
for each class
The class-specific sum-to-one constraints leave free parameters per variable
Integer representing the total number of free parameters in the model:
# Example 1: 3 binary variables (K_i=2), 2 latent classes poly.value <- c(2, 2, 2) # Three binary variables L <- 2 npar <- L * sum(poly.value - 1) + (L - 1) # = 2 * 3 + 1 = 7 get.npar.LCA(poly.value, L) # Returns 7 # Example 2: Mixed variable types (binary, ternary, quaternary) poly.value <- c(2, 3, 4) # Variables with 2, 3, and 4 categories L <- 3 npar <- L * sum(poly.value - 1) + (L - 1) # = 3 * (1+2+3) + 2 = 20 get.npar.LCA(poly.value, L) # Returns 20 # Example 3: Single polytomous variable with 5 categories, 4 latent classes poly.value <- 5 L <- 4 npar <- L * sum(poly.value - 1) + (L - 1) # = 4 * 4 + 3 = 19 get.npar.LCA(poly.value, L) # Returns 19# Example 1: 3 binary variables (K_i=2), 2 latent classes poly.value <- c(2, 2, 2) # Three binary variables L <- 2 npar <- L * sum(poly.value - 1) + (L - 1) # = 2 * 3 + 1 = 7 get.npar.LCA(poly.value, L) # Returns 7 # Example 2: Mixed variable types (binary, ternary, quaternary) poly.value <- c(2, 3, 4) # Variables with 2, 3, and 4 categories L <- 3 npar <- L * sum(poly.value - 1) + (L - 1) # = 3 * (1+2+3) + 2 = 20 get.npar.LCA(poly.value, L) # Returns 20 # Example 3: Single polytomous variable with 5 categories, 4 latent classes poly.value <- 5 L <- 4 npar <- L * sum(poly.value - 1) + (L - 1) # = 4 * 4 + 3 = 19 get.npar.LCA(poly.value, L) # Returns 19
Computes the total number of free parameters in an LPA model based on the number of observed
variables (), number of latent profiles (), and covariance structure constraints.
get.npar.LPA(I, L, constraint = "VV")get.npar.LPA(I, L, constraint = "VV")
I |
Integer specifying the number of continuous observed variables. |
L |
Integer specifying the number of latent profiles. |
constraint |
Character string specifying covariance structure constraints. Supported options:
Default: |
Parameter count breakdown:
Fixed components (always present):
Profile-specific means: parameters
Independent class proportions: parameters (since )
Covariance parameters (varies by constraint):
:"UE": 1 shared variance parameter
"UV": profile-specific variance parameters
:"E0": shared variance parameters (no covariances)
"V0": profile-specific variance parameters (no covariances)
"EE": parameters for one shared full covariance matrix
"VE": diagonal variances (free per profile) + off-diagonal covariances (shared across profiles)
"EV": diagonal variances (shared across profiles) + off-diagonal covariances (free per profile)
"VV": parameters for distinct full covariance matrices
Integer representing the total number of free parameters in the model:
Important considerations:
For , only "UE" and "UV" are meaningful; "EE", "E0", "VV", "V0", etc., are treated as "UE" or "UV" respectively.
Covariance parameters count only free elements in symmetric matrices (diagonal + upper triangle).
If an user-defined constraint is provided, the function defaults to "VV"
behavior but subtracts .
"VE" and "EV" constraints require to be meaningful (otherwise no covariances exist).
# Univariate examples (I=1) get.npar.LPA(I = 1, L = 2, constraint = "UE") get.npar.LPA(I = 1, L = 3, constraint = "UV") # Multivariate examples (I=3) get.npar.LPA(I = 3, L = 2, constraint = "E0") get.npar.LPA(I = 3, L = 2, constraint = "V0") get.npar.LPA(I = 3, L = 2, constraint = "EE") get.npar.LPA(I = 3, L = 2, constraint = "VV") get.npar.LPA(I = 3, L = 2, constraint = "VE") get.npar.LPA(I = 3, L = 2, constraint = "EV") # User defined example get.npar.LPA(I = 3, L = 2, constraint = list(c(1, 2), c(3, 3)))# Univariate examples (I=1) get.npar.LPA(I = 1, L = 2, constraint = "UE") get.npar.LPA(I = 1, L = 3, constraint = "UV") # Multivariate examples (I=3) get.npar.LPA(I = 3, L = 2, constraint = "E0") get.npar.LPA(I = 3, L = 2, constraint = "V0") get.npar.LPA(I = 3, L = 2, constraint = "EE") get.npar.LPA(I = 3, L = 2, constraint = "VV") get.npar.LPA(I = 3, L = 2, constraint = "VE") get.npar.LPA(I = 3, L = 2, constraint = "EV") # User defined example get.npar.LPA(I = 3, L = 2, constraint = list(c(1, 2), c(3, 3)))
Computes the total number of free parameters in a Latent Transition Analysis (LTA) model estimated via the three-step approach. The count depends on the number of latent classes, the number of time points, the number of covariates at each time point, and whether transition coefficients are constrained to be equal across time.
get.npar.LTA(covariates.ncol, L, covariates.time.cross = FALSE)get.npar.LTA(covariates.ncol, L, covariates.time.cross = FALSE)
covariates.ncol |
An integer vector of length |
L |
Integer scalar. Number of latent classes ( |
covariates.time.cross |
Logical. If |
Parameterization:
Multinomial logit model with classes (one class is the reference).
Number of free parameters: .
):For each transition, a multinomial logit model conditioned on previous class.
For each origin class and destination class (),
there is a coefficient vector of length .
Total per transition: parameters.
The constraint covariates.time.cross determines whether these parameters
are shared across transitions.
Integer representing the total number of free parameters:
where:
time-invariant effects corresponds to covariates.time.cross = TRUE
time-varying effects corresponds to covariates.time.cross = FALSE
Critical assumptions:
One latent class is selected as the reference category for all logits; this choice does not change the count.
When covariates.time.cross = TRUE, it is assumed that all time points after the first
have identical covariate structures
(). If violated, the function
requires the transition design matrices at times 2 through to have the same number of columns.
For , no transition parameters are estimated (pure latent class/profile analysis).
# Example 1: 2 time points, 2 classes, time-invariant transition coefficients # Time1: 2 covariates (intercept + 1 predictor) # Time2: 3 covariates; this determines the shared transition block covariates.ncol <- c(2, 3) L <- 2 get.npar.LTA(covariates.ncol, L, covariates.time.cross = TRUE) # Example 2: Same as above but time-varying coefficients get.npar.LTA(covariates.ncol, L, covariates.time.cross = FALSE) # Example 3: 3 time points, 3 classes, time-invariant coefficients covariates.ncol <- c(2, 2, 2) # All time points have identical covariates L <- 3 get.npar.LTA(covariates.ncol, L, covariates.time.cross = TRUE) # Example 4: 3 time points, 3 classes, time-varying coefficients covariates.ncol <- c(2, 3, 4) L <- 3 get.npar.LTA(covariates.ncol, L, covariates.time.cross = FALSE) # Example 5: Single time point (equivalent to LCA) covariates.ncol <- c(3) L <- 4 get.npar.LTA(covariates.ncol, L)# Example 1: 2 time points, 2 classes, time-invariant transition coefficients # Time1: 2 covariates (intercept + 1 predictor) # Time2: 3 covariates; this determines the shared transition block covariates.ncol <- c(2, 3) L <- 2 get.npar.LTA(covariates.ncol, L, covariates.time.cross = TRUE) # Example 2: Same as above but time-varying coefficients get.npar.LTA(covariates.ncol, L, covariates.time.cross = FALSE) # Example 3: 3 time points, 3 classes, time-invariant coefficients covariates.ncol <- c(2, 2, 2) # All time points have identical covariates L <- 3 get.npar.LTA(covariates.ncol, L, covariates.time.cross = TRUE) # Example 4: 3 time points, 3 classes, time-varying coefficients covariates.ncol <- c(2, 3, 4) L <- 3 get.npar.LTA(covariates.ncol, L, covariates.time.cross = FALSE) # Example 5: Single time point (equivalent to LCA) covariates.ncol <- c(3) L <- 4 get.npar.LTA(covariates.ncol, L)
Computes posterior probabilities of latent class membership for each observation
using fixed conditional response probabilities (par) and fixed class prior
probabilities (P.Z).
get.P.Z.Xn.LCA(response, par, P.Z, category.levels)get.P.Z.Xn.LCA(response, par, P.Z, category.levels)
response |
Numeric matrix ( |
par |
3D array (
|
P.Z |
Vector of length |
category.levels |
List of length |
Unlike an EM algorithm, this function does NOT iteratively update class prevalences. It performs a single calculation step based on Bayes' theorem:
where the class probabilities and conditional response probabilities are
fixed by the P.Z and par arguments.
Numeric matrix () of posterior probabilities.
Rows sum to 1. Columns are named "Class 1", "Class 2", and so on.
library(LCPA) set.seed(123) # Simulate data data.obj <- sim.LCA(N = 200, I = 3, L = 2, IQ = 0.85) # Fit a model to get parameters fit <- LCA(data.obj$response, L = 2, method = "EM", nrep = 5) # Calculate posteriors using fixed parameters from the fitted model P.Z.Xn <- get.P.Z.Xn.LCA( response = data.obj$response, par = fit$params$par, P.Z = fit$params$P.Z, category.levels = fit$params$category.levels ) head(P.Z.Xn)library(LCPA) set.seed(123) # Simulate data data.obj <- sim.LCA(N = 200, I = 3, L = 2, IQ = 0.85) # Fit a model to get parameters fit <- LCA(data.obj$response, L = 2, method = "EM", nrep = 5) # Calculate posteriors using fixed parameters from the fitted model P.Z.Xn <- get.P.Z.Xn.LCA( response = data.obj$response, par = fit$params$par, P.Z = fit$params$P.Z, category.levels = fit$params$category.levels ) head(P.Z.Xn)
Computes posterior probabilities of latent profile membership for each observation using fixed profile parameters (means, covariances) and fixed prior probabilities.
get.P.Z.Xn.LPA(response, means, covs, P.Z)get.P.Z.Xn.LPA(response, means, covs, P.Z)
response |
Numeric matrix ( |
means |
Numeric matrix (
Row |
covs |
3D array (
Each slice must be symmetric and positive definite. |
P.Z |
Vector of length |
Unlike an EM algorithm, this function does NOT iteratively update profile prevalences. It performs a single E-step calculation:
where the profile probabilities, means, and covariance matrices are fixed by
the P.Z, means, and covs arguments.
Numeric matrix () of posterior probabilities.
Rows sum to 1. Columns are named "Profile 1", "Profile 2", and so on.
library(LCPA) set.seed(123) data.obj <- sim.LPA(N = 300, I = 2, L = 2, constraint = "VV") fit <- LPA(data.obj$response, L = 2, method = "EM", nrep = 5) # Calculate posteriors using fixed parameters from a fitted model P.Z.Xn <- get.P.Z.Xn.LPA( response = data.obj$response, means = fit$params$means, covs = fit$params$covs, P.Z = fit$params$P.Z ) head(P.Z.Xn)library(LCPA) set.seed(123) data.obj <- sim.LPA(N = 300, I = 2, L = 2, constraint = "VV") fit <- LPA(data.obj$response, L = 2, method = "EM", nrep = 5) # Calculate posteriors using fixed parameters from a fitted model P.Z.Xn <- get.P.Z.Xn.LPA( response = data.obj$response, means = fit$params$means, covs = fit$params$covs, P.Z = fit$params$P.Z ) head(P.Z.Xn)
Computes standard errors (SEs) for parameters estimated by LCA() or LPA().
Three methods are available:
"Bootstrap": Nonparametric bootstrap with label-switching correction.
"Obs": Numerical observed information from adaptive central differences
of the analytic observed-data score.
"Louis": Analytic observed information based on Louis' identity.
get.SE(object, method = "Bootstrap", nrep.bootstrap = 100, vis = TRUE)get.SE(object, method = "Bootstrap", nrep.bootstrap = 100, vis = TRUE)
object |
An object of class |
method |
Character specifying |
nrep.bootstrap |
Integer. Number of successful bootstrap replicates when
|
vis |
Logical. If |
Class proportions and LCA conditional probabilities are represented by additive log-ratios with the final category as reference. Standard errors for every probability, including the reference probability, are obtained using the full softmax Jacobian.
For LPA, the observed-information parameterization includes only covariance parameters that
are free under the fitted constraint; variances are represented on the log scale.
This avoids the nonidentified Hessian directions produced by differentiating all class-specific
covariance elements under equality or zero constraints.
The analytic "Louis" method evaluates the observed information from the conditional
complete-data score and Hessian. For both LCA and LPA it accounts for posterior class uncertainty
and therefore is not the naive complete-data information matrix. The numerical "Obs"
method provides an independent check by differentiating the analytic observed-data score.
A list of class "SE" containing:
seSEs with the same parameter structure and dimnames as the fitted model.
vcovCovariance matrix on the unconstrained estimation scale for
"Obs" and "Louis"; NULL for bootstrap.
hessianObserved information matrix on the unconstrained estimation scale for
"Obs" and "Louis"; NULL for bootstrap.
diagnosticsMethod-specific diagnostics.
callFunction call that generated the object.
argumentsInput arguments.
Louis, T. A. (1982). Finding the observed information matrix when using the EM algorithm. Journal of the Royal Statistical Society: Series B (Methodological), 44(2), 226–233. doi:10.1111/j.2517-6161.1982.tb01203.x
McLachlan, G. J., & Peel, D. (2000). Finite mixture models. John Wiley & Sons.
set.seed(123) lca.data <- sim.LCA(N = 300, I = 4, L = 2, poly.value = 3) lca.fit <- LCA(lca.data$response, L = 2, nrep = 3, vis = FALSE) se.louis <- get.SE(lca.fit, method = "Louis") lpa.data <- sim.LPA(N = 300, I = 3, L = 2, constraint = "V0") lpa.fit <- LPA(lpa.data$response, L = 2, constraint = "V0", nrep = 3, vis = FALSE) se.louis.lpa <- get.SE(lpa.fit, method = "Louis")set.seed(123) lca.data <- sim.LCA(N = 300, I = 4, L = 2, poly.value = 3) lca.fit <- LCA(lca.data$response, L = 2, nrep = 3, vis = FALSE) se.louis <- get.SE(lca.fit, method = "Louis") lpa.data <- sim.LPA(N = 300, I = 3, L = 2, constraint = "V0") lpa.fit <- LPA(lpa.data$response, L = 2, constraint = "V0", nrep = 3, vis = FALSE) se.louis.lpa <- get.SE(lpa.fit, method = "Louis")
Checks whether essential Python packages required to run neural latent variable models
(e.g., LCAnet, LPAnet) are installed in the current Python environment. If any are missing,
the user is interactively prompted to install them via reticulate::py_install().
The targeted packages are:
numpy — Fundamental package for numerical computing in Python.
torch — PyTorch deep learning framework (supports CPU/GPU computation).
matplotlib — 2D plotting and visualization library.
scikit-learn — Machine learning utilities.
scipy — Scientific computing and advanced linear algebra routines.
six — Python 3 compatibility library.
For torch, users can choose between CPU-only or GPU-enabled versions (with CUDA support).
Available CUDA versions are filtered by OS compatibility.
This function is especially useful when deploying models that bridge R and Python via reticulate, ensuring all backend dependencies are met before model execution.
install_python_dependencies()install_python_dependencies()
The function performs the following steps for each dependency:
Uses reticulate::py_module_available() to test if the module is importable.
If not available, prints a message describing the package's purpose.
Prompts the user interactively (via readline) whether to proceed with installation.
For torch, offers CPU/GPU choice and CUDA version selection if GPU is chosen.
Installs the package using reticulate::py_install() with appropriate index URL if needed.
Returns a logical list indicating initial installation status of each package.
Note: This function requires reticulate to be loaded and a valid Python environment configured. It does NOT automatically install reticulate or configure Python — that must be done separately.
A named list of logical values indicating whether each package was already installed before running this function:
numpy_installed |
Logical. Was |
torch_installed |
Logical. Was |
matplotlib_installed |
Logical. Was |
sklearn_installed |
Logical. Was |
scipy_installed |
Logical. Was |
six_installed |
Logical. Was |
library(reticulate) # Ensure reticulate is loaded and Python is configured # need python # Run dependency installer deps <- install_python_dependencies() # Check which were missing print(deps)library(reticulate) # Ensure reticulate is loaded and Python is configured # need python # Run dependency installer deps <- install_python_dependencies() # Check which were missing print(deps)
Performs hard clustering of observations using K-means algorithm to generate
initial parameter estimates for Latent Class Analysis (LCA) models. This
provides a data-driven initialization strategy that often outperforms random
starts when the number of observed categorical variables is large
(i.e., ).
Kmeans.LCA(response, L, starts = 1)Kmeans.LCA(response, L, starts = 1)
response |
A numeric matrix of dimension |
L |
Integer specifying the number of latent classes. Must be |
starts |
Integer specifying the number of random starts for K-means algorithm (default: 1). The solution with the lowest within-cluster sum of squares is retained. |
The function executes the following steps:
Data preprocessing: Automatically adjusts non-sequential category values to sequential integers (e.g., categories {1,3,5} become {1,2,3}) using internal adjustment routines.
K-means clustering: Scales variables to mean=0 and SD=1 before clustering. Uses Lloyd's algorithm with Euclidean distance.
Parameter estimation:
For each cluster , computes empirical response probabilities
for all indicators and response
categories .
Posterior probabilities: Constructs hard-classification matrix where
for the assigned cluster and 0 otherwise.
A list containing:
paramsList of initialized parameters:
parAn array of initial conditional probabilities,
where is the maximum number of categories across indicators.
Dimension order: latent classes (1:L), indicators (1:I), response categories (1:K_max).
P.ZNumeric vector of length containing initial class prior probabilities
derived from cluster proportions.
P.Z.XnAn matrix of posterior class probabilities. Contains
hard assignments (0/1 values) based on K-means cluster memberships.
This function is primarily designed as an initialization method for
LCA() and not for final model estimation.
# Simulate response data set.seed(123) response <- matrix(sample(1:4, 200, replace = TRUE), ncol = 5) # Generate K-means initialization for 3-class LCA init_params <- Kmeans.LCA(response, L = 3, starts = 5) # Inspect initial class probabilities print(init_params$params$P.Z)# Simulate response data set.seed(123) response <- matrix(sample(1:4, 200, replace = TRUE), ncol = 5) # Generate K-means initialization for 3-class LCA init_params <- Kmeans.LCA(response, L = 3, starts = 5) # Inspect initial class probabilities print(init_params$params$P.Z)
Performs hard clustering with K-means and converts the resulting partition into initial means, covariance matrices, profile proportions, and hard posterior assignments for Latent Profile Analysis (LPA).
Kmeans.LPA(response, L, constraint = "VV", starts = 1)Kmeans.LPA(response, L, constraint = "VV", starts = 1)
response |
A numeric matrix or data frame of dimension |
L |
Positive integer specifying the number of latent profiles. It must be smaller than the number of observations. |
constraint |
Covariance structure used to construct the initial profile covariance
matrices. Named options are |
starts |
Positive integer specifying the number of internal K-means random starts (default: 1). The solution with the lowest within-cluster sum of squares is retained. |
The function performs four operations:
Runs K-means on the supplied standardized indicators using Lloyd's algorithm.
Uses the cluster centers as initial profile means and cluster proportions as initial profile probabilities.
Computes within-cluster maximum-likelihood covariance matrices, then applies the requested named or custom equality constraints.
A list containing:
paramsA list with:
meansAn matrix of initial profile means.
covsAn array of constrained,
positive-definite initial covariance matrices.
P.ZA numeric vector of length containing initial profile
proportions.
P.Z.XnAn hard-assignment matrix whose rows contain
one 1 and zeros.
This function constructs initialization values only; it does not fit a final LPA
model. When called directly with starts > 1, K-means selects one solution from
those internal starts. The package-level LPA(..., par.ini = "kmeans") workflow
instead calls Kmeans.LPA(..., starts = 1) separately for every outer start.
set.seed(123) response <- scale(matrix(rnorm(300), ncol = 3)) initial <- Kmeans.LPA(response, L = 2, constraint = "V0") initial$params$means initial$params$P.Zset.seed(123) response <- scale(matrix(rnorm(300), ncol = 3)) initial <- Kmeans.LPA(response, L = 2, constraint = "V0") initial$params$means initial$params$P.Z
This function estimates parameters of a Latent Class Analysis (LCA; Hagenaars & McCutcheon, 2002) model using
the Expectation-Maximization (EM) algorithm, stochastic EM (SEM) through flexmix
or RMixtComp, native EM/CEM/SEM strategies through Rmixmod,
Neural Network Estimation (NNE), or Mplus.
It supports flexible initialization strategies and provides comprehensive model diagnostics.
LCA( response, L = 2, category.levels = NULL, method = "EM", par.ini = "random", is.sort = TRUE, starts = 100, maxiter.warmup = 20, nrep = 20, vis = TRUE, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.flexmix = NULL, control.Rmixmod = NULL, control.RMixtComp = NULL )LCA( response, L = 2, category.levels = NULL, method = "EM", par.ini = "random", is.sort = TRUE, starts = 100, maxiter.warmup = 20, nrep = 20, vis = TRUE, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.flexmix = NULL, control.Rmixmod = NULL, control.RMixtComp = NULL )
response |
A numeric matrix of dimension |
L |
Integer specifying the number of latent classes (default: 2). |
category.levels |
Optional list fixing the ordered response categories for each
indicator. If |
method |
Character string specifying estimation algorithm:
|
par.ini |
Specification for parameter initialization. Options include:
For methods that expose this initialization interface, |
is.sort |
A logical value. If |
starts |
Positive integer. Number of warm-up analyses to run (default: 100).
Each analysis is initialized by the selected method and trained for at most
|
maxiter.warmup |
Positive integer. Maximum number of training iterations for
each of the |
nrep |
Positive integer not exceeding |
vis |
Logical. If |
control.EM |
List of control parameters for EM algorithm:
|
control.Mplus |
List of control parameters for Mplus estimation:
|
control.NNE |
List of control parameters for NNE algorithm:
|
control.flexmix |
List of control parameters for flexmix SEM estimation:
|
control.Rmixmod |
List of control parameters for Rmixmod estimation:
|
control.RMixtComp |
List of control parameters for RMixtComp SEM estimation:
|
An object of class "LCA" containing:
paramsList with estimated parameters:
par array of conditional response probabilities per latent class.
P.ZVector of length with latent class prior probabilities.
category.levelsFixed ordered response categories for each indicator.
nparNumber of free parameters in the model. see get.npar.LCA
Log.LikLog-likelihood of the final model. see get.Log.Lik.LCA
AICAkaike Information Criterion value.
BICBayesian Information Criterion value.
best_BICBest BIC value across nrep runs when applicable;
for native Rmixmod and RMixtComp paths, the selected native fit's BIC.
P.Z.Xn matrix of posterior class probabilities for each observation.
P.ZVector of length containing the prior probabilities/structural parameters/proportions for each latent class.
ZVector of length with MAP-classified latent class memberships.
probabilityList of item-specific conditional probability matrices with classes in rows and observed category labels in columns.
Log.Lik.historyVector tracking log-likelihood at each EM iteration.
Log.Lik.nrepVector of log-likelihoods from each replication run. For native Rmixmod and RMixtComp paths, this is the selected native fit's scalar log-likelihood.
modelBackend model object for method="NNE",
method="Mplus", method="flexmix",
method="Rmixmod", or method="RMixtComp", when supplied
by that backend.
callMatched function call.
argumentsA list containing all effective input arguments.
Except for method = "NNE", which intentionally uses its fixed backend seed,
every stochastic estimator is driven from R's current random-number generator.
The user only needs to call set.seed() immediately before LCA() to
reproduce EM, K-means, flexmix, Rmixmod, RMixtComp, and Mplus estimation. LCPA
automatically passes an R-derived seed to backends with independent random streams;
no backend-specific seed setting is required.
Write the response matrix as
, where
indexes participants and
indexes observed indicators. The response vector for
participant is
. The latent class variable
is , and
indexes a particular latent class. Response categories
for indicator are indexed by .
Under local independence, the observed-data log-likelihood is
When method = "EM", parameters are estimated via the Expectation-Maximization algorithm, which iterates between:
E-step: Compute posterior class probabilities given current parameters:
where is the standardized (0-based) response of participant
to indicator (see adjust.response).
M-step: Update parameters by maximizing expected complete-data log-likelihood:
Class probabilities:
Conditional probabilities:
Convergence: Stops when or maximum iterations reached.
When method = "NNE", parameters are estimated using a hybrid neural network architecture
that combines feedforward layers with transformer-based attention mechanisms. This approach jointly
optimizes profile parameters and posterior probabilities through stochastic optimization enhanced
with simulated annealing. See install_python_dependencies. Key components include:
Architecture:
Observed categorical responses are converted to 0-based integer indices per indicator (not one-hot encoded).
For example, original responses become .
A fully-connected neural network with layer sizes specified by hidden.layers and activation
function activation.function processes the integer-indexed responses. This network outputs
unnormalized logits for posterior class membership ( matrix).
A transformer encoder with nhead attention heads that learns latent class prior probabilities
directly from observed responses.
Global conditional probability parameters () are stored as learnable
parameters par (an tensor). A masked softmax is applied
along categories to enforce:
Probabilities sum to 1 within each indicator-class pair
Non-existent categories (beyond indicator's actual max response) are masked to zero probability
When method = "Mplus", estimation is delegated to external Mplus software.
The function automates the entire workflow:
Workflow:
Creates a timestamped "Mplus_LCA_YYYY-MM-DD_HH-MM-SS"
directory under control.Mplus$files.path, or under the current working directory when that path is empty, to store:
Mplus input syntax (.inp)
Data file in Mplus format (.dat)
Posterior probabilities output (.dat)
Files are automatically deleted after estimation unless control.Mplus$files.clean = FALSE.
Constructs Mplus syntax with:
CLASSES = c1(L) specification for latent classes
CATEGORICAL declaration for all indicator variables
ANALYSIS block with optimization controls:
TYPE = mixtureStandard mixture modeling setup
STARTS = starts nrepRandom starts and final stage optimizations
STSEEDRandom-start seed drawn from R's current random-number generator
STITERATIONS = maxiter.warmupmax itertions during starts.
MITERATIONS = maxiterMaximum EM iterations
CONVERGENCE = tolLog-likelihood convergence tolerance
MODEL block with %OVERALL%
Calls Mplus via MplusAutomation::mplusModeler(), which:
Converts R data to Mplus-compatible format with automatic recoding
Invokes Mplus executable (requires valid license and system PATH configuration)
With method = "flexmix", each SEM iteration performs one stochastic
classification draw from the current posterior probabilities before the M-step. LCPA runs
exactly starts short trajectories of maxiter.warmup iterations, promotes the best
nrep trajectories by observed log-likelihood, continues each for
control.flexmix$maxiter SEM iterations, and retains the largest-likelihood final state
across the promoted runs.
This selection compares the final state returned by each flexmix SEM run; flexmix's
classify = "SEM" does not retain the largest-likelihood state visited within a run.
Binary indicators use flexmix::FLXMCmvbinary(). When any indicator is polytomous,
LCPA supplies one joint categorical flexmix model driver whose M-step uses the exact
weighted-frequency solution instead of repeatedly optimizing intercept-only multinomial
regressions. The stochastic classification and iteration loop remain those of flexmix.
Setting control.flexmix$tol = 0 prevents likelihood-based early termination.
Only SEM is exposed; par.ini is not used by this backend.
When method = "Rmixmod", the model is estimated in
Rmixmod::mixmodCluster() using the unconstrained multinomial model
"Binary_pk_Ekjh". Indicators are converted to factors after the package-standard zero-based recoding.
With path="LCPA", the function generates exactly starts random balanced partitions and
runs exactly maxiter.warmup consecutive stochastic E-S-M iterations for every warm-up start.
The best nrep warm-up classifications are then passed directly to
independent Rmixmod SEM runs, and the finite solution with the largest final log-likelihood is
retained. Rmixmod's internal "smallEM" and "SEMMax" initialization searches are not
used; in particular, "SEMMax" is an initialization search rather than one consecutive SEM
trajectory for each user-level start. par.ini is not used for this method.
SEM stops after control.Rmixmod$maxiter iterations; an epsilon convergence
criterion is not defined for SEM in Rmixmod. Unlike flexmix's classify = "SEM", Rmixmod
retains the largest-likelihood parameter state visited within each SEM run, so equal iteration
counts do not imply identical final-state selection.
With path="Rmixmod", LCPA translates all non-NULL package-standard strategy controls
to Rmixmod::mixmodStrategy() and runs one native mixmodCluster() call. Its
algorithm may contain "EM", "CEM", "SEM", or an ordered combination
of these algorithms. LCPA does
not add its outer starts, maxiter.warmup, or nrep;
control.Rmixmod$nrep controls complete
strategy repetitions. The published strategy of Mulder et al.
(2015) uses 200 SEM iterations followed by EM with a relative likelihood-change tolerance of
1e-5. For reproducibility across Rmixmod versions, the example and simulation scripts
explicitly pin the contemporaneous documented defaults: smallEM, 50 initialization tries,
5 initialization iterations, tol.init=0.001, and a 200-iteration EM limit.
When method = "RMixtComp", LCPA calls RMixtComp::mixtCompLearn() in
classic, non-hierarchical learning mode with a multinomial model for every indicator.
This integration exposes only RMixtComp's stochastic EM (SEM) algorithm; it does not
introduce any other RMixtComp estimation algorithm. LCPA does not add its own
starts, maxiter.warmup, or nrep stages to this backend. Each native run
performs RMixtComp initialization, SEM burn-in, recorded SEM iterations, and then the
fixed-parameter Gibbs burn-in and recorded Gibbs iterations. One stochastic S-step is
performed per SEM iteration, between the E-step and M-step. Native repetition and
parallelization are controlled only by control.RMixtComp$nrep and
control.RMixtComp$ncores. RMixtComp's native random stream is independent of R;
LCPA therefore passes it one integer drawn from R's current random stream. The user only
needs an external set.seed() for reproducibility. The default algorithm controls reproduce
RMixtCompUtilities::createAlgo() defaults (version 4.1.4 or later).
par.ini is not used by this backend.
Biernacki, C. (2015). MixtComp software: Model-based clustering/imputation with mixed data, missing data and uncertain data. MISSDATA 2015. https://inria.hal.science/hal-01253393
Hagenaars, J. A., & McCutcheon, A. L. (Eds.). (2002). Applied latent class analysis. Cambridge University Press.
Leisch, F. (2004). FlexMix: A general framework for finite mixture models and latent class regression in R. Journal of Statistical Software, 11(8), 1–18. doi:10.18637/jss.v011.i08
McLachlan, G. J., & Peel, D. (2000). Finite mixture models. John Wiley & Sons.
Mulder, V. L., Lacoste, M., Martin, M. P., Richer-de-Forges, A., & Arrouays, D. (2015). Understanding large-extent controls of soil organic carbon storage in relation to soil depth and soil-landscape systems. Global Biogeochemical Cycles, 29(8), 1210–1229. doi:10.1002/2015GB005178
library(LCPA) # Example with simulated data set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 2, IQ=0.9) response <- data.obj$response # Fit 2-class model with EM algorithm fit.em <- LCA(response, L = 2, method = "EM", nrep = 10) # Fit 2-profile model using Mplus # need Mplus # An empty 'files.path' creates a timestamped subfolder # (e.g., "Mplus_LCA_YYYY-MM-DD_HH-MM-SS") under the current working directory # to store all temporary Mplus files (.inp, .dat, .out, etc.). ## Not run: fit.mplus <- LCA(response, L = 2, method = "Mplus", nrep = 3) ## End(Not run) # Fit 2-class model with neural network estimation # need Python ## Not run: fit.nne <- LCA(response, L = 2, method = "NNE", nrep = 3) ## End(Not run) # Fit 2-class model with flexmix SEM and LCPA warm-up/replication # need flexmix ## Not run: fit.flexmix <- LCA(response, L = 2, method = "flexmix", nrep = 2, starts = 5, maxiter.warmup = 5, control.flexmix = list(maxiter = 50)) ## End(Not run) # Fit 2-class model with the published Rmixmod SEM-to-EM strategy # need Rmixmod ## Not run: fit.rmixmod <- LCA(response, L = 2, method = "Rmixmod", control.Rmixmod = list(path = "Rmixmod", algorithm = c("SEM", "EM"), nrep = 1, method.init = "smallEM", starts = 50, maxiter.init = 5, tol.init = 0.001, maxiter = c(200, 200), tol = c(NA, 1e-5))) ## End(Not run)library(LCPA) # Example with simulated data set.seed(123) data.obj <- sim.LCA(N = 500, I = 4, L = 2, IQ=0.9) response <- data.obj$response # Fit 2-class model with EM algorithm fit.em <- LCA(response, L = 2, method = "EM", nrep = 10) # Fit 2-profile model using Mplus # need Mplus # An empty 'files.path' creates a timestamped subfolder # (e.g., "Mplus_LCA_YYYY-MM-DD_HH-MM-SS") under the current working directory # to store all temporary Mplus files (.inp, .dat, .out, etc.). ## Not run: fit.mplus <- LCA(response, L = 2, method = "Mplus", nrep = 3) ## End(Not run) # Fit 2-class model with neural network estimation # need Python ## Not run: fit.nne <- LCA(response, L = 2, method = "NNE", nrep = 3) ## End(Not run) # Fit 2-class model with flexmix SEM and LCPA warm-up/replication # need flexmix ## Not run: fit.flexmix <- LCA(response, L = 2, method = "flexmix", nrep = 2, starts = 5, maxiter.warmup = 5, control.flexmix = list(maxiter = 50)) ## End(Not run) # Fit 2-class model with the published Rmixmod SEM-to-EM strategy # need Rmixmod ## Not run: fit.rmixmod <- LCA(response, L = 2, method = "Rmixmod", control.Rmixmod = list(path = "Rmixmod", algorithm = c("SEM", "EM"), nrep = 1, method.init = "smallEM", starts = 50, maxiter.init = 5, tol.init = 0.001, maxiter = c(200, 200), tol = c(NA, 1e-5))) ## End(Not run)
Fits one of two independent auxiliary-variable paths. type.analysis = "XZ"
estimates the effect of observed covariates on latent class/profile membership.
type.analysis = "ZY" estimates class/profile-specific distributions of
external observed dependent variables; these variables are never treated as
indicators.
LCPA( response, L = 2, type.analysis = c("XZ", "ZY"), type.model = c("LCA", "LPA"), covariates = NULL, ref.class = L, dependent.variables = NULL, family = "gaussian", method.model = "EM", control.model = NULL, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.flexmix = NULL, control.Rmixmod = NULL, control.RMixtComp = NULL, method.3step = NULL, CEP.error = TRUE, method.regression = "Analytic", maxiter = 5000, tol = 1e-04, lower = -10, upper = 10, method.SE = "Bootstrap", nrep.bootstrap = 100, vis = TRUE )LCPA( response, L = 2, type.analysis = c("XZ", "ZY"), type.model = c("LCA", "LPA"), covariates = NULL, ref.class = L, dependent.variables = NULL, family = "gaussian", method.model = "EM", control.model = NULL, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.flexmix = NULL, control.Rmixmod = NULL, control.RMixtComp = NULL, method.3step = NULL, CEP.error = TRUE, method.regression = "Analytic", maxiter = 5000, tol = 1e-04, lower = -10, upper = 10, method.SE = "Bootstrap", nrep.bootstrap = 100, vis = TRUE )
response |
An |
L |
Integer number of latent classes/profiles in the Step 1 measurement
model (default: 2; must be at least 2). It has the same meaning as |
type.analysis |
Character string selecting the independent Step 3 path:
The default is |
type.model |
Character string selecting the Step 1 measurement model:
|
covariates |
Used only when |
ref.class |
Integer from 1 to |
dependent.variables |
Used only when |
family |
Used only when |
method.model |
Character string selecting the Step 1 parameter estimator.
It has exactly the same meaning and available values as the |
control.model |
Optional named list of common Step 1 measurement-model
settings. If
Element names must be unique; unknown or unnamed elements are rejected. |
control.EM |
Optional Step 1 control list for the EM estimator in
|
control.Mplus |
Optional list passed to the selected |
control.NNE |
Optional list passed to the selected |
control.flexmix |
Optional list passed to the selected |
control.Rmixmod |
Optional list passed to the selected |
control.RMixtComp |
Optional list passed to the selected |
method.3step |
Character string selecting the Step 2–3 correction:
If |
CEP.error |
Logical. If |
method.regression |
Character string controlling Step 3 point estimation
(default: |
maxiter |
Positive integer giving the maximum number of Step 3 |
tol |
Positive finite convergence tolerance for the Step 3 |
lower, upper
|
Finite lower and upper bounds applied to every |
method.SE |
Character string selecting Step 3 uncertainty estimation:
|
nrep.bootstrap |
Integer number of nonparametric bootstrap replications
used only when |
vis |
Logical (default: |
The notation distinguishes the Step 1 indicators from the Step 3 auxiliary
variables. Write the indicator matrix as
, where
indexes participants and
indexes observed indicators. Participant 's
indicator vector is
, and
, with
indexing latent classes/profiles.
The covariate vector is
, where
indexes the observed covariates and the leading
1 is the intercept. The dependent-variable vector is
, where
indexes the external observed dependent
variables. Neither nor is part
of the indicator vector .
"XZ" is the function-interface label for the covariate-to-latent path;
the formulas use for its covariates because
is reserved for the LCA/LPA indicator data.
type.analysis = "XZ" uses but not
; type.analysis = "ZY" uses but not
. These are separate Step 3 analyses rather than a
jointly estimated mediation model. Run both analyses when both the
covariate-to-class and class-to-dependent-variable associations are required.
For a ZY analysis, standardization applies to the continuous dependent variables
in , not to the Step 1 indicator matrix .
Each Gaussian dependent variable must be transformed before model fitting to
have observed sample mean 0 and sample standard deviation 1. Consequently,
its class/profile-specific estimates and standard errors are expressed in
observed-standard-deviation units. Categorical dependent variables retain
their original category values.
An object of class "LCPA". The selected result is available from
analysis$XZ or analysis$ZY; posterior probabilities, modal assignments,
and CEP matrices use the same list structure as LTA().
For type.analysis = "ZY", dependent.variables$t1 contains one fitted
model per observed dependent variable. A Gaussian model reports
class/profile-specific estimate, se, variance, variance.se, their
covariance matrices, omnibus Wald tests for both means and variances,
group weight masses, observations, omitted values, iterations, and
convergence. A categorical model reports class/profile-by-category
estimate and se matrices, their covariance matrix, an omnibus Wald
test of equality of the conditional category distributions, group weight
masses, observations, omitted values, iterations, and convergence.
The cross-sectional three-step analysis proceeds as follows.
Step 1 – Unconditional measurement model. Fit an unconditional
LCA() or LPA() to response. Let
. For LCA, the Step 1 observed-data log-likelihood is
For LPA, it is
These are the likelihoods defined in
get.Log.Lik.LCA() and
get.Log.Lik.LPA(). Bayes' theorem gives
for LCA and
for LPA.
The modal assignment is
. method.model selects the estimator of
this measurement model, and control.model supplies its initialization,
covariance-constraint, sorting, and replication settings. If
control.model$params is supplied, those fixed measurement parameters are
used to calculate .
Step 2 – Classification-error probabilities. The CEP matrix
has rows indexed by latent class/profile and columns indexed by modal
assignment , so
. The modal assignment, posterior-weight
estimator, matrix orientation, and pooling rules are defined in
get.CEP(). With CEP.error = FALSE,
is replaced
by the identity matrix and Step 3 becomes an uncorrected modal-assignment
analysis. The BCH and ML corrections for this classification error follow
Bolck, Croon, and Hagenaars (2004) and Vermunt (2010).
Step 3A – Covariates predicting latent membership (XZ). With reference
class ref.class denoted by , the multinomial-logit model is
with . If covariates = NULL,
and the model
contains class-specific intercepts only. Vermunt's (2010) ML/CEP estimator
maximizes
For each non-reference class/profile , the first derivative of
the observed-data log-likelihood with respect to the coefficient vector
is
This derivative is an vector: its entries correspond to
the intercept and the covariate coefficients in
. At an interior maximum, the ML estimates jointly
satisfy for every ; lower
and upper define the permitted coefficient range.
Vermunt's (2010) BCH-XZ estimator instead solves, for class
,
This is a BCH estimating equation, not the derivative of the ML corrected
likelihood above. Its equations correspond to the intercept and
covariate coefficients in .
Thus ML and BCH estimate the same multinomial-logit parameters but use
different corrections for modal-classification error.
Step 3B – Latent membership predicting dependent variables (ZY). The
bias-adjusted ML three-step formulation follows Bakk, Tekle, and Vermunt
(2013) and Nylund-Gibson, Grimm, and Masyn (2019), with the BCH
secondary-model formulation described by Asparouhov and Muthén (2014b). The model
estimates the conditional distribution of given
separately for . For family = "gaussian",
. For
family = "categorical",
, where indexes the observed
categories of dependent variable . No design matrix is required because the
model contains a separate intercept for every class/profile.
ML/CEP maximizes
The reported
class-shift rate compares the Step 3 modal class with
from Step 1.
BCH Gaussian means solve
and the corresponding Gaussian variances solve
and categorical probabilities solve
With method.regression = "Analytic", ML-XZ, BCH-XZ, and ML-ZY use their
exact scores or gradients within numerical optimization; BCH-ZY evaluates
the closed-form weighted estimates shown above. "Numeric" evaluates the
same likelihoods without supplied gradients or minimizes the squared BCH-ZY
estimating equations numerically.
For ML-XZ and BCH, the covariance has sandwich form
. In ML-XZ, is the observed information and
is formed from individual likelihood scores plus the influence
function of the estimated CEP matrix. In BCH, is the
estimating-equation Jacobian and is the empirical covariance of
individual estimating-function contributions. For BCH-ZY Gaussian models,
the mean and variance equations are stacked so their sandwich covariance
includes the covariance between and
. ML-ZY uses the inverse Louis
observed-information matrix; the variance standard error follows by applying
the delta method to the fitted log-standard-deviation parameter.
method.SE = "Numeric" evaluates the required
Hessian or Jacobian numerically. "Bootstrap" resamples individuals, recalculates posterior
assignments and CEP/BCH weights, and re-estimates Step 3 while holding the
Step 1 measurement parameters fixed. For a Gaussian dependent variable,
estimate and se report and its standard error, whereas
variance and variance.se report and its standard
error. Separate omnibus Wald tests assess equality of the conditional means
and equality of the conditional variances across classes/profiles. For a
categorical dependent variable, estimate and se report every
class/profile-specific category probability and its standard error; the
omnibus Wald test assesses equality of the complete conditional category
distributions.
ML is the default for XZ because its likelihood directly represents the
error-prone modal assignment through . BCH is the default for ZY
because its weights are calculated without using , so the Step 1
class/profile definition is not changed by the dependent variable. ML-ZY
provides a corrected-likelihood sensitivity analysis and reports class
shifts. Use a dedicated DCAT procedure when the DCAT estimand is required.
Asparouhov, T., & Muthén, B. (2014a). Auxiliary variables in mixture modeling: Three-step approaches using Mplus. Structural Equation Modeling: A Multidisciplinary Journal, 21(3), 329–341. doi:10.1080/10705511.2014.915181
Asparouhov, T., & Muthén, B. (2014b). Auxiliary variables in mixture modeling: Using the BCH method in Mplus to estimate a distal outcome model and an arbitrary secondary model (Mplus Web Note No. 21, Version 2). https://www.statmodel.com/examples/webnotes/webnote21.pdf
Bakk, Z., Tekle, F. B., & Vermunt, J. K. (2013). Estimating the association between latent class membership and external variables using bias-adjusted three-step approaches. Sociological Methodology, 43(1), 272–311. doi:10.1177/0081175012470644
Bolck, A., Croon, M., & Hagenaars, J. (2004). Estimating latent structure models with categorical variables: One-step versus three-step estimators. Political Analysis, 12(1), 3–27. doi:10.1093/pan/mph001
Nylund-Gibson, K., Grimm, R. P., & Masyn, K. E. (2019). Prediction from latent classes: A demonstration of different approaches to include distal outcomes in mixture models. Structural Equation Modeling: A Multidisciplinary Journal, 26(6), 967–985. doi:10.1080/10705511.2019.1590146
Vermunt, J. K. (2010). Latent class modeling with covariates: Two improved three-step approaches. Political Analysis, 18(4), 450–469. doi:10.1093/pan/mpq025
LCA(), LPA(), LTA(), get.CEP()
library(LCPA) set.seed(1245) N <- 2000 L <- 3 I <- 6 # Two observed covariates plus the required intercept covariates <- cbind( Intercept = 1, Zeta.1 = as.numeric(scale(rnorm(N))), Zeta.2 = rbinom(N, 1, 0.5) ) beta <- matrix(c( 0.70, 0.30, 0, 0.40, -0.20, 0, -0.30, 0.30, 0 ), ncol = L, byrow = TRUE) rownames(beta) <- colnames(covariates) data.LCPA <- sim.LTA( N = N, I = I, L = L, times = 1, type = "LPA", constraint = "VE", mean.range = c(-3, 3), covs.range = c(0.4, 0.8), covariates = list(covariates), ref.class = 3, beta = beta, is.sort = TRUE ) control.model <- list( constraint = "VE", is.sort = TRUE, starts = 10, maxiter.warmup = 10, nrep = 3 ) # Covariates predicting latent profiles: XZ analysis fit.LCPA.XZ <- LCPA( response = data.LCPA$responses[[1]], L = L, type.analysis = "XZ", type.model = "LPA", covariates = covariates, ref.class = 3, method.model = "EM", control.model = control.model, method.3step = "ML", method.regression = "Analytic", method.SE = "Analytic", maxiter = 500, vis = TRUE ) round(cbind( "True Class 1" = beta[, 1], "Estimate Class 1" = fit.LCPA.XZ$beta[, 1], "True Class 2" = beta[, 2], "Estimate Class 2" = fit.LCPA.XZ$beta[, 2] ), 3) # Latent profiles predicting two dependent variables: ZY analysis true.mean <- rbind( "Class 1" = c(Depression = 8, Anxiety = 12), "Class 2" = c(Depression = 10, Anxiety = 10), "Class 3" = c(Depression = 13, Anxiety = 8) ) dependent.variables <- scale( true.mean[data.LCPA$Zs[[1]], ] + matrix(rnorm(N * 2, sd = 1.5), N, 2) ) true.mean.standardized <- sweep( sweep(true.mean, 2, attr(dependent.variables, "scaled:center"), "-"), 2, attr(dependent.variables, "scaled:scale"), "/" ) true.variance.standardized <- (1.5 / attr(dependent.variables, "scaled:scale"))^2 dependent.variables <- as.data.frame(dependent.variables) fit.LCPA.ZY <- LCPA( response = data.LCPA$responses[[1]], L = L, type.analysis = "ZY", type.model = "LPA", dependent.variables = dependent.variables, family = "gaussian", method.model = "EM", control.model = control.model, method.3step = "BCH", method.regression = "Analytic", method.SE = "Analytic", vis = TRUE ) round(cbind( True.Depression = true.mean.standardized[, "Depression"], Estimate.Depression = fit.LCPA.ZY$dependent.variables$t1$Depression$estimate, True.Anxiety = true.mean.standardized[, "Anxiety"], Estimate.Anxiety = fit.LCPA.ZY$dependent.variables$t1$Anxiety$estimate ), 3) round(cbind( True.Variance.Depression = rep( true.variance.standardized["Depression"], L ), Estimate.Variance.Depression = fit.LCPA.ZY$dependent.variables$t1$Depression$variance, True.Variance.Anxiety = rep( true.variance.standardized["Anxiety"], L ), Estimate.Variance.Anxiety = fit.LCPA.ZY$dependent.variables$t1$Anxiety$variance ), 3)library(LCPA) set.seed(1245) N <- 2000 L <- 3 I <- 6 # Two observed covariates plus the required intercept covariates <- cbind( Intercept = 1, Zeta.1 = as.numeric(scale(rnorm(N))), Zeta.2 = rbinom(N, 1, 0.5) ) beta <- matrix(c( 0.70, 0.30, 0, 0.40, -0.20, 0, -0.30, 0.30, 0 ), ncol = L, byrow = TRUE) rownames(beta) <- colnames(covariates) data.LCPA <- sim.LTA( N = N, I = I, L = L, times = 1, type = "LPA", constraint = "VE", mean.range = c(-3, 3), covs.range = c(0.4, 0.8), covariates = list(covariates), ref.class = 3, beta = beta, is.sort = TRUE ) control.model <- list( constraint = "VE", is.sort = TRUE, starts = 10, maxiter.warmup = 10, nrep = 3 ) # Covariates predicting latent profiles: XZ analysis fit.LCPA.XZ <- LCPA( response = data.LCPA$responses[[1]], L = L, type.analysis = "XZ", type.model = "LPA", covariates = covariates, ref.class = 3, method.model = "EM", control.model = control.model, method.3step = "ML", method.regression = "Analytic", method.SE = "Analytic", maxiter = 500, vis = TRUE ) round(cbind( "True Class 1" = beta[, 1], "Estimate Class 1" = fit.LCPA.XZ$beta[, 1], "True Class 2" = beta[, 2], "Estimate Class 2" = fit.LCPA.XZ$beta[, 2] ), 3) # Latent profiles predicting two dependent variables: ZY analysis true.mean <- rbind( "Class 1" = c(Depression = 8, Anxiety = 12), "Class 2" = c(Depression = 10, Anxiety = 10), "Class 3" = c(Depression = 13, Anxiety = 8) ) dependent.variables <- scale( true.mean[data.LCPA$Zs[[1]], ] + matrix(rnorm(N * 2, sd = 1.5), N, 2) ) true.mean.standardized <- sweep( sweep(true.mean, 2, attr(dependent.variables, "scaled:center"), "-"), 2, attr(dependent.variables, "scaled:scale"), "/" ) true.variance.standardized <- (1.5 / attr(dependent.variables, "scaled:scale"))^2 dependent.variables <- as.data.frame(dependent.variables) fit.LCPA.ZY <- LCPA( response = data.LCPA$responses[[1]], L = L, type.analysis = "ZY", type.model = "LPA", dependent.variables = dependent.variables, family = "gaussian", method.model = "EM", control.model = control.model, method.3step = "BCH", method.regression = "Analytic", method.SE = "Analytic", vis = TRUE ) round(cbind( True.Depression = true.mean.standardized[, "Depression"], Estimate.Depression = fit.LCPA.ZY$dependent.variables$t1$Depression$estimate, True.Anxiety = true.mean.standardized[, "Anxiety"], Estimate.Anxiety = fit.LCPA.ZY$dependent.variables$t1$Anxiety$estimate ), 3) round(cbind( True.Variance.Depression = rep( true.variance.standardized["Depression"], L ), Estimate.Variance.Depression = fit.LCPA.ZY$dependent.variables$t1$Depression$variance, True.Variance.Anxiety = rep( true.variance.standardized["Anxiety"], L ), Estimate.Variance.Anxiety = fit.LCPA.ZY$dependent.variables$t1$Anxiety$variance ), 3)
This function computes the logistic (also known as sigmoid) transformation of the input. The logistic function maps real-valued numbers to the open interval (0, 1), and is widely used in machine learning, statistical modeling (e.g., logistic regression), and neural networks as an activation function or link function.
logit(x)logit(x)
x |
A numeric vector, matrix, or array. Accepts any real number, including |
The logistic function is defined as:
Note: Despite the name "logit", this function actually computes the inverse logit (i.e., the
logistic function). The true logit function is the inverse: .
However, in many applied contexts—especially in software—the term "logit" is sometimes
informally used to refer to the sigmoid. This function returns the
logistic/sigmoid transformation .
A numeric object of the same dimension as x, where each element is the
logistic transformation of the corresponding input:
If x = 0, returns 0.5
As x -> Inf, output approaches 1
As x -> -Inf, output approaches 0
NA values remain NA
logit(0) # 0.5 logit(c(-Inf, 0, Inf)) # c(0, 0.5, 1) logit(c(-2, -1, 0, 1, 2))logit(0) # 0.5 logit(c(-Inf, 0, Inf)) # c(0, 0.5, 1) logit(c(-2, -1, 0, 1, 2))
This function estimates parameters of a Latent Profile Analysis (LPA) model for continuous observed variables
using the Expectation-Maximization (EM) algorithm, stochastic EM (SEM) through flexmix
or RMixtComp, native EM/CEM/SEM strategies through Rmixmod,
Neural Network Estimation (NNE), or external Mplus software.
LPA( response, L = 2, constraint = "VV", method = "EM", par.ini = "random", is.sort = TRUE, starts = 100, maxiter.warmup = 20, nrep = 20, vis = TRUE, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.flexmix = NULL, control.Rmixmod = NULL, control.RMixtComp = NULL )LPA( response, L = 2, constraint = "VV", method = "EM", par.ini = "random", is.sort = TRUE, starts = 100, maxiter.warmup = 20, nrep = 20, vis = TRUE, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.flexmix = NULL, control.Rmixmod = NULL, control.RMixtComp = NULL )
response |
A numeric matrix of dimension |
L |
Integer specifying the number of latent profiles (default: 2). |
constraint |
Character string specifying covariance structure constraints:
For |
method |
Character string specifying estimation algorithm:
|
par.ini |
Specification for parameter initialization. Options include:
For methods that expose this initialization interface, |
is.sort |
A logical value. If |
starts |
Positive integer. Number of warm-up analyses to run (default: 100).
Each analysis is initialized by the selected method and trained for at most
|
maxiter.warmup |
Positive integer. Maximum number of training iterations for
each of the |
nrep |
Positive integer not exceeding |
vis |
Logical. If |
control.EM |
List of control parameters for EM algorithm:
|
control.Mplus |
List of control parameters for Mplus estimation:
|
control.NNE |
List of control parameters for NNE algorithm:
|
control.flexmix |
List of control parameters for flexmix SEM estimation:
|
control.Rmixmod |
List of control parameters for Rmixmod estimation:
|
control.RMixtComp |
List of control parameters for RMixtComp SEM estimation:
|
An object of class "LPA" containing:
paramsList with estimated profile parameters:
means matrix of estimated mean vectors for each profile.
covs array of estimated covariance matrices for each profile.
P.ZVector of length with profile prior probabilities.
nparNumber of free parameters in the model (depends on constraint).
Log.LikLog-likelihood of the final model.
AICAkaike Information Criterion value.
BICBayesian Information Criterion value.
best_BICBest BIC value across nrep runs when applicable;
for native Rmixmod and RMixtComp paths, the selected native fit's BIC.
P.Z.Xn matrix of posterior profile probabilities for each observation.
P.ZVector of length containing the prior probabilities/structural parameters/proportions for each latent class.
ZVector of length with MAP-classified profile memberships.
Log.Lik.historyVector tracking log-likelihood at each EM iteration (only for method="EM").
Log.Lik.nrepVector of log-likelihoods from each replication run. For native Rmixmod and RMixtComp paths, this is the selected native fit's scalar log-likelihood.
modelThe optimal model object:
For method="NNE": Trained neural network model.
For method="Mplus": Estimated Mplus model.
For method="flexmix": Selected flexmix SEM object.
For method="Rmixmod": Selected MixmodCluster object.
For method="RMixtComp": Selected MixtCompLearn SEM object.
callMatched function call.
argumentsA list containing all effective input arguments.
Except for method = "NNE", which intentionally uses its fixed backend seed,
every stochastic estimator is driven from R's current random-number generator.
The user only needs to call set.seed() immediately before LPA() to
reproduce EM, K-means, flexmix, Rmixmod, RMixtComp, and Mplus estimation. LCPA
automatically passes an R-derived seed to backends with independent random streams;
no backend-specific seed setting is required.
Write the response matrix as
, where
indexes participants and
indexes observed indicators. The response vector for
participant is
. The latent profile variable
is , and
indexes a particular latent profile.
With profile mean and covariance
, the observed-data log-likelihood is
When method = "EM", parameter estimation uses the Expectation-Maximization (EM) algorithm to maximize the observed-data log-likelihood:
The algorithm iterates between two steps until convergence (change in log-likelihood < tol or max iterations reached):
Compute posterior class probabilities (responsibilities) for participant
and class :
where is the multivariate normal density, is the prior class probability, and ,
are current parameters.
Update parameters using responsibilities :
Class probabilities:
Class means:
Class covariances: Updated under constraints:
"VV""EE"Shared covariance:
"VE" / "EV"
Hybrid constraints (e.g., "VE": varying variances, equal covariances).
The covariance part of the standard EM Q-function is maximized numerically under the exact equality constraints.
User-specified variances/covariances (e.g., list(c(1,2), c(2, 2)), meaning the
covariates of observed variable 1 and observed variable 2 are equal across latent classes,
and the variance of observed variable 2 is equal across classes) are estimated by maximizing
the covariance part of the standard EM Q-function under the specified equalities.
When method = "NNE", parameters are estimated using a hybrid neural network architecture
combining fully-connected layers with transformer-based attention mechanisms. This approach jointly
optimizes profile parameters and posterior probabilities through stochastic optimization with
simulated annealing. See install_python_dependencies. Key components include:
Architecture:
Continuous observed indicators are standardized
(mean-centered and scaled to unit variance) during training. No encoding is required.
A multi-layer perceptron with architecture defined by hidden.layers and activation.function
maps the continuous input vector into a latent space of dimension d.model. This layer learns non-linear
feature combinations predictive of latent profile membership.
A transformer encoder with nhead attention heads that learns latent class prior probabilities
directly from observed responses.
Two separate projection heads branch from the transformer output:
Means Head: Linear projection to matrix .
Covariance Head: Outputs one covariance matrix
for each profile.
Constraint handling:
Covariance constraints (constraint) are enforced after activation;
variances or covariances marked for equality are shared across profiles.
Custom constraints: e.g., list(c(1,2), c(3,3)), force equality of specific covariance elements
across profiles, with symmetry () automatically enforced.
When method = "Mplus", estimation is delegated to external Mplus software.
The function automates the entire workflow:
Workflow:
Creates a timestamped "Mplus_LPA_YYYY-MM-DD_HH-MM-SS"
directory under control.Mplus$files.path, or under the current working directory when that path is empty, to store:
Mplus input syntax (.inp)
Data file in Mplus format (.dat)
Posterior probabilities output (.dat)
Files are automatically deleted after estimation unless control.Mplus$files.clean = FALSE.
Constructs Mplus syntax with:
CLASSES = c1(L) specification for latent classes
ANALYSIS block with optimization controls:
TYPE = mixtureStandard mixture modeling setup
STARTS = starts nrepRandom starts and final stage optimizations
STSEEDRandom-start seed drawn from R's current random-number generator
STITERATIONS = maxiter.warmupmax itertions during starts.
MITERATIONS = maxiterMaximum EM iterations
CONVERGENCE = tolLog-likelihood convergence tolerance
MODEL block reflecting the specified constraint structure
Calls Mplus via MplusAutomation::mplusModeler()
, which:
Writes data to disk in Mplus-compatible format
Invokes the Mplus executable (requires valid license)
Captures convergence status and output
Constraint handling:
Covariance restrictions are encoded directly in the generated Mplus model syntax before estimation; they are not imposed by post-estimation averaging.
The named structures are translated as follows:
"E0": variances carry common equality labels across profiles and all covariances are fixed to zero.
"V0": variances are freely estimated within profile and all covariances are fixed to zero.
"EE": variances and covariances carry common equality labels across profiles.
"VE": variances vary across profiles while covariances carry common equality labels.
"EV": variances carry common equality labels while covariances vary across profiles.
"VV": all variances and covariances vary across profiles.
For a custom list, c(i, i) assigns a common cross-profile label to the variance of
variable , whereas c(i, j) assigns one to the covariance between variables
and . Unlisted parameters remain profile-specific, and covariance symmetry is
represented by a single Mplus WITH parameter for each variable pair.
"UE" and "UV" are not available through this Mplus backend because it requires
at least two indicators. They remain available for the univariate EM and flexmix paths.
With method = "flexmix", each SEM iteration performs one stochastic
classification draw from the current posterior probabilities before the M-step. LCPA runs
exactly starts short trajectories of maxiter.warmup iterations, promotes the best
nrep trajectories by observed log-likelihood, continues each for
control.flexmix$maxiter SEM iterations, and retains the largest-likelihood final state
across the promoted runs.
This selection compares the final state returned by each flexmix SEM run; flexmix's
classify = "SEM" does not retain the largest-likelihood state visited within a run.
The stochastic classification and iteration control remain those of flexmix. LCPA supplies a
joint Gaussian M-step driver so that "UE", "UV", "E0", "V0",
"EE", "VV", "VE", "EV", and custom equality lists obey the same
covariance definitions used by LCPA's EM estimator. Setting control.flexmix$tol = 0
prevents likelihood-based early termination. Only SEM is exposed; par.ini is not used.
When method = "Rmixmod", estimation uses Rmixmod::mixmodCluster(). The covariance
constraints map to Rmixmod Gaussian models as follows: "V0" to
"Gaussian_pk_Lk_Bk", "EE" to "Gaussian_pk_L_C", and "VV" to
"Gaussian_pk_Lk_Ck".
With path="LCPA", the function generates exactly starts random balanced
partitions and runs exactly maxiter.warmup consecutive stochastic E-S-M iterations for every
warm-up start. The best nrep warm-up parameter sets are then passed directly to independent
Rmixmod SEM runs, and the finite solution with the largest final log-likelihood is retained.
Rmixmod's internal "smallEM" and "SEMMax" initialization searches are not used;
in particular, "SEMMax" is an initialization search rather than one consecutive SEM
trajectory for each user-level start. par.ini is not used for this method.
SEM stops after control.Rmixmod$maxiter iterations; an epsilon convergence
criterion is not defined for SEM in Rmixmod. Unlike flexmix's classify = "SEM", Rmixmod
retains the largest-likelihood parameter state visited within each SEM run, so equal iteration
counts do not imply identical final-state selection.
Custom covariance-constraint lists are not accepted by the Rmixmod backend.
With path="Rmixmod", LCPA translates all non-NULL package-standard strategy controls
to Rmixmod::mixmodStrategy() and runs one native mixmodCluster() call. Its
algorithm may contain "EM", "CEM", "SEM", or an ordered combination
of these algorithms. LCPA does
not add its outer starts, maxiter.warmup, or nrep;
control.Rmixmod$nrep controls complete
strategy repetitions. The published strategy of Mulder et al.
(2015) uses 200 SEM iterations followed by EM with a relative likelihood-change tolerance of
1e-5. For reproducibility across Rmixmod versions, the example and simulation scripts
explicitly pin the contemporaneous documented defaults: smallEM, 50 initialization tries,
5 initialization iterations, tol.init=0.001, and a 200-iteration EM limit.
When method = "RMixtComp", LCPA calls RMixtComp::mixtCompLearn() in
classic, non-hierarchical learning mode with a univariate Gaussian model for every indicator.
This integration exposes only RMixtComp's stochastic EM (SEM) algorithm; it does not
introduce any other RMixtComp estimation algorithm. Conditional independence implies
diagonal, class-varying covariance matrices, so only constraint = "V0" is accepted.
Custom covariance-constraint lists are not accepted by the RMixtComp backend.
LCPA does not add its own starts, maxiter.warmup, or nrep stages to this
backend. Each native run performs RMixtComp initialization, SEM burn-in, recorded SEM
iterations, and then the fixed-parameter Gibbs burn-in and recorded Gibbs iterations.
One stochastic S-step is performed per SEM iteration, between the E-step and M-step.
Native repetition and parallelization are controlled only by
control.RMixtComp$nrep and control.RMixtComp$ncores.
RMixtComp's native random stream is independent of R, so LCPA passes it one integer drawn
from R's current random stream. The user only needs an external set.seed() for reproducibility.
The default algorithm controls reproduce RMixtCompUtilities::createAlgo()
defaults (version 4.1.4 or later).
par.ini is not used by this backend.
Biernacki, C. (2015). MixtComp software: Model-based clustering/imputation with mixed data, missing data and uncertain data. MISSDATA 2015. https://inria.hal.science/hal-01253393
Leisch, F. (2004). FlexMix: A general framework for finite mixture models and latent class regression in R. Journal of Statistical Software, 11(8), 1–18. doi:10.18637/jss.v011.i08
McLachlan, G. J., & Peel, D. (2000). Finite mixture models. John Wiley & Sons.
Mulder, V. L., Lacoste, M., Martin, M. P., Richer-de-Forges, A., & Arrouays, D. (2015). Understanding large-extent controls of soil organic carbon storage in relation to soil depth and soil-landscape systems. Global Biogeochemical Cycles, 29(8), 1210–1229. doi:10.1002/2015GB005178
library(LCPA) # Simulate bivariate continuous data for 2 profiles set.seed(123) data.obj <- sim.LPA(N = 500, I = 3, L = 2, constraint = "VV") response <- data.obj$response ## It is strongly recommended to perform the following ## standardization to obtain more stable results. ## Standardization is not performed here in order to ## compare estimated values with true values. # response <- normalize(response) # Fit 2-profile model with VV constraint (default) fit_vv <- LPA(response, L = 2, constraint = "VV") # Fit 2-profile model with E0 constraint using neural network estimation # need Python ## Not run: fit_e0_nne <- LPA(response, L = 2, constraint = "E0", method = "NNE", nrep = 2) ## End(Not run) # Fit 2-profile model using Mplus # Requires Mplus to be installed and available in system PATH. # An empty 'files.path' instead uses the current working directory. # This example creates a timestamped subdirectory # (e.g., "Mplus_LPA_YYYY-MM-DD_HH-MM-SS") under './inst' # to store all temporary Mplus files (.inp, .dat, .out, etc.). # The 'inst' directory will be created if it does not exist. # Setting files.clean=FALSE means temporary files will be preserved after execution. ## Not run: fit_mplus <- LPA(response, L = 2, method = "Mplus", constraint = list(c(1, 2), c(3, 3)), control.Mplus = list(files.path = "inst", files.clean=FALSE)) ## End(Not run) # Fit an EE model with flexmix SEM and the joint constrained M-step # need flexmix ## Not run: fit_flexmix <- LPA(response, L = 2, constraint = "EE", method = "flexmix", nrep = 2, starts = 5, maxiter.warmup = 5, control.flexmix = list(maxiter = 50)) ## End(Not run) # Fit 2-profile model with the published Rmixmod SEM-to-EM strategy # need Rmixmod ## Not run: fit_rmixmod <- LPA(response, L = 2, constraint = "VV", method = "Rmixmod", control.Rmixmod = list(path = "Rmixmod", algorithm = c("SEM", "EM"), nrep = 1, method.init = "smallEM", starts = 50, maxiter.init = 5, tol.init = 0.001, maxiter = c(200, 200), tol = c(NA, 1e-5))) ## End(Not run)library(LCPA) # Simulate bivariate continuous data for 2 profiles set.seed(123) data.obj <- sim.LPA(N = 500, I = 3, L = 2, constraint = "VV") response <- data.obj$response ## It is strongly recommended to perform the following ## standardization to obtain more stable results. ## Standardization is not performed here in order to ## compare estimated values with true values. # response <- normalize(response) # Fit 2-profile model with VV constraint (default) fit_vv <- LPA(response, L = 2, constraint = "VV") # Fit 2-profile model with E0 constraint using neural network estimation # need Python ## Not run: fit_e0_nne <- LPA(response, L = 2, constraint = "E0", method = "NNE", nrep = 2) ## End(Not run) # Fit 2-profile model using Mplus # Requires Mplus to be installed and available in system PATH. # An empty 'files.path' instead uses the current working directory. # This example creates a timestamped subdirectory # (e.g., "Mplus_LPA_YYYY-MM-DD_HH-MM-SS") under './inst' # to store all temporary Mplus files (.inp, .dat, .out, etc.). # The 'inst' directory will be created if it does not exist. # Setting files.clean=FALSE means temporary files will be preserved after execution. ## Not run: fit_mplus <- LPA(response, L = 2, method = "Mplus", constraint = list(c(1, 2), c(3, 3)), control.Mplus = list(files.path = "inst", files.clean=FALSE)) ## End(Not run) # Fit an EE model with flexmix SEM and the joint constrained M-step # need flexmix ## Not run: fit_flexmix <- LPA(response, L = 2, constraint = "EE", method = "flexmix", nrep = 2, starts = 5, maxiter.warmup = 5, control.flexmix = list(maxiter = 50)) ## End(Not run) # Fit 2-profile model with the published Rmixmod SEM-to-EM strategy # need Rmixmod ## Not run: fit_rmixmod <- LPA(response, L = 2, constraint = "VV", method = "Rmixmod", control.Rmixmod = list(path = "Rmixmod", algorithm = c("SEM", "EM"), nrep = 1, method.init = "smallEM", starts = 50, maxiter.init = 5, tol.init = 0.001, maxiter = c(200, 200), tol = c(NA, 1e-5))) ## End(Not run)
Conducts a likelihood ratio test to compare the fit of two LCA or LPA models with any numbers of latent classes/profiles, including equal class counts. The test evaluates whether a model with more parameters provides a significantly better fit than a model with fewer parameters.
LRT.test(object1, object2)LRT.test(object1, object2)
object1 |
Fitted LCA or LPA model. When both models have the same number of free parameters, this is treated as the null model. |
object2 |
Fitted LCA or LPA model of the same type. When both models have the same number of free parameters, this is treated as the alternative model. |
Note that since the small model may be nested within the large model, the result
of LRT.test may not be accurate and is provided for reference only.
More reliable conclusions should be based on a combination of fit indices (i.e., get.fit.index),
classification accuracy measures (i.e., get.entropy, get.AvePP), and a bootstrapped
likelihood-ratio test (i.e., BLRT, LRT.test.Bootstrap, which is very time-consuming).
Above all and the most important criterion, is that the better model is the one that aligns with theoretical
expectations and offers clear interpretability.
The LRT.test test statistic is defined as:
The models must be nested (i.e., the model with fewer parameters is a constrained version of the more one).
Both models must be fit on the identical dataset with the same response variables.
The test statistic asymptotically follows a chi-square distribution.
where:
: Log-likelihood of the smaller model (fewer parameters).
: Log-likelihood of the larger model (more parameters).
Under the null hypothesis (H_0: small model is true), LRT asymptotically follows
a chi-square distribution with degrees of freedom.
Models may have any class counts; they do not need to differ by exactly one
class. If both models have the same number of free parameters, the
likelihood-ratio statistic is returned but the chi-square p-value is
NA because its reference distribution has zero degrees of freedom.
An object of class "htest" containing:
statistic: Standard likelihood ratio test statistic
parameter: Degrees of freedom ()
p.value: P-value from distribution
method: Name of the test
data.name: Model comparison description
Conducts a bootstrap likelihood ratio test (BLRT) to compare two latent class analysis (LCA) or latent profile analysis (LPA) models with any numbers of latent classes/profiles, including equal class counts. Implements both fixed-replicate and sequential stopping procedures for computational efficiency.
LRT.test.Bootstrap( object1, object2, nrep.bootstrap = 100, vis = TRUE, use.sequential = TRUE )LRT.test.Bootstrap( object1, object2, nrep.bootstrap = 100, vis = TRUE, use.sequential = TRUE )
object1 |
Fitted model object. Must be of class |
object2 |
Fitted model object of the same class as |
nrep.bootstrap |
Maximum number of bootstrap replicates (default = 100).
If |
vis |
Logical. If |
use.sequential |
Logical. If |
Models may have any class counts; they do not need to differ by exactly one
class. Models are ordered by their numbers of free parameters. If these are
equal, the supplied order is retained and bootstrap samples are generated
under object1.
Core Workflow (Parametric Bootstrap):
Parameter Extraction: Parameters from the null model (object1) are treated as the population truth.
Data Simulation: Generates datasets using sim.LCA or sim.LPA,
preserving the original sample size and indicator structure. Class labels are sampled with replacement
from a pool constructed by rounding and adjusting the largest class to length .
Model Re-fitting: For each simulated dataset, both the null and alternative models are re-estimated.
Each refit uses update() to preserve the supplied model's fitting controls while replacing
response, setting par.ini = "random" and
is.sort = FALSE, and suppressing progress output. If that refit errors,
the same update is retried with method = "EM".
LRT Distribution: Computes bootstrap LRT statistics:
.
P-value Calculation: The p-value is the proportion of bootstrap LRTs that are greater than or equal to the observed LRT statistic.
Sequential Stopping Rule (Nylund et al., 2007):
When use.sequential = TRUE, the algorithm checks three stopping criteria after each replication:
Upper Stopping Points: Stop if the current estimated p-value () is
greater than or equal to a pre-specified threshold at replication ,
indicating insufficient evidence against the null model.
Lower Stopping Points: Stop if at replication ,
indicating strong evidence in favor of the alternative model.
Conditional Lower Stopping Points: Stop if and
the observed LRT exceeds the mean of the current bootstrap LRTs by more than standard deviations.
This leverages the approximate normality of the LRT distribution under the null (Nylund et al., 2007).
The exact stopping thresholds follow Appendix A of Nylund et al. (2007):
Upper: (n, 2/n) for n = 2-3; (n, 3/n) for n = 4-9; (n, 4/n) for n = 10-17; (n, 5/n) for n = 18-26; (n, 6/n) for n = 27-99; (100, 0).
Lower: (49, 0), (78, 1/78).
Conditional: (5, 0, 20), (10, 0, 10), (20, 0, 5), where the third value denotes the required z-score threshold.
This rule ensures >95% agreement with the decision that would be made using an infinite number of replicates
when the true p-value is below 0.10. Near , the algorithm continues until the maximum
number of replicates to avoid premature conclusions.
Critical Interpretation Notes:
The BLRT should not be used in isolation. Always integrate results with:
Information criteria: e.g., BIC, aBIC, CAIC via get.fit.index.
Classification quality metrics: e.g., entropy (get.entropy),
average posterior probability (get.AvePP).
Substantive interpretability and theoretical coherence of the extracted classes.
The BLRT is especially valuable in LCA/LPA contexts where the standard asymptotic LRT is invalid due to boundary parameter issues (e.g., class probabilities approaching 0 or 1; McLachlan & Peel, 2000).
Early termination under sequential stopping (e.g., at ) yields reliable p-values
when the evidence is strong ( or ).
However, if stopping occurs near nrep.bootstrap with ,
the result should be considered inconclusive.
In small samples () or with poorly separated classes,
the BLRT may exhibit inflated Type I error rates; corroborate findings with BIC and other indices.
An object of class "htest" containing:
statistic: Observed likelihood ratio test statistic,
.
parameter: Degrees of freedom (set to NA; the bootstrap p-value does not rely on asymptotic chi-square distribution).
p.value: Bootstrap p-value, computed as the proportion of bootstrap LRT statistics
greater than or equal to the observed LRT statistic.
method: Character string indicating the procedure used:
either "Bootstrap LRT with Sequential Stopping" or "Bootstrap LRT (Fixed Replicates)".
data.name: Descriptive string summarizing the model comparison,
including the actual number of bootstrap replicates performed and, if applicable,
the reason for early stopping under the sequential rule.
LRT.Bootstrap: Numeric vector of length (where is the actual number of
bootstrap replicates performed) containing the bootstrap LRT statistics:
.
These are generated under the null hypothesis (i.e., data simulated from the null model).
This vector can be used for diagnostic purposes, such as inspecting the empirical null distribution
or computing alternative p-value estimates.
nrep.bootstrap.requested: Maximum number of bootstrap replicates requested.
nrep.bootstrap.completed: Actual number of bootstrap replicates completed.
negative.LRT: Number of bootstrap LRT statistics below zero.
negative.LRT.proportion: Proportion of bootstrap LRT statistics below zero.
McLachlan, G. J., & Peel, D. (2000). Finite mixture models. John Wiley & Sons.
Nylund, K. L., Asparouhov, T., & Muthén, 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: A Multidisciplinary Journal, 14(4), 535–569. doi:10.1080/10705510701575396
Computes the Mplus TECH11 implementation of the Vuong-Lo-Mendell-Rubin likelihood ratio test (VLMR; Vermunt, 2024) and the Lo-Mendell-Rubin adjusted test (aLMR; Lo et al., 2001) for LCA or LPA models with any numbers of latent classes/profiles, including equal class counts.
LRT.test.VLMR(object1, object2)LRT.test.VLMR(object1, object2)
object1 |
Fitted |
object2 |
Fitted model of the same type and fitted to the same data. When both models have the same number of free parameters, this is treated as the alternative model. |
This function reports two related tests. The unadjusted VLMR is based on the
likelihood-ratio test for nested or overlapping models developed by Vuong
(1989), which Lo, Mendell, and Rubin (2001) applied to the comparison of
- and -component mixture models. The adjusted LMR test
(aLMR) is the modification proposed by Lo et al. (2001). The robust
reference distribution follows the Mplus TECH11 reconstruction described by
Vermunt (2024) and is calculated from observation-level scores and
observed-information matrices as follows.
Let denote the null model and the alternative model,
ordered by parameter count as described above. For model
, let
be participant 's log-likelihood contribution,
its score vector, and
the observed information
matrix. Define
and the robust sandwich covariance matrix
Following the Mplus implementation identified by Vermunt (2024), the reference-distribution weights are the eigenvalues of
If these eigenvalues are , the unadjusted
statistic and its estimated null distribution are
Thus, the VLMR p-value is . It is evaluated
using Imhof's (1961) method, retaining negative eigenvalue weights. The
reported mean and standard deviation of the reference distribution are
and
, respectively (Vermunt, 2024).
The aLMR adjustment is specifically Equation (15) of Lo et al. (2001):
where is the difference in the numbers of free parameters. The
adjusted statistic uses the same weighted chi-square reference distribution
as the unadjusted VLMR statistic (Vermunt, 2024). Consequently,
p.value is the unadjusted VLMR result derived from Vuong (1989) and
Lo et al. (2001), whereas adjusted.p.value is the aLMR result based
on Equation (15) of Lo et al. (2001).
Models may have any class counts; they do not need to differ by exactly one
class. When their numbers of free parameters are equal, the unadjusted VLMR
is computed, but the aLMR correction factor, adjusted statistic, and adjusted
p-value are NA because Equation (15) contains in the
denominator.
The result matches the TECH11 calculation conditional on the two supplied maximum-likelihood solutions. Local maxima, singular information matrices, and boundary solutions can invalidate the comparison.
An object of classes "VLMR" and "htest" containing:
statistic: Unadjusted VLMR likelihood ratio statistic.
p.value: VLMR p-value from the Mplus weighted chi-square distribution.
adjusted.statistic: LMR statistic adjusted using Equation
(15) of Lo et al. (2001).
adjusted.p.value: Adjusted LMR p-value from the same weighted chi-square distribution.
correction.factor: Correction factor from Equation (15) of
Lo et al. (2001).
distribution: Mean, standard deviation, and eigenvalue weights
of the estimated VLMR reference distribution.
diagnostics: Score, information-matrix, eigenvalue, and Imhof
integration diagnostics.
Imhof, J. P. (1961). Computing the distribution of quadratic forms in normal variables. Biometrika, 48(3–4), 419–426. doi:10.1093/biomet/48.3-4.419
Lo, Y., Mendell, N. R., & Rubin, D. B. (2001). Testing the number of components in a normal mixture. Biometrika, 88(3), 767–778. doi:10.1093/biomet/88.3.767
Vermunt, J. K. (2024). The Vuong-Lo-Mendell-Rubin test for latent class and latent profile analysis: A note on the different implementations in Mplus and LatentGOLD. Methodology, 20(1), 72–83. doi:10.5964/meth.12467
Vuong, Q. H. (1989). Likelihood ratio tests for model selection and non-nested hypotheses. Econometrica, 57(2), 307–333. doi:10.2307/1912557
Estimates initial latent-state probabilities and conditional transition
matrices across repeated LCA/LPA measurements. type.analysis = "XZ"
estimates covariate effects on initial status and transitions; "ZY"
estimates external observed dependent-variable distributions conditional on
a latent state at a selected time or on the complete longitudinal
latent-state path. These auxiliary variables are not included among the
indicators.
LTA( responses, L = 2, type.analysis = c("XZ", "ZY"), type.model = c("LCA", "LPA"), covariates = NULL, covariates.time.cross = FALSE, ref.class = L, dependent.variables = NULL, family = "gaussian", dependent.variable.structure = c("State", "Path"), dependent.variable.time = NULL, dependent.variable.time.cross = FALSE, method.model = "EM", step1.pool = FALSE, control.model = NULL, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.Rmixmod = NULL, method.3step = NULL, CEP.error = TRUE, CEP.time.cross = FALSE, method.regression = "Analytic", maxiter = 5000, tol = 1e-04, lower = -10, upper = 10, method.SE = "Bootstrap", nrep.bootstrap = 100, vis = TRUE )LTA( responses, L = 2, type.analysis = c("XZ", "ZY"), type.model = c("LCA", "LPA"), covariates = NULL, covariates.time.cross = FALSE, ref.class = L, dependent.variables = NULL, family = "gaussian", dependent.variable.structure = c("State", "Path"), dependent.variable.time = NULL, dependent.variable.time.cross = FALSE, method.model = "EM", step1.pool = FALSE, control.model = NULL, control.EM = NULL, control.Mplus = NULL, control.NNE = NULL, control.Rmixmod = NULL, method.3step = NULL, CEP.error = TRUE, CEP.time.cross = FALSE, method.regression = "Analytic", maxiter = 5000, tol = 1e-04, lower = -10, upper = 10, method.SE = "Bootstrap", nrep.bootstrap = 100, vis = TRUE )
responses |
A non-empty list of |
L |
Integer number of latent classes/profiles at every time point
(default: 2; must be at least 2). It has the same meaning as |
type.analysis |
Character string selecting the analysis path:
The default is |
type.model |
Character string selecting the common Step 1 measurement
model: |
covariates |
Used only when |
covariates.time.cross |
Logical used only for |
ref.class |
Integer from 1 to |
dependent.variables |
Used only when |
family |
Used only for |
dependent.variable.structure |
Used only for |
dependent.variable.time |
Used only for |
dependent.variable.time.cross |
Logical used only for |
method.model |
Character string selecting the Step 1 parameter estimator.
It has the same meaning as the |
step1.pool |
Logical (default: |
control.model |
Optional named list of common Step 1 measurement-model
settings. If
Element names must be unique; unknown or unnamed elements are rejected. |
control.EM |
Optional Step 1 control list for the EM estimator in
|
control.Mplus |
Optional list passed to the selected |
control.NNE |
Optional list passed to the selected |
control.Rmixmod |
Optional list passed to the selected |
method.3step |
Character string selecting the Step 2–3 correction:
If |
CEP.error |
Logical. If |
CEP.time.cross |
Logical. If |
method.regression |
Character string controlling Step 3 point estimation
(default: |
maxiter |
Positive integer giving the maximum number of Step 3 |
tol |
Positive finite convergence tolerance for the Step 3 |
lower, upper
|
Finite lower and upper bounds applied to each |
method.SE |
Character string selecting Step 3 uncertainty estimation:
|
nrep.bootstrap |
Integer number of bootstrap replications used only when
|
vis |
Logical (default: |
At time , write the indicator matrix as
, where
indexes participants and
indexes observed indicators. Participant 's
indicator vector is
, and
, with
indexing latent states.
The covariate vector is
, where
indexes the observed covariates available at time
, and . The leading 1 is the intercept. The
dependent-variable vector is
, where
indexes external observed dependent variables and
. Neither nor
is part of .
"XZ" is the function-interface label for the covariate-to-latent path;
the formulas use for its covariates because
is reserved for the LCA/LPA indicator data.
type.analysis = "XZ" uses but not
; type.analysis = "ZY" uses but
not . These are separate Step 3 analyses rather
than one jointly estimated longitudinal mediation model.
For a ZY analysis, standardization applies to the continuous dependent variables
in , not to the Step 1 indicator matrices
. When dependent.variable.time.cross = FALSE, standardize
each Gaussian dependent-variable column separately within its supplied time
point. When dependent.variable.time.cross = TRUE, apply one common centering
and scaling transformation to the corresponding Gaussian column pooled over
all selected time points. The resulting estimates and standard errors are
expressed in observed-standard-deviation units. Categorical dependent variables
retain their original category values.
An object of class "LTA", organized by analysis stage:
type.analysis, type.model, method.3step,
dependent.variable.structure, and
analysis. analysis$type records the selected path; exactly one of
analysis$XZ and analysis$ZY is non-NULL. The same selected result
is also available from the top-level XZ or ZY component.
When
type.analysis = "XZ", beta[, l] is
, and
gamma[[t - 1]][[k]][[l]] is
, the coefficient vector defining row
, column of . beta.se,
gamma.se, z statistics, p-values, vcov, information, and
SE.diagnostics provide the corresponding inference. Log.Lik, AIC,
BIC, iterations, converged, Log.Lik.history, and
bound.diagnostics describe estimation of the initial-state and
transition likelihood. These fields are available both in XZ and at
the top level; ZY is NULL.
params contains the common
fitted or supplied LCA/LPA measurement parameters. P.Z.Xns, P.Zs,
and Zs are time-indexed posterior probabilities, marginal
latent-state proportions, and modal assignments. CEP is the
corresponding list of time-specific or constrained classification-error
matrices, and step1.pool records the measurement-model data source.
When type.analysis = "ZY",
dependent.variables is nested by selected measurement time and
observed dependent variable. Each fitted variable contains its
family, state- or path-specific estimate and se, convergence
status, and method-specific diagnostics. Gaussian models additionally
contain variance, variance.se, variance.vcov,
mean.variance.vcov, and separate omnibus and omnibus.variance
Wald tests. Categorical models contain class-by-category probability
estimate and se matrices and an omnibus test of their complete
conditional distributions. vcov,
SE.diagnostics, classification, case.weights where applicable,
and diagnostics provide the associated inference and correction
information. With dependent.variable.structure = "Path",
latent.paths gives the matrix that maps each
reported path name to its time-specific latent states. ML path models
also report their fitted initial-state and transition parameters within
each dependent-variable model. XZ is NULL, and ZY
points to dependent.variables.
call stores the matched call and arguments
stores the normalized public arguments, including step1.pool and the
expanded control.model defaults.
For type.analysis = "XZ", the central model is the evolution of
. Let ref.class be the reference
destination state . The initial-state probabilities are
For each , participant 's conditional transition
matrix is
Its row gives the destination-state distribution conditional on
origin state ; its column corresponds to destination state
. Every row therefore sums to 1. The entry is
Equivalently, for ,
Thus, is the intercept for the transition from origin
state to destination state , relative to destination
; is the corresponding change in log relative
transition odds for a one-unit increase in covariate .
When covariates = NULL, every contains only
the intercept, so is a population transition matrix
rather than a participant-specific matrix. When
covariates.time.cross = TRUE,
; the coefficient structure is time-invariant,
although transition probabilities may still differ between participants or
times when their covariate values differ.
Conditional on the covariates, the probability of one latent-state path is
If the latent states were observed, the transition-model log-likelihood would be
The measurement and classification stages follow the same three-step
principles as LCPA(). A common unconditional
LCA() or LPA() measurement model is
fitted at the first time point or to the pooled time points according to
step1.pool; control.model$params may instead supply fixed common
measurement parameters. This assumes measurement invariance over the
analyzed times. At each time, posterior classification produces
and an matrix with entries
. See
get.CEP() for its estimator and orientation. This
longitudinal three-step construction follows the latent Markov/LTA
developments of Bartolucci, Montanari, and Pandolfi (2015), Di Mari,
Oberski, and Vermunt (2016), and Liang, de la Torre, and Law (2023).
The bias-adjusted ML approach of Di Mari et al. (2016) combines those classification-error probabilities with the complete transition model and sums over every possible latent-state path:
The CEP term is the conditional probability of the observed modal state
given the corresponding latent state. With CEP.error = FALSE, every CEP
matrix is the identity matrix and the likelihood reduces to the likelihood
of the modal state sequence.
The likelihood is evaluated by the forward recursion
so that
A backward recursion supplies the posterior initial-state and adjacent-state probabilities used by the analytic score.
The free parameter vector contains and
for ; all reference-state
coefficient vectors are fixed at zero. ML minimizes
subject to
lower and upper. All latent-state posterior probabilities in the score
below are conditional on the complete modal-state sequence and the supplied
covariates. For , the first derivative with respect to the
initial-state coefficient vector is
This is an derivative vector whose entries correspond
to the intercept and the covariate coefficients in
.
Let
. Here indexes the origin
state at time , indexes the destination state at time
, and identifies the transition from time to time
. The first derivative with respect to
is
This is an derivative vector whose entries correspond
to the intercept and the covariate coefficients in
. At an interior maximum, these derivatives
and the derivatives with respect to jointly equal
. If covariates.time.cross = TRUE, the derivatives are
summed over for the transition coefficient vector shared
across time. With
method.regression = "Analytic", the exact forward–backward score is
supplied to box-constrained L-BFGS optimization. With "Numeric", the same
corrected log-likelihood is optimized using its numerical gradient. tol
and maxiter control this Step 3 optimization.
With method.3step = "BCH", the modified inverse-CEP weighting of Vermunt
(2010) and Bakk, Tekle, and Vermunt (2013) extends to multiple latent states
when the time-specific classification errors are conditionally independent
given the latent-state path. Bakk, Tekle, and Vermunt (2013) describe the
multiple-latent-variable extension, and Nylund-Gibson, Arch, and Carter
(2026) apply BCH to auxiliary-variable analysis in LTA. The resulting
criterion is
It decomposes into one initial-state multinomial model and origin-state-specific transition multinomial models. Analytic estimation supplies their exact scores to L-BFGS-B; numeric estimation optimizes the same criterion without a supplied score. The adjacent-state BCH correction assumes that classification errors are conditionally independent across times given the latent-state path.
type.analysis = "ZY" relates the external observed
to the latent states without treating any
as an indicator in . The Gaussian and
categorical conditional distributions have the same definitions as in
LCPA(); see Nylund-Gibson et al. (2014), Asparouhov and
Muthén (2014a), and Nylund-Gibson, Arch, and Carter (2026) for longitudinal
auxiliary-variable models. The longitudinal model offers the following two
structures. Here denotes a selected dependent-variable time,
indexes all measurement times, and indexes the
observed categories of a categorical dependent variable.
With dependent.variable.structure = "State", each selected
is conditional only on the latent state at the same selected
time. Then
for a Gaussian dependent variable, or
for a categorical dependent variable. Following the manual ML
distal-outcome formulation of Bakk, Tekle, and Vermunt (2013) and
Nylund-Gibson, Grimm, and Masyn (2019), the LTA State model sums over all
complete latent-state paths while constraining paths with the same state at
selected time to share the same dependent-variable parameters. For
a Gaussian dependent variable,
For a categorical dependent variable, the normal density is replaced by
. Selecting
therefore estimates the commonly used final-state model
.
With dependent.variable.structure = "Path", each dependent-variable
distribution is conditional on the complete latent-state path, where every
:
with an analogous path-specific categorical probability
. This gives dependent-variable
distributions for all latent-state combinations or transition patterns.
Nylund-Gibson, Arch, and Carter (2026) describe distal analyses by transition
pattern in LTA, and Bakk, Tekle, and Vermunt (2013) provide the
multiple-latent-variable correction underlying this construction. ML
estimates an intercept-only initial-state distribution,
time-specific first-order transition matrices, and the path-specific
dependent-variable distributions jointly. For a Gaussian dependent variable,
For a categorical dependent variable, the final normal density is replaced
by
.
BCH uses the inverse CEP entries directly. In the State model, summing the
complete-path BCH weights over every state other than the state at selected
time gives the selected-time equations used in the BCH
distal-dependent-variable construction of Asparouhov and Muthén (2014b).
The Gaussian mean and variance satisfy
and the categorical probability satisfies
In the Path model, the corresponding equations are
The products across time assume conditionally independent classification
errors given the latent-state path. When
dependent.variable.time.cross = TRUE, the selected times share the
same state- or path-specific dependent-variable parameters.
For ML-XZ and BCH-XZ, method.SE = "Analytic" uses the analytic
information or estimating-equation derivative with participant-level score
contributions. "Numeric" evaluates the corresponding Hessian or Jacobian
numerically. "Bootstrap" resamples participants jointly across all times,
recalculates the posterior classifications and CEP matrices, and re-estimates
the initial-state and transition model while holding the common measurement
parameters fixed. The ML-XZ covariance also includes the sampling influence
of the estimated CEP matrices. State-ZY uncertainty estimation follows
LCPA(). Path-ML uses Louis observed information for the
jointly estimated initial-state, transition, and dependent-variable
parameters. State-ML imposes equality of the dependent-variable parameters
across complete paths sharing the selected state; Path-ML leaves them
path-specific. Path-BCH uses the estimating-equation sandwich formed from the
complete-path BCH products. The ZY bootstrap resamples each participant's
complete longitudinal record. Gaussian ZY models report the state- or
path-specific conditional means and variances, their respective standard
errors and covariance matrices, and separate omnibus Wald tests for equality
of means and variances. Categorical ZY models report every state- or
path-specific category probability, its standard error and covariance
matrix, and an omnibus Wald test of equality of the complete conditional
category distributions.
Asparouhov, T., & Muthén, B. (2014a). Auxiliary variables in mixture modeling: Three-step approaches using Mplus. Structural Equation Modeling: A Multidisciplinary Journal, 21(3), 329–341. doi:10.1080/10705511.2014.915181
Asparouhov, T., & Muthén, B. (2014b). Auxiliary variables in mixture modeling: Using the BCH method in Mplus to estimate a distal outcome model and an arbitrary secondary model (Mplus Web Note No. 21, Version 2). https://www.statmodel.com/examples/webnotes/webnote21.pdf
Bakk, Z., Tekle, F. B., & Vermunt, J. K. (2013). Estimating the association between latent class membership and external variables using bias-adjusted three-step approaches. Sociological Methodology, 43(1), 272–311. doi:10.1177/0081175012470644
Bartolucci, F., Montanari, G. E., & Pandolfi, S. (2015). Three-step estimation of latent Markov models with covariates. Computational Statistics & Data Analysis, 83, 287–301. doi:10.1016/j.csda.2014.10.017
Di Mari, R., Oberski, D. L., & Vermunt, J. K. (2016). Bias-adjusted three-step latent Markov modeling with covariates. Structural Equation Modeling: A Multidisciplinary Journal, 23(5), 649–660. doi:10.1080/10705511.2016.1191015
Liang, Q., de la Torre, J., & Law, N. (2023). Latent transition cognitive diagnosis model with covariates: A three-step approach. Journal of Educational and Behavioral Statistics, 48(6), 690–718. doi:10.3102/10769986231163320
Nylund-Gibson, K., Grimm, R., Quirk, M., & Furlong, M. (2014). A latent transition mixture model using the three-step specification. Structural Equation Modeling: A Multidisciplinary Journal, 21(3), 439–454. doi:10.1080/10705511.2014.915375
Nylund-Gibson, K., Grimm, R. P., & Masyn, K. E. (2019). Prediction from latent classes: A demonstration of different approaches to include distal outcomes in mixture models. Structural Equation Modeling: A Multidisciplinary Journal, 26(6), 967–985. doi:10.1080/10705511.2019.1590146
Nylund-Gibson, K., Arch, D. A. N., & Carter, D. (2026). Latent transition analysis with auxiliary variables: A demonstration of the ML 3-step and BCH in Mplus. The Quantitative Methods for Psychology, 22(1), 1–8. doi:10.20982/tqmp.22.1.p001
Vermunt, J. K. (2010). Latent class modeling with covariates: Two improved three-step approaches. Political Analysis, 18(4), 450–469. doi:10.1093/pan/mpq025
LCA(), LPA(), LCPA(), get.CEP()
library(LCPA) set.seed(1245) N <- 2000 L <- 3 I <- 6 times <- 2 # Two observed covariates plus the required intercept at both times zeta.1 <- as.numeric(scale(rnorm(N))) zeta.2 <- rbinom(N, 1, 0.5) covariates <- list( cbind(Intercept = 1, Zeta.1 = zeta.1, Zeta.2 = zeta.2), cbind(Intercept = 1, Zeta.1 = zeta.1, Zeta.2 = zeta.2) ) beta <- matrix(c( 0.70, 0.30, 0, 0.40, -0.20, 0, -0.30, 0.30, 0 ), ncol = L, byrow = TRUE) rownames(beta) <- colnames(covariates[[1]]) gamma <- list(list( list(c( 0.90, 0.25, -0.15), c( 0.20, -0.15, 0.20), c(0, 0, 0)), list(c( 0.10, 0.20, 0.10), c( 0.80, -0.20, -0.15), c(0, 0, 0)), list(c(-0.10, 0.15, -0.10), c( 0.20, 0.10, 0.15), c(0, 0, 0)) )) data.LTA <- sim.LTA( N = N, I = I, L = L, times = times, type = "LCA", poly.value = 2, IQ = 0.95, distribution = "uniform", covariates = covariates, ref.class = 3, beta = beta, gamma = gamma, is.sort = TRUE ) control.model <- list( is.sort = TRUE, starts = 10, maxiter.warmup = 10, nrep = 3 ) # Covariates predicting initial states and transitions: XZ analysis fit.LTA.XZ <- LTA( responses = data.LTA$responses, L = L, type.analysis = "XZ", type.model = "LCA", covariates = covariates, ref.class = 3, method.model = "EM", control.model = control.model, method.3step = "ML", method.regression = "Analytic", method.SE = "Analytic", maxiter = 500, vis = TRUE ) round(cbind( "True Class 1" = beta[, 1], "Estimate Class 1" = fit.LTA.XZ$beta[, 1], "True Class 2" = beta[, 2], "Estimate Class 2" = fit.LTA.XZ$beta[, 2] ), 3) gamma.matrix <- function(value) { do.call(rbind, lapply(seq_len(L), function(k) { do.call(rbind, value[[1]][[k]][seq_len(L - 1)]) })) } gamma.true <- gamma.matrix(gamma) gamma.estimate <- gamma.matrix(fit.LTA.XZ$gamma) rownames(gamma.true) <- rownames(gamma.estimate) <- paste0( "Origin.", rep(seq_len(L), each = L - 1), ".To.", rep(seq_len(L - 1), times = L) ) colnames(gamma.true) <- paste0("True.", colnames(covariates[[2]])) colnames(gamma.estimate) <- paste0("Estimate.", colnames(covariates[[2]])) round(cbind(gamma.true, gamma.estimate), 3) # Final latent state predicting two dependent variables: ZY-State true.state.mean <- rbind( "Class 1" = c(Depression = 8, Anxiety = 12), "Class 2" = c(Depression = 10, Anxiety = 10), "Class 3" = c(Depression = 13, Anxiety = 8) ) dependent.variables <- scale( true.state.mean[data.LTA$Zs[[times]], ] + matrix(rnorm(N * 2, sd = 1.5), N, 2) ) true.state.mean.standardized <- sweep( sweep(true.state.mean, 2, attr(dependent.variables, "scaled:center"), "-"), 2, attr(dependent.variables, "scaled:scale"), "/" ) true.state.variance.standardized <- (1.5 / attr(dependent.variables, "scaled:scale"))^2 dependent.variables <- as.data.frame(dependent.variables) fit.LTA.ZY <- LTA( responses = data.LTA$responses, L = L, type.analysis = "ZY", type.model = "LCA", dependent.variables = dependent.variables, family = "gaussian", dependent.variable.structure = "State", dependent.variable.time = times, method.model = "EM", control.model = control.model, method.3step = "BCH", method.regression = "Analytic", method.SE = "Analytic", vis = TRUE ) round(cbind( True.Depression = true.state.mean.standardized[, "Depression"], Estimate.Depression = fit.LTA.ZY$dependent.variables$t2$Depression$estimate, True.Anxiety = true.state.mean.standardized[, "Anxiety"], Estimate.Anxiety = fit.LTA.ZY$dependent.variables$t2$Anxiety$estimate ), 3) round(cbind( True.Variance.Depression = rep( true.state.variance.standardized["Depression"], L ), Estimate.Variance.Depression = fit.LTA.ZY$dependent.variables$t2$Depression$variance, True.Variance.Anxiety = rep( true.state.variance.standardized["Anxiety"], L ), Estimate.Variance.Anxiety = fit.LTA.ZY$dependent.variables$t2$Anxiety$variance ), 3) # Complete latent-state paths predicting two dependent variables: ZY-Path paths <- expand.grid(t1 = seq_len(L), t2 = seq_len(L)) true.path.mean <- cbind( Wellbeing = 16 - paths$t1 - 2 * paths$t2, Stress = 5 + paths$t2 + 1.5 * (paths$t1 != paths$t2) ) rownames(true.path.mean) <- paste("Path", paste(paths$t1, paths$t2, sep = "-")) path.index <- data.LTA$Zs[[1]] + L * (data.LTA$Zs[[2]] - 1L) dependent.variables.path <- scale( true.path.mean[path.index, ] + matrix(rnorm(N * 2, sd = 1.5), N, 2) ) true.path.mean.standardized <- sweep( sweep(true.path.mean, 2, attr(dependent.variables.path, "scaled:center"), "-"), 2, attr(dependent.variables.path, "scaled:scale"), "/" ) true.path.variance.standardized <- (1.5 / attr(dependent.variables.path, "scaled:scale"))^2 dependent.variables.path <- as.data.frame(dependent.variables.path) fit.LTA.ZY.path <- LTA( responses = data.LTA$responses, L = L, type.analysis = "ZY", type.model = "LCA", dependent.variables = dependent.variables.path, family = "gaussian", dependent.variable.structure = "Path", dependent.variable.time = times, method.model = "EM", control.model = control.model, method.3step = "BCH", method.regression = "Analytic", method.SE = "Analytic", vis = TRUE ) round(cbind( True.Wellbeing = true.path.mean.standardized[, "Wellbeing"], Estimate.Wellbeing = fit.LTA.ZY.path$dependent.variables$t2$Wellbeing$estimate, True.Stress = true.path.mean.standardized[, "Stress"], Estimate.Stress = fit.LTA.ZY.path$dependent.variables$t2$Stress$estimate ), 3) round(cbind( True.Variance.Wellbeing = rep( true.path.variance.standardized["Wellbeing"], L^times ), Estimate.Variance.Wellbeing = fit.LTA.ZY.path$dependent.variables$t2$Wellbeing$variance, True.Variance.Stress = rep( true.path.variance.standardized["Stress"], L^times ), Estimate.Variance.Stress = fit.LTA.ZY.path$dependent.variables$t2$Stress$variance ), 3)library(LCPA) set.seed(1245) N <- 2000 L <- 3 I <- 6 times <- 2 # Two observed covariates plus the required intercept at both times zeta.1 <- as.numeric(scale(rnorm(N))) zeta.2 <- rbinom(N, 1, 0.5) covariates <- list( cbind(Intercept = 1, Zeta.1 = zeta.1, Zeta.2 = zeta.2), cbind(Intercept = 1, Zeta.1 = zeta.1, Zeta.2 = zeta.2) ) beta <- matrix(c( 0.70, 0.30, 0, 0.40, -0.20, 0, -0.30, 0.30, 0 ), ncol = L, byrow = TRUE) rownames(beta) <- colnames(covariates[[1]]) gamma <- list(list( list(c( 0.90, 0.25, -0.15), c( 0.20, -0.15, 0.20), c(0, 0, 0)), list(c( 0.10, 0.20, 0.10), c( 0.80, -0.20, -0.15), c(0, 0, 0)), list(c(-0.10, 0.15, -0.10), c( 0.20, 0.10, 0.15), c(0, 0, 0)) )) data.LTA <- sim.LTA( N = N, I = I, L = L, times = times, type = "LCA", poly.value = 2, IQ = 0.95, distribution = "uniform", covariates = covariates, ref.class = 3, beta = beta, gamma = gamma, is.sort = TRUE ) control.model <- list( is.sort = TRUE, starts = 10, maxiter.warmup = 10, nrep = 3 ) # Covariates predicting initial states and transitions: XZ analysis fit.LTA.XZ <- LTA( responses = data.LTA$responses, L = L, type.analysis = "XZ", type.model = "LCA", covariates = covariates, ref.class = 3, method.model = "EM", control.model = control.model, method.3step = "ML", method.regression = "Analytic", method.SE = "Analytic", maxiter = 500, vis = TRUE ) round(cbind( "True Class 1" = beta[, 1], "Estimate Class 1" = fit.LTA.XZ$beta[, 1], "True Class 2" = beta[, 2], "Estimate Class 2" = fit.LTA.XZ$beta[, 2] ), 3) gamma.matrix <- function(value) { do.call(rbind, lapply(seq_len(L), function(k) { do.call(rbind, value[[1]][[k]][seq_len(L - 1)]) })) } gamma.true <- gamma.matrix(gamma) gamma.estimate <- gamma.matrix(fit.LTA.XZ$gamma) rownames(gamma.true) <- rownames(gamma.estimate) <- paste0( "Origin.", rep(seq_len(L), each = L - 1), ".To.", rep(seq_len(L - 1), times = L) ) colnames(gamma.true) <- paste0("True.", colnames(covariates[[2]])) colnames(gamma.estimate) <- paste0("Estimate.", colnames(covariates[[2]])) round(cbind(gamma.true, gamma.estimate), 3) # Final latent state predicting two dependent variables: ZY-State true.state.mean <- rbind( "Class 1" = c(Depression = 8, Anxiety = 12), "Class 2" = c(Depression = 10, Anxiety = 10), "Class 3" = c(Depression = 13, Anxiety = 8) ) dependent.variables <- scale( true.state.mean[data.LTA$Zs[[times]], ] + matrix(rnorm(N * 2, sd = 1.5), N, 2) ) true.state.mean.standardized <- sweep( sweep(true.state.mean, 2, attr(dependent.variables, "scaled:center"), "-"), 2, attr(dependent.variables, "scaled:scale"), "/" ) true.state.variance.standardized <- (1.5 / attr(dependent.variables, "scaled:scale"))^2 dependent.variables <- as.data.frame(dependent.variables) fit.LTA.ZY <- LTA( responses = data.LTA$responses, L = L, type.analysis = "ZY", type.model = "LCA", dependent.variables = dependent.variables, family = "gaussian", dependent.variable.structure = "State", dependent.variable.time = times, method.model = "EM", control.model = control.model, method.3step = "BCH", method.regression = "Analytic", method.SE = "Analytic", vis = TRUE ) round(cbind( True.Depression = true.state.mean.standardized[, "Depression"], Estimate.Depression = fit.LTA.ZY$dependent.variables$t2$Depression$estimate, True.Anxiety = true.state.mean.standardized[, "Anxiety"], Estimate.Anxiety = fit.LTA.ZY$dependent.variables$t2$Anxiety$estimate ), 3) round(cbind( True.Variance.Depression = rep( true.state.variance.standardized["Depression"], L ), Estimate.Variance.Depression = fit.LTA.ZY$dependent.variables$t2$Depression$variance, True.Variance.Anxiety = rep( true.state.variance.standardized["Anxiety"], L ), Estimate.Variance.Anxiety = fit.LTA.ZY$dependent.variables$t2$Anxiety$variance ), 3) # Complete latent-state paths predicting two dependent variables: ZY-Path paths <- expand.grid(t1 = seq_len(L), t2 = seq_len(L)) true.path.mean <- cbind( Wellbeing = 16 - paths$t1 - 2 * paths$t2, Stress = 5 + paths$t2 + 1.5 * (paths$t1 != paths$t2) ) rownames(true.path.mean) <- paste("Path", paste(paths$t1, paths$t2, sep = "-")) path.index <- data.LTA$Zs[[1]] + L * (data.LTA$Zs[[2]] - 1L) dependent.variables.path <- scale( true.path.mean[path.index, ] + matrix(rnorm(N * 2, sd = 1.5), N, 2) ) true.path.mean.standardized <- sweep( sweep(true.path.mean, 2, attr(dependent.variables.path, "scaled:center"), "-"), 2, attr(dependent.variables.path, "scaled:scale"), "/" ) true.path.variance.standardized <- (1.5 / attr(dependent.variables.path, "scaled:scale"))^2 dependent.variables.path <- as.data.frame(dependent.variables.path) fit.LTA.ZY.path <- LTA( responses = data.LTA$responses, L = L, type.analysis = "ZY", type.model = "LCA", dependent.variables = dependent.variables.path, family = "gaussian", dependent.variable.structure = "Path", dependent.variable.time = times, method.model = "EM", control.model = control.model, method.3step = "BCH", method.regression = "Analytic", method.SE = "Analytic", vis = TRUE ) round(cbind( True.Wellbeing = true.path.mean.standardized[, "Wellbeing"], Estimate.Wellbeing = fit.LTA.ZY.path$dependent.variables$t2$Wellbeing$estimate, True.Stress = true.path.mean.standardized[, "Stress"], Estimate.Stress = fit.LTA.ZY.path$dependent.variables$t2$Stress$estimate ), 3) round(cbind( True.Variance.Wellbeing = rep( true.path.variance.standardized["Wellbeing"], L^times ), Estimate.Variance.Wellbeing = fit.LTA.ZY.path$dependent.variables$t2$Wellbeing$variance, True.Variance.Stress = rep( true.path.variance.standardized["Stress"], L^times ), Estimate.Variance.Stress = fit.LTA.ZY.path$dependent.variables$t2$Stress$variance ), 3)
Standardizes each column of a numeric matrix or data frame to have mean zero and standard deviation one. This transformation is essential for many multivariate techniques that assume standardized inputs. The function preserves all dimension names and returns a pure numeric matrix with attributes storing original column means and standard deviations.
normalize(response)normalize(response)
response |
A numeric matrix or data frame of dimension
Non-numeric columns will be coerced to numeric with a warning. Missing values are not allowed
and will cause the function to fail. Constant columns (zero variance) will produce |
A standardized numeric matrix of dimension with attributes:
scaled:center: Vector of original column means ()
scaled:scale: Vector of original column standard deviations ()
Row names: Preserved from original input's row names or row indices
Column names: Preserved from original input's column names
Values: Z-scores calculated as
where:
= original value for observation and variable
= sample mean of variable :
= sample standard deviation of variable :
The denominator provides an unbiased estimator of population variance.
For each column in the input matrix , the standardization is performed as:
where:
is the -th column vector of
is the sample mean of column
is the sample standard deviation of column
The resulting matrix has the properties:
for all .
# Basic usage with matrix set.seed(123) mat <- matrix(rnorm(30, mean = 5:7, sd = 1:3), ncol = 3, dimnames = list(paste0("Obs", 1:10), paste0("Var", 1:3))) norm_mat <- normalize(mat) # Verify attributes attr(norm_mat, "scaled:center") # Original column means attr(norm_mat, "scaled:scale") # Original column standard deviations # Verify properties apply(norm_mat, 2, mean) # Should be near zero apply(norm_mat, 2, sd) # Should be exactly 1 # With data frame input df <- as.data.frame(mat) norm_df <- normalize(df) all.equal(norm_mat, norm_df, check.attributes = FALSE) # Should be identical # Handling constant columns (produces NaN) const_mat <- cbind(mat, Constant = rep(4.2, 10)) normalize(const_mat)# Basic usage with matrix set.seed(123) mat <- matrix(rnorm(30, mean = 5:7, sd = 1:3), ncol = 3, dimnames = list(paste0("Obs", 1:10), paste0("Var", 1:3))) norm_mat <- normalize(mat) # Verify attributes attr(norm_mat, "scaled:center") # Original column means attr(norm_mat, "scaled:scale") # Original column standard deviations # Verify properties apply(norm_mat, 2, mean) # Should be near zero apply(norm_mat, 2, sd) # Should be exactly 1 # With data frame input df <- as.data.frame(mat) norm_df <- normalize(df) all.equal(norm_mat, norm_df, check.attributes = FALSE) # Should be identical # Handling constant columns (produces NaN) const_mat <- cbind(mat, Constant = rep(4.2, 10)) normalize(const_mat)
Generates user-friendly, publication-ready visualizations for objects generated by the LCPA package.
This generic function dispatches to class-specific methods that produce diagnostic and interpretive plots
tailored to each model type. Designed for interactive exploration, model interpretation, and presentation-quality output.
## S3 method for class 'LCA' plot(x, y = NULL, ncol = 2, ...) ## S3 method for class 'LPA' plot(x, y = NULL, ncol = 2, ...)## S3 method for class 'LCA' plot(x, y = NULL, ncol = 2, ...) ## S3 method for class 'LPA' plot(x, y = NULL, ncol = 2, ...)
x |
An object of one of the following classes: |
y |
Reserved for compatibility with the base |
ncol |
Number of columns in the multi-panel layout (default: 2). Controls arrangement of latent class/profile panels. |
... |
Additional arguments passed to methods. |
Each method produces a structured, visually intuitive plot optimized for its object type.
See plot.LCA and plot.LPA for
detailed parameter options.
Invisibly returns the final ggplot or patchwork object. No data is modified.
plot(LCA): Plot method for LCA objects
plot(LPA): Plot method for LPA objects (with covariance heatmap)
Creates a publication-quality density plot showing the distribution of responses across multiple indicators/items/variables. Automatically handles variable ordering, color scaling, and legend layout based on the number of variables.
plotResponse(response)plotResponse(response)
response |
A matrix or data frame containing response data where:
Non-numeric columns (except row identifiers) will cause errors. |
A ggplot object containing:
Density curves for each variable colored by indicator
Adaptive color schemes based on number of variables
Optimized legend layout for large numbers of indicators
Publication-ready theme with grid lines and clean styling
The plot can be further customized using standard ggplot2 syntax.
The plot uses a minimal theme with:
Light grey grid lines for readability
Black axis lines and ticks (0.8pt thickness)
White background with no panel border
Optimized font sizes (13pt axis titles, 11pt tick labels)
Legend positioned on right with adaptive sizing
# Simulate response data for 5 indicators set.seed(42) resp_data <- data.frame( indicator1 = rnorm(100, mean = 3, sd = 1), indicator2 = rnorm(100, mean = 2, sd = 0.8), indicator3 = rnorm(100, mean = 4, sd = 1.2), indicator4 = rnorm(100, mean = 3.5, sd = 0.9), indicator5 = rnorm(100, mean = 2.5, sd = 1.1) ) library(LCPA) # Generate and display plot p <- plotResponse(resp_data) print(p) # For data with many indicators (18 indicators example) many_indicators <- as.data.frame(replicate(18, rnorm(50, mean = runif(1, 1, 5), sd = 1))) names(many_indicators) <- paste0("Q", 1:18) p_large <- plotResponse(many_indicators) print(p_large)# Simulate response data for 5 indicators set.seed(42) resp_data <- data.frame( indicator1 = rnorm(100, mean = 3, sd = 1), indicator2 = rnorm(100, mean = 2, sd = 0.8), indicator3 = rnorm(100, mean = 4, sd = 1.2), indicator4 = rnorm(100, mean = 3.5, sd = 0.9), indicator5 = rnorm(100, mean = 2.5, sd = 1.1) ) library(LCPA) # Generate and display plot p <- plotResponse(resp_data) print(p) # For data with many indicators (18 indicators example) many_indicators <- as.data.frame(replicate(18, rnorm(50, mean = runif(1, 1, 5), sd = 1))) names(many_indicators) <- paste0("Q", 1:18) p_large <- plotResponse(many_indicators) print(p_large)
Provides user-friendly, formatted console output for objects generated by the LCPA package.
This generic function dispatches to class-specific methods that display concise summaries of model results,
simulated datasets, fit indices, model comparisons, and standard errors. Designed for interactive use
and quick diagnostic inspection.
## S3 method for class 'LCA' print(x, ...) ## S3 method for class 'summary.LCA' print(x, ...) ## S3 method for class 'LPA' print(x, ...) ## S3 method for class 'summary.LPA' print(x, ...) ## S3 method for class 'LTA' print(x, ...) ## S3 method for class 'summary.LTA' print(x, ...) ## S3 method for class 'LCPA' print(x, ...) ## S3 method for class 'summary.LCPA' print(x, ...) ## S3 method for class 'sim.LCA' print(x, ...) ## S3 method for class 'summary.sim.LCA' print(x, ...) ## S3 method for class 'sim.LPA' print(x, ...) ## S3 method for class 'summary.sim.LPA' print(x, ...) ## S3 method for class 'sim.LTA' print(x, ...) ## S3 method for class 'summary.sim.LTA' print(x, ...) ## S3 method for class 'fit.index' print(x, ...) ## S3 method for class 'summary.fit.index' print(x, ...) ## S3 method for class 'compare.model' print(x, ...) ## S3 method for class 'summary.compare.model' print(x, ...) ## S3 method for class 'SE' print(x, digits = 4, I.max = 5, L.max = 3, ...) ## S3 method for class 'summary.SE' print(x, ...)## S3 method for class 'LCA' print(x, ...) ## S3 method for class 'summary.LCA' print(x, ...) ## S3 method for class 'LPA' print(x, ...) ## S3 method for class 'summary.LPA' print(x, ...) ## S3 method for class 'LTA' print(x, ...) ## S3 method for class 'summary.LTA' print(x, ...) ## S3 method for class 'LCPA' print(x, ...) ## S3 method for class 'summary.LCPA' print(x, ...) ## S3 method for class 'sim.LCA' print(x, ...) ## S3 method for class 'summary.sim.LCA' print(x, ...) ## S3 method for class 'sim.LPA' print(x, ...) ## S3 method for class 'summary.sim.LPA' print(x, ...) ## S3 method for class 'sim.LTA' print(x, ...) ## S3 method for class 'summary.sim.LTA' print(x, ...) ## S3 method for class 'fit.index' print(x, ...) ## S3 method for class 'summary.fit.index' print(x, ...) ## S3 method for class 'compare.model' print(x, ...) ## S3 method for class 'summary.compare.model' print(x, ...) ## S3 method for class 'SE' print(x, digits = 4, I.max = 5, L.max = 3, ...) ## S3 method for class 'summary.SE' print(x, ...)
x |
An object of one of the following classes:
|
... |
Reserved for S3 method compatibility; no additional arguments are used. |
digits |
Number of decimal places for numeric output (default: varies by method, often 4).
Used by |
I.max |
Maximum number of variables/items to display before truncation (default: varies, e.g., 5).
Used by |
L.max |
Maximum number of latent classes/profiles to display before truncation (default: varies, e.g., 3).
Used by |
Each method produces a structured, human-readable summary optimized for its object type:
LCA/LPA/LCPA/LTA)Invokes summary() internally and prints comprehensive output including:
Model call and configuration (method, constraints, reference class)
Data characteristics (N, I, time points, distribution)
Likelihood fit statistics for models for which they are defined
Class/profile prior probabilities and frequencies
Item-response probabilities (LCA) or profile means (LPA)
For XZ: regression coefficients with significance markers and 95% CIs
For Gaussian ZY: conditional means and variances with standard errors, 95% CIs, and omnibus equality tests
For categorical ZY: conditional category probabilities with standard errors, 95% CIs, and an omnibus distributional test
Convergence diagnostics (iterations, tolerance, hardware)
Replication details (if nrep > 1)
sim.LCA/sim.LPA/sim.LTA)Displays simulation design and true parameter structure:
Configuration (N, I, L, times, constraint, distribution)
True class/profile proportions and observed frequencies
For sim.LCA: item category structure and conditional probabilities
For sim.LPA: covariance constraint description and mean ranges
For sim.LTA: transition mode (fixed/covariate), initial/transition coefficients
Output is truncated for high-dimensional structures using I.max and L.max.
fit.index)Presents a clean table of model fit criteria:
Header with dimensions (N, I, L, npar)
Formatted table: AIC, BIC, SABIC, CAIC, AWE, -2LL, SIC
Interpretation note: lower values are preferred for information criteria
Values rounded to digits decimal places
compare.model)Compares two nested models with statistical tests:
Comparative fit table (npar, LogLik, AIC, BIC, entropy)
Classification quality (AvePP per class, overall entropy)
Bayes Factor with interpretive guidance
Likelihood ratio tests (standard, VLMR, Bootstrap) with p-values and significance codes
Clear section headers and visual separators
SE)Displays uncertainty estimates for model parameters:
Class probability SEs (always fully shown)
Profile means SEs (LPA) or item-response SEs (LCA), truncated by L.max/I.max
Covariance SE summary (non-zero count only; full access via extract())
Method-specific diagnostics
All summary methods are called internally by their corresponding
print methods.
They pre-compute and structure output for consistent formatting. Direct calls are also supported.
Invisibly returns the input object x. No data is modified.
print(LCA): Print method for LCA objects
print(summary.LCA): Print method for summary.LCA objects
print(LPA): Print method for LPA objects
print(summary.LPA): Print method for summary.LPA objects
print(LTA): Print method for LTA objects
print(summary.LTA): Print method for summary.LTA objects
print(LCPA): Print method for LCPA objects
print(summary.LCPA): Print method for summary.LCPA objects
print(sim.LCA): Print method for sim.LCA objects
print(summary.sim.LCA): Print method for summary.sim.LCA objects
print(sim.LPA): Print method for sim.LPA objects
print(summary.sim.LPA): Print method for summary.sim.LPA objects
print(sim.LTA): Print method for sim.LTA objects
print(summary.sim.LTA): Print method for summary.sim.LTA objects
print(fit.index): Print method for fit.index objects
print(summary.fit.index): Print method for summary.fit.index objects
print(compare.model): Print method for compare.model objects
print(summary.compare.model): Print method for summary.compare.model objects
print(SE): Print method for SE objects
print(summary.SE): Print method for summary.SE objects
Numeric values are typically rounded to 4 decimal places unless overridden by digits.
Large matrices (e.g., item parameters, transition coefficients) are truncated with clear messages.
Significance markers: *** (<0.001), ** (<0.01), * (<0.05), . (<0.1).
95% confidence intervals computed as: Estimate ± 1.96 × Std.Error.
Reference classes (for multinomial models) are explicitly stated.
rdirichlet generates n random observations from a Dirichlet distribution
with a specified concentration parameter vector alpha.
rdirichlet(n, alpha)rdirichlet(n, alpha)
n |
Integer. The number of random vectors to generate. |
alpha |
Numeric vector. The concentration parameters (must be positive).
The length of this vector determines the number of dimensions |
The Dirichlet distribution is a family of continuous multivariate probability distributions
parameterized by a vector of positive reals. It is the multivariate
generalization of the beta distribution and is commonly used as a conjugate prior
to the multinomial distribution in Bayesian statistics.
Probability density function:
For a vector
on the unit simplex,
where and , the density is
where the normalizing constant is the multivariate beta function:
Simulation method:
The function uses independent Gamma random variables
:
The resulting vector follows a Dirichlet
distribution with parameter vector .
A matrix with n rows and length(alpha) columns.
Each row sums to 1, representing a single sample from the Dirichlet distribution.
# Generate 5 samples from a 3-dimensional Dirichlet distribution set.seed(123) alpha_params <- c(1, 2, 5) result <- rdirichlet(n = 5, alpha = alpha_params) print(result) # Check that rows sum to 1 rowSums(result)# Generate 5 samples from a 3-dimensional Dirichlet distribution set.seed(123) alpha_params <- c(1, 2, 5) result <- rdirichlet(n = 5, alpha = alpha_params) print(result) # Check that rows sum to 1 rowSums(result)
This function generates a random correlation matrix using the C-vine partial correlation
parameterization described in Joe & Kurowicka (2026). The method constructs the matrix recursively using
partial correlations organized in a C-vine structure, with distributional properties controlled by LKJ
concentration and skewness parameters.
sim.correlation( I, eta = 1, skew = 0, positive = FALSE, permute = TRUE, maxattempts = 10 )sim.correlation( I, eta = 1, skew = 0, positive = FALSE, permute = TRUE, maxattempts = 10 )
I |
Dimension of the correlation matrix (must be |
eta |
LKJ concentration parameter ( |
skew |
Skewness parameter (
|
positive |
Logical. If |
permute |
Logical. If |
maxattempts |
Integer. Maximum number of matrix draws when |
The algorithm follows four key steps:
Partial correlation sampling:
For tree level and node , partial correlations are sampled as:
If positive = FALSE:
If positive = TRUE:
Recursive matrix construction (C-vine):
The correlation matrix is built without matrix inversion using backward recursion:
Tree 1 (raw correlations): for
Trees : For pairs where and :
This implements the dynamic programming approach from Joe & Kurowicka (2026, Section 2.1).
Positive definiteness (when positive = TRUE):
The returned matrix is positive definite.
Exchangeability (optional):
If permute = TRUE, rows/columns are randomly permuted before returning the matrix.
An positive definite correlation matrix with unit diagonal.
The theoretical condition is recommended for
positive-definite matrices in high dimensions.
Joe, H., & Kurowicka, D. (2026). Random correlation matrices generated via partial correlation C-vines. Journal of Multivariate Analysis, 211, 105519. doi:10.1016/j.jmva.2025.105519
# Default 3x3 correlation matrix sim.correlation(3) # 5x5 matrix concentrated near identity (eta=3) sim.correlation(5, eta = 3) # Skewed toward positive correlations (no permutation) sim.correlation(4, skew = 0.7, permute = FALSE) # Positive partial correlations (enforced positive definiteness) R <- sim.correlation(6, positive = TRUE) min(eigen(R, symmetric = TRUE, only.values = TRUE)$values) # > 0 # High-dimensional case (I=20) with theoretical guarantee R <- sim.correlation(20, eta = 10) # eta=10 > (20-2)/2=9 min(eigen(R, symmetric = TRUE, only.values = TRUE)$values)# Default 3x3 correlation matrix sim.correlation(3) # 5x5 matrix concentrated near identity (eta=3) sim.correlation(5, eta = 3) # Skewed toward positive correlations (no permutation) sim.correlation(4, skew = 0.7, permute = FALSE) # Positive partial correlations (enforced positive definiteness) R <- sim.correlation(6, positive = TRUE) min(eigen(R, symmetric = TRUE, only.values = TRUE)$values) # > 0 # High-dimensional case (I=20) with theoretical guarantee R <- sim.correlation(20, eta = 10) # eta=10 > (20-2)/2=9 min(eigen(R, symmetric = TRUE, only.values = TRUE)$values)
Generates synthetic multivariate categorical data from a latent class model with L latent classes.
Each observed variable follows a multinomial distribution within classes, with flexible control over
class separation via the IQ parameter and class size distributions.
sim.LCA( N = 1000, I = 10, L = 3, poly.value = 5, IQ = "random", distribution = "random", params = NULL, is.sort = TRUE )sim.LCA( N = 1000, I = 10, L = 3, poly.value = 5, IQ = "random", distribution = "random", params = NULL, is.sort = TRUE )
N |
Integer; total number of observations to simulate. Must be |
I |
Integer; number of categorical observed variables. Must be |
L |
Integer; number of latent classes. Must be |
poly.value |
Integer or integer vector; number of categories (levels) for each observed variable.
If scalar, all variables share the same number of categories. If vector, must have length |
IQ |
Character or numeric; controls category probability distributions:
|
distribution |
Character; distribution of class sizes. Options: |
params |
List with fixed parameters for simulation:
|
is.sort |
A logical value. If |
Probability generation:
Dirichlet Sampling (IQ="random"):
For each variable-class combination, probabilities are drawn from
where .
High-Discrimination Mode (IQ=numeric):
For each variable i:
Generate special probabilities par.special of length L containing:
, , and values uniformly sampled from .
For each class l, assign par.special[l] to one category, distribute
remaining probability uniformly (via Dirichlet) across other categories.
Shuffle category assignments to avoid position bias.
Data generation:
Class assignments Z are generated first according to distribution.
For each observation p and variable i:
Retrieve cumulative probabilities for class Z[p]
Draw uniform random number
Assign category k where
Entire dataset is regenerated if any category of any variable has zero observations.
Critical constraints:
When IQ is numeric: and min(poly.value) >= 2
N must be sufficiently large to observe all categories, especially when IQ is high
or poly.value is large. Simulation may fail for small N.
For distribution="uniform", empty classes possible when .
A list containing:
Integer matrix () of simulated observations. Rows are observations (named "O1", "O2", ...),
columns are variables named "I1", "I2", ... Values range from 0 to poly.value[i]-1.
Array () of true class-specific category probabilities,
where (i.e., the maximum number of categories across variables).
Dimensions: classes x variables x categories.
Note: For variables with poly.value[i] < K, unused category dimensions contain NA.
Dimension names: "Class 1", "Class 2", and so on for classes;
"I1", "I2", and so on for indicators;
"poly0", "poly1", ... (categories).
Integer vector (length ) of true class assignments (1 to L). Named with observation IDs (e.g., "O1").
Numeric vector (length ) of true class proportions,
named "Class 1", "Class 2", and so on.
Integer vector (length ) specifying number of categories per variable.
Binary matrix () of true class membership indicators (one-hot encoded).
Row i, column l = 1 if observation i belongs to class l, else 0.
Row/column names match Z and class labels.
A list containing all input arguments.
The matched simulation call.
Controls the discriminative power of observed variables:
IQ = "random"(Default)
Category probabilities for each variable-class combination are drawn from a symmetric Dirichlet distribution
(), resulting in moderate class separation.
IQ = numeric(0.5 < IQ < 1) Forces high discriminative power for each variable:
Across the classes, construct focal
probabilities containing , , and, when
, values sampled from .
For each class, assign its focal probability to one category and distribute the remaining mass over the other categories using a symmetric Dirichlet draw.
Randomize category positions within each class.
Higher IQ values (closer to 1) yield stronger class separation but increase simulation failure risk.
"random"(Default) Class proportions drawn from Dirichlet distribution ( for all classes),
ensuring no empty classes. Sizes are rounded to integers with adjustment for exact N.
"uniform"Equal probability of class membership ( per class), sampled with replacement.
May produce empty classes if N is small relative to L.
The simulation enforces a critical constraint: every category of every observed variable must appear at least once in the dataset. If initial generation violates this (e.g., a rare category is missing), parameters and responses are regenerated until satisfied. This ensures compatibility with standard LCA estimation.
# Example 1: Default settings (moderate separation, random class sizes) sim_data <- sim.LCA(N = 30, I = 5, L = 3) # Example 2: High-discrimination indicators (IQ=0.85), uniform class sizes sim_high_disc <- sim.LCA( N = 30, I = 4, L = 2, poly.value = c(3,4,3,5), # Variable category counts IQ = 0.85, distribution = "uniform" ) # Example 3: Binary indicators (poly.value=2) with high separation sim_binary <- sim.LCA(N = 300, I = 10, L = 2, poly.value = 2, IQ = 0.9)# Example 1: Default settings (moderate separation, random class sizes) sim_data <- sim.LCA(N = 30, I = 5, L = 3) # Example 2: High-discrimination indicators (IQ=0.85), uniform class sizes sim_high_disc <- sim.LCA( N = 30, I = 4, L = 2, poly.value = c(3,4,3,5), # Variable category counts IQ = 0.85, distribution = "uniform" ) # Example 3: Binary indicators (poly.value=2) with high separation sim_binary <- sim.LCA(N = 300, I = 10, L = 2, poly.value = 2, IQ = 0.9)
Generates synthetic multivariate continuous data from a latent profile model with L latent classes.
Supports flexible covariance structure constraints (including custom equality constraints) and
class size distributions. All covariance matrices are ensured to be positive definite.
sim.LPA( N = 1000, I = 5, L = 2, constraint = "VV", distribution = "random", mean.range = c(-2, 2), covs.range = c(0.01, 4), params = NULL, is.sort = TRUE )sim.LPA( N = 1000, I = 5, L = 2, constraint = "VV", distribution = "random", mean.range = c(-2, 2), covs.range = c(0.01, 4), params = NULL, is.sort = TRUE )
N |
Integer; total number of observations to simulate. Must be |
I |
Integer; number of continuous observed variables. Must be |
L |
Integer; number of latent profiles (classes). Must be |
constraint |
Character string or list specifying covariance constraints. See detailed description below.
Default is |
distribution |
Character; distribution of class sizes. Options: |
mean.range |
Numeric vector of length 2; range for sampling class-specific means.
Each variable's means are sampled uniformly from |
covs.range |
Numeric vector of length 2; range for sampling variance parameters (diagonal elements).
Must satisfy |
params |
List with fixed parameters for simulation:
|
is.sort |
A logical value. If |
Mean Generation: For each variable, candidate means are sampled uniformly from mean.range.
distinct means are selected without replacement to ensure separation between classes.
Covariance Generation:
Univariate case (I=1): Constraints "UE" and "UV" are enforced automatically.
Predefined constraints like "E0" map to "UE".
Class Assignment:
"random": Uses Dirichlet distribution () to avoid extremely small classes.
Sizes are rounded and adjusted to sum exactly to N.
"uniform": Simple random sampling with equal probability. May produce empty classes if N is small.
Data Generation: Observations are simulated using mvtnorm::rmvnorm per class.
Final data and class labels are shuffled to remove ordering artifacts.
A list containing:
Numeric matrix () of simulated observations. Rows are observations,
columns are variables named "V1", "V2", ..., or "V" for univariate data.
Numeric matrix () of true class-specific means.
Row names: "Profile 1", "Profile 2", and so on; column names
match response.
Array () of true class-specific covariance matrices.
Dimensions: variables x variables x classes. Constrained parameters have identical values across class slices.
Dimension names match response and class labels.
Numeric matrix () of true class membership probabilities (one-hot encoded).
Row i, column l = 1 if observation i belongs to class l, else 0.
Row names: "O1", "O2", ...; column names:
"Profile 1", "Profile 2", and so on.
Numeric vector (length ) of true class proportions.
Named "Profile 1", "Profile 2", and so on.
Integer vector (length ) of true class assignments (1 to L).
Named with observation IDs (e.g., "O1").
Original constraint specification (character string or list) passed to the function.
Matched simulation call.
List of the effective simulation arguments.
The constraint parameter controls equality constraints on covariance parameters across classes:
"UE" (Univariate only)Equal variance across all classes.
"UV" (Univariate only)Varying variances across classes.
"E0"Equal variances across classes, zero covariances (diagonal matrix with shared variances).
"V0"Varying variances across classes, zero covariances (diagonal matrix with free variances).
"EE"Equal full covariance matrix across all classes (homogeneous).
"EV"Equal variances but varying covariances (equal diagonal, free off-diagonal).
"VE"Varying variances but equal correlations (free diagonal, equal correlation structure).
"VV"Varying full covariance matrices across classes (heterogeneous; default).
Each element specifies a pair of variables whose covariance parameters are constrained equal across classes:
c(i,i)Constrains variance of variable i to be equal across all classes.
c(i,j)Constrains covariance between variables i and j to be equal across all classes
(symmetric: automatically includes c(j,i)).
Unconstrained parameters vary freely, and the returned covariance matrices are positive definite. Critical requirements for custom constraints:
I.I=1), only list(c(1,1)) is valid."random"(Default) Class proportions drawn from Dirichlet distribution ( for all classes),
ensuring no empty classes. Sizes are rounded to integers with adjustment for exact N.
"uniform"Equal probability of class membership ( per class), sampled with replacement.
# Example 1: Bivariate data, 3 classes, heterogeneous covariances (default) sim_data <- sim.LPA(N = 500, I = 2, L = 3, constraint = "VV") # Example 2: Univariate data, equal variances # 'E0' automatically maps to 'UE' for I=1 sim_uni <- sim.LPA(N = 200, I = 1, L = 2, constraint = "E0") # Example 3: Custom constraints # - Equal covariance between V1 and V2 across classes # - Equal variance for V3 across classes sim_custom <- sim.LPA( N = 300, I = 3, L = 4, constraint = list(c(1, 2), c(3, 3)) ) # Example 4: VE constraint (varying variances, equal correlations) sim_ve <- sim.LPA(N = 400, I = 3, L = 3, constraint = "VE") # Example 5: Uniform class sizes sim_uniform <- sim.LPA(N = 300, I = 4, L = 5, distribution = "uniform")# Example 1: Bivariate data, 3 classes, heterogeneous covariances (default) sim_data <- sim.LPA(N = 500, I = 2, L = 3, constraint = "VV") # Example 2: Univariate data, equal variances # 'E0' automatically maps to 'UE' for I=1 sim_uni <- sim.LPA(N = 200, I = 1, L = 2, constraint = "E0") # Example 3: Custom constraints # - Equal covariance between V1 and V2 across classes # - Equal variance for V3 across classes sim_custom <- sim.LPA( N = 300, I = 3, L = 4, constraint = list(c(1, 2), c(3, 3)) ) # Example 4: VE constraint (varying variances, equal correlations) sim_ve <- sim.LPA(N = 400, I = 3, L = 3, constraint = "VE") # Example 5: Uniform class sizes sim_uniform <- sim.LPA(N = 300, I = 4, L = 5, distribution = "uniform")
Simulates longitudinal latent class/profile data where initial class membership and transition probabilities may be influenced by time-varying covariates. Supports both Latent Class Analysis (LCA) for categorical outcomes and Latent Profile Analysis (LPA) for continuous outcomes. Measurement invariance is assumed by default (identical indicator parameters across time).
sim.LTA( N = 500, I = 5, L = 3, times = 2, type = "LCA", distribution = "random", constraint = "VV", poly.value = 5, IQ = "random", mean.range = c(-2, 2), covs.range = c(0.01, 4), params = NULL, is.sort = TRUE, rate = NULL, covariates = NULL, ref.class = L, beta = NULL, gamma = NULL )sim.LTA( N = 500, I = 5, L = 3, times = 2, type = "LCA", distribution = "random", constraint = "VV", poly.value = 5, IQ = "random", mean.range = c(-2, 2), covs.range = c(0.01, 4), params = NULL, is.sort = TRUE, rate = NULL, covariates = NULL, ref.class = L, beta = NULL, gamma = NULL )
N |
Integer; sample size. |
I |
Integer; number of observed indicators/items/indicators per time point. |
L |
Integer; number of latent classes/profiles. |
times |
Integer; number of time points (must be |
type |
Character; type of latent model. |
distribution |
Character; distribution of initial class probabilities when not using covariates or |
constraint |
Character; covariance structure for LPA ( |
poly.value |
Integer; number of categories for polytomous LCA indicators (default: 5). |
IQ |
Character; method for generating indicator discrimination in LCA. |
mean.range |
Numeric vector; range for randomly generated class means in LPA (default: |
covs.range |
Numeric vector; range for covariance matrix diagonals in LPA (default: |
params |
List or NULL; pre-specified measurement and initial-class parameters in the final first-time-point class order (see Details). |
is.sort |
A logical value. If |
rate |
List of matrices or NULL; transition probability matrices in the final class order for non-covariate mode.
Each matrix is |
covariates |
List of matrices or NULL; covariate matrices for each time point. Each matrix must have
dimensions |
ref.class |
Integer between 1 and |
beta |
Matrix or NULL; initial state regression coefficients of dimension
|
gamma |
List or NULL; transition regression coefficients. Must be a list of length |
Covariate Requirements:
Covariate matrices must include an intercept (first column = 1). If omitted, the function adds an intercept and issues a warning.
When covariates is provided but beta or gamma is NULL, coefficients are
randomly generated from (non-reference classes only).
All supplied simulation parameters use the final first-time-point class order. They are never reordered or reparameterized. Internally generated initial coefficients are ordered and reparameterized before they are returned. The same order is used for every transition.
Parameter Compatibility:
Use params to fix indicator parameters (LCA) or class means/covariances (LPA) across simulations.
In non-covariate mode, rate must be a list of valid transition matrices (ignored when times=1).
In covariate mode with times>=2, all three (covariates, beta, gamma) must be consistent in dimensions.
A list of class "sim.LTA" containing:
responsesList of length times; observed data matrices ().
ZsList of length times; true latent class memberships ( vectors).
P.ZsList of length times; marginal class probabilities at each time.
parIndicator parameters for LCA (if type="LCA").
meansClass means for LPA (if type="LPA").
covsClass covariance matrices for LPA (if type="LPA").
poly.valueCategory counts for LCA (if type="LCA").
rateTrue transition matrices (non-covariate mode only; NULL when times=1).
covariatesList of covariate matrices used (covariate mode only).
betaTrue initial state coefficients (covariate mode only).
gammaTrue transition coefficients (covariate mode only; NULL when times=1).
ref.classReference class of the returned beta and gamma coefficients.
callFunction call.
argumentsInput arguments.
For participant at time 1, the probability of belonging to
latent class is
Here
; the leading 1 is the
intercept, and indexes observed covariates. The
coefficient vector
has the corresponding intercept and slopes.
The class selected by ref.class is the reference class and has a zero coefficient vector.
For participant transitioning from class at time
to class at time (),
Here
, and
contains the
corresponding intercept and slopes. The destination class selected by
ref.class has a zero
coefficient vector for every origin class.
Initial probabilities follow a multinomial distribution with probabilities .
When , transitions follow a Markov process with fixed
probabilities
, where
for each origin class and
time .
####################### Example 1: Single time point (times=1) ###################### library(LCPA) set.seed(123) sim_single <- sim.LTA(N = 200, I = 4, L = 3, times = 1, type = "LCA") print(sim_single) ####################### Example 2: LPA without covariates ###################### set.seed(123) sim_lta <- sim.LTA(N = 200, I = 3, L = 3, times = 3, type = "LPA", constraint = "VE") print(sim_lta) ################## Example 3: With custom covariates (times>=2) ###################### set.seed(123) N <- 200 ## sample size ## Covariates at time point T1 covariates.inter <- rep(1, N) # Intercept term is always 1 for each n covariates.X1 <- rnorm(N) # Covariate X1 is a continuous variable covariates.X2 <- rbinom(N, 1, 0.5) # Covariate X2 is a binary variable covariates.X1.X2 <- covariates.X1 * covariates.X2 # Interaction between covariates X1 and X2 covariates.T1 <- cbind(inter=covariates.inter, X1=covariates.X1, X2=covariates.X2, X1.X2=covariates.X1.X2) # Combine into covariates at T1 ## Covariates at time point T2 covariates.inter <- rep(1, N) # Intercept term is always 1 for each n covariates.X1 <- rnorm(N) # Covariate X1 is a continuous variable covariates.X2 <- rbinom(N, 1, 0.5) # Covariate X2 is a binary variable covariates.X1.X2 <- covariates.X1 * covariates.X2 # Interaction between covariates X1 and X2 covariates.T2 <- cbind(inter=covariates.inter, X1=covariates.X1, X2=covariates.X2, X1.X2=covariates.X1.X2) # Combine into covariates at T2 covariates <- list(t1=covariates.T1, t2=covariates.T2) # Combine into final covariates list ## Simulate beta coefficients # 3x3 matrix (last column is zero because the last category is used as reference) beta <- matrix(c( 1.3, 0.5, 0.0, 0.1, 0.4, 0.0, -0.6, -0.8, 0.0, -0.3, -0.2, 0.0), ncol=3, byrow=TRUE) ## Simulate gamma coefficients (only needed when times>=2) gamma <- list( lapply(1:3, function(l) { lapply(1:3, function(k) if(k < 3) runif(4, -1.0, 1.0) else c(0, 0, 0, 0)) # Last class as reference }) ) ## Simulate the data sim_custom <- sim.LTA( N=N, I=4, L=3, times=2, type="LPA", ref.class=3, covariates=covariates, beta=beta, gamma=gamma ) summary(sim_custom)####################### Example 1: Single time point (times=1) ###################### library(LCPA) set.seed(123) sim_single <- sim.LTA(N = 200, I = 4, L = 3, times = 1, type = "LCA") print(sim_single) ####################### Example 2: LPA without covariates ###################### set.seed(123) sim_lta <- sim.LTA(N = 200, I = 3, L = 3, times = 3, type = "LPA", constraint = "VE") print(sim_lta) ################## Example 3: With custom covariates (times>=2) ###################### set.seed(123) N <- 200 ## sample size ## Covariates at time point T1 covariates.inter <- rep(1, N) # Intercept term is always 1 for each n covariates.X1 <- rnorm(N) # Covariate X1 is a continuous variable covariates.X2 <- rbinom(N, 1, 0.5) # Covariate X2 is a binary variable covariates.X1.X2 <- covariates.X1 * covariates.X2 # Interaction between covariates X1 and X2 covariates.T1 <- cbind(inter=covariates.inter, X1=covariates.X1, X2=covariates.X2, X1.X2=covariates.X1.X2) # Combine into covariates at T1 ## Covariates at time point T2 covariates.inter <- rep(1, N) # Intercept term is always 1 for each n covariates.X1 <- rnorm(N) # Covariate X1 is a continuous variable covariates.X2 <- rbinom(N, 1, 0.5) # Covariate X2 is a binary variable covariates.X1.X2 <- covariates.X1 * covariates.X2 # Interaction between covariates X1 and X2 covariates.T2 <- cbind(inter=covariates.inter, X1=covariates.X1, X2=covariates.X2, X1.X2=covariates.X1.X2) # Combine into covariates at T2 covariates <- list(t1=covariates.T1, t2=covariates.T2) # Combine into final covariates list ## Simulate beta coefficients # 3x3 matrix (last column is zero because the last category is used as reference) beta <- matrix(c( 1.3, 0.5, 0.0, 0.1, 0.4, 0.0, -0.6, -0.8, 0.0, -0.3, -0.2, 0.0), ncol=3, byrow=TRUE) ## Simulate gamma coefficients (only needed when times>=2) gamma <- list( lapply(1:3, function(l) { lapply(1:3, function(k) if(k < 3) runif(4, -1.0, 1.0) else c(0, 0, 0, 0)) # Last class as reference }) ) ## Simulate the data sim_custom <- sim.LTA( N=N, I=4, L=3, times=2, type="LPA", ref.class=3, covariates=covariates, beta=beta, gamma=gamma ) summary(sim_custom)
Generates structured, comprehensive summaries of objects produced by the LCPA package.
This generic function dispatches to class-specific methods that extract and organize key information
including model configurations, fit statistics, parameter estimates, simulation truths, and diagnostics.
Designed for programmatic access and downstream reporting.
## S3 method for class 'LCA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'LPA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'LTA' summary(object, digits = 4, ...) ## S3 method for class 'LCPA' summary(object, digits = 4, ...) ## S3 method for class 'sim.LCA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'sim.LPA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'sim.LTA' summary(object, digits = 4, I.max = 5, L.max = 5, ...) ## S3 method for class 'fit.index' summary(object, digits = 4, ...) ## S3 method for class 'compare.model' summary(object, digits = 4, ...) ## S3 method for class 'SE' summary(object, ...)## S3 method for class 'LCA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'LPA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'LTA' summary(object, digits = 4, ...) ## S3 method for class 'LCPA' summary(object, digits = 4, ...) ## S3 method for class 'sim.LCA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'sim.LPA' summary(object, digits = 4, I.max = 5, ...) ## S3 method for class 'sim.LTA' summary(object, digits = 4, I.max = 5, L.max = 5, ...) ## S3 method for class 'fit.index' summary(object, digits = 4, ...) ## S3 method for class 'compare.model' summary(object, digits = 4, ...) ## S3 method for class 'SE' summary(object, ...)
object |
An object of one of the following classes:
|
digits |
Number of decimal places for numeric output (default: 4). Applied universally across all methods. |
I.max |
Maximum number of variables/items to display for |
... |
Reserved for S3 method compatibility; no additional arguments are used. |
L.max |
Maximum number of latent classes/profiles to display before truncation ( |
Each method returns a class-specific list designed both for its corresponding print method and for programmatic access:
LCAA summary.LCA object with:
callOriginal fitting call.
model.configNumber of latent classes and estimation method.
data.infoSample size, item count, number of categories per item, and whether category counts are uniform.
fit.statsLog-likelihood, AIC, BIC, entropy, and number of free parameters.
class.probsData frame containing class labels, modal-assignment counts, and estimated class proportions.
item.probsConditional response-probability matrices for the first I.max items.
convergenceBackend-specific algorithm, iteration, tolerance, initialization, and diagnostic information.
replicationReplication count and best BIC when replication selection applies; otherwise NULL.
digits, I.max.shown, total.items
Formatting and truncation metadata.
LPAA summary.LPA object with:
callOriginal fitting call.
model.configNumber of profiles, requested covariance constraint, its expanded description, and estimation method.
data.infoSample size, variable count, and multivariate-normal distribution label.
fit.statsLog-likelihood, AIC, BIC, entropy, and number of free parameters.
class.probsData frame containing profile labels, modal-assignment counts, and estimated profile proportions.
class.meansProfile-specific means for the first I.max variables.
convergenceBackend-specific algorithm, iteration, tolerance, initialization, and diagnostic information.
replicationReplication count and best BIC when replication selection applies; otherwise NULL.
digits, I.max.shown, total.vars
Formatting and truncation metadata.
LCPAA summary.LCPA object with:
callOriginal fitting call.
model.configAnalysis path, number of classes/profiles, model and three-step methods, Step 1 source, dependent-variable structure where applicable, and classification-error handling.
data.infoSample size and number of response variables.
fit.statsFor XZ, log-likelihood, AIC, BIC, and number of free parameters.
class.probsData frame containing class probabilities, proportions, and modal-assignment frequencies.
coefficientsFor XZ, the non-reference-class coefficient table with estimates, standard errors, 95 percent confidence limits, z statistics, and two-sided p-values.
dependent.variablesFor ZY, fitted conditional distributions nested by model and dependent variable. Gaussian entries contain class/profile-specific means and variances, their standard errors and covariance matrices, and separate omnibus Wald tests. Categorical entries contain class/profile-specific category probabilities, standard errors, covariance matrices, and an omnibus test of equality of the conditional distributions.
covariates.names, ref.class
Displayed covariate names and the multinomial-logit reference class.
convergenceOverall and model-specific Step 3 convergence and iteration information.
digits, vars.to.show, total.vars, has.covariates
Formatting and covariate metadata.
LTAA summary.LTA object with:
callOriginal fitting call.
model.configNumber of time points and classes, model type, Step 1 source, reference class, covariate mode, classification-error handling, and transition mode.
data.infoSample size, response-variable count, and number of time points.
fit.statsFor XZ, log-likelihood, AIC, BIC, and number of free parameters.
class.probsTime-indexed data frames containing class probabilities, proportions, and modal-assignment frequencies.
initial.modelFor XZ, the initial-status coefficient table, covariate names, and reference class.
transition.modelsFor XZ, time-invariant or time-indexed transition coefficient tables with origin class, destination class, covariate, estimate, standard error, confidence limits, z statistic, and p-value.
dependent.variablesFor ZY, state- or path-specific Gaussian means and variances or categorical probabilities, together with their standard errors, covariance matrices, confidence-interval inputs, group masses, and omnibus Wald tests.
convergenceOverall and model-specific Step 3 convergence and iteration information.
digits, total.vars, covariates.time.cross, ref.class
Formatting, covariate, and reference-class metadata.
sim.LCAA summary.sim.LCA object with:
callOriginal simulation call.
configSample size, item count, class count, category counts, category-count uniformity, item quality, and generating distribution.
class.probsTrue class probabilities and realized frequencies.
item.probsTrue conditional response probabilities for the first I.max items.
digits, I.max.shown, total.vars
Formatting and truncation metadata.
sim.LPAA summary.sim.LPA object with:
callOriginal simulation call.
configSample size, variable count, profile count, constraint specification and description, and generating distribution.
class.probsTrue profile probabilities and realized frequencies.
class.meansTrue profile means for the first I.max variables.
constraintExpanded description of the covariance constraint.
digits, I.max.shown, total.vars
Formatting and truncation metadata.
sim.LTAA summary.sim.LTA object with:
callOriginal simulation call.
configSample size, variable count, class count, time points, model type, generating distribution, coefficient reference class, and LPA constraint when applicable.
class.probsTime-indexed true class probabilities and realized frequencies.
item.probs, class.means
Truncated true measurement parameters for LCA or LPA simulations, respectively.
transitionFixed-rate or covariate-dependent transition specification, including beta/gamma parameters and time indices when present.
covariatesTime-indexed covariate summaries containing minima, maxima, and means, or NULL.
digits, I.max.shown, L.max.shown, total.vars, total.classes
Formatting and truncation metadata.
fit.indexA
summary.fit.index object with:
callCall that produced the fit-index object.
data.infoList containing the sample size N.
fit.tableData frame with Statistic, Value, and Description columns for
npar, Log.Lik, -2LL, AIC, BIC, SIC, CAIC, AWE, and SABIC.
digitsRequested numeric precision.
compare.modelA summary.compare.model object with:
callCall that produced the model comparison.
data.infoLists the named model-specific sample sizes and indicator counts and the two class counts.
fit.tableSide-by-side table of class count, parameter count, log-likelihood, -2LL, AIC, BIC, SIC, CAIC, AWE, and SABIC.
model.comparisonData frame comparing class counts, parameter counts, diagonal average posterior probabilities, and entropy.
BF, BF.interpretation
Bayes factor computed from SIC and its evidence label.
LRT.tableSeparate rows for the standard LRT, VLMR, adjusted LMR, and BLRT when available, with statistics, degrees of freedom, p-values, and significance symbols.
LRT.objectsNamed list containing the unmodified hypothesis-test objects used to build LRT.table.
digitsRequested numeric precision.
SEA summary.SE object with:
callCall that produced the standard-error object.
methodSelected "Bootstrap", "Obs", or "Louis" method.
diagnosticsComplete method-specific diagnostic list from get.SE().
type"LCA", "LPA", or "Unknown", inferred from the standard-error components.
L, I
Number of classes/profiles and variables/items.
nonzero.countsCounts of nonzero standard errors for P.Z and, as applicable, par, means, and covs.
total.P.ZTotal number of class-proportion standard errors.
A structured list whose S3 class identifies the corresponding
summary method, such as summary.LCA or
summary.LPA. Every summary method returns its object
visibly, so an interactive call to summary() dispatches
automatically to the corresponding print method.
summary(LCA): Summary method for LCA objects
summary(LPA): Summary method for LPA objects
summary(LTA): Summary method for LTA objects
summary(LCPA): Summary method for LCPA objects
summary(sim.LCA): Summary method for sim.LCA objects
summary(sim.LPA): Summary method for sim.LPA objects
summary(sim.LTA): Summary method for sim.LTA objects
summary(fit.index): Summary method for fit.index objects
summary(compare.model): Summary method for compare.model objects
summary(SE): Summary method for summary.SE objects
The update function provides a unified and convenient interface to refresh or modify
existing objects generated by the LCPA package. It allows users to re-run model fitting
or data simulation with new parameter settings while preserving all other original configurations.
Supported classes include: LCA, LPA,
LCPA, LTA, sim.LCA,
sim.LPA, and sim.LTA.
update(object, ...) ## S3 method for class 'LCA' update(object, ...) ## S3 method for class 'LPA' update(object, ...) ## S3 method for class 'LCPA' update(object, ...) ## S3 method for class 'LTA' update(object, ...) ## S3 method for class 'sim.LCA' update(object, ...) ## S3 method for class 'sim.LPA' update(object, ...) ## S3 method for class 'sim.LTA' update(object, ...)update(object, ...) ## S3 method for class 'LCA' update(object, ...) ## S3 method for class 'LPA' update(object, ...) ## S3 method for class 'LCPA' update(object, ...) ## S3 method for class 'LTA' update(object, ...) ## S3 method for class 'sim.LCA' update(object, ...) ## S3 method for class 'sim.LPA' update(object, ...) ## S3 method for class 'sim.LTA' update(object, ...)
object |
An object of one of the following classes: |
... |
Named arguments that override values in the original call.
Every argument must be named, unique, and present in the formal arguments
of the corresponding constructor. Valid arguments depend on the class of
|
Internally, each method extracts the stored arguments list from the input object object,
merges it with user-provided ... using modifyList, then re-invokes
the corresponding constructor function (LCA(),
LPA(), LCPA(),
LTA(), sim.LCA(), etc.) with the
merged argument list.
This ensures that:
Only explicitly overridden parameters are changed.
Default values from the original call remain intact.
Complex nested structures (e.g., control lists) can be partially updated.
Invalid, unnamed, or duplicated arguments are rejected before the constructor is called. This prevents a misspelled option from appearing to update an object while leaving the fitted model unchanged.
An object of the same class as object, reconstructed using the original arguments
updated with any provided in .... All unchanged parameters are preserved from the original call.
update(LCA): Update method for LCA objects
update(LPA): Update method for LPA objects
update(LCPA): Update method for LCPA objects
update(LTA): Update method for LTA objects
update(sim.LCA): Update method for sim.LCA objects
update(sim.LPA): Update method for sim.LPA objects
update(sim.LTA): Update method for sim.LTA objects
library(LCPA) # --- Update LCA --- data <- sim.LCA(N=500, I=5, L=3) lca.obj <- LCA(data$response, L=3) lca.updated <- update(lca.obj, method="EM", nrep=5) # --- Update LPA --- data2 <- sim.LPA(N=300, I=4, L=2) lpa.obj <- LPA(data2$response, L=2, constraint="VE") lpa.updated <- update(lpa.obj, constraint="VV") # --- Update Simulation Objects --- sim.obj1 <- sim.LCA(N=1000) sim.obj1_updated <- update(sim.obj1, N=2000, IQ=0.8) sim.obj2 <- sim.LPA(I=6) sim.obj2_updated <- update(sim.obj2, I=8, mean.range=c(-2,2)) sim.obj3 <- sim.LTA(N=200, I=5, L=2, times=3) sim.obj3_updated <- update(sim.obj3, N=300, times=4, constraint="ER")library(LCPA) # --- Update LCA --- data <- sim.LCA(N=500, I=5, L=3) lca.obj <- LCA(data$response, L=3) lca.updated <- update(lca.obj, method="EM", nrep=5) # --- Update LPA --- data2 <- sim.LPA(N=300, I=4, L=2) lpa.obj <- LPA(data2$response, L=2, constraint="VE") lpa.updated <- update(lpa.obj, constraint="VV") # --- Update Simulation Objects --- sim.obj1 <- sim.LCA(N=1000) sim.obj1_updated <- update(sim.obj1, N=2000, IQ=0.8) sim.obj2 <- sim.LPA(I=6) sim.obj2_updated <- update(sim.obj2, I=8, mean.range=c(-2,2)) sim.obj3 <- sim.LTA(N=200, I=5, L=2, times=3) sim.obj3_updated <- update(sim.obj3, N=300, times=4, constraint="ER")