Growth Models with Nonlinearity in Parameters - Multilevel & SEM Implementation in R

Overview

This tutorial walks through the fitting of growth models with nonlinearity in parameters in several different frameworks (e.g., multilevel modeling framework, structural equation modeling framework), and demonstrates these models using different R packages. Knowing how to fit the models in different packages can be helpful when trying to fit more complex models as each packages as its own advantages and limitations. In this tutorial, we will be using a sample data set that includes repeated measures of individuals’ height during infancy and early childhood.

The code and example provided in this tutorial are from Chapter 11 of Grimm, Ram, and Estabrook (2016), with a few additions in code and commentary. Please refer to the chapter for further interpretations and insights about the analyses.

Preliminaries

Loading libraries used in this script.

library(tidyverse)  #for data management
library(psych)  #for basic functions
library(ggplot2)  #for plotting
library(nlme) #for mixed effects models
library(lavaan) #for SEM 
library(semPlot) #for making SEM diagrams

Read in and prep the long and wide data.

filepath <- "https://raw.githubusercontent.com/LRI-2/Data/main/GrowthModeling/bgs_height_long.dat"
#read in the text data file using the url() function
hght_long <- read.table(file=url(filepath),
                  na.strings = ".")  #indicates the missing data designator

#Add names the columns of the data set
names(hght_long) <- c('id', 'age', 'hght')

#view the first few observations in the data set 
head(hght_long, 10)
##    id age hght
## 1   1   1 53.5
## 2   1   3 60.4
## 3   1   6 67.2
## 4   1   9 70.9
## 5   1  12 76.2
## 6   1  15 81.1
## 7   1  18   NA
## 8   1  24 89.5
## 9   1  36 95.9
## 10  2   1 54.5
#quick summary of the data
summary(hght_long)
##        id          age             hght       
##  Min.   : 1   Min.   : 1.00   Min.   : 49.50  
##  1st Qu.:21   1st Qu.: 6.00   1st Qu.: 65.00  
##  Median :42   Median :12.00   Median : 74.20  
##  Mean   :42   Mean   :13.78   Mean   : 74.34  
##  3rd Qu.:63   3rd Qu.:18.00   3rd Qu.: 83.20  
##  Max.   :83   Max.   :36.00   Max.   :106.00  
##                               NA's   :166
#creating a wide version of the data file for the SEM framework
hght_wide <- hght_long %>% 
  spread(age, hght)

names(hght_wide) <- c("id","hght01","hght03", "hght06", "hght09","hght12", "hght15", "hght18", "hght24", "hght36")
head(hght_wide)
##   id hght01 hght03 hght06 hght09 hght12 hght15 hght18 hght24 hght36
## 1  1   53.5   60.4   67.2   70.9   76.2   81.1     NA   89.5   95.9
## 2  2   54.5   58.8   66.0   69.1   74.5   78.2   81.2   86.7     NA
## 3  3   49.5   56.0   61.9   64.1   67.9   69.6   73.8     NA   85.1
## 4  4   56.9   64.6   70.0   72.5   76.1   80.4   82.3   87.9   94.3
## 5  5   56.7   63.1     NA   73.1   77.3   81.8   83.5   88.9   99.1
## 6  6   56.1   61.9   67.9   72.3   77.3   79.9   82.3   88.3   97.5

Jenss-Bayley Growth Model in MLM using nlme

Fitting the model using the nlme library.

hght.jb.nlme <- nlme(hght~b_1i + b_2i * (age/12) + b_3i * (exp(gamma*(age/12))-1),
                      data = hght_long,
                      fixed = b_1i + b_2i + b_3i + gamma ~ 1,
                      random = b_1i + b_2i + b_3i ~ 1,
                      groups =~ id,
                      start = c(50, 10, -18, -2),
                      na.action = na.exclude)
summary(hght.jb.nlme)
## Nonlinear mixed-effects model fit by maximum likelihood
##   Model: hght ~ b_1i + b_2i * (age/12) + b_3i * (exp(gamma * (age/12)) -      1) 
##   Data: hght_long 
##        AIC      BIC    logLik
##   2000.239 2048.252 -989.1197
## 
## Random effects:
##  Formula: list(b_1i ~ 1, b_2i ~ 1, b_3i ~ 1)
##  Level: id
##  Structure: General positive-definite, Log-Cholesky parametrization
##          StdDev    Corr       
## b_1i     2.7199531 b_1i  b_2i 
## b_2i     0.8590517 0.260      
## b_3i     3.0567735 0.583 0.248
## Residual 0.8193595            
## 
## Fixed effects:  b_1i + b_2i + b_3i + gamma ~ 1 
##           Value Std.Error  DF   t-value p-value
## b_1i   51.04753 0.3501220 495 145.79927       0
## b_2i    9.31413 0.1649162 495  56.47799       0
## b_3i  -17.81609 0.4537913 495 -39.26054       0
## gamma  -2.08950 0.0731175 495 -28.57738       0
##  Correlation: 
##       b_1i   b_2i   b_3i  
## b_2i  -0.034              
## b_3i   0.412  0.543       
## gamma  0.350 -0.655 -0.392
## 
## Standardized Within-Group Residuals:
##         Min          Q1         Med          Q3         Max 
## -3.62957185 -0.51594951 -0.02464761  0.52653168  2.87228852 
## 
## Number of Observations: 581
## Number of Groups: 83

Jenss-Bayley Growth Model in SEM using lavaan

Specifying the model.

jb.hght.lavaan <- '
#latent variable definitions

  #defining the intercept
  eta_1 =~ 1*hght01 + 
            1*hght03 + 
              1*hght06 + 
                1*hght09 + 
                  1*hght12 + 
                    1*hght15 + 
                      1*hght18 + 
                        1*hght24 + 
                          1*hght36 

  #defining the slope
  eta_2 =~ .0833*hght01 + 
              .25*hght03 + 
                .5*hght06 + 
                  .75*hght09 + 
                    1*hght12 + 
                      1.25*hght15 + 
                        1.5*hght18 + 
                          2*hght24 + 
                            3*hght36 

  #defining the "vertical distance between the actual intercept and the intercept of the linear asymptote" (p.252, Grimm, Ram, & Estabrook, 2006)
  eta_3 =~ start(-.15)*L13*hght01 + 
            start(-.40)*L23*hght03 + 
              start(-.64)*L33*hght06 + 
                start(-.78)*L43*hght09 + 
                  start(-.87)*L53*hght12 + 
                    start(-.92)*L63*hght15 + 
                      start(-.95)*L73*hght18 + 
                        start(-.98)*L83*hght24 + 
                          start(-.99)*L93*hght36 

     
