Make a function on
lavaan
object usable in a lavaan
model syntax.
Usage
gen_userp(func, sem_out)
gen_sem_out_userp(
userp,
sem_out,
userp_name = "semlbciuserp1234",
fix = TRUE,
control_args = list(),
iter.max = 10000,
max_attempts = 5
)
Arguments
- func
A function that receives a
lavaan
-object and returns a scalar. See Details on the restriction on this function.- sem_out
A
lavaan
-class object to be modified.- userp
A function that is generated by
gen_userp()
.- userp_name
The name of the function
userp
to be used in thelavaan
model. It does not have to be the name of the function inuserp
. Should be changed only if it conflicts with another object in the parent environment, which should not happen if the model is always fitted in a clean R session.- fix
If
TRUE
, the default, the function generated is used to fix the value ofuserp
to a target value using an equality constraint. IfFALSE
, then the function simply fits the model to the data.- control_args
To be passed to the argument of the same name in
lavaan::lavaan()
. Default islist()
. Can be used to set the default values of this argument in the generated function.- iter.max
The maximum number of iteration when the generated function fit the model. Default is 10000.
- max_attempts
If the initial fit with the equality constraint fails, how many more attempts will be made by the generated function. Default is 5.
Value
gen_userp
It returns a function that
accepts a numeric vector of length
equals to the number of free parameters
in sem_out
, and returns a scalar
which is the output of func
. If this
vector is not supplied, it will try to
find it in the parent.frame()
. This
is how it works inside a lavaan
model.
gen_sem_out_userp
If fix
is TRUE
, it returns a
function with these arguments:
target
: The value to which the user-defined parameter will be fixed to.verbose
: IfTRUE
, additional information will be printed when fitting the model.control
: The values to be passed as a list to the argument of the same name inlavaan::lavaan()
.seed
: Numeric. If supplied, it will be used inset.seed()
to initialize the random number generator. Necessary to reproduce some results because random numbers are used in some steps inlavaan
. IfNULL
, the default,set.seed()
will not be called.
If fix
is `FALSE, then it returns a
function with optional arguments that
will be ignored, Calling it will
simply fit the modified model to the
data. Useful for getting the value of
the user-defined parameter.
Details
gen_userp
There are cases in which we want to
create a user parameter which is a
function of other free parameters,
computed by a function. However such
a function may work only on a
lavaan
object.
If the target function works by
extracting parameter estimates stored
in the Model
slot and/or the
implied
slot, then gen_userp()
can be used to convert it to a
function that retrieves the parameter
estimates when being called by
lavaan::lavaan()
or its wrappers,
modifies the stored
lavaan
object using
lavaan::lav_model_set_parameters()
and lavaan::lav_model_implied()
to
change the estimates, and call the
target function.
Note that this is an unconventional way to define a user parameter and the generated function should always be checked to see whether it works as expected.
As shown in the examples, the parameter computed this may not have standard error nor p-value.
The main purpose is for the point
estimate, for searching the
likelihood-based confidence bound
using ci_bound_ur()
and
ci_bound_ur_i()
.
Note that the target function
specified in func
should work
directly on the parameter estimates
stored in the Model
slot and then
get the estimates using
lavaan::lav_model_get_parameters()
.
Functions that work on the unmodified
output generated by
lavaan::lavaan()
usually do not
work.
Users are not recommended to use
gen_userp()
and gen_sem_out_userp()
directly because they require
unconventional way to extract
parameter estimates from a lavaan
model. However, developers may use
them to include functions
they wrote in a lavaan model. This
is the technique used by
ci_bound_ur_i()
to constrain any
parameter in a model to an arbitrary
value.
Examples
library(lavaan)
data(simple_med)
dat <- simple_med
mod <-
"
m ~ a*x
y ~ b*m
ab := a*b
"
fit_med <- sem(mod, simple_med, fixed.x = FALSE)
parameterEstimates(fit_med)
#> lhs op rhs label est se z pvalue ci.lower ci.upper
#> 1 m ~ x a 1.676 0.431 3.891 0.000 0.832 2.520
#> 2 y ~ m b 0.535 0.073 7.300 0.000 0.391 0.679
#> 3 m ~~ m 34.710 3.471 10.000 0.000 27.907 41.513
#> 4 y ~~ y 40.119 4.012 10.000 0.000 32.256 47.982
#> 5 x ~~ x 0.935 0.094 10.000 0.000 0.752 1.118
#> 6 ab := a*b ab 0.897 0.261 3.434 0.001 0.385 1.409
# A trivial example for verifying the results
my_ab <- function(object) {
# Need to use lav_model_get_parameters()
# because the object is only a modified
# lavaan-object, not one directly
# generated by lavaan function
est <- lavaan::lav_model_get_parameters(object@Model, type = "user")
unname(est[1] * est[2])
}
# Check the function
my_ab(fit_med)
#> [1] 0.8968668
coef(fit_med, type = "user")["ab"]
#> ab
#> 0.8968668
# Create the function
my_userp <- gen_userp(func = my_ab,
sem_out = fit_med)
# Try it on the vector of free parameters
my_userp(coef(fit_med))
#> [1] 0.8968668
# Generate a modified lavaan model
fit_userp <- gen_sem_out_userp(userp = my_userp,
userp_name = "my_userp",
sem_out = fit_med)
# This function can then be used in the model syntax.
# Note that the following example only work when called inside the
# workspace or inside other functions such as ci_bound_ur()`
# and `ci_bound_ur_i()` because `lavaan::sem()` will
# search `my_userp()` in the global environment.
# Therefore, the following lines are commented out.
# They should be run only in a "TRUE" interactive
# session.
# mod2 <-
# "
# m ~ x
# y ~ m
# ab := my_userp()
# "
# fit_med2 <- sem(mod2, simple_med, fixed.x = FALSE)
# parameterEstimates(fit_med2)
#
# # Fit the model with the output of the function, a*b
# # fixed to .50
#
# fit_new <- fit_userp(.50)
#
# # Check if the parameter ab is fixed to .50
# parameterEstimates(fit_new)