Skip to contents

It tries to generate a 'lavaan' model syntax from a lavaan parameter table.

Usage

ptable_to_syntax(object, allow_incomplete = FALSE)

compare_ptables(object1, object2)

Arguments

object

If set to a lavaan object, such as the output of lavaan::sem() or lavaan::cfa(), the parameter table will be extracted from it by lavaan::parameterTable(). If set to a parameter table, it will be used to generate the model syntax. It can also the output of lavaan::lavParseModelString() with as.data.frame. = TRUE, if allow_incomplete is set to TRUE. Note that allow_incomplete is set to FALSE by default because lavaan::lavParseModelString() only parses the model syntax and there is no guarantee that the model defined is valid.

allow_incomplete

Whether incomplete parameter table formed by lavaan::lavParseModelString() with as.data.frame. = TRUE is allowed. Default if FALSE.

object1

The first lavaan parameter table, to be compared with object2. If it is set to a lavaan object (e.g., the output of lavaan::sem() or lavaan::cfa()), then the parameter table will be extracted from it.

object2

The second lavaan parameter table, to be compared with object1. If it is set to a lavaan object (e.g., the output of lavaan::sem() or lavaan::cfa()), then the parameter table will be extracted from it.

Value

ptable_to_syntax() returns a length-one character vector that stores the generated lavaan model syntax.

compare_ptables() returns a length-one logical vector. TRUE

if the two models are identical in form. FALSE if they are not identical.

Details

This function tries to convert a lavaan parameter table to a text representation of the lavaan model specified in model syntax.

When users call lavaan::sem(), in addition to the model syntax, other arguments not stored in the syntax are also used to produce the final model (e.g., meanstructure, fixed.x, and std.lv). To produce exactly the same model, these arguments are also needed to be specified, which is difficult to generate using only the parameter table.

Therefore, the model syntax produced will state all aspects of a model explicitly, even for those aspects that usually can be omitted due to the default values of these arguments. This approach requires users to call lavaan::lavaan() directly, instead of its wrappers (e.g, lavaan::sem()), to produce the same parameter table.

The model syntax produced this way is more difficult to read. However, it ensures that original model can be reproduced, without the need to know the arguments to set.

Due to the nearly unlimited possibilities in the form of a model, it is recommended to compare the model generated by the model syntax with the original parameter table using compare_ptables(). It only compares the forms of the two models, including user starting values, if any. It does not compare parameter estimates and standard errors.

Raw Specification From lavaan::lavParseModelString()

There may be cases in which the parameter table is the "incomplete" table generated by lavaan::lavParseModelString(), with as.data.frame. = TRUE. This table is "incomplete" because it is formed merely by parsing the model syntax. There is no guarantee that the model is valid.

The function ptable_to_syntax() has basic support for this kind of tables but it is disabled by default. To process an incomplete parameter table formed by lavaan::lavParseModelString(), set allow_incomplete to TRUE.

Limitations

The function ptable_to_syntax() does not yet support the following models:

  • Multiple-group models.

  • Multilevel models.

  • Models with categorical variables.

  • Models with user-specified lower or upper bounds.

  • Models with the operator <~.

  • Models with constraints imposed by equal().

  • Models with labels having spaces.

  • Models with labels having syntax operators (e.g., ~, =~, etc.).

Functions

  • ptable_to_syntax(): Convert a lavaan parameter a lavaan model syntax.

  • compare_ptables(): Compare two lavaan parameter tables.

Author

Shu Fai Cheung https://orcid.org/0000-0002-9871-9448. This function is inspired by a discussion at the Google Group https://groups.google.com/g/lavaan/c/1ueFiue9qLM/m/cJhxDoqeBAAJ.

Examples


library(lavaan)

mod <-
"
visual  =~ x3 + x1 + x2
textual =~ x4 + x6 + x5
speed   =~ x7 + x8 + x9 + start(0.1) * x6
visual ~ a*textual
speed ~ b*visual
ab := a * b
"

fit <- sem(mod, data = HolzingerSwineford1939)

