R - model.frame()和非标准评估

时间:2016-05-21 15:06:35

标签: r survival-analysis cox-regression

我对我试图编写的函数的行为感到困惑。我的例子来自library(survival) data(bladder) ## this will load "bladder", "bladder1" and "bladder2" mod_init <- coxph(Surv(start, stop, event) ~ rx + number, data = bladder2, method = "breslow") survfit(mod_init) 包,但我认为问题比这更普遍。基本上,以下代码

my_function <- function(formula, data) {
  mod_init <- coxph(formula = formula, data = data, method = "breslow")
  survfit(mod_init)
  }

my_function(Surv(start, stop, event) ~ rx + number, data = bladder2)

将产生我感兴趣的对象。但是,当我在函数中写入它时,

 Error in eval(predvars, data, env) : 
  invalid 'envir' argument of type 'closure' 
10 eval(predvars, data, env) 
9 model.frame.default(formula = Surv(start, stop, event) ~ rx + 
    number, data = data) 
8 stats::model.frame(formula = Surv(start, stop, event) ~ rx + 
    number, data = data) 
7 eval(expr, envir, enclos) 
6 eval(temp, environment(formula$terms), parent.frame()) 
5 model.frame.coxph(object) 
4 stats::model.frame(object) 
3 survfit.coxph(mod_init) 
2 survfit(mod_init) 
1 my_function(Surv(start, stop, event) ~ rx + number, data = bladder2) 

该函数将在最后一行返回错误:

my_function

我很好奇是否有一些明显的遗漏或这种行为是否正常。我觉得很奇怪,因为在survival的环境中,当运行代码的第一部分时,我将拥有与全局环境中相同的对象。

编辑:我还收到了来自{{1}}包的作者Terry Therneau的有用信息。这是他的回答:

这是一个问题,源于model.frame进行的非标准评估。我找到的唯一方法是将model.frame = TRUE添加到原始的coxph调用中。我认为这是R中一个严重的设计缺陷。非标准的评价就像黑暗的一面 - 一个总是很糟糕的诱人和简单的道路。 特里T.

2 个答案:

答案 0 :(得分:6)

<强>诊断

从错误消息:

2 survfit(mod_init, newdata = base_case)
1 my_function(Surv(start, stop, event) ~ rx + number, data = bladder2) 

在模型拟合期间问题显然不是coxph,而是survfit

从这条消息:

10 eval(predvars, data, env) 
 9 model.frame.default(formula = Surv(start, stop, event) ~ rx + 
     number, data = data) 

我可以说问题是在survfit的早期阶段,函数model.frame.default()无法找到包含公式{{1}中使用的相关数据的模型框架 }}。因此它抱怨。

什么是模型框架?

模型框架由传递给拟合例程的Surv(start, stop, event) ~ rx + number参数构成,如datalm()glm()。它是一个与mgcv:::gam()具有相同行数的数据框,但是:

  • 删除data
  • 未引用的所有变量
  • 添加了许多属性,其中最重要的是formula

大多数模型拟合例程,例如envrionementlm()glm(),默认情况下会将模型框保留在其拟合对象中。这样做的好处是,如果我们稍后调用mgcv:::gam(),并且未提供predict,则会从此模型框架中找到用于评估的数据。但是,一个明显的缺点是它会大大增加你装配物体的尺寸。

但是,newdata是一个例外。默认情况下,会在其拟合对象中保留此类模型框架。嗯,显然,这会使得到的合适物体的尺寸变得更小,但是,让您遇到遇到的问题。 如果我们想要survival:::coxph()保留此模型框架,请使用此功能的survival:::coxph()

使用model = TRUE

进行测试
survial:::coxph()

现在,这个函数调用将失败,如您所见:

library(survival); data(bladder)

my_function <- function(myformula, mydata, keep.mf = TRUE) {
  fit <- coxph(myformula, mydata, method = "breslow", model = keep.mf)
  survfit(fit)
  }

但是这个函数调用会成功:

my_function(Surv(start, stop, event) ~ rx + number, bladder2, keep.mf = FALSE)

my_function(Surv(start, stop, event) ~ rx + number, bladder2, keep.mf = TRUE)

的行为相同

我们实际上可以在lm()中展示相同的行为:

lm()

现在,通过保持模型框架,这将成功:

## generate some toy data
foo <- data.frame(x = seq(0, 1, length = 20), y = seq(0, 1, length = 20) + rnorm(20, 0, 0.15))

## a wrapper function
bar <- function(myformula, mydata, keep.mf = TRUE) {
  fit <- lm(myformula, mydata, model = keep.mf)
  predict.lm(fit)
  }

虽然这会失败,但是丢弃了模型框架:

bar(y ~ x - 1, foo, keep.mf = TRUE)

使用参数bar(y ~ x - 1, foo, keep.mf = FALSE)

请注意,newdata的示例有点人为,因为我们实际上可以在lm()中使用newdata参数来解决此问题:

predict.lm()

现在我们是否保留模型框架,两者都会成功:

bar1 <- function(myformula, mydata, keep.mf = TRUE) {
  fit <- lm(myformula, mydata, model = keep.mf)
  predict.lm(fit, newdata = lapply(mydata, mean))
  }

然后你可能想知道:我们可以为bar1(y ~ x - 1, foo, keep.mf = TRUE) bar1(y ~ x - 1, foo, keep.mf = FALSE) 做同样的事吗?

survfit()是一个通用函数,在您的代码中,您实际上是在调用survfit()。这个函数确实有一个survfit.coxph()参数。文档内容如下:

  

newdata:

     

一个数据框,其变量名称与出现在该数据框中的变量名称相同     'coxph'公式。 ......默认是使用的协变量的平均值     'coxph'适合。

所以,试试吧:

newdata

我们希望这项工作:

my_function1 <- function(myformula, mydata) {
  mtrace.off()
  fit <- coxph(myformula, mydata, method = "breslow")
  survival:::survfit.coxph(fit, newdata = lapply(mydata, mean))
  }

可是:

my_function1(Surv(start, stop, event) ~ rx + number, bladder2)

请注意,虽然我们传递Error in is.data.frame(data) (from #5) : object 'mydata' not found 1: my_function1(Surv(start, stop, event) ~ rx + number, bladder2) 2: #5: survival:::survfit.coxph(fit, lapply(mydata, mean)) 3: stats::model.frame(object) 4: model.frame.coxph(object) 5: eval(temp, environment(formula$terms), parent.frame()) 6: eval(expr, envir, enclos) 7: stats::model.frame(formula = Surv(start, stop, event) ~ rx + number, data = 8: model.frame.default(formula = Surv(start, stop, event) ~ rx + number, data 9: is.data.frame(data) ,但它不用于构建模型框架:

newdata

只有3: stats::model.frame(object) (已安装模型的副本)会传递给object

这与model.frame.default()predict.lm()predict.glm()中的情况截然不同。在这些例程中,mgcv:::predict.gam()将传递给newdata。例如,在model.frame.default()中,有:

lm()

我没有使用m <- model.frame(Terms, newdata, na.action = na.action, xlev = object$xlevels) 软件包,因此不确定survival在此软件包中的工作原理。所以我认为我们真的需要一些专家来解释这一点。

答案 1 :(得分:-2)

我认为可能是你的

Surv(start, stop, event) ~ rx + number

作为参数,无法正确创建。试试吧

is.Surv(formula)

作为函数的第一行。我怀疑它不会工作,然后我会建议使用apply系列函数。