Newly fitted models retain the specification used to construct them.
You can inspect and plot the returned object without repeating its
outcome names, curve family, or selected waves. The underlying OpenMx
model remains available in fit@mxOutput.
This example generates five measurements for 80 people with a random intercept and slope. Times are measured from the first occasion. The example is deliberately small so that it can be fitted when this article is built.
set.seed(20261024)
n <- 80L
times <- 0:4
intercept <- rnorm(n, mean = 10, sd = 2)
slope <- rnorm(n, mean = 1.5, sd = 0.3)
dat <- data.frame(ID = seq_len(n))
for (j in seq_along(times)) {
dat[[paste0("T", j)]] <- times[j]
dat[[paste0("Y", j)]] <- intercept + slope * times[j] + rnorm(n, sd = 0.5)
}
fit <- getLGCM(
dat, t_var = "T", y_var = "Y", records = 1:5,
curveFun = "LIN", intrinsic = FALSE, paramOut = TRUE
)The compact print method gives an overview. The summary method delegates to OpenMx, so its convergence and fit diagnostics remain available.
print(fit)
#> nlpsem fit: LGCM, Linear Functional Form
#> Fitted with: getLGCM()
#> Observations: 80
#> Free parameters: 6
#> -2 log-likelihood: 1018
#> Optimizer status: 0
summary(fit)
#> Summary of LGCM, Linear Functional Form
#>
#> free parameters:
#> name matrix row col Estimate Std.Error A
#> 1 Y_residuals S Y1 Y1 0.226498519 0.02067648
#> 2 Y_psi00 S eta0 eta0 3.992536537 0.65290505
#> 3 Y_psi01 S eta0 eta1 0.000168311 0.07117458
#> 4 Y_psi11 S eta1 eta1 0.074688813 0.01552890
#> 5 Y_mueta0 M 1 eta0 9.893848969 0.22716833
#> 6 Y_mueta1 M 1 eta1 1.549372372 0.03488170
#>
#> Model Statistics:
#> | Parameters | Degrees of Freedom | Fit (-2lnL units)
#> Model: 6 394 1018.368
#> Saturated: 20 380 NA
#> Independence: 10 390 NA
#> Number of observations/statistics: 80/400
#>
#> Information Criteria:
#> | df Penalty | Parameters Penalty | Sample-Size Adjusted
#> AIC: 230.3684 1030.368 1031.519
#> BIC: -708.1501 1044.661 1025.740
#> To get additional fit indices, see help(mxRefModels)
#> timestamp: 2026-11-01 23:57:15
#> Wall clock time: 0.06041312 secs
#> optimizer: SLSQP
#> OpenMx version number: 2.22.11
#> Need help? See help(mxSummary)Optimizer completion does not establish statistical admissibility. Inspect stored covariance matrices, uncertainty information and component sizes without refitting:
diagnostics <- getDiagnostics(fit)
diagnostics
#> $optimizer
#> $optimizer$status
#> [1] 0
#>
#> $optimizer$status_zero
#> [1] TRUE
#>
#> $optimizer$finite_likelihood
#> [1] TRUE
#>
#>
#> $covariance
#> model matrix block dimension
#> 1 LGCM, Linear Functional Form S RAM disturbances 7
#> 2 LGCM, Linear Functional Form S Latent disturbances 2
#> 3 LGCM, Linear Functional Form S Manifest residuals 5
#> 4 LGCM, Linear Functional Form Y_psi0 Stored covariance algebra 2
#> status minimum_variance minimum_eigenvalue
#> 1 positive_definite 0.07468881 0.07468881
#> 2 positive_definite 0.07468881 0.07468881
#> 3 positive_definite 0.22649852 0.22649852
#> 4 positive_definite 0.07468881 0.07468881
#> scaled_minimum_eigenvalue negative_variances
#> 1 0.9996918
#> 2 0.9996918
#> 3 1.0000000
#> 4 0.9996918
#> detail
#> 1 Stored matrix; diagonal-scaled eigenvalue classification
#> 2 Stored matrix; diagonal-scaled eigenvalue classification
#> 3 Stored matrix; diagonal-scaled eigenvalue classification
#> 4 Stored matrix; diagonal-scaled eigenvalue classification
#>
#> $cross_covariances
#> [1] model matrix rows columns
#> [5] row_variables column_variables status detail
#> <0 rows> (or 0-length row.names)
#>
#> $hessian
#> $hessian$dimension
#> [1] 6
#>
#> $hessian$status
#> [1] "positive_definite"
#>
#> $hessian$minimum_diagonal
#> [1] 4.741344
#>
#> $hessian$minimum_eigenvalue
#> [1] 4.691115
#>
#> $hessian$scaled_minimum_eigenvalue
#> [1] 0.7983534
#>
#> $hessian$negative_diagonals
#> [1] ""
#>
#> $hessian$detail
#> [1] "Stored matrix; diagonal-scaled eigenvalue classification"
#>
#> $hessian$source
#> [1] "calculatedHessian"
#>
#> $hessian$openmx_info_definite
#> [1] TRUE
#>
#>
#> $standard_errors
#> $standard_errors$status
#> [1] "no_issues_detected"
#>
#> $standard_errors$expected
#> [1] 6
#>
#> $standard_errors$available
#> [1] 6
#>
#> $standard_errors$nonfinite
#> [1] 0
#>
#> $standard_errors$nonpositive
#> [1] 0
#>
#>
#> $component_sizes
#> $component_sizes$status
#> [1] "not_applicable"
#>
#> $component_sizes$table
#> data frame with 0 columns and 0 rows
#>
#> $component_sizes$detail
#> [1] "Not a recognized nlpsem mixture"
#>
#>
#> $issues
#> character(0)
#>
#> $repeated_starts
#> [1] "not_assessed"
#>
#> $limitations
#> [1] "No refitting or statistical acceptance is performed"
#> [2] "Definition-variable covariances and other unsupported expectations are not fully checked"
#> [3] "Identification, global optimality, repeated-start stability, and interval calibration are not established"
#> [4] "Stored outputs can be stale if a fitted model was manually modified"A materially negative variance or indefinite latent covariance requires investigation. Small boundary eigenvalues need scale-aware interpretation. Missing Hessian or standard error information means that check is unavailable. These checks do not establish global optimality: repeat fitting with defensible alternative starts and compare admissible solutions. Keep the seed, optimizer and package versions with the analysis.
The coefficient method returns the named free parameters at full
precision. printTable(fit) displays the package’s estimate
table, including derived quantities when that model supplies them; these
serve different purposes.
coef(fit)
#> Y_residuals Y_psi00 Y_psi01 Y_psi11 Y_mueta0 Y_mueta1
#> 0.226498519 3.992536537 0.000168311 0.074688813 9.893848969 1.549372372
logLik(fit)
#> 'log Lik.' -509.1842 (df=6)
printTable(fit)
#>
#> Point Estimates and Standard Errors:
#> Name Estimate SE
#> 1 mueta0 9.893848969 0.22716833
#> 2 mueta1 1.549372372 0.03488170
#> 3 psi00 3.992536537 0.65290505
#> 4 psi01 0.000168311 0.07117458
#> 5 psi11 0.074688813 0.01552890
#> 6 residuals 0.226498519 0.02067648Use getSpecification() to inspect the recorded modeling
choices and fitting provenance. This is useful when returning to a saved
analysis or explaining how a figure was constructed.
provenance <- getSpecification(fit)
names(provenance)
#> [1] "schema_version" "call" "function_name" "specification"
#> [5] "fitting" "data" "versions" "diagnostics"
provenance$specification
#> $t_var
#> [1] "T"
#>
#> $y_var
#> [1] "Y"
#>
#> $curveFun
#> [1] "LIN"
#>
#> $intrinsic
#> [1] FALSE
#>
#> $records
#> [1] 1 2 3 4 5
#>
#> $growth_TIC
#> NULL
#>
#> $TVC
#> NULL
#>
#> $decompose
#> NULL
#>
#> $x_type
#> NULL
#>
#> $x_var
#> NULL
#>
#> $m_var
#> NULL
#>
#> $grp_var
#> NULL
#>
#> $cluster_TIC
#> NULL
#>
#> $sub_Model
#> [1] "LGCM"
#>
#> $y_model
#> [1] "LGCM"
#>
#> $container
#> [1] "single"
#>
#> $nClass
#> NULL
provenance$versions
#> $R
#> [1] "R version 4.6.2 (2026-10-29)"
#>
#> $OpenMx
#> [1] "2.22.11"
#>
#> $nlpsem
#> [1] "1.0.0"The full object also retains resolved starting values, fitting settings, and available RNG state. Save the original data together with the fitted object; provenance records the specification but is not an automatically replayable call.
The plotting method reads the recorded model specification. Supply only presentation choices such as the time label and outcome title.
plot(fit, xstarts = 0, xlab = "Years since baseline", outcome = "Simulated outcome")
#> figOutput Object
#> --------------------
#> Trajectories: 1
#> Figure 1:The intervals describe uncertainty in the model-implied mean at each plotted time. They are pointwise intervals, not prediction intervals for individual people or a simultaneous confidence band for the entire curve.
For further customization, request the plotting object without drawing it:
figures <- plot(fit, draw = FALSE, xstarts = 0, xlab = "Years since baseline",
outcome = "Simulated outcome")
class(figures)
#> [1] "figOutput"
#> attr(,"package")
#> [1] "nlpsem"Its figures slot contains the ggplot objects. The layout
follows getFigure(): one entry per outcome, with
growth-status or change/rate plots within that entry.
getSimData() generates wide-format data from a model
with known parameter values. Each data set carries those values as the
attribute truth, retaining the generating parameterization
and time origin. This example generates six measurements for 500 people
from a quadratic latent growth curve model with the default parameter
values.
sim <- getSimData(n = 500, curveFun = "QUAD", nT = 6, seed = 20261027)
truth <- attr(sim, "truth")
sim_fit <- getLGCM(
sim, t_var = "T", y_var = "Y", records = 1:6,
curveFun = "QUAD", intrinsic = FALSE, paramOut = TRUE
)
estimates <- sim_fit@Estimates[match(names(truth), sim_fit@Estimates$Name), ]
data.frame(truth = truth, estimate = round(estimates$Estimate, 3), SE = round(estimates$SE, 3))
#> truth estimate SE
#> mueta0 50.00 50.171 0.220
#> mueta1 20.00 20.024 0.058
#> mueta2 -1.00 -0.979 0.014
#> psi00 25.00 23.321 1.526
#> psi01 1.50 1.556 0.287
#> psi02 0.45 0.343 0.073
#> psi11 1.00 0.945 0.108
#> psi12 0.09 0.088 0.019
#> psi22 0.09 0.076 0.007
#> residuals 1.00 0.980 0.036Equal parameter names do not always mean equal parameters. For
example, a quadratic growth curve’s eta0 is its level at
time zero, while an LCSM’s eta0 is its level at the first
fitted occasion. For quadratic and nonparametric data,
truth_lcsm supplies the parameter targets at the first
generated occasion:
shifted <- getSimData(n = 30, curveFun = "QUAD", times = 5:14, jitter = 0, seed = 1)
cbind(LGCM = attr(shifted, "truth")[c("mueta0", "psi00")],
LCSM = attr(shifted, "truth_lcsm")[c("mueta0", "psi00")])
#> LGCM LCSM
#> mueta0 50 125.00
#> psi00 25 166.25
attr(shifted, "truth_info")$first_observation_time
#> [1] 5The quadratic transformation includes the mean, covariance and
covariate effects; the initial mean and variance above are 125 and
166.25 for the LCSM, versus the time-zero values 50 and 25. The
nonparametric generator already starts at the first observation. These
targets cover all generated records; a later selected baseline needs
reparameterization, potentially for each person if that time is
jittered. truth_info records the origin, reference records
and applicable limits.
truth_lcsm is unavailable for EXP and JB: their midpoint
approximation, and a random ratio’s nonlinear transformation, preclude
treating a simple conversion as an exact recovery target. Direct
comparisons with the generating truth need a zero first
fitted time for every person and the approximation qualifications in
?getSimData. For TVC models, transformed factor targets
alone do not establish equivalence of the full growth-curve and
change-score data-generating models.
The data also carry each person’s growth factors
(growth_factors) and the full generating values
(settings). Supply params to change the
generating values, and a list of parameter sets with prop
to generate latent classes. The same function generates data for
multivariate, time-varying covariate and mediation models:
bivariate <- getSimData(n = 200, sub_Model = "MGM", curveFun = "LIN", seed = 1)
with_tvc <- getSimData(n = 200, sub_Model = "TVC", curveFun = "LIN", decompose = 1, seed = 1)
mediation <- getSimData(n = 200, sub_Model = "MED", curveFun = "LIN", x_type = "baseline", seed = 1)
head(attr(mediation, "truth"))
#> muX phi11 alphaM0 alphaM1 psiM0M0_r psiM0M1_r
#> 0.0 1.0 50.0 5.0 25.0 1.5The matching fit for each is getMGM(),
getTVCmodel() and getMediation() with the same
functional form; ?getSimData lists the parameter values
each model takes.
The fitting functions retain their existing arguments and output slots. Calls that work directly with OpenMx models, such as the following, remain available:
getSummary(model_list = list(fit@mxOutput))
#> Model No_Params -2ll AIC BIC Y_residuals
#> 1 Model1 6 1018.37 1030.37 1044.66 0.2265Objects saved by older nlpsem versions may not contain specification metadata. Their OpenMx results can still be inspected, but structural choices must be supplied when plotting. For example, for an older univariate linear fit:
plot(
old_fit, sub_Model = "LGCM", y_var = "Y", curveFun = "LIN",
t_var = "T", records = 1:5, xstarts = 0,
xlab = "Years since baseline", outcome = "Outcome"
)Use the specification from the original analysis. Do not infer the selected waves or curve family solely from the appearance of a plotted trajectory. See the model-specific vignettes for time-varying covariates, multiple outcomes, mediation, groups, and mixtures.