#introducing phantom factor, setting all parameters to 0 and mean to gamma
      phantom =~ 0*hght01
      phantom ~~ 0*phantom
      phantom ~~ 0*eta_1
      phantom ~~ 0*eta_2
      phantom ~~ 0*eta_3
      phantom ~ start(-2)*gamma*1
#contraints to define factor loadings   
      gamma < -2   #this constraint is added so that it finds the viable solution
      L13 == exp(gamma*0.0833)-1
      L23 == exp(gamma*0.25)-1
      L33 == exp(gamma*0.50)-1
      L43 == exp(gamma*0.75)-1
      L53 == exp(gamma*1.00)-1
      L63 == exp(gamma*1.25)-1
      L73 == exp(gamma*1.50)-1
      L83 == exp(gamma*2.00)-1
      L93 == exp(gamma*3.00)-1

#factor variances 
  eta_1 ~~ start(7.3)*eta_1 
  eta_2 ~~ start(0.7)*eta_2
  eta_3 ~~ start(9.4)*eta_3

#factor covariances
  eta_1 ~~ start(0.61)*eta_2
  eta_2 ~~ start(0.67)*eta_3
  eta_3 ~~ start(4.85)*eta_1

#latent means
  eta_1 ~ start(51)*1
  eta_2 ~ start(9.2)*1
  eta_3 ~ start(-17.8)*1

#manifest variances 
  hght01 ~~ start(.67)*theta*hght01
  hght03 ~~ start(.67)*theta*hght03
  hght06 ~~ theta*hght06
  hght09 ~~ theta*hght09
  hght12 ~~ theta*hght12
  hght15 ~~ theta*hght15
  hght18 ~~ theta*hght18
  hght24 ~~ theta*hght24
  hght36 ~~ theta*hght36

#manifest means (fixed to zero)
  hght01 ~ 0*1
  hght03 ~ 0*1
  hght06 ~ 0*1
  hght09 ~ 0*1
  hght12 ~ 0*1
  hght15 ~ 0*1
  hght18 ~ 0*1
  hght24 ~ 0*1
  hght36 ~ 0*1'

Fitting the model.

hght.jb.fit <- lavaan(jb.hght.lavaan,
                data = hght_wide,
                meanstructure = TRUE,
                estimator = "ML",
                missing = "fiml",
                fixed.x = FALSE,
                mimic="mplus",
                control=list(iter.max=500),
                verbose=FALSE)
summary(hght.jb.fit, fit.measures=TRUE)
## lavaan 0.6-9 ended normally after 1255 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        28
##   Number of inequality constraints                   1
##                                                       
##   Number of observations                            83
##   Number of missing patterns                        31
##                                                       
## Model Test User Model:
##                                                       
##   Test statistic                               106.612
##   Degrees of freedom                                43
##   P-value (Chi-square)                           0.000
## 
## Model Test Baseline Model:
## 
##   Test statistic                               955.844
##   Degrees of freedom                                36
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.931
##   Tucker-Lewis Index (TLI)                       0.942
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)               -989.047
##   Loglikelihood unrestricted model (H1)       -935.741
##                                                       
##   Akaike (AIC)                                2000.094
##   Bayesian (BIC)                              2026.701
##   Sample-size adjusted Bayesian (BIC)         1992.005
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.134
##   90 Percent confidence interval - lower         0.102
##   90 Percent confidence interval - upper         0.166
##   P-value RMSEA <= 0.05                          0.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.309
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Observed
##   Observed information based on                Hessian
## 
## Latent Variables:
##                    Estimate  Std.Err  z-value   P(>|z|)
##   eta_1 =~                                             
##     hght01            1.000                            
##     hght03            1.000                            
##     hght06            1.000                            
##     hght09            1.000                            
##     hght12            1.000                            
##     hght15            1.000                            
##     hght18            1.000                            
##     hght24            1.000                            
##     hght36            1.000                            
##   eta_2 =~                                             
##     hght01            0.083                            
##     hght03            0.250                            
##     hght06            0.500                            
##     hght09            0.750                            
##     hght12            1.000                            
##     hght15            1.250                            
##     hght18            1.500                            
##     hght24            2.000                            
##     hght36            3.000                            
##   eta_3 =~                                             
##     hght01   (L13)   -0.158    0.005   -30.722    0.000
##     hght03   (L23)   -0.403    0.011   -36.846    0.000
##     hght06   (L33)   -0.644    0.013   -49.279    0.000
##     hght09   (L43)   -0.787    0.012   -67.307    0.000
##     hght12   (L53)   -0.873    0.009   -93.760    0.000
##     hght15   (L63)   -0.924    0.007  -132.998    0.000
##     hght18   (L73)   -0.955    0.005  -191.771    0.000
##     hght24   (L83)   -0.984    0.002  -415.792    0.000
##     hght36   (L93)   -0.998    0.000 -2212.578    0.000
##   phantom =~                                           
##     hght01            0.000                            
## 
## Covariances:
##                    Estimate  Std.Err  z-value   P(>|z|)
##   eta_1 ~~                                             
##     phantom           0.000                            
##   eta_2 ~~                                             
##     phantom           0.000                            
##   eta_3 ~~                                             
##     phantom           0.000                            
##   eta_1 ~~                                             
##     eta_2             0.615    0.375     1.642    0.101
##   eta_2 ~~                                             
##     eta_3             0.671    0.489     1.371    0.170
##   eta_1 ~~                                             
##     eta_3             4.859    1.420     3.422    0.001
## 
## Intercepts:
##                    Estimate  Std.Err  z-value   P(>|z|)
##     phantom (gamm)   -2.063    0.073   -28.158    0.000
##     eta_1            51.093    0.349   146.217    0.000
##     eta_2             9.274    0.167    55.411    0.000
##     eta_3           -17.881    0.458   -39.001    0.000
##    .hght01            0.000                            
##    .hght03            0.000                            
##    .hght06            0.000                            
##    .hght09            0.000                            
##    .hght12            0.000                            
##    .hght15            0.000                            
##    .hght18            0.000                            
##    .hght24            0.000                            
##    .hght36            0.000                            
## 
## Variances:
##                    Estimate  Std.Err  z-value   P(>|z|)
##     phantom           0.000                            
##     eta_1             7.389    1.352     5.466    0.000
##     eta_2             0.740    0.181     4.082    0.000
##     eta_3             9.423    2.145     4.393    0.000
##    .hght01  (thet)    0.671    0.050    13.411    0.000
##    .hght03  (thet)    0.671    0.050    13.411    0.000
##    .hght06  (thet)    0.671    0.050    13.411    0.000
##    .hght09  (thet)    0.671    0.050    13.411    0.000
##    .hght12  (thet)    0.671    0.050    13.411    0.000
##    .hght15  (thet)    0.671    0.050    13.411    0.000
##    .hght18  (thet)    0.671    0.050    13.411    0.000
##    .hght24  (thet)    0.671    0.050    13.411    0.000
##    .hght36  (thet)    0.671    0.050    13.411    0.000
## 
## Constraints:
##                                                |Slack|
##     -2 - (gamma)                                 0.063
##     L13 - (exp(gamma*0.0833)-1)                  0.000
##     L23 - (exp(gamma*0.25)-1)                    0.000
##     L33 - (exp(gamma*0.50)-1)                    0.000
##     L43 - (exp(gamma*0.75)-1)                    0.000
##     L53 - (exp(gamma*1.00)-1)                    0.000
##     L63 - (exp(gamma*1.25)-1)                    0.000
##     L73 - (exp(gamma*1.50)-1)                    0.000
##     L83 - (exp(gamma*2.00)-1)                    0.000
##     L93 - (exp(gamma*3.00)-1)                    0.000

