On 2025-03-19 12:49 p.m., Therneau, Terry M., Ph.D. via R-devel wrote:
In response to the tidyverse habit of adding another zillion functions to one's
search space, the use of things like survival::coxph is becoming more common.
But this practice breaks the use of the specials argument in terms.formula,
something that I make heavy use of in the survival package. The following two
pairs of models give different answers, and the second is wrong.
fit1a <- lm(GNP ~ Year + offset(Unemployed), longley)
fit1b<- lm(GNP ~ Year + stats::offset(Unemployed), longley)
fit1a <- survdiff(Surv(time, status) ~ rx + cluster(litter), rats)
fit1b <- survdiff(Surv(time, status) ~ rx + survival::cluster(litter), rats)
zed <- survival::cluster(rats$litter)
fit1c <- survdiff(Surv(time, status) ~ rx + zed, rats)
In the most recent CRAN version of survival I added some pre-processing steps that
successfully catch fit1b, by stripping off "survival::" from formulas before
calling terms.formula. But I can't prevent fit1c, and don't yet know if there are other
case not covered by my current hack. At least one of the CRAN packages that depends on
survival has an example of exactly fit1c in their test suite.
The survival package uses the special argument a lot: strata, cluster, pspline,
tt, frailty, and ridge. I'm trying to think of a good plan for long term
changes. I list 3 below, and am hoping for better ideas or input.
a. Caveat emptor: If you work hard to fool the specials argument, and succeed, then
"Congratulations, you fooled the parser."
b. What I did early on with tt(), which is to make the function defition
completely internal to coxph (the only survival function that uses tt).
People who type survival::tt or survival:::tt get an error message. A plus
of this is that the error message will wean users from pasting survival:: to
everything inside a formula. The disadvantages are first that it will break
existing user code (most of which should be broken -- its not doing what they
think), a second is that there may be use cases for strata, say, outside of a
survival formula; it is essentially factor with shorter default labels.
c. Make all of these functions have a class, and rewrite the code to depend on
the class rather than specials. The Surv function is recognized in this way,
so is not harmed by survival::Surv. It is also why calling Surv to create a
new variable is fine. I'd still retain specials, to support legacy code.
The more ornery part of me votes for b (what is the effect on help files)?
In any case a change won't happen overnight.
Do we leave offset in the caveat emptor group?
A small section needs to be added to the "user written packages" document,
where it talks about specials.
What other packages use specials?
For those functions where the "survival::" prefix leads to the wrong
behaviour, I'd suggest defining a function that always generates an
error. For example,
cluster <- function(...)
stop("In the survival package, cluster() is not a real function. It
has special meaning in formulas and should only be used there.")
This will conflict with any of those functions (like survival::strata?)
that are supposed to be meaningful outside of a formula. In that case
you'd have to rename either the special or the function, so you use two
different names, e.g.
rata
strata <- function(...)
stop("In the survival package version x.y or later, strata() is not a
real function. It has special meaning in formulas and should only be
used there. Use Strata() outside of a formula.")
Duncan Murdoch
______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel