# test_anova.R library(lmerTest) # WRE says "using if(requireNamespace("pkgname")) is preferred, if possible." # even in tests: assertError <- function(expr, ...) if(requireNamespace("tools")) tools::assertError(expr, ...) else invisible() assertWarning <- function(expr, ...) if(requireNamespace("tools")) tools::assertWarning(expr, ...) else invisible() # Kenward-Roger only available with pbkrtest and only then validated in R >= 3.3.3 # (faulty results for R < 3.3.3 may be due to unstated dependencies in pbkrtest) has_pbkrtest <- requireNamespace("pbkrtest", quietly = TRUE) && getRversion() >= "3.3.3" data("sleepstudy", package="lme4") TOL <- 1e-4 #################################### ## Basic anova tests #################################### m <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) ####### ddf argument: (an1 <- anova(m)) # Also testing print method. (an2 <- anova(m, ddf="Satterthwaite")) (an2b <- anova(m, ddf="Satterthwaite", type=3)) (an2c <- anova(m, ddf="Satterthwaite", type=2)) stopifnot(isTRUE( all.equal(an1, an2, tolerance=TOL) )) (an3 <- anova(m, ddf="Sat")) ## Abbreviated argument stopifnot(isTRUE( all.equal(an1, an3, tolerance=TOL) )) if(has_pbkrtest) { (anova(m, ddf="Kenward-Roger")) (anova(m, ddf="Kenward-Roger", type=3)) } (an1 <- anova(m, ddf="lme4")) (an2 <- anova(m, ddf="lme4", type=3)) # 'type' is ignored with ddf="lme4" stopifnot(isTRUE( all.equal(an1, an2, tolerance=TOL) )) res <- assertError(anova(m, ddf="KR")) ## Error on incorrect arg. stopifnot( grepl("'arg' should be one of ", unlist(res[[1]])$message) ) ## lme4 method: an1 <- anova(m, ddf="lme4") an2 <- anova(as(m, "lmerMod")) stopifnot(isTRUE( all.equal(an1, an2, tolerance=TOL) )) ###### type argument: (an1 <- anova(m, type="1")) # valid type arg. (an2 <- anova(m, type="I")) # same stopifnot(isTRUE( all.equal(an1, an2, tolerance=TOL) )) (an3 <- anova(m, type=1)) # Not strictly valid, but accepted stopifnot(isTRUE( all.equal(an1, an3, tolerance=TOL) )) (an1 <- anova(m, type="2")) # valid type arg. (an2 <- anova(m, type="II")) # same stopifnot(isTRUE( all.equal(an1, an2, tolerance=TOL) )) (an3 <- anova(m, type=3)) # Not strictly valid, but accepted stopifnot(isTRUE( all.equal(an1, an3, check.attributes=FALSE, tolerance=TOL) )) (an1 <- anova(m, type="3")) # valid type arg. (an2 <- anova(m, type="III")) # same stopifnot(isTRUE( all.equal(an1, an2, tolerance=TOL) )) (an3 <- anova(m, type=3)) # Not strictly valid, but accepted stopifnot(isTRUE( all.equal(an1, an3, tolerance=TOL) )) assertError(anova(m, type=0)) # Not valid arg. assertError(anova(m, type="i")) # Not valid arg. ####### Model comparison: fm <- lm(Reaction ~ Days, sleepstudy) (an <- anova(m, fm)) stopifnot( nrow(an) == 2L, rownames(an)[2] == "m" ) m2 <- lmer(Reaction ~ Days + I(Days^2) + (Days | Subject), sleepstudy) (an <- anova(m, m2, refit=FALSE)) stopifnot( nrow(an) == 2L, rownames(an)[1] == "m" ) #################################### ## Example with factor fixef: #################################### ## 'temp' is continuous, 'temperature' an ordered factor with 6 levels data("cake", package="lme4") m <- lmer(angle ~ recipe * temp + (1|recipe:replicate), cake) (an <- anova(m)) (an_lme4 <- anova(m, ddf="lme4")) if(has_pbkrtest) { (an_KR <- anova(m, ddf="Kenward-Roger")) # res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], # an_lme4[, c("Sum Sq", "Mean Sq", "F value")]) # stopifnot(isTRUE(res)) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_KR[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) } stopifnot(all.equal(c(2, 1, 2), an$NumDF, tol=1e-6), all.equal(c(254.0157612, 222, 222), an$DenDF, tol=TOL)) an3 <- anova(m, type=3) an2 <- anova(m, type=2) an1 <- anova(m, type=1) ## Data is balanced, so Type II and III should be identical: ## One variable is continuous, so Type I and II/III are different: stopifnot( isTRUE(all.equal(an3, an2, check.attributes=FALSE, tolerance=TOL)), !isTRUE(all.equal(an1, an2, check.attributes=FALSE, tolerance=1e-8)) ) # Using an ordered factor: m <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake) (an1 <- anova(m, type=1)) (an2 <- anova(m, type=2)) # Type 3 is also available with ordered factors: (an3 <- anova(m, type=3)) ## Balanced data and only factors: Type I, II and III should be the same: stopifnot( isTRUE(all.equal(an1, an2, check.attributes=FALSE, tolerance=TOL)), isTRUE(all.equal(an1, an3, check.attributes=FALSE, tolerance=TOL)) ) (an <- anova(m, type=1)) (an_lme4 <- anova(m, type=1, ddf="lme4")) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_lme4[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) if(has_pbkrtest) { (an_KR <- anova(m, type=1, ddf="Kenward-Roger")) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_KR[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) } stopifnot(all.equal(c(2, 5, 10), an$NumDF, tolerance=TOL), all.equal(c(42, 210, 210), an$DenDF, tolerance=TOL)) ######## ## Make case with balanced unordered factors: cake2 <- cake cake2$temperature <- factor(cake2$temperature, ordered = FALSE) # str(cake2) stopifnot( !is.ordered(cake2$temperature) ) m <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake2) (an1 <- anova(m, type=1)) (an2 <- anova(m, type=2)) (an3 <- anova(m, type=3)) ## Balanced data and only factors: Type I, II, and III should be the same: stopifnot( isTRUE(all.equal(an1, an2, check.attributes=FALSE, tolerance=TOL)), isTRUE(all.equal(an3, an2, check.attributes=FALSE, tolerance=TOL)) ) ######## # No intercept: m <- lmer(angle ~ 0 + recipe * temp + (1|recipe:replicate), cake) (an <- anova(m, type=1)) (an2 <- anova(m, type=2)) (an2 <- anova(m, type=3)) if(has_pbkrtest) (an_KR <- anova(m, ddf="Kenward-Roger")) (an_lme4 <- anova(m, ddf="lme4")) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_lme4[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) # ML-fit: m <- lmer(angle ~ recipe * temp + (1|recipe:replicate), cake, REML=FALSE) (an <- anova(m, type=1)) if(has_pbkrtest) assertError(an <- anova(m, ddf="Kenward-Roger")) # KR fits should be REML (an_lme4 <- anova(m, ddf="lme4")) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_lme4[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) #################################### ## Using contr.sum: #################################### m <- lmer(angle ~ recipe * temp + (1|recipe:replicate), cake, contrasts = list('recipe' = "contr.sum")) (an <- anova(m, type=1)) (an2 <- anova(m, type=2)) (an3 <- anova(m, type=3)) stopifnot( isTRUE(all.equal(an2, an3, check.attributes=FALSE, tolerance=TOL)) ) if(has_pbkrtest) (an_KR <- anova(m, type=1, ddf="Kenward-Roger")) (an_lme4 <- anova(m, ddf="lme4")) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_lme4[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) #################################### ## Example with continuous fixef: #################################### # Example with no fixef: m <- lmer(Reaction ~ -1 + (Days | Subject), sleepstudy) # m <- lmer(Reaction ~ 0 + (Days | Subject), sleepstudy) # alternative stopifnot(length(fixef(m)) == 0L) (an <- anova(m, type=1)) (an_2 <- anova(m, type=2)) (an_3 <- anova(m, type=3)) stopifnot(nrow(an) == 0L, nrow(an_2) == 0L, nrow(an_3) == 0L) # anova(m, ddf="lme4") # Bug in lme4 it seems if(has_pbkrtest) { (an_KR <- anova(m, ddf="Kenward-Roger")) stopifnot( nrow(an_KR) == 0L ) } # Example with intercept only: m <- lmer(Reaction ~ (Days | Subject), sleepstudy) # m <- lmer(Reaction ~ 1 + (Days | Subject), sleepstudy) # alternative stopifnot(length(fixef(m)) == 1L, names(fixef(m)) == "(Intercept)") (an <- anova(m)) (an_2 <- anova(m, type=2)) (an_3 <- anova(m, type=3)) (an_lme4 <- anova(m, ddf="lme4")) stopifnot(nrow(an) == 0L, nrow(an_2) == 0L, nrow(an_3) == 0L, nrow(an_lme4) == 0L) if(has_pbkrtest) { (an_KR <- anova(m, ddf="Kenward-Roger")) stopifnot( nrow(an_KR) == 0L ) } # Example with 1 fixef without intercept: # for packageVersion("lme4") < 1.1.20 # mOld <- lmer(Reaction ~ Days - 1 + (Days | Subject), sleepstudy) # for packageVersion("lme4") >= 1.1.20 we need to specify the old default # optimizer to get the model to converge well enough. m <- lmer(Reaction ~ Days - 1 + (Days | Subject), sleepstudy, control=lmerControl(optimizer="bobyqa")) # m <- lmer(Reaction ~ 0 + Days + (Days | Subject), sleepstudy) # alternative stopifnot(length(fixef(m)) == 1L, names(fixef(m)) == "Days") (an <- anova(m)) (an_2 <- anova(m, type=2)) (an_3 <- anova(m, type=3)) (an_lme4 <- anova(m, ddf="lme4")) stopifnot(nrow(an) == 1L, nrow(an_2) == 1L, nrow(an_3) == 1L, nrow(an_lme4) == 1L) if(has_pbkrtest) { (an_KR <- anova(m, ddf="Kenward-Roger")) stopifnot( nrow(an_KR) == 1L ) } res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_lme4[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) stopifnot(isTRUE(all.equal( c(1, 17), unname(unlist(an[, c("NumDF", "DenDF")])), tolerance=TOL ))) # Example with >1 fixef without intercept: m <- lmer(Reaction ~ Days - 1 + I(Days^2) + (Days | Subject), sleepstudy) stopifnot(length(fixef(m)) == 2L, names(fixef(m)) == c("Days", "I(Days^2)")) (an <- anova(m)) (an_2 <- anova(m, type=2)) (an_3 <- anova(m, type=3)) (an_lme4 <- anova(m, ddf="lme4")) stopifnot(nrow(an) == 2L, nrow(an_3) == 2L, nrow(an_lme4) == 2L) if(has_pbkrtest) { (an_KR <- anova(m, ddf="Kenward-Roger")) stopifnot( nrow(an_KR) == 2L ) } # Here is a diff in SSQ which doesn't seem well-defined anyway... # SSQ for I(Days^2) agree though. # t-statistics also agree: coef(summary(m)) Lmat <- diag(length(fixef(m))) lmerTest:::rbindall(lapply(1:nrow(Lmat), function(i) contest1D(m, Lmat[i, ]))) # Example with >1 fixef and intercept: m <- lmer(Reaction ~ Days + I(Days^2) + (Days | Subject), sleepstudy) stopifnot(length(fixef(m)) == 3L) (an <- anova(m, type=1)) (an_2 <- anova(m, type=2)) (an_3 <- anova(m, type=3)) (an_lme4 <- anova(m, ddf="lme4")) res <- all.equal(an[, c("Sum Sq", "Mean Sq", "F value")], an_lme4[, c("Sum Sq", "Mean Sq", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) if(has_pbkrtest) { (an_KR <- anova(m, ddf="Kenward-Roger")) res <- all.equal(an_3[, c("Sum Sq", "Mean Sq", "DenDF", "F value")], an_KR[, c("Sum Sq", "Mean Sq", "DenDF", "F value")], tolerance=TOL) stopifnot(isTRUE(res)) } ## FIXME: Test the use of refit arg to lme4:::anova.merMod ############################## # Test that type III anova is the same regardless of contrast coding: # 3 x 3 factorial with missing diagonal data("cake", package="lme4") cake4 <- cake cake4$temperature <- factor(cake4$temperature, ordered=FALSE) cake4 <- droplevels(subset(cake4, temperature %in% levels(cake4$temperature)[1:3])) cake4 <- droplevels(subset(cake4, !((recipe == "A" & temperature == "175") | (recipe == "B" & temperature == "185") | (recipe == "C" & temperature == "195") ))) str(cake4) with(cake4, table(recipe, temperature)) # load_all(r2path) fm1 <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake4) fm2 <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake4, contrasts=list(recipe="contr.sum", temperature="contr.SAS")) fm3 <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake4, contrasts=list(recipe="contr.sum", temperature="contr.poly")) fm4 <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake4, contrasts=list(recipe=contr.helmert, temperature="contr.poly")) (an1 <- anova(fm1)) (an2 <- anova(fm2)) (an3 <- anova(fm3)) (an4 <- anova(fm4)) options("contrasts") options(contrasts = c("contr.sum", "contr.poly")) fm5 <- lmer(angle ~ recipe * temperature + (1|recipe:replicate), cake4) (an5 <- anova(fm5)) options(contrasts = c("contr.treatment", "contr.poly")) options("contrasts") stopifnot( isTRUE(all.equal(an1, an2, check.attributes=FALSE, tolerance=TOL)), isTRUE(all.equal(an1, an3, check.attributes=FALSE, tolerance=TOL)), isTRUE(all.equal(an1, an4, check.attributes=FALSE, tolerance=TOL)), isTRUE(all.equal(an1, an5, check.attributes=FALSE, tolerance=TOL)) )