jgellar / pcox

Penalized Cox regression models
1 stars 0 forks source link

Separating data from s() arguments from `...` #10

Closed jgellar closed 9 years ago

jgellar commented 9 years ago

The declaration for p() will be something like:

p <- function(..., limits=NULL, additive=TRUE, tv=FALSE, basistype=c("s", "te", "t2")) {
}

The "ellipsis" ... will thus contain both data, and arguments to be passed to the basistype function. What is the best way of separating out the two types of arguments? The best thing that I can come up with is to first get the list of formals that are available for whatever is supplied as the basistype, and then pull those out from the ..., and assume that everything else is data.

Is this the best way to do it? This would cause problems if someone included an argument by mistake that wasn't in the list of formals for the basistype function, because p() would think that this was data. But I can't really think of a way to avoid this.

fabian-s commented 9 years ago

seems doable.

if you want to do proper input checking directly in pcox before handing the args over to the basistype-function you'd have to split arguments for the "basis" from the variable names given in ... by doing something like (not tested!):

p <- function(..., limits = NULL, 
              additive = TRUE, 
              tv = FALSE, 
              basistype = c("s", "te", "t2"),
              basisargs = list()) {

  basistype <- match.arg(basistype)                

  ## DANGER:  get(basistype, "package:mgcv") : 
  ## only works if mgcv is loaded (via Depends: or library), not just "Imported:"
  ## match.fun(basistype) yields s() from pcox if pcox re-defines s()!
  default_basisargs <- formals(match.fun(basistype))              
  default_basisargs$`...` <- NULL

  stopifnot(is.list(basisargs), !length(basisargs) | !is.null(names(basisargs)),
            !length(basisargs) | all(names(basisargs) != ""),
            !length(basisargs) | all(names(basisargs) %in% names(default_basisargs)))

  basisargs <- append(list(...), basisargs)
  basiscall <- do.call(call, append(list(name=basistype), 
                                    basisargs))
  basiscall
}

where all the arguments for s, te, etc. go into the basisargs-list.... this is a little more cumbersome for the user to specify, but a little cleaner, I think.

jgellar commented 9 years ago

I've been trying to avoid using a basisargs argument - as you say, it's more difficult for the user to specify. What exactly makes you say that it is cleaner? Do you mean just for us, in terms of programming?

I agree that having ... refer to both data and basis arguments makes the programming messier.