We can try to make a diagram to check if we specified and estimated the model as intended.

#diagram of fitted model
semPaths(hght.jb.fit, what = "path", whatLabels = "par")

However, it does not work, probably becasue the function does not know how to deal with the phantom variables and constraints.

Latent Basis Growth Model in MLM using nlme

Fitting the model using the nlme library.

#creating a set of dummy variables that can be used to "turn on" and "turn off" each possible age value in each row of data
for(i in 1:length(hght_long$age)){
       if (hght_long$age[i] == 1) {hght_long$age1[i]=1}
       if (hght_long$age[i] != 1) {hght_long$age1[i]=0}
       if (hght_long$age[i] == 3) {hght_long$age3[i]=1}
       if (hght_long$age[i] != 3) {hght_long$age3[i]=0}
       if (hght_long$age[i] == 6) {hght_long$age6[i]=1}
       if (hght_long$age[i] != 6) {hght_long$age6[i]=0}
       if (hght_long$age[i] == 9) {hght_long$age9[i]=1}
       if (hght_long$age[i] != 9) {hght_long$age9[i]=0}
       if (hght_long$age[i] == 12) {hght_long$age12[i]=1}
       if (hght_long$age[i] != 12) {hght_long$age12[i]=0}
       if (hght_long$age[i] == 15) {hght_long$age15[i]=1} 
       if (hght_long$age[i] != 15) {hght_long$age15[i]=0} 
       if (hght_long$age[i] == 18) {hght_long$age18[i]=1} 
       if (hght_long$age[i] != 18) {hght_long$age18[i]=0}
       if (hght_long$age[i] == 24) {hght_long$age24[i]=1}
       if (hght_long$age[i] != 24) {hght_long$age24[i]=0}
       if (hght_long$age[i] == 36) {hght_long$age36[i]=1}
       if (hght_long$age[i] != 36) {hght_long$age36[i]=0}
       }

#fitting the model in nlme
hght.latent.nlme <- nlme(hght ~ age1 *(b_1i + b_2i*0) +
                              age3 *(b_1i + b_2i*A_2) +
                              age6 *(b_1i + b_2i*A_3) +
                              age9 *(b_1i + b_2i*A_4) +
                              age12*(b_1i + b_2i*A_5) +
                              age15*(b_1i + b_2i*A_6) +
                              age18*(b_1i + b_2i*A_7) +
                              age24*(b_1i + b_2i*A_8) +
                              age36*(b_1i + b_2i*1),
                         data = hght_long,
                         fixed = b_1i + b_2i + A_2 + A_3 + A_4 + A_5 + A_6 + A_7 + A_8 ~ 1,
                         random = b_1i + b_2i ~ 1,
                         groups =~ id,
                         start = c(60, 20, .3, .4, .5, .6, .7, .8, .9),
                         na.action = na.exclude) 

