In this vignette we demonstrate how to define new methods.
We will generate a dataset comprising two clusters with a different intercept and slope. Firstly, we configure the default id, time, and response variable, so that these do not need to be provided to the other function calls.
library(latrend)
library(data.table)
options(
latrend.id = "Traj",
latrend.time = "Time",
latrend.verbose = TRUE
)
Next, we generate the dataset, with 40 trajectories for cluster A and 60 trajectories for cluster B. Cluster A involves trajectories with a downward slope, whereas cluster B has an upward slope. We define a fixed mean of 1, such that all the cluster trajectories are shifted, placing cluster A at intercept 2, and cluster B at intercept 1.
set.seed(1)
<- generateLongData(
casedata sizes = c(40, 60),
data = data.frame(Time = 0:10),
fixed = Y ~ 1,
fixedCoefs = 1,
cluster = ~ Time,
clusterCoefs = cbind(c(2, -.1), c(0, .05)),
random = ~ Time,
randomScales = cbind(c(.2, .02), c(.2, .02)),
noiseScales = .05
%>%
) as.data.table()
We plot the data to get a sense of different trajectories that have been generated. Visually, there is little group separation.
plotTrajectories(casedata, response = "Y")
Since we generated the data, we have the luxury of looking at
reference cluster trajectories, as stored in the Mu.cluster
column. Note that Mu.fixed
must be added to obtain the
correct values.
plotClusterTrajectories(casedata, response = "Y", cluster = "Class")
Rather than starting with clustering, in some case studies there may be prior knowledge on how to sensibly stratify the trajectories. Either by an observed independent variable (e.g., age or gender), or a certain aspect of the observed trajectory (e.g., the intercept, slope, variability).
Let’s presume in this example that domain knowledge suggests that stratifying by the intercept may provide a sensible grouping of the trajectories. This approach is further supported by the density plot of the trajectory intercepts, which shows a bimodal distribution.
ggplot(casedata[Time == 0], aes(x = Mu)) +
geom_density(fill = "gray", adjust = .3)
Based on the density plot, we will assign trajectories with an intercept above 1.6 to cluster A, and the remainder to cluster B.
<- lcMethodStratify(response = "Y", Y[1] > 1.6)
method <- latrend(method, casedata) model
clusterProportions(model)
#> A B
#> 0.6 0.4
The approach we specified requires the first observation to be available, and is sensitive to noise. A more robust alternative would be to fit a linear model per trajectory, and to use the estimated intercept to stratify the trajectories on.
We can specify this stratification model by defining a function which takes the data of separate trajectories as input. This function should return a single cluster assignment for the respective trajectory. By returning a factor, we can pre-specify the cluster names.
<- function(data) {
stratfun <- coef(lm(Y ~ Time, data))[1]
int factor(int > 1.7, levels = c(FALSE, TRUE), labels = c("Low", "High"))
}<- lcMethodStratify(response = "Y", stratify = stratfun, center = mean)
m2 <- latrend(m2, casedata)
model2
clusterProportions(model2)
#> Low High
#> 0.6 0.4
In case the linear regression step is time-intensive, a more efficient approach is to save the pre-computed trajectory intercepts as a column in the original data. This column can then be referred to in the expression of the stratification model.
:= coef(lm(Y ~ Time, .SD))[1], by = Traj]
casedata[, Intercept
<- lcMethodStratify(
m3 response = "Y",
stratify = Intercept[1] > 1.7,
clusterNames = c("Low", "High")
)<- latrend(m3, casedata) model3
We can take the approach involving the estimation of a linear model per trajectory one step further. Instead of using a pre-defined threshold on the intercept, we use a cluster algorithm on both the intercept and slope to automatically find clusters.
We first define the representation step, which estimates the model
coefficients per trajectory, and outputs a matrix
with the
coefficients per trajectory per row.
<- function(method, data, verbose) {
repStep <- as.data.table(data)
dt <- dt[, lm(Y ~ Time, .SD) %>% coef() %>% as.list(), keyby = Traj]
coefdata <- subset(coefdata, select = -1) %>% as.matrix()
coefmat rownames(coefmat) <- coefdata$Traj
coefmat }
The cluster step takes the coefficient matrix as input. A
cross-sectional cluster algorithm can then be applied to the matrix. In
this example, we apply \(k\)-means. The
cluster step should output a lcModel
object.
The lcModelPartition
function creates a
lcModel
object for a given vector of cluster
assignments.
<- function(method, data, repMat, envir, verbose) {
clusStep <- kmeans(repMat, centers = 3)
km
lcModelPartition(
response = method$response,
data = data,
trajectoryAssignments = km$cluster,
center = method$center,
method = method,
model = km
) }
We are now ready to create the lcMethodFeature
method.
<- lcMethodFeature(
m.twostep response = "Y",
representationStep = repStep,
clusterStep = clusStep
)
<- latrend(m.twostep, data = casedata)
model.twostep summary(model.twostep)
#> Longitudinal cluster model using part
#> lcMethodFeature specifying "two-step clustering"
#> standardize: `scale`
#> center: `meanNA`
#> time: "Time"
#> id: "Traj"
#> response: "Y"
#> representationStep:`repStep`
#> clusterStep: `clusStep`
#>
#> Cluster sizes (K=3):
#> A B C
#> 35 (35%) 25 (25%) 40 (40%)
#>
#> Number of obs: 1100, strata (Traj): 100
#>
#> Scaled residuals:
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> -3.08943 -0.67260 0.03618 0.00000 0.73723 2.64516
The two-step model defined above is hard-coded for a given formula
and a fixed number of clusters. In an exploratory setting, it is
convenient to define a parameterized method. Here, we change the two
functions to take arguments through the lcMethod
object in
the method
variable.
Note that we can introduce new arguments which are not originally
part of lcMethodFeature
(e.g., nClusters
) to
enable the specification of the number of clusters in our method.
<- function(method, data, verbose) {
repStep.gen <- as.data.table(data)
dt <- dt[, lm(method$formula, .SD) %>% coef() %>% as.list(), keyby = c(method$id)]
coefdata # exclude the id column
<- subset(coefdata, select = -1) %>% as.matrix()
coefmat rownames(coefmat) <- coefdata[[method$id]]
coefmat
}
<- function(method, data, repMat, envir, verbose) {
clusStep.gen <- kmeans(repMat, centers = method$nClusters)
km
lcModelPartition(
response = method$response,
data = data,
trajectoryAssignments = km$cluster,
center = method$center,
method = method,
model = km
) }
We create a new lcMethodFeature
instance with the more
generic functions. Defining values for formula
and
nClusters
here makes these arguments values act as default
values in a call of latrend
.
<- lcMethodFeature(
m.twostepgen response = "Y",
representationStep = repStep.gen,
clusterStep = clusStep.gen
)
However, because we omitted the specification of formula
and nClusters
, these need to be provided in the
latrend
call.
<- latrend(m.twostepgen, formula = Y ~ Time, nClusters = 2, casedata)
model.twostepgen summary(model.twostepgen)
#> Longitudinal cluster model using part
#> lcMethodFeature specifying "two-step clustering"
#> nClusters: 2
#> formula: Y ~ Time
#> standardize: `scale`
#> center: `meanNA`
#> time: "Time"
#> id: "Traj"
#> response: "Y"
#> representationStep:`repStep.gen`
#> clusterStep: `clusStep.gen`
#>
#> Cluster sizes (K=2):
#> A B
#> 40 (40%) 60 (60%)
#>
#> Number of obs: 1100, strata (Traj): 100
#>
#> Scaled residuals:
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> -2.9747 -0.6992 0.0589 0.0000 0.6958 2.9260
The use of lcMethodStratify
and
lcMethodFeature
enables rapid prototyping. Once the desired
model has been identified, it may be worthwhile to implement it as a
standalone method in the framework. This way the model can be extended
to output more representative cluster trajectories, or to extend the
model with predictive capabilities such that it can be validated on
external data.
To illustrate this, we will implement a basic group-based trajectory
model (GBTM), also known as latent-class growth analysis. We make use of
the implementation available in the lcmm
package through
the lcmm()
function, which allows for a relatively concise
illustration.
lcMethod
classFirstly, we create a new method class named
lcMethodSimpleGBTM
, which extends the lcMethod
class.
setClass("lcMethodSimpleGBTM", contains = "lcMethod")
Note that a lcMethod
class has a single slot
call
, which stores all the arguments to the method. The
other relevant aspects of a lcMethod
implementation are the
prepareData()
and fit()
functions, which are
called when passing a lcMethod
object to the
latrend()
function or any other model estimation
function.
<- function(...) {
lcMethodSimpleGBTM = match.call()
mc $Class = 'lcMethodSimpleGBTM'
mcdo.call(new, as.list(mc))
}
setMethod("getArgumentDefaults", "lcMethodSimpleGBTM", function(object, ...) {
list(
formula = Value ~ Time,
time = getOption("latrend.time"),
id = getOption("latrend.id"),
nClusters = 2,
nwg = FALSE
) })
Next, we override the getName()
and
getShortName()
functions in our method class to ensure that
the model is easily distinguishable from other methods in the summary
output. While we return a simple constant character sequence, the naming
functions could be improved by generating a more detailed description of
the model based on the arguments of the method object.
setMethod("getName", "lcMethodSimpleGBTM",
function(object, ...) "simple group-based trajectory model")
setMethod("getShortName", "lcMethodSimpleGBTM", function(object, ...) "sgbtm")
Implementing the prepareData()
function enables the
lcMethod
to do data processing or transforming the data or
other arguments in a structure which is suitable for the model fitting
procedure. The prepare function takes a lcMethod
, the data
as a data.frame
, and the verbosity level as inputs. The
processing is passed onto the fit
function by returning an
environment
with the relevant variables.
In this example, we need to ensure that the Id
column is
an integer.
setMethod("prepareData", "lcMethodSimpleGBTM", function(method, data, verbose, ...) {
<- new.env()
envir $data <- as.data.frame(data)
envir$data[[method$id]] <- as.integer(factor(data[[method$id]]))
envir
envir })
The fit()
function estimates the model and should return
an object that extends the lcModel
class. In our
implementation, we call the hlme()
with the appropriate
arguments, and we construct a lcModelSimpleGBTM
instance.
The class is defined in the subsection below. The envir
argument contains the return value of the prepare()
function, which would be NULL
here.
setMethod("fit", "lcMethodSimpleGBTM", function(method, data, envir, verbose, ...) {
<- as.list(method, args = lcmm::hlme)
args $data <- envir$data
args$fixed <- method$formula
argsif (method$nClusters > 1) {
$mixture <- update(method$formula, NULL ~ .)
argselse {
} $mixture <- NULL
args
}$subject <- method$id
args$ng <- method$nClusters
args$returndata <- TRUE
args# initialization, with a work-around for lcmm's dynamic evaluation
<- do.call(lcmm::hlme, modifyList(args, list(ng = 1, mixture = NULL)))
.m1 $B <- quote(random(dynGet('.m1', inherits = TRUE)))
args# fit latent-class model
<- do.call(lcmm::hlme, args)
model
new(
"lcModelSimpleGBTM",
method = method,
data = data,
model = model,
clusterNames = LETTERS[seq_len(method$nClusters)]
) })
lcModel
classWe start off by defining the lcModelSimpleGBTM
as an
extension of the lcModel
class. Extending the base class
also enables the addition of new fields to the class, although there is
no need for this in the present model.
setClass("lcModelSimpleGBTM", contains = "lcModel")
Our model inherits a couple of slots from the lcModel
class, namely:
slotNames("lcModelSimpleGBTM")
#> [1] "model" "method" "call" "data"
#> [5] "id" "time" "response" "label"
#> [9] "ids" "times" "clusterNames" "date"
#> [13] "estimationTime" "tag"
The "model"
slot is free to be used to assign an
arbitrary data structure that represents the internal model
representation(s). In our implementation, this slot contains the
lcmm
model. The other slots are assigned in the constructor
of the lcModel
class, or by the latrend()
estimation function. It is best practice to use the relevant getter
functions for obtaining these values (via e.g.,
idVariable()
, getLcMethod()
,
clusterNames()
).
Typically, most effort of implementing the model interface goes to
the predictForCluster()
function. While implementing the
function is optional, it is used for obtaining the fitted values, fitted
trajectories, residuals, and cluster trajectories. Without this
function, there is little functionality from a model except for the
partitioning of the fitted trajectories.
The fitted()
function returns the fitted values per
trajectory per cluster. If the clusters are not provided, the function
is expected to return a matrix with the fitted values for each cluster.
This logic is handled in the transformFitted()
function,
which is available in the package.
<- function(object, clusters = trajectoryAssignments(object)) {
fitted.lcModelSimpleGBTM <- paste0("pred_m", 1:nClusters(object))
predNames <- as.matrix(object@model$pred[predNames])
predMat colnames(predMat) <- clusterNames(object)
transformFitted(pred = predMat, model = object, clusters = clusters)
}
The easiest way to add predictive capability to a model is by
implementing the function predictForCluster()
. This
function returns the predicted values of a specific cluster, for the
respective observations. This function is used by
predict.lcModel(model, newdata)
function, which also
reduces the output of fitted(model)
in case of
newdata = NULL
.
setMethod("predictForCluster", "lcModelSimpleGBTM", function(
what = 'mu', ...)
object, newdata, cluster,
{= lcmm::predictY(object@model, newdata = newdata)$pred %>%
predMat set_colnames(clusterNames(object))
= match(cluster, clusterNames(object))
clusIdx
predMat[, clusIdx] })
In order for the model implementation to return cluster assignments
through the trajectoryAssignments()
function, we need to
implement the postprob()
function to return the posterior
probability matrix for the fitted data.
setMethod("postprob", signature("lcModelSimpleGBTM"), function(object) {
as.matrix(object@model$pprob)[, c(-1, -2), drop = FALSE]
})
Lastly, we override the converged()
function to return
the convergence code of the internal model.
setMethod("converged", signature("lcModelSimpleGBTM"), function(object, ...) {
@model$conv
object })
<- lcMethodSimpleGBTM(formula = Y ~ Time)
m show(m)
#> lcMethodSimpleGBTM specifying "simple group-based trajectory model"
#> formula: Y ~ Time
#> time: "Time"
#> id: "Traj"
#> nClusters: 2
#> nwg: FALSE
<- latrend(m, casedata)
sgbtm #> ------------------ iteration 0 ------------------
#> Function value -20633.88
#> Convergence criteria: parameters stability= 1.0001
#> : function stability= 1.0001
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 1.767509 5.759015 33.16625
#> parameter2 1.000000 10.241137 104.88089
#> parameter3 1.000000 341.454838 116591.40619
#>
#> ------------------ iteration 1 ------------------
#> Function value -20633.73
#> Convergence criteria: parameters stability= 0
#> : function stability= 0.142273
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 1.7674992 5.759023 33.16634
#> parameter2 0.9999947 10.241152 104.88120
#> parameter3 0.9999969 341.454672 116591.29332
#>
#> ------------------ iteration 2 ------------------
#> Function value -3610.327
#> Convergence criteria: parameters stability= 5.25559
#> : function stability= 17023.41
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.3359932 6.861143 47.07529
#> parameter2 0.1376645 12.201022 148.86494
#> parameter3 0.7045363 183.979743 33848.54595
#>
#> ------------------ iteration 3 ------------------
#> Function value -3610.314
#> Convergence criteria: parameters stability= 0
#> : function stability= 0.01315729
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.3359832 6.861152 47.0754
#> parameter2 0.1376641 12.201044 148.8655
#> parameter3 0.7045343 183.979909 33848.6069
#>
#> ------------------ iteration 4 ------------------
#> Function value -3610.296
#> Convergence criteria: parameters stability= 0
#> : function stability= 0.01810068
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.3359731 6.861164 47.07557
#> parameter2 0.1376639 12.201071 148.86614
#> parameter3 0.7045328 183.979808 33848.56965
#>
#> ------------------ iteration 5 ------------------
#> Function value -1729.07
#> Convergence criteria: parameters stability= 2.184653
#> : function stability= 1881.226
#> : relative distance to maximum(RDM)= 364.4379
#> coef SE.coef Var.coef
#> parameter1 1.1339175 0.03982233 0.00158582
#> parameter2 0.1532237 0.00793766 0.00006301
#> parameter3 0.5501563 0.01002606 0.00010052
#>
#> ------------------ iteration 6 ------------------
#> Function value -1238.211
#> Convergence criteria: parameters stability= 0.2981123
#> : function stability= 490.8581
#> : relative distance to maximum(RDM)= 43.33083
#> coef SE.coef Var.coef
#> parameter1 1.66206514 0.03080617 0.00094902
#> parameter2 0.01517586 0.00520224 0.00002706
#> parameter3 0.53942962 0.00829838 0.00006886
#>
#> ------------------ iteration 7 ------------------
#> Function value -1160.566
#> Convergence criteria: parameters stability= 0.01811566
#> : function stability= 77.64567
#> : relative distance to maximum(RDM)= 7.149929
#> coef SE.coef Var.coef
#> parameter1 1.76620108 0.03520603 0.00123946
#> parameter2 -0.00170179 0.00595092 0.00003541
#> parameter3 0.62301498 0.01154771 0.00013335
#>
#> ------------------ iteration 8 ------------------
#> Function value -1148.588
#> Convergence criteria: parameters stability= 0.00452068
#> : function stability= 11.97804
#> : relative distance to maximum(RDM)= 0.3094911
#> coef SE.coef Var.coef
#> parameter1 1.81009024 0.03798904 0.00144317
#> parameter2 -0.00883937 0.00642130 0.00004123
#> parameter3 0.67344788 0.01393836 0.00019428
#>
#> ------------------ iteration 9 ------------------
#> Function value -1148.109
#> Convergence criteria: parameters stability= 0.00024124
#> : function stability= 0.4785324
#> : relative distance to maximum(RDM)= 0.00073164
#> coef SE.coef Var.coef
#> parameter1 1.81843804 0.03872259 0.00149944
#> parameter2 -0.01019779 0.00654533 0.00004284
#> parameter3 0.68647512 0.01461783 0.00021368
#>
#> ------------------ iteration 10 ------------------
#> Function value -1148.108
#> Convergence criteria: parameters stability= 5.6e-07
#> : function stability= 0.00109858
#> : relative distance to maximum(RDM)= 0
#> coef SE.coef Var.coef
#> parameter1 1.81876844 0.03876025 0.00150236
#> parameter2 -0.01025184 0.00655167 0.00004292
#> parameter3 0.68714788 0.01465366 0.00021473
#>
#> ------------------ iteration 11 ------------------
#> Function value -1148.108
#> Convergence criteria: parameters stability= 0
#> : function stability= 0
#> : relative distance to maximum(RDM)= 0
#> coef SE.coef Var.coef
#> parameter1 1.81876852 0.03876048 0.00150238
#> parameter2 -0.01025187 0.00655172 0.00004293
#> parameter3 0.68714919 0.01465374 0.00021473
#>
#> The program took 0.08 seconds
#> ------------------ iteration 0 ------------------
#> Function value -1135.184
#> Convergence criteria: parameters stability= 1.0001
#> : function stability= 1.0001
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 0.00000000 3.976795 15.8149
#> parameter2 1.82147999 52.862084 -2794.3999
#> parameter3 1.77842030 51.701762 -2673.0722
#> parameter4 -0.00260985 142.546570 -20319.5245
#> parameter5 -0.01073129 128.704625 -16564.8804
#> parameter6 0.66831800 69.107331 4775.8233
#>
#> ------------------ iteration 1 ------------------
#> Function value -1109.112
#> Convergence criteria: parameters stability= 0.00013837
#> : function stability= 26.07158
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.00001132 0.00000 0.0000
#> parameter2 1.82377436 27.39797 -750.6488
#> parameter3 1.77616317 19.42025 -377.1459
#> parameter4 0.00532231 106.58460 11360.2769
#> parameter5 -0.01878620 146.35207 21418.9277
#> parameter6 0.66786357 63.98615 4094.2278
#>
#> ------------------ iteration 2 ------------------
#> Function value -1037.083
#> Convergence criteria: parameters stability= 0.00552034
#> : function stability= 72.02933
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.00490621 2.182051 4.761348
#> parameter2 1.86080932 4.798913 23.029565
#> parameter3 1.71278021 5.685525 32.325195
#> parameter4 0.01370861 9.578693 91.751368
#> parameter5 -0.02291505 9.391523 88.200702
#> parameter6 0.66338987 55.416972 3071.040772
#>
#> ------------------ iteration 3 ------------------
#> Function value -802.8879
#> Convergence criteria: parameters stability= 0.08221898
#> : function stability= 234.195
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.03601449 2.224965 4.95047
#> parameter2 2.10308186 5.945289 35.34646
#> parameter3 1.59007196 6.599512 43.55356
#> parameter4 0.02444832 10.702268 114.53853
#> parameter5 -0.02880232 11.786627 138.92458
#> parameter6 0.57766968 50.772525 2577.84926
#>
#> ------------------ iteration 4 ------------------
#> Function value -802.8766
#> Convergence criteria: parameters stability= 0
#> : function stability= 0.01131964
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.03601544 2.226128 4.955648
#> parameter2 2.10309186 5.945332 35.346970
#> parameter3 1.59006564 6.599558 43.554169
#> parameter4 0.02444747 10.702349 114.540278
#> parameter5 -0.02880176 11.786696 138.926200
#> parameter6 0.57766229 50.773669 2577.965481
#>
#> ------------------ iteration 5 ------------------
#> Function value -486.757
#> Convergence criteria: parameters stability= 0.2436012
#> : function stability= 316.1196
#> : relative distance to maximum(RDM)= 1.0001
#> coef SE.coef Var.coef
#> parameter1 -0.07824161 2.235159 4.995937
#> parameter2 2.44101402 8.460175 71.574568
#> parameter3 1.37549296 9.362729 87.660686
#> parameter4 0.00429267 15.044598 226.339934
#> parameter5 -0.01521719 16.649542 277.207254
#> parameter6 0.29306689 200.212579 40085.076632
#>
#> ------------------ iteration 6 ------------------
#> Function value 21.00824
#> Convergence criteria: parameters stability= 0.3206699
#> : function stability= 507.7652
#> : relative distance to maximum(RDM)= 10.02194
#> coef SE.coef Var.coef
#> parameter1 -0.13700015 0.20046810 0.04018746
#> parameter2 2.90434720 0.01904396 0.00036267
#> parameter3 1.08608931 0.01533896 0.00023528
#> parameter4 -0.08627462 0.00318065 0.00001012
#> parameter5 0.04177020 0.00257520 0.00000663
#> parameter6 0.20742096 0.00418958 0.00001755
#>
#> ------------------ iteration 7 ------------------
#> Function value 51.5374
#> Convergence criteria: parameters stability= 0.02660172
#> : function stability= 30.52916
#> : relative distance to maximum(RDM)= 0.4484829
#> coef SE.coef Var.coef
#> parameter1 -0.25493575 0.20162697 0.04065344
#> parameter2 3.00059665 0.01892609 0.00035820
#> parameter3 1.02938472 0.01544891 0.00023867
#> parameter4 -0.09828278 0.00319875 0.00001023
#> parameter5 0.04867298 0.00261118 0.00000682
#> parameter6 0.21207990 0.00437476 0.00001914
#>
#> ------------------ iteration 8 ------------------
#> Function value 52.91617
#> Convergence criteria: parameters stability= 0.01650014
#> : function stability= 1.37877
#> : relative distance to maximum(RDM)= 0.00380866
#> coef SE.coef Var.coef
#> parameter1 -0.38208405 0.20366005 0.04147742
#> parameter2 3.01578402 0.01931419 0.00037304
#> parameter3 1.02068312 0.01576999 0.00024869
#> parameter4 -0.10059053 0.00326469 0.00001066
#> parameter5 0.04998647 0.00266561 0.00000711
#> parameter6 0.21655516 0.00460656 0.00002122
#>
#> ------------------ iteration 9 ------------------
#> Function value 52.92761
#> Convergence criteria: parameters stability= 0.00052055
#> : function stability= 0.01144183
#> : relative distance to maximum(RDM)= 1.45e-06
#> coef SE.coef Var.coef
#> parameter1 -0.40487091 0.20411111 0.04166134
#> parameter2 3.01671053 0.01934637 0.00037428
#> parameter3 1.02014145 0.01579624 0.00024952
#> parameter4 -0.10073544 0.00327013 0.00001069
#> parameter5 0.05007042 0.00267005 0.00000713
#> parameter6 0.21691600 0.00462577 0.00002140
#>
#> ------------------ iteration 10 ------------------
#> Function value 52.92761
#> Convergence criteria: parameters stability= 3.5e-07
#> : function stability= 4.35e-06
#> : relative distance to maximum(RDM)= 0
#> coef SE.coef Var.coef
#> parameter1 -0.40546306 0.20412341 0.04166637
#> parameter2 3.01671454 0.01934652 0.00037429
#> parameter3 1.02013784 0.01579636 0.00024952
#> parameter4 -0.10073613 0.00327016 0.00001069
#> parameter5 0.05007098 0.00267007 0.00000713
#> parameter6 0.21691766 0.00462586 0.00002140
#>
#> The program took 0.42 seconds
summary(sgbtm)
#> Longitudinal cluster model using simple group-based trajectory model
#> lcMethodSimpleGBTM specifying "simple group-based trajectory model"
#> formula: Y ~ Time
#> time: "Time"
#> id: "Traj"
#> nClusters: 2
#> nwg: FALSE
#>
#> Cluster sizes (K=2):
#> A B
#> 40 (40%) 60 (60%)
#>
#> Number of obs: 1100, strata (Traj): 100
#>
#> Scaled residuals:
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> -2.98015 -0.69195 0.05702 0.00000 0.69910 2.91739
plot(sgbtm)