Here is a modification of the base R anova call. I believe this should work for general lm/aov objects, but I haven't tested it thoroughly. Note that the reg_collapse argument will collapse to Source by default, but by setting it to FALSE you can get the original rows back, just with a Total SS row.
anova_alt = function (object, reg_collapse=TRUE,...)
{
if (length(list(object, ...)) > 1L)
return(anova.lmlist(object, ...))
if (!inherits(object, "lm"))
warning("calling anova.lm(<fake-lm-object>) ...")
w <- object$weights
ssr <- sum(if (is.null(w)) object$residuals^2 else w * object$residuals^2)
mss <- sum(if (is.null(w)) object$fitted.values^2 else w *
object$fitted.values^2)
if (ssr < 1e-10 * mss)
warning("ANOVA F-tests on an essentially perfect fit are unreliable")
dfr <- df.residual(object)
p <- object$rank
if (p > 0L) {
p1 <- 1L:p
comp <- object$effects[p1]
asgn <- object$assign[stats:::qr.lm(object)$pivot][p1]
nmeffects <- c("(Intercept)", attr(object$terms, "term.labels"))
tlabels <- nmeffects[1 + unique(asgn)]
ss <- c(vapply(split(comp^2, asgn), sum, 1), ssr)
df <- c(lengths(split(asgn, asgn)), dfr)
if(reg_collapse){
if(attr(object$terms, "intercept")){
collapse_p<-2:(length(ss)-1)
ss<-c(ss[1],sum(ss[collapse_p]),ss[length(ss)])
df<-c(df[1],sum(df[collapse_p]),df[length(df)])
tlabels<-c(tlabels[1],"Source")
} else{
collapse_p<-1:(length(ss)-1)
ss<-c(sum(ss[collapse_p]),ss[length(ss)])
df<-c(df[1],sum(df[collapse_p]),df[length(df)])
tlabels<-c("Regression")
}
}
}else {
ss <- ssr
df <- dfr
tlabels <- character()
if(reg_collapse){
collapse_p<-1:(length(ss)-1)
ss<-c(sum(ss[collapse_p]),ss[length(ss)])
df<-c(df[1],sum(df[collapse_p]),df[length(df)])
}
}
ms <- ss/df
f <- ms/(ssr/dfr)
P <- pf(f, df, dfr, lower.tail = FALSE)
table <- data.frame(df, ss, ms, f, P)
table <- rbind(table,
colSums(table))
table$ms[nrow(table)]<-table$ss[nrow(table)]/table$df[nrow(table)]
table[length(P):(length(P)+1), 4:5] <- NA
dimnames(table) <- list(c(tlabels, "Error","Total"),
c("Df","SS", "MS", "F",
"P"))
if (attr(object$terms, "intercept")){
table <- table[-1, ]
table$MS[nrow(table)]<-table$MS[nrow(table)]*(table$Df[nrow(table)])/(table$Df[nrow(table)]-1)
table$Df[nrow(table)]<-table$Df[nrow(table)]-1
}
structure(table, heading = c("Analysis of Variance Table\n"),
class = c("anova", "data.frame"))
}
## Warpbreaks example
fm1 <- lm(breaks ~ wool*tension, data = warpbreaks)
anova_alt(fm1)
#> Analysis of Variance Table
#>
#> Df SS MS F P
#> Source 5 3488 697.54 5.8279 0.0002772
#> Error 48 5745 119.69
#> Total 53 52018 981.47
Created on 2019-10-11 by the reprex package (v0.3.0)