summary(hght.latent.nlme)
## Nonlinear mixed-effects model fit by maximum likelihood
##   Model: hght ~ age1 * (b_1i + b_2i * 0) + age3 * (b_1i + b_2i * A_2) +      age6 * (b_1i + b_2i * A_3) + age9 * (b_1i + b_2i * A_4) +      age12 * (b_1i + b_2i * A_5) + age15 * (b_1i + b_2i * A_6) +      age18 * (b_1i + b_2i * A_7) + age24 * (b_1i + b_2i * A_8) +      age36 * (b_1i + b_2i * 1) 
##   Data: hght_long 
##        AIC      BIC   logLik
##   2022.224 2078.966 -998.112
## 
## Random effects:
##  Formula: list(b_1i ~ 1, b_2i ~ 1)
##  Level: id
##  Structure: General positive-definite, Log-Cholesky parametrization
##          StdDev    Corr  
## b_1i     2.2669367 b_1i  
## b_2i     3.1982959 -0.128
## Residual 0.9031948       
## 
## Fixed effects:  b_1i + b_2i + A_2 + A_3 + A_4 + A_5 + A_6 + A_7 + A_8 ~ 1 
##         Value Std.Error  DF   t-value p-value
## b_1i 54.54834 0.2797289 490 195.00427       0
## b_2i 42.08689 0.4113588 490 102.31187       0
## A_2   0.14678 0.0035631 490  41.19609       0
## A_3   0.30874 0.0034520 490  89.43782       0
## A_4   0.40956 0.0034124 490 120.02170       0
## A_5   0.50299 0.0033746 490 149.05122       0
## A_6   0.58723 0.0035781 490 164.11636       0
## A_7   0.65704 0.0035792 490 183.57248       0
## A_8   0.78358 0.0036713 490 213.43595       0
##  Correlation: 
##      b_1i   b_2i   A_2    A_3    A_4    A_5    A_6    A_7   
## b_2i -0.239                                                 
## A_2  -0.290  0.148                                          
## A_3  -0.244  0.061  0.429                                   
## A_4  -0.215  0.021  0.386  0.398                            
## A_5  -0.183 -0.005  0.338  0.358  0.365                     
## A_6  -0.140 -0.047  0.282  0.318  0.338  0.346              
## A_7  -0.119 -0.076  0.248  0.293  0.321  0.345  0.341       
## A_8  -0.076 -0.122  0.189  0.253  0.291  0.329  0.340  0.365
## 
## Standardized Within-Group Residuals:
##         Min          Q1         Med          Q3         Max 
## -3.58052646 -0.53575511  0.02044749  0.52029421  4.35506534 
## 
## Number of Observations: 581
## Number of Groups: 83

Latent Basis Growth Model in SEM using lavaan

Specifying the model

lb.hght.lavaan <- '
#latent variable definitions

  #defining the intercept
  eta_1 =~ 1*hght01 + 
            1*hght03 + 
              1*hght06 + 
                1*hght09 + 
                  1*hght12 + 
                    1*hght15 + 
                      1*hght18 + 
                        1*hght24 + 
                          1*hght36 

  #defining the slope
  eta_2 =~ 0*hght01 + 
            hght03 + 
              hght06 + 
                hght09 + 
                  hght12 + 
                    hght15 + 
                      hght18 + 
                        hght24 + 
                          1*hght36 

#factor variances 
  eta_1 ~~ start(60)*eta_1 
  eta_2 ~~ start(4)*eta_2

#factor covariances
  eta_1 ~~ eta_2

#latent means 
  eta_1 ~ 1
  eta_2 ~ 1

#manifest variances (set equal by naming theta) 
  hght01 ~~ theta*hght01
  hght03 ~~ theta*hght03
  hght06 ~~ theta*hght06
  hght09 ~~ theta*hght09
  hght12 ~~ theta*hght12
  hght15 ~~ theta*hght15
  hght18 ~~ theta*hght18
  hght24 ~~ theta*hght24
  hght36 ~~ theta*hght36

#manifest means (fixed to zero)
  hght01 ~ 0*1    
  hght03 ~ 0*1
  hght06 ~ 0*1
  hght09 ~ 0*1
  hght12 ~ 0*1
  hght15 ~ 0*1
  hght18 ~ 0*1
  hght24 ~ 0*1
  hght36 ~ 0*1'

Fitting the model

lb.hght.fit <- lavaan(lb.hght.lavaan,
                data = hght_wide,
                meanstructure = TRUE,
                estimator = "ML",
                missing = "fiml",
                fixed.x = FALSE)
summary(lb.hght.fit, fit.measures=TRUE)
## lavaan 0.6-9 ended normally after 143 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        21
##   Number of equality constraints                     8
##                                                       
##   Number of observations                            83
##   Number of missing patterns                        31
##                                                       
## Model Test User Model:
##                                                       
##   Test statistic                               124.722
##   Degrees of freedom                                41
##   P-value (Chi-square)                           0.000
## 
## Model Test Baseline Model:
## 
##   Test statistic                               955.844
##   Degrees of freedom                                36
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.909
##   Tucker-Lewis Index (TLI)                       0.920
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)               -998.102
##   Loglikelihood unrestricted model (H1)       -935.741
##                                                       
##   Akaike (AIC)                                2022.204
##   Bayesian (BIC)                              2053.649
##   Sample-size adjusted Bayesian (BIC)         2012.644
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.157
##   90 Percent confidence interval - lower         0.126
##   90 Percent confidence interval - upper         0.189
##   P-value RMSEA <= 0.05                          0.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.054
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Observed
##   Observed information based on                Hessian
## 
## Latent Variables:
##                    Estimate  Std.Err  z-value  P(>|z|)
##   eta_1 =~                                            
##     hght01            1.000                           
##     hght03            1.000                           
##     hght06            1.000                           
##     hght09            1.000                           
##     hght12            1.000                           
##     hght15            1.000                           
##     hght18            1.000                           
##     hght24            1.000                           
##     hght36            1.000                           
##   eta_2 =~                                            
##     hght01            0.000                           
##     hght03            0.147    0.004   41.572    0.000
##     hght06            0.309    0.003   90.148    0.000
##     hght09            0.410    0.003  120.745    0.000
##     hght12            0.503    0.003  150.125    0.000
##     hght15            0.587    0.004  165.354    0.000
##     hght18            0.657    0.004  184.921    0.000
##     hght24            0.783    0.004  215.121    0.000
##     hght36            1.000                           
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)
##   eta_1 ~~                                            
##     eta_2            -0.928    0.973   -0.954    0.340
## 
## Intercepts:
##                    Estimate  Std.Err  z-value  P(>|z|)
##     eta_1            54.543    0.278  196.354    0.000
##     eta_2            42.100    0.409  102.997    0.000
##    .hght01            0.000                           
##    .hght03            0.000                           
##    .hght06            0.000                           
##    .hght09            0.000                           
##    .hght12            0.000                           
##    .hght15            0.000                           
##    .hght18            0.000                           
##    .hght24            0.000                           
##    .hght36            0.000                           
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)
##     eta_1             5.139    0.889    5.783    0.000
##     eta_2            10.231    1.981    5.165    0.000
##    .hght01  (thet)    0.816    0.056   14.476    0.000
##    .hght03  (thet)    0.816    0.056   14.476    0.000
##    .hght06  (thet)    0.816    0.056   14.476    0.000
##    .hght09  (thet)    0.816    0.056   14.476    0.000
##    .hght12  (thet)    0.816    0.056   14.476    0.000
##    .hght15  (thet)    0.816    0.056   14.476    0.000
##    .hght18  (thet)    0.816    0.056   14.476    0.000
##    .hght24  (thet)    0.816    0.056   14.476    0.000
##    .hght36  (thet)    0.816    0.056   14.476    0.000

