从公式中删除偏移项

时间:2016-10-28 16:01:35

标签: r formula

R有一个方便的工具来操纵公式,update.formula()。当你想得到类似于"公式的公式包含以前公式中除x"之外的所有术语,例如

时,这很有效。
f1 <- z ~ a + b + c
(f2 <- update.formula(f1, . ~ . - c))
## z ~ a + b

但是,这似乎不适用于抵消条款:

f3 <- z ~ a + offset(b) 
update(f3, . ~ . - offset(b))
## z ~ a + offset(b)

我已经挖掘到terms.formula?update.formula引用:

  

[替换之后,...]结果然后简化为 via 'terms.formula(simplify = TRUE)'。

terms.formula(z ~ a + offset(b) - offset(b), simplify=TRUE)
## z ~ a + offset(b)

(即,这似乎不会删除offset(b) ...)

我知道我可以通过使用deparse()和文本处理来解决问题,或者通过递归处理公式来删除我不想要的术语,但这些解决方案很丑陋和/或讨厌实施。无论是为什么这不起作用的启示,还是一个相当紧凑的解决方案,都会很棒......

2 个答案:

答案 0 :(得分:7)

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
  

请注意,如果您不需要preserve参数,那么该行   初始化k可以省略,if简化为:

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
  

请注意,如果您不需要preserve参数,则表示该行   zaps偏移属性可以简化为:

attr(tt, "offset") <- NULL

答案 1 :(得分:4)

这似乎是设计上的。但一个简单的解决方法是

offset2 = offset
f3 <- z ~ a + offset2(b) 
update(f3, . ~ . - offset2(b))
# z ~ a

如果您需要灵活地接受包含offset()的公式,例如,如果公式是由可能不知道需要使用{{1}的软件包用户提供的代替offset2,我们还应添加一行来更改传入公式中offset的所有实例:

offset()