2

I'm trying to use purrr::safely with coxph so that I can capture error messages. I've made a safe version of coxph as follows

library(survival)
library(purrr)

coxph_safe <- safely(coxph)

This works perfectly when my only inputs are the formula and data, however, if I add another input such as subset or weights, I get the following error message:

simpleError in eval(substitute(subset), data, env): ..3 used in an incorrect context, no ... to look in

Does anyone know how to apply safely to coxph when additional inputs are required? I also get the same error using quietly instead of safely, and also if I make a safe version of lm and specify a subset. I'm using R 3.6.1 and purrr 0.3.2. For now, I've programmed a workaround, where I subset the data before applying coxph_safe, but it would be good to know if there was a better solution.

Here's a simple example:

test1 <- list(time=c(4,3,1,1,2,2,3), 
              status=c(1,1,1,0,1,1,0), 
              x=c(0,2,1,1,1,0,0), 
              sex=c(0,0,0,0,1,1,1))

# Without subset
coxph(Surv(time, status) ~ x, test1) # Works as expected
coxph_safe(Surv(time, status) ~ x, test1) # Works as expected

# With subset
coxph(Surv(time, status) ~ x, test1, subset = !sex) # Works as expected
coxph_safe(Surv(time, status) ~ x, test1, subset = !sex) # Error!

Edit

On a related note, I also get a similar error when applying anova to a coxph object generated via coxph_safe.

cox_1 <- coxph(Surv(time, status) ~ x, test1) # Works as expected
anova(cox_1) # Works as expected

cox_1s <- coxph_safe(Surv(time, status) ~ x, test1) # Works as expected
anova(cox_1s$result) # Error in is.data.frame(data) : ..2 used in an incorrect context, no ... to look in

As far as I can tell, this has something to do with how the call is stored. I can fix it by over-writing the call.

cox_1$call # coxph(formula = Surv(time, status) ~ x, data = test1)
cox_1s$result$call # .f(formula = ..1, data = ..2)
cox_1s$result$call <- cox_1$call
anova(cox_1s$result) # Now works as expected

Is there a better way around this?

Kelly
  • 43
  • 5

1 Answers1

0

This actually has nothing to do with purrr::safely. The issue is function nesting. Consider:

f <- function(...) {coxph(...)}

f(Surv(time, status) ~ x, test1)               # Works
f(Surv(time, status) ~ x, test1, subset=!sex)  # Error

The real reason for why it fails has to do with the behavior of substitute() inside nested functions. coxph() uses substitute(), and safely() creates a nested function, leading to the scenario described in my link.

To address this issue, we need to wrap coxph() into a function that properly handles non-standard evaluation (NSE):

coxph_nse <- function(...) {eval(rlang::expr(coxph( !!!rlang::enexprs(...) )))}

The new function no longer suffers the same nesting issues and can be safely passed to safely():

coxph_safe <- safely(coxph_nse)

coxph_safe(Surv(time, status) ~ x, test1)                     # works
cx1 <- coxph_safe(Surv(time, status) ~ x, test1, subset=!sex) # now also works!

anova(cx1$result)                                             # works as well!
Artem Sokolov
  • 13,196
  • 4
  • 43
  • 74