R有一个方便的工具来操纵公式,update.formula().当你想得到类似"包含以前公式中所有术语的公式除外x"时,这很有效,例如
f1 <- z ~ a + b + c
(f2 <- update.formula(f1, . ~ . - c))
## z ~ a + b
Run Code Online (Sandbox Code Playgroud)
但是,这似乎不适用于抵消条款:
f3 <- z ~ a + offset(b)
update(f3, . ~ . - offset(b))
## z ~ a + offset(b)
Run Code Online (Sandbox Code Playgroud)
我已经挖了下来terms.formula,?update.formula参考:
[替换后,...]然后通过 'terms.formula(simplify = TRUE)'简化结果.
terms.formula(z ~ a + offset(b) - offset(b), simplify=TRUE)
## z ~ a + offset(b)
Run Code Online (Sandbox Code Playgroud)
(也就是说,这似乎没有删除offset(b)......)
我知道我可以通过使用deparse()和文本处理来破解解决方案,或者通过递归处理公式来删除我不想要的术语,但是这些解决方案实现起来很丑陋和/或烦人.无论是为什么这不起作用的启示,还是一个相当紧凑的解决方案,都会很棒......
1)递归递归下降通过公式替换offset(...),offset然后删除offset使用update.没有进行字符串操作,虽然它确实需要多行代码,但它仍然相当短,并且确实删除了单个和多个offset术语.
如果存在多个偏移,则可以通过设置来保留它们中的一些preserve,例如,如果preserve = 2那么保留第二偏移并且移除任何其他偏移.默认是保留none,即全部删除它们.
no.offset <- function(x, preserve = NULL) {
k <- 0
proc <- function(x) {
if (length(x) == 1) return(x)
if (x[[1]] == as.name("offset") && !((k<<-k+1) %in% preserve)) return(x[[1]])
replace(x, -1, lapply(x[-1], proc))
}
update(proc(x), . ~ . - offset)
}
# tests
no.offset(z ~ a + offset(b))
## z ~ a
no.offset(z ~ a + offset(b) + offset(c))
## z ~ a
Run Code Online (Sandbox Code Playgroud)
请注意,如果您不需要
preserve参数,则k可以省略行初始化并将其if简化为:Run Code Online (Sandbox Code Playgroud)if (x[[1]] == as.name("offset")) return(x[[1]])
2)术语 这既不直接使用字符串操作也不使用递归.首先获取terms对象,删除它的offset属性并使用fixFormulaObject它从我们的内容中提取它来修复它terms.formula.通过将源代码复制fixFormulaObject到源中并删除eval下面的行,可以减少这一点. preserve行为如(1).
no.offset2 <- function(x, preserve = NULL) {
tt <- terms(x)
attr(tt, "offset") <- if (length(preserve)) attr(tt, "offset")[preserve]
eval(body(terms.formula)[[2]]) # extract fixFormulaObject
f <- fixFormulaObject(tt)
environment(f) <- environment(x)
f
}
# tests
no.offset2(z ~ a + offset(b))
## z ~ a
no.offset2(z ~ a + offset(b) + offset(c))
## z ~ a
Run Code Online (Sandbox Code Playgroud)
请注意,如果您不需要
preserve参数,那么zaps offset属性的行可以简化为:Run Code Online (Sandbox Code Playgroud)attr(tt, "offset") <- NULL