Using MxModel names (that include spaces) in MxAlgebras

12 replies [Last post]
Ryne's picture
Offline
Joined: 07/31/2009

I'm writing a helper function that takes a set of existing MxModels and returns a parent model that (a) includes them all as submodels, and (b) has an mxAlgebraObjective that sums the objectives of the existing MxModels. I have always avoided putting spaces in MxModel names when I plan to use that model in an algebra. Is it possible to refer to the objective slot in a model named "My Poorly Named Model" in an MxAlgebra? All of my usual tricks involving quotes don't work.

ryne

tbates's picture
Offline
Joined: 07/31/2009
Maybe rename the models

Maybe rename the models first, replacing illegal characters?

It's a pain when people want to enter your name into a calculator – can't call your kids µ or anything cute :-)

Ryne's picture
Offline
Joined: 07/31/2009
I thought about that.

I thought about that. Renaming would work, because you couldn't refer to the space-name model elsewhere in the model tree without hitting the same error. It's possible that someone could pass "model1" and "model 1" as different models, and that I'd create a conflict by renaming. I'm still not thrilled about changing people's models behind their backs, and if "Model 1" is a valid model name, then we should have a way to get full functionality in algebras. I was leaning towards an error unless someone has a way to include a variation of "Model 1".objective in an algebra, though screening for spaces may take more lines than the rest of the function.

Relevant to Tim's joke: http://xkcd.com/327/

tbrick's picture
Offline
Joined: 07/31/2009
Standard R practice tends to

Standard R practice tends to replace spaces with a '.'; some places prefer '_'. As long as you catch the case where two models are identical and either handle it or throw an appropriate error, it should be fine.

I think the code to sub out spaces is something like newString <- gsub("[ ]", ".", oldString).

Ryne's picture
Offline
Joined: 07/31/2009
I did figure out the gsub to

I did figure out the gsub to replace non-alphanumerics with a "_". I'd like to avoid the dot, just because someone could name a model "Whatever data" or "whatever objective" and then we'd have real problems. Mike, if you have a solution that keeps me from renaming people's models, let me know!

newNames <- gsub('[^[:alnum:]_]', "_", modelNames)
if (sum(newNames==modelNames)!=numModels)message("Model names used in algebras should avoid spaces and punctuation that can be confused for algebraic terms. Non-alphanumeric characters replaced with '_'.")

mspiegel's picture
Offline
Joined: 07/31/2009
There's probably a fancier

There's probably a fancier way to do it, but the following will work. I'm assuming that 'modelnames' is a vector of strings.

addDotObjective <- function(modelname) {
&nbsp;&nbsp;return(paste(modelname, "objective", sep = "."))
}
 
makeAlgebraSummation <- function(modelnames) {
&nbsp;&nbsp;modelnames <- lapply(modelnames, addDotObjective)
&nbsp;&nbsp;modelnames <- lapply(modelnames, as.symbol)
&nbsp;&nbsp;if (length(modelnames) == 1) {
&nbsp;&nbsp;&nbsp;&nbsp;return(eval(substitute(mxAlgebra(`x`, name = 'sum'), 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list(x = modelnames[[1]]))))
&nbsp;&nbsp;} else if (length(modelnames) == 2) {
&nbsp;&nbsp;&nbsp;&nbsp;return(eval(substitute(mxAlgebra(`x` + `y`, name = 'sum'), 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list(x = modelnames[[1]], y = modelnames[[2]]))))
&nbsp;&nbsp;} else {
&nbsp;&nbsp;&nbsp;&nbsp;expression <- substitute(`x` + `y`, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list(x = modelnames[[1]], y = modelnames[[2]]))
&nbsp;&nbsp;&nbsp;&nbsp;for(i in 3:length(modelnames)) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expression <- substitute(x + `y`,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list(x = expression, y = modelnames[[i]]))
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;return(eval(substitute(mxAlgebra(x, name = 'sum'),
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list(x = expression))))
&nbsp;&nbsp;}
}

Ryne's picture
Offline
Joined: 07/31/2009
I thought you were figuring

I thought you were figuring out a way to include spaces in model names. Sorry to make you do extra work, but I had a solution once the model names are scrubbed. I'll post complete code when the library this goes in is done.

Here's what I came up with for pasting it all together into an algebra. I used the gsub above to swap out non-alphanumerics for "_", but I'd like to better preserve people's model names if possible. newNames is assumed to be a vector of model names (after I scrub out the spaces and junk). alg is then the new mxAlgebra. Industrious users can probably figure out the rest of the function from there.

exp <- paste(newNames, ".objective", sep="")
exp <- paste(exp, collapse=" + ")
algName <- paste("name=", objName, "", sep="\"")
alg <- paste("mxAlgebra(", exp, ",", algName, ")")
alg <- eval(parse(text=alg))

mspiegel's picture
Offline
Joined: 07/31/2009
The following doesn't

The following doesn't work?

exp <- paste(newNames, ".objective", sep="")
exp <- sapply(exp, function(x) { paste("`", x, "`", sep = "") })
exp <- paste(exp, collapse=" + ")
algName <- paste("name=", objName, "", sep="\"")
alg <- paste("mxAlgebra(", exp, ",", algName, ")")
alg <- eval(parse(text=alg))

Ryne's picture
Offline
Joined: 07/31/2009
No. mxAlgebra breaks if I try

No. mxAlgebra breaks if I try any variation on 'Model 1'.objective, and the algebraObjective breaks if I try 'Model 1.objective'. If I use single quotes, I get an algebra objective error "non-numeric argument to binary operator. With the apostrophe/accent/thingee-under-the-tilde, the error is "object '`Model 1.objective`' not found." Mixing the quoting and dot references seems to be what's throwing it off.

Edit: added some testing code if anyone wants it.

modelA <- mxModel("Model 1",
	mxData(matrix(1, dimnames=list("x", "x")), "cov", numObs=100),
	mxMatrix("Symm", 1, 1, TRUE, 0, "a", name="S"),
	mxMLObjective("S", dimnames="x")
	)
modelB <- mxModel("Model 2",
	mxData(matrix(2, dimnames=list("x", "x")), "cov", numObs=100),
	mxMatrix("Symm", 1, 1, TRUE, 0, "a", name="S"),
	mxMLObjective("S", dimnames="x")
	)
mult <- mxModel("Mult",
	modelA, modelB,
	mxAlgebra(`Model A.objective` + `Model B.objective`, name="C"),
	mxAlgebraObjective("C")
	)	
test <- mxRun(mult)

AttachmentSize
quote testing.R 517 bytes
mspiegel's picture
Offline
Joined: 07/31/2009
Umm, shouldn't that be

Umm, shouldn't that be mxAlgebra(`Model 1.objective` + `Model 2.objective`, name="C").

Ryne's picture
Offline
Joined: 07/31/2009
Wow. I forgot how to count to

Wow. I forgot how to count to B. I spent half the day trying variations on `model 1`.objective, then did the rest of my testing with letters and numbers mixed up. Thanks, Mike.

mspiegel's picture
Offline
Joined: 07/31/2009
There is a solution to this

There is a solution to this problem. I'll look it up in the OpenMx code base and post this evening.

tbates's picture
Offline
Joined: 07/31/2009
Probably ok to replace " "

Probably ok to replace " " with "_" to avoid the model 1 = model1 problem.

ti;rm -r *;bates