Search code examples
rdplyrtidyevalsurvival

Using tidy selection together with the survival package - "Error: promise already under evaluation"


I am writing an R function that allows the user to specify an individual time variable when creating a survival::survfit object. The survival package has a "string-free" syntax, which means that the name of the time variable (in my case "dtime") does not need any quotation marks:

survival::survfit(formula = survival::Surv(dtime, death) ~ 1, rotterdam)

Hence, I figured that I could use tidy evaluation for my purpose. Here is my code:

# My function
get_survfit <- function(.data, .time){
  return(survival::survfit(formula = survival::Surv({{ .time }}, status) ~ 1, .data))
}

# Application example
library(survival)
data(cancer)
rotterdam
colnames(rotterdam)[which(names(rotterdam) == "death")] = "status"
get_survfit(.data=rotterdam, .time=dtime)

However, I always get the following errors:

Error in in survival::Surv({ : object 'dtime' not found

and when looking into 'dtime' in the debugger, I get:

Error during wrapup : promise already under evaluation: recursive default argument reference or earlier problems?

How can I fix this and still obtain my feature?


Solution

  • survfit() is not a tidy eval function so you're using {{ out of context. See https://rlang.r-lib.org/reference/topic-inject-out-of-context.html.

    Instead you'll need to use inject() to enable injection from the outside. Since survfit() is not a tidy eval function it won't support quosures, which {{ creates. So you'll need to use !! instead, together with ensym().

    get_survfit <- function(data, time) {
      time <- ensym(time)
    
      inject(
        survival::survfit(formula = survival::Surv(!!time, status) ~ 1, data)
      )
    }
    

    ensym() ensures that simple variable names are passed, and defuses them. !! injects the variable in the formula.