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

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

我正在编写一个 R 函数,允许用户在创建 survival::survfit 对象时指定一个单独的时间变量。 survival 包具有“无字符串”语法,这意味着时间变量的名称(在我的例子中为“dtime”)不需要任何引号:

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

因此,我想我可以使用 tidy evaluation 来达到我的目的。这是我的代码:

# 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)

但是,我总是遇到以下错误:

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

在调试器中查看 'dtime' 时,我得到:

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

如何解决此问题并仍然获得我的功能?

survfit() 不是一个整洁的 eval 函数,因此您在断章取意地使用 {{。参见 https://rlang.r-lib.org/reference/topic-inject-out-of-context.html

相反,您需要使用 inject() 来启用从外部注入。由于 survfit() 不是整洁的 eval 函数,因此它不支持 {{ 创建的 quosures。所以你需要使用 !!ensym().

get_survfit <- function(data, time) {
  time <- ensym(time)

  inject(
    survival::survfit(formula = survival::Surv(!!time, status) ~ 1, data)
  )
}

ensym() 确保传递简单的变量名,并消除它们。 !! 在公式中注入变量。

基于 Lionel Henry 的回答并受到 的启发,我想补充一点,使用 enquoget_expr 也可以达到相同的结果:

get_survfit <- function(.data, .time){
  .time = rlang::enquo(.time)
  .time = rlang::get_expr(.time)
  
  rlang::inject(
    return(survival::survfit(formula = survival::Surv(!!.time, status) ~ 1, .data))
  )
}