2016-10-28 87 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