We can try to make a diagram to check if we specified and estimated the model as intended.

#diagram of fitted model
semPaths(lb.hght.fit, what = "path", whatLabels = "par")

Bilinear Spline Growth Model with estimated knot point in MLM using nlme

Fitting the model using the nlme library.

hght.spline.nlme <- nlme(hght ~ b_1i + b_2i*(pmin(0,age-gamma)) + b_3i*(pmax(0,age-gamma)),
                      data = hght_long,
                      fixed = b_1i + b_2i + b_3i + gamma ~ 1,
                      random = b_1i + b_2i + b_3i ~ 1,
                      groups =~ id,
                      start = c(60, 5, 2, 8),
                      na.action = na.exclude)
summary(hght.spline.nlme) 
## Nonlinear mixed-effects model fit by maximum likelihood
##   Model: hght ~ b_1i + b_2i * (pmin(0, age - gamma)) + b_3i * (pmax(0,      age - gamma)) 
##   Data: hght_long 
##        AIC      BIC    logLik
##   2287.346 2335.359 -1132.673
## 
## Random effects:
##  Formula: list(b_1i ~ 1, b_2i ~ 1, b_3i ~ 1)
##  Level: id
##  Structure: General positive-definite, Log-Cholesky parametrization
##          StdDev     Corr       
## b_1i     2.43555766 b_1i  b_2i 
## b_2i     0.26036626 0.418      
## b_3i     0.05674639 0.548 0.550
## Residual 1.21623023            
## 
## Fixed effects:  b_1i + b_2i + b_3i + gamma ~ 1 
##          Value Std.Error  DF   t-value p-value
## b_1i  71.82716 0.3353841 495 214.16390       0
## b_2i   2.53762 0.0525520 495  48.28782       0
## b_3i   0.90117 0.0098949 495  91.07447       0
## gamma  7.62649 0.1372391 495  55.57085       0
##  Correlation: 
##       b_1i   b_2i   b_3i  
## b_2i  -0.040              
## b_3i   0.041  0.199       
## gamma  0.514 -0.650 -0.234
## 
## Standardized Within-Group Residuals:
##         Min          Q1         Med          Q3         Max 
## -2.64758397 -0.55058442  0.01787727  0.58480211  2.59728429 
## 
## Number of Observations: 581
## Number of Groups: 83

Bilinear Spline Growth Model with estimated knot point in SEM using lavaan

Note: this code contains a few bugs.

est.knot.spline.hght.lavaan <- '
#latent variable definitions

  #defining the intercept
  eta_1s =~ 1*hght01 + 
            1*hght03 + 
            1*hght06 + 
            1*hght09 + 
            1*hght12 + 
            1*hght15 + 
            1*hght18 + 
            1*hght24 + 
            1*hght36 

  #defining the slope
  eta_2s =~ 1*hght01 + 
            3*hght03 + 
            6*hght06 + 
            9*hght09 + 
            12*hght12 + 
            15*hght15 + 
            18*hght18 + 
            24*hght24 + 
            36*hght36 

  #defining eta_3
    eta_3s =~ NA*L13*hght01 + 
              L23*hght03 + 
              L33*hght06 + 
              L43*hght09 + 
              L53*hght12 + 
              L63*hght15 + 
              L73*hght18 + 
              L83*hght24 + 
              L93*hght36 

#latent means 
  eta_1s ~ start(58)*alpha_1s*1
  eta_2s ~ start(1.7)*alpha_2s*1
  eta_3s ~ start(-0.8)*alpha_3s*1

#factor variances 
  eta_1s ~~ start(4.6)*psi_11s*eta_1s 
  eta_2s ~~ start(0.03)*psi_22s*eta_2s
  eta_3s ~~ start(0.02)*psi_33s*eta_3s

#factor covariances
  #fixing variances to zero b/c getting negative estimates
  eta_1s ~~ start(0)*psi_21s*eta_2s
  eta_1s ~~ start(0)*psi_31s*eta_3s
  eta_2s ~~ start(-0.01)*psi_32s*eta_3s

