2016-10-28 49 views
10

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引用:代

[后,...]然后通过'terms.formula(simplify = TRUE)'将结果简化为

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

(即,这似乎并没有删除offset(b) ...)

我知道我可以通过使用deparse()和文本处理砍了一个解决方案,或处理的公式递归地删除术语我不想要的,但这些解决方案是丑陋的和/或恼人的实现。要么启示为为什么这不起作用,或合理紧凑的解决方案,将是伟大的...

+2

多一点挖'terms.formula'表明它**明确地保留了偏移项**,尽管这似乎还没有被记录在任何地方...... –

+3

查看'?offset'文档说''可以有多于一个模型公式中的一个偏移量,但是 - 是n不支持偏移项(和等于+)。“'。这可能是你的'offset()'术语没有简化的原因吗? –

+0

不是最迷人的,但你可以尝试添加一个'offset(-b)'而不是?你的公式不会简化,但我认为效果应该是一样的。如果您尝试使用lm(mpg_ cyl,data = mtcars); lm(mpg_cyl + offset(disp),data = mtcars); lm(mpg_cyl + offset(disp)+ offset(-disp),data = mtcars);'你看第一个和第三个'lm()'是一样的。 –

回答

7

1)递归递归通过式代表下降花边offset(...)offset,然后使用update删除offset。没有字符串操作完成,虽然它需要一些代码行,但它仍然相当短,并删除单个和多个offset条款。

如果存在多个偏移量,则可以通过设置preserve来保留其中的一些偏移量,例如,如果preserve = 2则保留第二偏移量并且删除其他偏移量。默认值是不保留,即全部删除。

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属性删除,并使用​​修复它,我们从terms.formula的内容中提取出该属性。通过将源代码​​复制到您的源代码并删除下面的eval一行,可以使其稍微变得脆弱一点。 (1)中的作用如同preserve

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论点那么 跳越offset属性线可以简化为:的代码中

attr(tt, "offset") <- NULL 
+0

我不清楚这是否是OP正在寻找的行为。公式中可能有多个偏移项,并且此方法将删除所有这些项。我得到的印象是,OP只想删除公式中的特定术语,例如'offset(b)',这意味着将'offset(c)'留在原地。也许@BenBolker可以评论哪些行为是必需的? – dww

+0

不确定这是否重要,但已将此功能添加到(1)和(2)。 –

4

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

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

如果你需要更灵活地接受的公式包括offset(),例如,如果公式是由包装用户提供谁可能没有意识到需要在地方使用offset2offset,那么我们还应该增加一个线改变offset()任何实例传入式中:

f3 <- z ~ a + offset(b) 

f4 <- as.formula(gsub("offset\\(", "offset2(", deparse(f3))) 
f4 <- update(f4, . ~ . - offset2(b)) 

# finally, just in case there are any references to offset2 remaining, we should revert them back to offset 
f4 <- as.formula(gsub("offset2\\(", "offset(", deparse(f4))) 
# z ~ a 
+0

这对ben来说很好,但如果用户将配方给予他的包装,那么他们就必须事先知道这个警告,对吧? – rawr

+0

@rawr - 是的,如果预期的用途是其他用户提供公式的包,那么这将是一个问题。然后,有必要对公式进行deparse并在Ben的包中用offset2替换offset中的任何实例。开始变得丑陋然后 – dww