我想编写一个这样的函数:
library(survival)
getFit = function(x, data){
survfit(Surv(start, stop, event) ~ x, data = data)
}
getFit(surgery, heart)
getFit("surgery", heart) #if not possible, this would be fine too
当然,不会读取x
。请注意,我将survfit
与heart
用作示例,但是对于几乎每个基于公式的函数(lm,glm等),我都深感困惑。
我知道我可以用paste
和as.formula
来写东西,但是我想知道是否有类似的事情可以用tidyverse
来完成,例如:
getSurvPlot = function(x, data=db){
xx = enquo(x)
survfit(Surv(start, stop, event) ~ !!xx, data = data)
}
最后一个代码也不起作用,但是我认为这是因为survfit
不是tidyverse
的一部分。
有什么干净的方法可以在基数R中写这样的东西吗?
编辑:在这个示例中,我现在使用的是survminer::surv_fit
,它是survfit
的包装,可以在公式中提供更大的灵活性。
答案 0 :(得分:2)
这里有3个选项,第一个是纯基R,接下来的两个是rlang
。
基数R
getFit1 = function(x, data){
survfit(eval(substitute(Surv(start, stop, event) ~ x)), data = data)
}
rlang
这里没有理由使用enquo
来构建包含父环境的对象,在您的示例中,对象surgery
在您的全局环境中不存在,只需要在公式的上下文中进行评估。因此substitute
在这里也是合适的功能。
要使用!!
参数,我们需要一个支持拟定额参数的函数,然后我们需要对其求值或将其转换为公式(我不知道在一步)。
因此,我们最终得到的结果看起来并不比基本版本好。
getFit2 = function(x, data){
xx <- substitute(x)
survfit(eval(expr(Surv(start, stop, event) ~ !!xx)), data = data)
}
使用new_formula再次rlang
我们可以根据其lhs和rhs建立一个公式,但是现在我们需要引用lhs,并且我们仍然需要expr
在rhs上,因此基本解似乎仍然更适合这种情况。
getFit3 = function(x, data){
xx <- substitute(x)
survfit(new_formula(quote(Surv(start, stop, event)), expr(!!xx)), data = data)
}
输出
getFit1(surgery, heart)
# Call: survfit(formula = eval(substitute(Surv(start, stop, event) ~
# x)), data = data)
#
# records n.max n.start events median 0.95LCL 0.95UCL
# surgery=0 143 87 0 66 80 66 188
# surgery=1 29 16 0 9 980 186 NA
getFit2(surgery, heart)
# Call: survfit(formula = eval(expr(Surv(start, stop, event) ~ !!xx)),
# data = data)
#
# records n.max n.start events median 0.95LCL 0.95UCL
# surgery=0 143 87 0 66 80 66 188
# surgery=1 29 16 0 9 980 186 NA
getFit3(surgery, heart)
# Call: survfit(formula = new_formula(quote(Surv(start, stop, event)),
# expr(!!xx)), data = data)
#
# records n.max n.start events median 0.95LCL 0.95UCL
# surgery=0 143 87 0 66 80 66 188
# surgery=1 29 16 0 9 980 186 NA
答案 1 :(得分:2)
Moody_mudskipper提供了一个非常详细的答案。我只想指出,您对getSurvPlot
的定义几乎是正确的。您的问题不在于rlang / tidyverse,而是在另一个公式中使用带引号的参数(这是一个公式)。
调用getSurvPlot(surgery, heart)
时,enquo
会将第一个参数捕获为~surgery
,它已经是一个公式。您无需使用~
和xx
和Surv
来创建新公式,只需更新已有公式的左侧即可。可以使用来自基数R的stats::update()
来完成:
getSurvPlot <- function(x, data=db){
xx <- enquo(x)
survfit(stats::update( xx, Surv(start, stop, event) ~ . ), data = data)
}
getSurvPlot(surgery, heart)
现在应该可以正常工作了。
如@Moody_mudskipper所指出的,实际工作由stats::update.formula()
完成,stats::update()
是S3 generic xx
对公式对象(例如scanf
)的实现。
答案 2 :(得分:0)
我们可以用所需的变量从字面上替换公式的RHS:
getFit = function(var, data){
var=as.name(substitute(var))
survfit(`[<-`(Surv(time, status)~. ,3,list(var)), data = data)
}
getFit(x,aml)
Call: survfit(formula = `[<-`(Surv(time, status) ~ ., 3, list(var)),
data = data)
n events median 0.95LCL 0.95UCL
x=Maintained 11 7 31 18 NA
x=Nonmaintained 12 11 23 8 NA
getFit("x",aml)
Call: survfit(formula = `[<-`(Surv(time, status) ~ ., 3, list(var)),
data = data)
n events median 0.95LCL 0.95UCL
x=Maintained 11 7 31 18 NA
x=Nonmaintained 12 11 23 8 NA
我们怎么知道它是正确的?
survfit(Surv(time, status) ~ x, data = aml)
Call: survfit(formula = Surv(time, status) ~ x, data = aml)
n events median 0.95LCL 0.95UCL
x=Maintained 11 7 31 18 NA
x=Nonmaintained 12 11 23 8 NA
您可以使用:
getFit = function(var, data){
var=as.name(substitute(var))
a = `[<-`(Surv(time, status)~. ,3,list(var))
survfit(a, data = data)
}
或
getFit = function(var, data){
var=as.name(substitute(var))
survfit(formula(substitute(Surv(time, status)~var,list(var=var))), data = data)
}