umx for OpenMx users
Previously, we saw how to make umxRAM models and use umxPath. The following is a guide for those familiar with OpenMx’s mxModel and mxPath functions.
umxRAM vs mxModel
umxRAM differs from mxModel in eight major ways:
- No Model
typeneeded.- Unlike
mxModel,umxRAMumxACEetc knows the model type that’s not needed.
- Unlike
- No
manifestVarslist.- In umxRAM, the manifests are simply the names found in umxPath statements that are also in the data. So OpenMx no need too explicitly list up
manifestVars.
- In umxRAM, the manifests are simply the names found in umxPath statements that are also in the data. So OpenMx no need too explicitly list up
- No
latentVarslist.- In
umx, latents are just variables not found in the data.
- In
- Data is explicitly passed in as
data =- In
mxModel, data has to be processed by mxData. umx can handle this for you, including dropping unused variables, taking a covariance matrix, etc.
- In
- Path labels are automatic
- umxRAM gives plain english path names like “a_to_b”. or
a_r1c1. InmxModel, each parameter has no label by default.
- umxRAM gives plain english path names like “a_to_b”. or
- Start values are automatic and smart.
- In
mxModel, each parameter starts at 0 unless manually set viavalues=.
- In
- No need to separately
mxRunthe model- In
umxRAMthe model is automatically run and output tables and graphs produced. (turn this off withautoRun = FALSE)
- In
- No need to separately
mxSummarythe model- In
umxRAMtheumxSummaryis automatically printed.
- In
mxPath vs umxPath
umxPath differs from mxPath in that it supports new arguments to say what you want with less typing and (I think) more readably.
umxPath adds several new verbs which describe common patterns:
umxPath("A", with = "B", fixedAt = 1)
In mxPath, arrows, values, and free among others must be set independently. So, a path with 2 arrows, fixed at 1 requires you to specify all of those things, and to can mean “with”.
mxPath(from = "A", to = "B", arrows = 2, values = 1, free = FALSE)
In umx, you would say:
umxPath(cov = c("A", "B"), fixedAt = 1)
# or
umxPath("A", with = "B", fixedAt = 1)
In total there are about a dozen new words for describing paths (with, var, cov, unique.bivariate, unique.pairs, Cholesky, defn, means, v0m0, v1m0, v.m., fixedAt, freeAt, firstAt). You can learn more about and memorise these key new path writing ideas here
What follows in this post, is the construction of our toy model using base OpenMx functions. Nicely, mxModels can be updated by passing an existing model into mxModel and adding or subtracting objects “line by line”.
Build a model
To build the model, we need umx and some data:
require("umx")
data(mtcars)
I like to begin with making up a list of the manifests.
manifests = c("mpg", "disp", "wt")
We will use this to tell the model which columns are being modeled, but it’s also helpful to allow us to be clearer in listing what boxes connect where in more complex models.
First, make a model, give it a name, and let it know it is a RAM model (a model type that understands how to accepts mxPaths)
The name is a memorable string allowing us to refer across models (OpenMx handles multiple nested groups), and also for making umxCompare tables easier to understand. I usually pick a name that say what the model claims.
m1 <- mxModel("big_motor_bad_mpg", type = "RAM")
tip: If the first unnamed parameter is a string, it will be used as a name.
Next, we add a list of manifests to m1:
m1 <- mxModel(m1, manifestVars = manifests )
Important: manifestVars is a special reserved word. RAM models need it, and will complain (informatively) if you forget to set this list of boxes for the model.
We don’t have any latents in this model. If we did, they’d be included using latentVars = c("list", "the", "latents")
Now we can add the paths from disp and from wt to mpg
m1 <- mxModel(m1, mxPath(from = c("disp", "wt"), to = "mpg") )
These default to single-arrow paths with values left free, starting at 0 (probably jiggled to .01 when mxRun sees the model).
This exposes a great advanced feature of mxPath (and umxPath): They’re smart about reusing from and to. In this case, two paths to “mpg” are created, one from each of the two elements from. Learn more in the chapter on using umxPath.
So that was the same as:
m1 <- mxModel(m1,
mxPath(from = "disp", to = "mpg"),
mxPath(from = "wt" , to = "mpg")
)
PS: You can execute this - it will just re-write the two existing paths. This introduces a neat power in OpenMx: updating models by overwriting existing elements with new ones.
PPS: If you’ve got a graphviz graphing program installed, you can visualise what you’ve done in your model at every step.
Assuming you’ve got umx loaded, just say:
plot(m1)
Alternatively use the built in graphing function, omxGraphviz. This adds color, and uses circles to draw residuals (I use lines in plot), but doesn’t show path values, doesn’t allow customizing what gets drawn.
Next, we allow displacement and weight to correlate (a two headed path between them)
m1 = mxModel(m1, mxPath(from = "disp", to = "wt", arrows = 2))
Here we add arrows = 2. In the previous cases, we just used the default, which is 1-headed arrows.
Now we need to give the IVs some variance (2-head arrows). To be kind to the optimizer, we can start these at the known values:
m1 <- mxModel(m1,
mxPath(from = "disp", arrows = 2, free = TRUE, values = var(mtcars$disp)),
mxPath(from = "wt" , arrows = 2, free = TRUE, values = var(mtcars$wt))
)
mpg will need some residual variance:
m1 = mxModel(m1,
mxPath(from = "mpg", arrows = 2)
)
Finally, we must add the data against which we are testing our hypothesis:
m1 = mxModel(m1,
mxData(cov(mtcars[,manifests]), type = "cov", numObs = nrow(mtcars))
)
note: with cov data, we no longer need a means statement. umxRAM is pretty smart about this and if you feed it raw data but no means, it will add means for the manifests and latents.
We can be a bit more verbose about that for clarity
covData = cov(mtcars[,manifests], use ="pairwise.complete.obs")
numObs = nrow(mtcars)
m1 = mxModel(m1,
mxData(covData, type = "cov", numObs = numObs)
)
Done! So now we have a complete model, with all our hypothesised paths (variances and covariances) and the data. We are ready to run the model.
Run the model
Mow we run the model. In this case we take advantage of umxRun to also set labels and start values. Of course this won’t touch fixed values.
nb: See tutorials on using labels, and on values.
m1 = umxRun(m1, setLabels = TRUE, setValues = TRUE)
This exposes a lovely (and unique) feature of OpenMx: running a model returns a valid model that can be updated and run again
Report on the model
umxSummary(m1, std = TRUE)
Now we can compare this to competing models with umxCompare()