是否可以在保持简化符号的同时更新模型 object 的公式?

Is it possible to update the formula of a model object while maintaining simplified notation?

我有一个包含一些交互项的模型公式。当我通过 update() 更新模型时,* 运算符被删除,取而代之的是扩展的 x + y + x:y 形式。这不是什么大问题,但是当通过 anova()(或其他地方)比较模型时,它往往会使模型公式在视觉上不那么可口,并且会更改 anova object 的标题不方便

问题是 terms.formula()(通过 update.formula())似乎并不容易强制简化公式(应该通过简化选项?)。

例如:

# Looking for Dep ~ Ind1 * Ind2 + Ind3

update.formula(Dep ~ Ind1 * Ind2, . ~ . + Ind3)
##Dep ~ Ind1 + Ind2 + Ind3 + Ind1:Ind2

也许通过 simplify = TRUE 就可以了?

update.formula(Dep ~ Ind1 * Ind2, . ~ . + Ind3, simplify = TRUE)
##Dep ~ Ind1 + Ind2 + Ind3 + Ind1:Ind2

或者条款得到 re-ordered,这会阻止它返回简化版本?

update.formula(Dep ~ Ind1 * Ind2, . ~ . + Ind3, simplify = TRUE, keep.order = TRUE)
##Dep ~ Ind1 + Ind2 + Ind3 + Ind1:Ind2

我目前使用的解决方法是只适合一个新模型,当模型调用需要多个附加参数时,这不太方便。是否有更好的解决方案来更新公式 object,同时保持简化的 * 表示法?


稍微清晰一点的用例(全因子设计):

i1 <- sample(c("A", "B"), 100, replace = TRUE)
i2 <- sample(c("C", "D"), 100, replace = TRUE)
i3 <- sample(c("E", "F"), 100, replace = TRUE)
i4 <- sample(c("G", "H"), 100, replace = TRUE)
d1 <- rnorm(100)
df <- data.frame(d1, i1, i2, i3, i4)

m1 <- lm(d1 ~ i1 * i2 * i3, data = df)
m2 <- update(m1, formula = . ~ . * i4)
m2s <- lm(d1 ~ i1 * i2 * i3 * i4, data  = df) # Explicitly declare new model

anova(m1, m2)
##Analysis of Variance Table

##Model 1: d1 ~ i1 * i2 * i3
##Model 2: d1 ~ i1 + i2 + i3 + i4 + i1:i2 + i1:i3 + i2:i3 + i1:i4 + i2:i4 + 
##    i3:i4 + i1:i2:i3 + i1:i2:i4 + i1:i3:i4 + i2:i3:i4 + i1:i2:i3:i4
##  Res.Df    RSS Df Sum of Sq      F Pr(>F)
##1     92 121.07                           
##2     84 118.70  8    2.3646 0.2092 0.9885

anova(m1, m2s)
##Analysis of Variance Table

##Model 1: d1 ~ i1 * i2 * i3
##Model 2: d1 ~ i1 * i2 * i3 * i4
##  Res.Df    RSS Df Sum of Sq      F Pr(>F)
##1     92 121.07                           
##2     84 118.70  8    2.3646 0.2092 0.9885

simplify in terms.formula 与您认为的相反。您实际上想要 simplify = FALSE,但使用默认值 stats::update.formula 无法做到这一点。这是一个可以满足您需求的版本。请注意,默认方法刚刚更改为使用我的 update_no_simplify.formula 版本,公式方法刚刚更改为使用 simplify = FALSE:

update_no_simplify <- function(object, ...) {
  UseMethod("update_no_simplify")
}

update_no_simplify.formula <- function(old, new) {
  tmp <- .Call(stats:::C_updateform, as.formula(old), as.formula(new))
  formula(terms.formula(tmp, simplify = FALSE))
}

update_no_simplify.default <- function (object, formula., ..., evaluate = TRUE) {
  if (is.null(call <- getCall(object))) 
    stop("need an object with call component")
  extras <- match.call(expand.dots = FALSE)$...
  if (!missing(formula.)) 
    call$formula <- update_no_simplify.formula(formula(object), formula.)
  if (length(extras)) {
    existing <- !is.na(match(names(extras), names(call)))
    for (a in names(extras)[existing]) call[[a]] <- extras[[a]]
    if (any(!existing)) {
      call <- c(as.list(call), extras[!existing])
      call <- as.call(call)
    }
  }
  if (evaluate) 
    eval(call, parent.frame())
  else call
}

m3 <- update_no_simplify(m1, . ~ . * i4)
anova(m1, m3)

输出:

##Analysis of Variance Table
##
##Model 1: d1 ~ i1 * i2 * i3
##Model 2: d1 ~ i1 * i2 * i3 * i4
##  Res.Df    RSS Df Sum of Sq     F Pr(>F)
##1     92 95.496                          
##2     84 89.193  8    6.3032 0.742 0.6542