Using MxModel names (that include spaces) in MxAlgebras

Posted on
Picture of user. Ryne 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

Replied on Mon, 11/15/2010 - 11:25
Picture of user. tbates Joined: 07/31/2009

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 :-)

Replied on Mon, 11/15/2010 - 12:21
Picture of user. Ryne Joined: 07/31/2009

In reply to by tbates

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/

Replied on Mon, 11/15/2010 - 14:47
Picture of user. tbrick Joined: 07/31/2009

In reply to by Ryne

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).

Replied on Mon, 11/15/2010 - 14:56
Picture of user. Ryne Joined: 07/31/2009

In reply to by tbrick

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 '_'.")

Replied on Mon, 11/15/2010 - 16:43
Picture of user. mspiegel Joined: 07/31/2009

In reply to by Ryne

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) {
  return(paste(modelname, "objective", sep = "."))
}

makeAlgebraSummation <- function(modelnames) {
  modelnames <- lapply(modelnames, addDotObjective)
  modelnames <- lapply(modelnames, as.symbol)
  if (length(modelnames) == 1) {
    return(eval(substitute(mxAlgebra(`x`, name = 'sum'), 
      list(x = modelnames[[1]]))))
  } else if (length(modelnames) == 2) {
    return(eval(substitute(mxAlgebra(`x` + `y`, name = 'sum'), 
      list(x = modelnames[[1]], y = modelnames[[2]]))))
  } else {
    expression <- substitute(`x` + `y`, 
      list(x = modelnames[[1]], y = modelnames[[2]]))
    for(i in 3:length(modelnames)) {
      expression <- substitute(x + `y`,
      list(x = expression, y = modelnames[[i]]))
    }
    return(eval(substitute(mxAlgebra(x, name = 'sum'),
      list(x = expression))))
  }
}
Replied on Mon, 11/15/2010 - 16:56
Picture of user. Ryne Joined: 07/31/2009

In reply to by mspiegel

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))

Replied on Mon, 11/15/2010 - 17:02
Picture of user. mspiegel Joined: 07/31/2009

In reply to by Ryne

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))

Replied on Mon, 11/15/2010 - 22:16
Picture of user. Ryne Joined: 07/31/2009

In reply to by mspiegel

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)