#introducing phantom factors,  setting all parameters to 0 and mean to gamma

  gamma > 7   #added to get convergence
  gamma < 8   #added to get convergence
  phantom1 =~ 0*hght01
  phantom1 ~~ 0*phantom1
  phantom1 ~~ 0*eta_1s
  phantom1 ~~ 0*eta_2s
  phantom1 ~~ 0*eta_3s
  phantom1 ~ start(8)*gamma*1

  phantom2 =~ 0*hght01
  phantom2 ~~ 0*phantom2
  phantom2 ~~ 0*phantom1
  phantom2 ~~ 0*eta_1s
  phantom2 ~~ 0*eta_2s
  phantom2 ~~ 0*eta_3s
  phantom2 ~ start(71.7)*alpha_1*1

  phantom3 =~ 0*hght01
  phantom3 ~~ 0*phantom3
  phantom3 ~~ 0*phantom2
  phantom3 ~~ 0*phantom1
  phantom3 ~~ 0*eta_1s
  phantom3 ~~ 0*eta_2s
  phantom3 ~~ 0*eta_3s
  phantom3 ~ start(2.5)*alpha_2*1

  phantom4 =~ 0*hght01
  phantom4 ~~ 0*phantom4
  phantom4 ~~ 0*phantom3
  phantom4 ~~ 0*phantom2
  phantom4 ~~ 0*phantom1
  phantom4 ~~ 0*eta_1s
  phantom4 ~~ 0*eta_2s
  phantom4 ~~ 0*eta_3s
  phantom4 ~ start(0.9)*alpha_3*1

  phantom5 =~ 0*hght01
  phantom5 ~~ 0*phantom5
  phantom5 ~~ 0*phantom4
  phantom5 ~~ 0*phantom3
  phantom5 ~~ 0*phantom2
  phantom5 ~~ 0*phantom1
  phantom5 ~~ 0*eta_1s
  phantom5 ~~ 0*eta_2s
  phantom5 ~~ 0*eta_3s
  phantom5 ~ start(5.9)*psi_11*1

  phantom6 =~ 0*hght01
  phantom6 ~~ 0*phantom6
  phantom6 ~~ 0*phantom5
  phantom6 ~~ 0*phantom4
  phantom6 ~~ 0*phantom3
  phantom6 ~~ 0*phantom2
  phantom6 ~~ 0*phantom1
  phantom6 ~~ 0*eta_1s
  phantom6 ~~ 0*eta_2s
  phantom6 ~~ 0*eta_3s
  phantom6 ~ start(0.05)*psi_22*1

  phantom7 =~ 0*hght01
  phantom7 ~~ 0*phantom7
  phantom7 ~~ 0*phantom6
  phantom7 ~~ 0*phantom5
  phantom7 ~~ 0*phantom4
  phantom7 ~~ 0*phantom3
  phantom7 ~~ 0*phantom2
  phantom7 ~~ 0*phantom1
  phantom7 ~~ 0*eta_1s
  phantom7 ~~ 0*eta_2s
  phantom7 ~~ 0*eta_3s
  phantom7 ~ start(0.001)*psi_33*1

  phantom8 =~ 0*hght01
  phantom8 ~~ 0*phantom8
  phantom8 ~~ 0*phantom7
  phantom8 ~~ 0*phantom6
  phantom8 ~~ 0*phantom5
  phantom8 ~~ 0*phantom4
  phantom8 ~~ 0*phantom3
  phantom8 ~~ 0*phantom2
  phantom8 ~~ 0*phantom1
  phantom8 ~~ 0*eta_1s
  phantom8 ~~ 0*eta_2s
  phantom8 ~~ 0*eta_3s
  phantom8 ~ start(0.26)*psi_21*1

  phantom9 =~ 0*hght01
  phantom9 ~~ 0*phantom9
  phantom9 ~~ 0*phantom8
  phantom9 ~~ 0*phantom7
  phantom9 ~~ 0*phantom6
  phantom9 ~~ 0*phantom5
  phantom9 ~~ 0*phantom4
  phantom9 ~~ 0*phantom3
  phantom9 ~~ 0*phantom2
  phantom9 ~~ 0*phantom1
  phantom9 ~~ 0*eta_1s
  phantom9 ~~ 0*eta_2s
  phantom9 ~~ 0*eta_3s
  phantom9 ~ start(0.07)*psi_31*1

  phantom10 =~ 0*hght01
  phantom10 ~~ 0*phantom10
  phantom10 ~~ 0*phantom9
  phantom10 ~~ 0*phantom8
  phantom10 ~~ 0*phantom7
  phantom10 ~~ 0*phantom6
  phantom10 ~~ 0*phantom5
  phantom10 ~~ 0*phantom4
  phantom10 ~~ 0*phantom3
  phantom10 ~~ 0*phantom2
  phantom10 ~~ 0*phantom1
  phantom10 ~~ 0*eta_1s
  phantom10 ~~ 0*eta_2s
  phantom10 ~~ 0*eta_3s
  phantom10 ~ start(0.008)*psi_32*1

#contraints to define factor loadings

  L13 == sqrt(( 1 - gamma)^2)
  L23 == sqrt(( 3 - gamma)^2)
  L33 == sqrt(( 6 - gamma)^2)
  L43 == sqrt(( 9 - gamma)^2)
  L53 == sqrt((12 - gamma)^2)
  L63 == sqrt((15 - gamma)^2)
  L73 == sqrt((18 - gamma)^2)
  L83 == sqrt((24 - gamma)^2)
  L93 == sqrt((36 - gamma)^2)

  alpha_1 == alpha_1s + gamma*alpha_2s
  alpha_2 == alpha_2s - alpha_3s
  alpha_3 == alpha_2s + alpha_3s

  psi_11 == (psi_11s + gamma*gamma*psi_22s) + 2*psi_21s*gamma
  psi_22 == (psi_22s + psi_33s) - 2*psi_32s
  psi_33 == (psi_22s + psi_33s) + 2*psi_32s

  psi_21 == psi_21s - psi_31s + gamma*(psi_22s-psi_32s)
  psi_31 == psi_21s + psi_31s + gamma*(psi_22s+psi_32s)
  psi_32 == psi_22s - psi_33s

#manifest variances 
  hght01 ~~ start(1.47)*theta*hght01
  hght03 ~~ theta*hght03
  hght06 ~~ theta*hght06
  hght09 ~~ theta*hght09
  hght12 ~~ theta*hght12
  hght15 ~~ theta*hght15
  hght18 ~~ theta*hght18
  hght24 ~~ theta*hght24
  hght36 ~~ theta*hght36

#manifest means (fixed to zero)
  hght01 ~ 0*1
  hght03 ~ 0*1
  hght06 ~ 0*1
  hght09 ~ 0*1
  hght12 ~ 0*1
  hght15 ~ 0*1
  hght18 ~ 0*1
  hght24 ~ 0*1
  hght36 ~ 0*1'

Fitting the model.

spline.hght.fit <- lavaan(est.knot.spline.hght.lavaan,
                data = hght_wide,
                meanstructure = TRUE,
                estimator = "ML",
                missing = "fiml",
                fixed.x = FALSE,
                mimic="mplus",
                control=list(iter.max=1000),
                verbose=FALSE)
