Convert a 'lavaan' Parameter Table to a 'lavaan' Model Syntax
Source:R/ptable_to_syntax.R
ptable_to_syntax.Rd
It tries to generate a
'lavaan' model syntax from a
lavaan
parameter table.
Arguments
- object
If set to a
lavaan
object, such as the output oflavaan::sem()
orlavaan::cfa()
, the parameter table will be extracted from it bylavaan::parameterTable()
. If set to a parameter table, it will be used to generate the model syntax. It can also the output oflavaan::lavParseModelString()
withas.data.frame. = TRUE
, ifallow_incomplete
is set toTRUE
. Note thatallow_incomplete
is set toFALSE
by default becauselavaan::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()
withas.data.frame. = TRUE
is allowed. Default ifFALSE
.- object1
The first
lavaan
parameter table, to be compared withobject2
. If it is set to alavaan
object (e.g., the output oflavaan::sem()
orlavaan::cfa()
), then the parameter table will be extracted from it.- object2
The second
lavaan
parameter table, to be compared withobject1
. If it is set to alavaan
object (e.g., the output oflavaan::sem()
orlavaan::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-19 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-19 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