"object not found" within function: nlmer edition

1.1k views Asked by At

Over the past few hours, I've found several questions on here regarding an R script that works just fine outside of a function, but that returns an "object not found" error for an argument once the code is refactored to include functions. Unfortunately, the resolution to these errors seems to be highly package and context specific, so I have to throw my own, nlmer-specific problem into the fray.

In summary: I'd like to run an nlmer model several times, but with a slightly different model specification each time. I've made sure that the model works as specified outside of any function. I've written a function that takes a data.table and a formula (in string form) and runs the model, but it returns an error that my model formula is "not found", even though the 'model_formula' variable is definitely in the namespace of the function.

Example:

# set up toy dataset
data <- data.table(patient_id=c(rep("pat_1", 10), rep("pat_2", 10), rep("pat_3", 10)),
                   agesero=c(rep(25,10), rep(19, 10), rep(34, 10)))
data$row <- as.numeric(rownames(data))
data[, variable:= ifelse(row%%10==0, "observed_survival", "vl")]
data[, M_visit_time:= ifelse(variable=="vl", row/6 + 0.25, 0)] 
data[, value:= ifelse(variable=="vl", 3 + exp(-4*M_visit_time), row/10 + 2)]
data[, M_agesero:= ifelse(variable=="vl", agesero, 0)]
data[, D_intercept:= ifelse(variable=="observed_survival", 1, 0)]
data[, D_agesero:= ifelse(variable=="observed_survival", agesero, 0)]
data[, row:=NULL]

example_formula <- "value~ModelGradient(time=M_visit_time,b0,b2,b3)~M_agesero + D_intercept + D_agesero + (b0|patient_id)"

#this works, outside of a function:
Model<- ~b0+b2*exp(-b3*time)
ModelGradient<-deriv(Model,namevec=c("b0","b2","b3"),
               function.arg=c("time","b0","b2","b3"))

out<-nlmer(as.formula(example_formula), data=data,
           start = c(b0=3,b2=1,b3=4),
           control=nlmerControl(optimizer="bobyqa", optCtrl=list(maxfun=200000)))

#but when I write a function:
run_nonlin<- function(model_formula, data){
            Model<- ~b0+b2*exp(-b3*time)
            ModelGradient<-deriv(Model,namevec=c("b0","b2","b3"),
                           function.arg=c("time","b0","b2","b3"))

          print(paste("hello I am the model formula and I exist in this namespace! I am:",
         model_formula))
         out<-nlmer(as.formula(model_formula), data=data,
                 start = c(b0=3,b2=1,b3=4),
                 control=nlmerControl(optimizer="bobyqa",
                  optCtrl=list(maxfun=200000)))
           return(out)
}

#and call:
function_output <- run_nonlin(model_formula=example_formula, data=data)

# I get the error: 
# Error in as.formula(model_formula) : object 'model_formula' not found

As a side note, I've done something very similar to this in glmer, without problems.

Any advice is greatly appreciated.

(edited to include the error message)

1

There are 1 answers

0
MrFlick On BEST ANSWER

It seems nlmer has an odd way of parsing the formula you pass to the function. You apparently cannot pass a variable that's not defined in the global environment. This seems to be because the nlformula calls as.formula on the unevaluated symbol that's pass as in as the formula. This means that lexical scoping is used to resolve the symbols so it's searching for model_formula in the stats namespace and then the global environment and not in the scope defined by your function.

A work-around would be to evaluate that parameter and then pass along that value via do.call(), for example

run_nonlin<- function(model_formula, data){
    Model<- ~b0+b2*exp(-b3*time)
    ModelGradient<-deriv(Model,namevec=c("b0","b2","b3"),
                       function.arg=c("time","b0","b2","b3"))

    out <- do.call('nlmer', list(as.formula(model_formula), 
        data=quote(data),
        start = c(b0=3,b2=1,b3=4),
        control=nlmerControl(optimizer="bobyqa",
        optCtrl=list(maxfun=200000))))

    return(out)
}