summary(spline.hght.fit, fit.measures=TRUE)
## lavaan 0.6-9 ended normally after 845 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of model parameters                        37
##   Number of inequality constraints                   2
##   Row rank of the constraints matrix                27
##                                                       
##   Number of observations                            83
##   Number of missing patterns                        31
##                                                       
## Model Test User Model:
##                                                       
##   Test statistic                               393.205
##   Degrees of freedom                                43
##   P-value (Chi-square)                           0.000
## 
## Model Test Baseline Model:
## 
##   Test statistic                               955.844
##   Degrees of freedom                                36
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.619
##   Tucker-Lewis Index (TLI)                       0.681
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)              -1132.344
##   Loglikelihood unrestricted model (H1)       -935.741
##                                                       
##   Akaike (AIC)                                2286.687
##   Bayesian (BIC)                              2313.294
##   Sample-size adjusted Bayesian (BIC)         2278.598
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.313
##   90 Percent confidence interval - lower         0.285
##   90 Percent confidence interval - upper         0.342
##   P-value RMSEA <= 0.05                          0.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.494
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Observed
##   Observed information based on                Hessian
## 
## Latent Variables:
##                    Estimate  Std.Err  z-value  P(>|z|)
##   eta_1s =~                                           
##     hght01            1.000                           
##     hght03            1.000                           
##     hght06            1.000                           
##     hght09            1.000                           
##     hght12            1.000                           
##     hght15            1.000                           
##     hght18            1.000                           
##     hght24            1.000                           
##     hght36            1.000                           
##   eta_2s =~                                           
##     hght01            1.000                           
##     hght03            3.000                           
##     hght06            6.000                           
##     hght09            9.000                           
##     hght12           12.000                           
##     hght15           15.000                           
##     hght18           18.000                           
##     hght24           24.000                           
##     hght36           36.000                           
##   eta_3s =~                                           
##     hght01   (L13)    6.517    0.133   48.979    0.000
##     hght03   (L23)    4.517    0.133   33.947    0.000
##     hght06   (L33)    1.517    0.133   11.400    0.000
##     hght09   (L43)    1.483    0.133   11.148    0.000
##     hght12   (L53)    4.483    0.133   33.696    0.000
##     hght15   (L63)    7.483    0.133   56.243    0.000
##     hght18   (L73)   10.483    0.133   78.791    0.000
##     hght24   (L83)   16.483    0.133  123.886    0.000
##     hght36   (L93)   28.483    0.133  214.077    0.000
##   phantom1 =~                                         
##     hght01            0.000                           
##   phantom2 =~                                         
##     hght01            0.000                           
##   phantom3 =~                                         
##     hght01            0.000                           
##   phantom4 =~                                         
##     hght01            0.000                           
##   phantom5 =~                                         
##     hght01            0.000                           
##   phantom6 =~                                         
##     hght01            0.000                           
##   phantom7 =~                                         
##     hght01            0.000                           
##   phantom8 =~                                         
##     hght01            0.000                           
##   phantom9 =~                                         
##     hght01            0.000                           
##   phantom10 =~                                        
##     hght01            0.000                           
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)
##   eta_1s ~~                                           
##     eta_2s  (p_21)    0.003    0.048    0.057    0.955
##     eta_3s  (p_31)    0.030    0.046    0.642    0.521
##   eta_2s ~~                                           
##     eta_3s  (p_32)   -0.017    0.005   -3.244    0.001
##   eta_1s ~~                                           
##     phantm1           0.000                           
##   eta_2s ~~                                           
##     phantm1           0.000                           
##   eta_3s ~~                                           
##     phantm1           0.000                           
##   phantom1 ~~                                         
##     phantm2           0.000                           
##   eta_1s ~~                                           
##     phantm2           0.000                           
##   eta_2s ~~                                           
##     phantm2           0.000                           
##   eta_3s ~~                                           
##     phantm2           0.000                           
##   phantom2 ~~                                         
##     phantm3           0.000                           
##   phantom1 ~~                                         
##     phantm3           0.000                           
##   eta_1s ~~                                           
##     phantm3           0.000                           
##   eta_2s ~~                                           
##     phantm3           0.000                           
##   eta_3s ~~                                           
##     phantm3           0.000                           
##   phantom3 ~~                                         
##     phantm4           0.000                           
##   phantom2 ~~                                         
##     phantm4           0.000                           
##   phantom1 ~~                                         
##     phantm4           0.000                           
##   eta_1s ~~                                           
##     phantm4           0.000                           
##   eta_2s ~~                                           
##     phantm4           0.000                           
##   eta_3s ~~                                           
##     phantm4           0.000                           
##   phantom4 ~~                                         
##     phantm5           0.000                           
##   phantom3 ~~                                         
##     phantm5           0.000                           
##   phantom2 ~~                                         
##     phantm5           0.000                           
##   phantom1 ~~                                         
##     phantm5           0.000                           
##   eta_1s ~~                                           
##     phantm5           0.000                           
##   eta_2s ~~                                           
##     phantm5           0.000                           
##   eta_3s ~~                                           
##     phantm5           0.000                           
##   phantom5 ~~                                         
##     phantm6           0.000                           
##   phantom4 ~~                                         
##     phantm6           0.000                           
##   phantom3 ~~                                         
##     phantm6           0.000                           
##   phantom2 ~~                                         
##     phantm6           0.000                           
##   phantom1 ~~                                         
##     phantm6           0.000                           
##   eta_1s ~~                                           
##     phantm6           0.000                           
##   eta_2s ~~                                           
##     phantm6           0.000                           
##   eta_3s ~~                                           
##     phantm6           0.000                           
##   phantom6 ~~                                         
##     phantm7           0.000                           
##   phantom5 ~~                                         
##     phantm7           0.000                           
##   phantom4 ~~                                         
##     phantm7           0.000                           
##   phantom3 ~~                                         
##     phantm7           0.000                           
##   phantom2 ~~                                         
##     phantm7           0.000                           
##   phantom1 ~~                                         
##     phantm7           0.000                           
##   eta_1s ~~                                           
##     phantm7           0.000                           
##   eta_2s ~~                                           
##     phantm7           0.000                           
##   eta_3s ~~                                           
##     phantm7           0.000                           
##   phantom7 ~~                                         
##     phantm8           0.000                           
##   phantom6 ~~                                         
##     phantm8           0.000                           
##   phantom5 ~~                                         
##     phantm8           0.000                           
##   phantom4 ~~                                         
##     phantm8           0.000                           
##   phantom3 ~~                                         
##     phantm8           0.000                           
##   phantom2 ~~                                         
##     phantm8           0.000                           
##   phantom1 ~~                                         
##     phantm8           0.000                           
##   eta_1s ~~                                           
##     phantm8           0.000                           
##   eta_2s ~~                                           
##     phantm8           0.000                           
##   eta_3s ~~                                           
##     phantm8           0.000                           
##   phantom8 ~~                                         
##     phantm9           0.000                           
##   phantom7 ~~                                         
##     phantm9           0.000                           
##   phantom6 ~~                                         
##     phantm9           0.000                           
##   phantom5 ~~                                         
##     phantm9           0.000                           
##   phantom4 ~~                                         
##     phantm9           0.000                           
##   phantom3 ~~                                         
##     phantm9           0.000                           
##   phantom2 ~~                                         
##     phantm9           0.000                           
##   phantom1 ~~                                         
##     phantm9           0.000                           
##   eta_1s ~~                                           
##     phantm9           0.000                           
##   eta_2s ~~                                           
##     phantm9           0.000                           
##   eta_3s ~~                                           
##     phantm9           0.000                           
##   phantom9 ~~                                         
##     phntm10           0.000                           
##   phantom8 ~~                                         
##     phntm10           0.000                           
##   phantom7 ~~                                         
##     phntm10           0.000                           
##   phantom6 ~~                                         
##     phntm10           0.000                           
##   phantom5 ~~                                         
##     phntm10           0.000                           
##   phantom4 ~~                                         
##     phntm10           0.000                           
##   phantom3 ~~                                         
##     phntm10           0.000                           
##   phantom2 ~~                                         
##     phntm10           0.000                           
##   phantom1 ~~                                         
##     phntm10           0.000                           
##   eta_1s ~~                                           
##     phntm10           0.000                           
##   eta_2s ~~                                           
##     phntm10           0.000                           
##   eta_3s ~~                                           
##     phntm10           0.000                           
## 
## Intercepts:
##                    Estimate  Std.Err  z-value  P(>|z|)
##     et_1 (alph_1s)   58.653    0.264  222.323    0.000
##     et_2 (alph_2s)    1.734    0.028   62.401    0.000
##     et_3 (alph_3s)   -0.831    0.026  -32.163    0.000
##     phn1    (gamm)    7.517    0.133   56.495    0.000
##     phn2 (alpha_1)   71.688    0.333  215.282    0.000
##     phn3 (alpha_2)    2.565    0.053   48.639    0.000
##     phn4 (alpha_3)    0.903    0.010   90.984    0.000
##     phn5    (p_11)    5.906    1.045    5.650    0.000
##     phn6    (p_22)    0.070    0.020    3.403    0.001
##     phn7    (p_33)    0.003    0.001    2.915    0.004
##     phn8    (p_21)    0.266    0.112    2.365    0.018
##     phn9    (p_31)    0.076    0.025    3.037    0.002
##     ph10    (p_32)    0.008    0.003    2.434    0.015
##    .hg01              0.000                           
##    .hg03              0.000                           
##    .hg06              0.000                           
##    .hg09              0.000                           
##    .hg12              0.000                           
##    .hg15              0.000                           
##    .hg18              0.000                           
##    .hg24              0.000                           
##    .hg36              0.000                           
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)
##     eta_1s  (p_11)    4.603    0.820    5.615    0.000
##     eta_2s  (p_22)    0.022    0.006    4.056    0.000
##     eta_3s  (p_33)    0.014    0.005    2.667    0.008
##     phantm1           0.000                           
##     phantm2           0.000                           
##     phantm3           0.000                           
##     phantm4           0.000                           
##     phantm5           0.000                           
##     phantm6           0.000                           
##     phantm7           0.000                           
##     phantm8           0.000                           
##     phantm9           0.000                           
##     phntm10           0.000                           
##    .hght01  (thet)    1.476    0.110   13.379    0.000
##    .hght03  (thet)    1.476    0.110   13.379    0.000
##    .hght06  (thet)    1.476    0.110   13.379    0.000
##    .hght09  (thet)    1.476    0.110   13.379    0.000
##    .hght12  (thet)    1.476    0.110   13.379    0.000
##    .hght15  (thet)    1.476    0.110   13.379    0.000
##    .hght18  (thet)    1.476    0.110   13.379    0.000
##    .hght24  (thet)    1.476    0.110   13.379    0.000
##    .hght36  (thet)    1.476    0.110   13.379    0.000
## 
## Constraints:
##                                                |Slack|
##     gamma - (7)                                  0.517
##     8 - (gamma)                                  0.483
##     L13 - (sqrt((1-gamma)^2))                    0.000
##     L23 - (sqrt((3-gamma)^2))                    0.000
##     L33 - (sqrt((6-gamma)^2))                    0.000
##     L43 - (sqrt((9-gamma)^2))                    0.000
##     L53 - (sqrt((12-gamma)^2))                   0.000
##     L63 - (sqrt((15-gamma)^2))                   0.000
##     L73 - (sqrt((18-gamma)^2))                   0.000
##     L83 - (sqrt((24-gamma)^2))                   0.000
##     L93 - (sqrt((36-gamma)^2))                   0.000
##     alpha_1 - (alpha_1s+gamma*alpha_2s)          0.000
##     alpha_2 - (alpha_2s-alpha_3s)                0.000
##     alpha_3 - (alpha_2s+alpha_3s)                0.000
##     ps_11-((ps_11s+gmm*gmm*ps_22s)+2*ps_21s*)    0.000
##     psi_22 - ((psi_22s+psi_33s)-2*psi_32s)       0.000
##     psi_33 - ((psi_22s+psi_33s)+2*psi_32s)       0.000
##     ps_21-(ps_21s-ps_31s+gmm*(ps_22s-ps_32s))    0.000
##     ps_31-(ps_21s+ps_31s+gmm*(ps_22s+ps_32s))    0.000
##     psi_32 - (psi_22s-psi_33s)                   0.000

We can try to make a diagram to check if we specified and estimated the model as intended.

#diagram of fitted model
semPaths(spline.hght.fit, what = "path", whatLabels = "par")

However, again this does not work, probably because the function does not know how to work with the phantom variables.

Conclusion

We have demonstrated how to fit growth models that have nonlinearity in their parameters. This greatly expands what it possible. However, be cautious in interpretaion regarding the homoggeneity of shape or knot point across persons.

All that change is so cool!