Reputation: 10032
I'm building a package that will:
The result is stored in a list that contains the data, model, and nls result. Everything works until step 4. I've stored my constructed models in the list, and R no longer recognizes them as selfStart functions.
Here's a toy example illustrating my problem. The following function (from the SSlogis
manual) works fine at top-level:
Chick.1 <- ChickWeight[ChickWeight$Chick == 1, ]
fm1 <- nls(weight ~ SSlogis(Time, Asym, xmid, scal), data = Chick.1)
fm1
Nonlinear regression model
model: weight ~ SSlogis(Time, Asym, xmid, scal) data: Chick.1
Asym xmid scal
937.02 35.22 11.41
residual sum-of-squares: 76.66Number of iterations to convergence: 0
Achieved convergence tolerance: 7.778e-07
But when I store the function and data together in a list, R doesn't see the selfstarting function as selfstarting anymore:
myobj <- list()
myobj$model <- SSlogis
myobj$data <- ChickWeight[ChickWeight$Chick == 1, ]
nls(weight ~ myobj$model(Time, Asym, xmid, scal), data = myobj$data)
Error in getInitial.default(func, data, mCall = as.list(match.call(func, :
no 'getInitial' method found for "function" objects
My workflow will eventually include processing dozens of datasets, so I'd like to keep each dataset and its associated model in its own object (and these objects will probably end up in a list of objects). Is there some way to maintain or restore the environment of my selfStart functions even after they're stored inside another list?
In response to Gregor's suggestion, I tried this:
nls(as.formula(sprintf("weight ~ %s(Time, Asym, xmid, scal)",
"myobj$model")), data = myobj$data)
Error in nls(as.formula(sprintf("weight ~ %s(Time, Asym, xmid, scal)", :
singular gradient In addition: Warning message:
In nls(as.formula(sprintf("weight ~ %s(Time, Asym, xmid, scal)", :
No starting values specified for some parameters.
Initializing ‘Asym’, ‘xmid’, ‘scal’ to '1.'.
Consider specifying 'start' or using a selfStart model
Inspired by @Gregor I came up with a workaround:
nlsDispatch <- function(obj){
GLOBAL_NLS <<- obj$model
nls(weight ~ GLOBAL_NLS(Time, Asym, xmid, scal), data = myobj$data)
}
nlsDispatch(myobj)
Nonlinear regression model
model: weight ~ GLOBAL_NLS(Time, Asym, xmid, scal)
data: myobj$data
Asym xmid scal
937.02 35.22 11.41
residual sum-of-squares: 76.66Number of iterations to convergence: 0
Achieved convergence tolerance: 6.621e-07
That works, but dropping my function into the global environment is ugly. And it suggests that if I had a better handle on environments I should be able to avoid abusing the global environment to get this done.
1: In my application, this is mostly a matter of counting peaks, and figuring out how many Normal curves are needed to model them.
Upvotes: 1
Views: 2597
Reputation: 270075
1) This works:
Model <- myobj$model
nls(weight ~ Model(Time, Asym, xmid, scal), data = myobj$data)
giving
Nonlinear regression model
model: weight ~ Model(Time, Asym, xmid, scal)
data: myobj$data
Asym xmid scal
937.02 35.22 11.41
residual sum-of-squares: 76.66
Number of iterations to convergence: 0
Achieved convergence tolerance: 6.621e-07
2) It does seem scoping is messed up for self starting functions. The problem appears in getInitial.formula
which uses this line:
func <- get(as.character(object[[2L]][[1L]]))
Note that there is no second argument to get
(the environment) so it does not pay attention to environments.
Thus if you want to put the solution above in a function then it you will need to work around it like this:
f <- function() {
myobj <- list()
myobj$model <- SSlogis
myobj$data <- ChickWeight[ChickWeight$Chick == 1, ]
Model <<- myobj$model
nls(weight ~ Model(Time, Asym, xmid, scal), data = myobj$data)
}
f()
3) Another workaound is to attach Model. That would keep it out of the global workspace. Note that we detach it afterwards so the function does not leave any trace.
f2 <- function() {
on.exit(detach())
if (any(grepl("list", search()))) stop("list already on search path")
myobj <- list()
myobj$model <- SSlogis
myobj$data <- ChickWeight[ChickWeight$Chick == 1, ]
attach(list(Model = myobj$model))
nls(weight ~ Model(Time, Asym, xmid, scal), data = myobj$data)
}
f2()
Note that if the detach
does not occur then the next time an attach
is done there would be two items attached on the search list (whereas in the prior approach it always overwrites the global variable so that can't happen) so we check that there is no such list on the search path and stop with an error if there is.
Upvotes: 2