mod_chk <- ptable_to_syntax(fit)
cat(mod_chk, sep = "\n")
#> visual =~ 1*x3 + x1 + x2
#> textual =~ 1*x4 + x6 + x5
#> speed =~ 1*x7 + x8 + x9 + x6 + start(0.1)*x6
#> visual ~ textual + a*textual
#> speed ~ visual + b*visual
#> ab := a*b
#> x3 ~~ x3
#> x1 ~~ x1
#> x2 ~~ x2
#> x4 ~~ x4
#> x6 ~~ x6
#> x5 ~~ x5
#> x7 ~~ x7
#> x8 ~~ x8
#> x9 ~~ x9
#> visual ~~ visual
#> textual ~~ textual
#> speed ~~ speed
# Need to call lavaan() directly
fit_chk <- lavaan(mod_chk, data = HolzingerSwineford1939)
fit_chk
#> lavaan 0.6.17 ended normally after 33 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                        21
#> 
#>   Number of observations                           301
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                                85.782
#>   Degrees of freedom                                24
#>   P-value (Chi-square)                           0.000
fit
#> lavaan 0.6.17 ended normally after 33 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                        21
#> 
#>   Number of observations                           301
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                                85.782
#>   Degrees of freedom                                24
#>   P-value (Chi-square)                           0.000
# Compare the parameter table:
(ptable1 <- parameterTable(fit))
#>    id     lhs op     rhs user block group free ustart exo label plabel start
#> 1   1  visual =~      x3    1     1     1    0    1.0   0         .p1. 1.000
#> 2   2  visual =~      x1    1     1     1    1     NA   0         .p2. 0.903
#> 3   3  visual =~      x2    1     1     1    2     NA   0         .p3. 0.702
#> 4   4 textual =~      x4    1     1     1    0    1.0   0         .p4. 1.000
#> 5   5 textual =~      x6    1     1     1    3     NA   0         .p5. 0.924
#> 6   6 textual =~      x5    1     1     1    4     NA   0         .p6. 1.133
#> 7   7   speed =~      x7    1     1     1    0    1.0   0         .p7. 1.000
#> 8   8   speed =~      x8    1     1     1    5     NA   0         .p8. 1.221
#> 9   9   speed =~      x9    1     1     1    6     NA   0         .p9. 0.874
#> 10 10   speed =~      x6    1     1     1    7    0.1   0        .p10. 0.100
#> 11 11  visual  ~ textual    1     1     1    8     NA   0     a  .p11. 0.000
#> 12 12   speed  ~  visual    1     1     1    9     NA   0     b  .p12. 0.000
#> 13 13      x3 ~~      x3    0     1     1   10     NA   0        .p13. 0.637
#> 14 14      x1 ~~      x1    0     1     1   11     NA   0        .p14. 0.679
#> 15 15      x2 ~~      x2    0     1     1   12     NA   0        .p15. 0.691
#> 16 16      x4 ~~      x4    0     1     1   13     NA   0        .p16. 0.675
#> 17 17      x6 ~~      x6    0     1     1   14     NA   0        .p17. 0.598
#> 18 18      x5 ~~      x5    0     1     1   15     NA   0        .p18. 0.830
#> 19 19      x7 ~~      x7    0     1     1   16     NA   0        .p19. 0.592
#> 20 20      x8 ~~      x8    0     1     1   17     NA   0        .p20. 0.511
#> 21 21      x9 ~~      x9    0     1     1   18     NA   0        .p21. 0.508
#> 22 22  visual ~~  visual    0     1     1   19     NA   0        .p22. 0.050
#> 23 23 textual ~~ textual    0     1     1   20     NA   0        .p23. 0.050
#> 24 24   speed ~~   speed    0     1     1   21     NA   0        .p24. 0.050
#> 25 25      ab :=     a*b    1     0     0    0     NA   0    ab        0.000
#>      est    se
#> 1  1.000 0.000
#> 2  1.366 0.200
#> 3  0.759 0.138
#> 4  1.000 0.000
#> 5  0.914 0.057
#> 6  1.112 0.065
#> 7  1.000 0.000
#> 8  1.193 0.168
#> 9  1.091 0.153
#> 10 0.060 0.082
#> 11 0.307 0.057
#> 12 0.456 0.097
#> 13 0.846 0.090
#> 14 0.558 0.109
#> 15 1.135 0.102
#> 16 0.369 0.048
#> 17 0.358 0.043
#> 18 0.445 0.059
#> 19 0.806 0.082
#> 20 0.484 0.075
#> 21 0.566 0.071
#> 22 0.336 0.077
#> 23 0.982 0.112
#> 24 0.288 0.069
#> 25 0.140 0.034
(ptable2 <- parameterTable(fit_chk))
#>    id     lhs op     rhs user block group free ustart exo label plabel start
#> 1   1  visual =~      x3    1     1     1    0    1.0   0         .p1. 1.000
#> 2   2  visual =~      x1    1     1     1    1     NA   0         .p2. 0.903
#> 3   3  visual =~      x2    1     1     1    2     NA   0         .p3. 0.702
#> 4   4 textual =~      x4    1     1     1    0    1.0   0         .p4. 1.000
#> 5   5 textual =~      x6    1     1     1    3     NA   0         .p5. 0.924
#> 6   6 textual =~      x5    1     1     1    4     NA   0         .p6. 1.133
#> 7   7   speed =~      x7    1     1     1    0    1.0   0         .p7. 1.000
#> 8   8   speed =~      x8    1     1     1    5     NA   0         .p8. 1.221
#> 9   9   speed =~      x9    1     1     1    6     NA   0         .p9. 0.874
#> 10 10   speed =~      x6    1     1     1    7    0.1   0        .p10. 0.100
#> 11 11  visual  ~ textual    1     1     1    8     NA   0     a  .p11. 0.000
#> 12 12   speed  ~  visual    1     1     1    9     NA   0     b  .p12. 0.000
#> 13 13      x3 ~~      x3    1     1     1   10     NA   0        .p13. 0.637
#> 14 14      x1 ~~      x1    1     1     1   11     NA   0        .p14. 0.679
#> 15 15      x2 ~~      x2    1     1     1   12     NA   0        .p15. 0.691
#> 16 16      x4 ~~      x4    1     1     1   13     NA   0        .p16. 0.675
#> 17 17      x6 ~~      x6    1     1     1   14     NA   0        .p17. 0.598
#> 18 18      x5 ~~      x5    1     1     1   15     NA   0        .p18. 0.830
#> 19 19      x7 ~~      x7    1     1     1   16     NA   0        .p19. 0.592
#> 20 20      x8 ~~      x8    1     1     1   17     NA   0        .p20. 0.511
#> 21 21      x9 ~~      x9    1     1     1   18     NA   0        .p21. 0.508
#> 22 22  visual ~~  visual    1     1     1   19     NA   0        .p22. 0.050
#> 23 23 textual ~~ textual    1     1     1   20     NA   0        .p23. 0.050
#> 24 24   speed ~~   speed    1     1     1   21     NA   0        .p24. 0.050
#> 25 25      ab :=     a*b    1     0     0    0     NA   0    ab        0.000
#>      est    se
#> 1  1.000 0.000
#> 2  1.366 0.200
#> 3  0.759 0.138
#> 4  1.000 0.000
#> 5  0.914 0.057
#> 6  1.112 0.065
#> 7  1.000 0.000
#> 8  1.193 0.168
#> 9  1.091 0.153
#> 10 0.060 0.082
#> 11 0.307 0.057
#> 12 0.456 0.097
#> 13 0.846 0.090
#> 14 0.558 0.109
#> 15 1.135 0.102
#> 16 0.369 0.048
#> 17 0.358 0.043
#> 18 0.445 0.059
#> 19 0.806 0.082
#> 20 0.484 0.075
#> 21 0.566 0.071
#> 22 0.336 0.077
#> 23 0.982 0.112
#> 24 0.288 0.069
#> 25 0.140 0.034
compare_ptables(ptable1, ptable2)
#> [1] TRUE