## Chapter 10: Multi-level Models, and Repeated Measures
##                  Corn yield measurements example
library(lattice); library(DAAG)
Site <- with(ant111b, reorder(site, harvwt, FUN=mean))
stripplot(Site ~ harvwt, data=ant111b, scales=list(tck=0.5),
          xlab="Harvest weight of corn")

plot of chunk unnamed-chunk-1

## Sec 10.1: Corn Yield Data --- Analysis Using {aov()}
library(DAAG)
ant111b.aov <- aov(harvwt ~ 1 + Error(site), data=ant111b)

summary(ant111b.aov)
## 
## Error: site
##           Df Sum Sq Mean Sq F value Pr(>F)
## Residuals  7   70.3    10.1               
## 
## Error: Within
##           Df Sum Sq Mean Sq F value Pr(>F)
## Residuals 24   13.9   0.578
##                   Interpreting the mean squares
##                    Details of the calculations
##         Practical use of the analysis of variance results
##                  Random effects vs. fixed effects
##            Nested factors -- a variety of applications
## ss 10.1.1: A More Formal Approach
##       Relations between variance components and mean squares
##               Interpretation of variance components
##                      Intra-class correlation

## Sec 10.2: Analysis using {lmer()}, from the { lme4} package
library(lme4)
## Loading required package: Matrix
## Loading required package: Rcpp
ant111b.lmer <- lmer(harvwt ~ 1 + (1 | site), data=ant111b)

## Note that there is no degrees of freedom information.
print(ant111b.lmer, ranef.comp="Variance", digits=3)
## Linear mixed model fit by REML ['lmerMod']
## Formula: harvwt ~ 1 + (1 | site)
##    Data: ant111b
## REML criterion at convergence: 94.42
## Random effects:
##  Groups   Name        Variance
##  site     (Intercept) 2.368   
##  Residual             0.578   
## Number of obs: 32, groups:  site, 8
## Fixed Effects:
## (Intercept)  
##        4.29
##              The processing of  output from {lmer()}
print(coef(summary(ant111b.lmer)), digits=3)
##             Estimate Std. Error t value
## (Intercept)     4.29       0.56    7.66
##              Fitted values and residuals in {lmer()}
s2W <- 0.578; s2L <- 2.37; n <- 4
sitemeans <- with(ant111b, sapply(split(harvwt, site), mean))
grandmean <- mean(sitemeans)
shrinkage <- (n*s2L)/(n*s2L+s2W)
grandmean + shrinkage*(sitemeans - grandmean)
##  DBAN  LFAN  NSAN  ORAN  OVAN  TEAN  WEAN  WLAN 
## 4.851 4.212 2.217 6.764 4.801 3.108 5.455 2.925
##
## More directly, use fitted() with the lmer object
unique(fitted(ant111b.lmer))
## [1] 4.851 4.212 2.217 6.764 4.801 3.108 5.455 2.925
##
## Compare with site means
sitemeans
##  DBAN  LFAN  NSAN  ORAN  OVAN  TEAN  WEAN  WLAN 
## 4.885 4.207 2.090 6.915 4.833 3.036 5.526 2.841
##  *Uncertainty in the parameter estimates --- profile  likelihood and alternatives
prof.lmer <- profile(ant111b.lmer)
CI95 <- confint(prof.lmer, level=0.95)
rbind("sigmaL^2"=CI95[1,]^2, "sigma^2"=CI95[2,]^2)
##           2.5 % 97.5 %
## sigmaL^2 0.7965  6.936
## sigma^2  0.3444  1.079
CI95[3,]
##  2.5 % 97.5 % 
##  3.128  5.456
library(lattice)
print(xyplot(prof.lmer, conf=c(50, 80, 95, 99)/100, 
             aspect=0.8, between=list(x=0.35)))

plot of chunk unnamed-chunk-1

##         Handling more than two levels of random variation

## Sec 10.3: Survey Data, with Clustering
## Footnote Code
## Means of like (data frame science: DAAG), by class
classmeans <- with(science,
                   aggregate(like, by=list(PrivPub, Class), mean) )
  # NB: Class identifies classes independently of schools
  #     class identifies classes within schools
names(classmeans) <- c("PrivPub", "Class", "avlike")
with(classmeans, {
     ## Boxplots: class means by Private or Public school
     boxplot(split(avlike, PrivPub), horizontal=TRUE, las=2,
                   xlab = "Class average of score", boxwex = 0.4)
     rug(avlike[PrivPub == "private"], side = 1)
     rug(avlike[PrivPub == "public"], side = 3)
})

plot of chunk unnamed-chunk-1

## ss 10.3.1: Alternative models
science.lmer <- lmer(like ~ sex + PrivPub + (1 | school) +
                     (1 | school:class), data = science,
                     na.action=na.exclude)

print(VarCorr(science.lmer), comp="Variance", digits=3)
##  Groups       Name        Variance
##  school:class (Intercept) 0.321   
##  school       (Intercept) 0.000   
##  Residual                 3.052
print(coef(summary(science.lmer)), digits=2)
##               Estimate Std. Error t value
## (Intercept)       4.72      0.162    29.1
## sexm              0.18      0.098     1.9
## PrivPubpublic     0.41      0.186     2.2
summary(science.lmer)$ngrps
## school:class       school 
##           66           41
science1.lmer <- lmer(like ~ sex + PrivPub + (1 | school:class),
                      data = science, na.action=na.exclude)
print(VarCorr(science1.lmer), comp="Variance", digits=3)
##  Groups       Name        Variance
##  school:class (Intercept) 0.321   
##  Residual                 3.052
print(coef(summary(science1.lmer)), digits=2)
##               Estimate Std. Error t value
## (Intercept)       4.72      0.162    29.1
## sexm              0.18      0.098     1.9
## PrivPubpublic     0.41      0.186     2.2
library(afex)
## Loading required package: car
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:DAAG':
## 
##     vif
## 
## Loading required package: pbkrtest
## Loading required package: MASS
## 
## Attaching package: 'MASS'
## 
## The following object is masked from 'package:DAAG':
## 
##     hills
## 
## Loading required package: parallel
## Loading required package: reshape2
## ************
## Welcome to afex. Important notes:
## 
## Due to popular demand, afex doesn't change the contrasts globally anymore.
## To set contrasts globally to contr.sum run set_sum_contrasts().
## To set contrasts globally to the default (treatment) contrasts run set_default_contrasts().
## 
## All afex functions are unaffected by global contrasts and use contr.sum as long as check.contr = TRUE (which is the default).
## ************
mixed(like ~ sex + PrivPub + (1 | school:class),
      data = na.omit(science), method="KR")
## Contrasts set to contr.sum for the following variables: sex, PrivPub, school, class
## Fitting 3 (g)lmer() models:
## [...]
## Obtaining 2 p-values:
## [..]
##    Effect    F ndf     ddf F.scaling p.value
## 1     sex 3.44   1 1379.49      1.00     .06
## 2 PrivPub 4.91   1   60.44      1.00     .03
##              More detailed examination of the output
## Use profile likelihood
pp <- profile(science1.lmer, which="theta_")
  # which="theta_": all random parameters
  # which="beta_": fixed effect parameters
var95 <- confint(pp, level=0.95)^2
  # Square to get variances in place of SDs
rownames(var95) <- c("sigma_Class^2", "sigma^2")
signif(var95, 3)
##               2.5 % 97.5 %
## sigma_Class^2 0.178  0.511
## sigma^2       2.830  3.300
science1.lmer <- lmer(like ~ sex + PrivPub + (1 | school:class),
                      data = science, na.action=na.omit)
ranf <- ranef(obj = science1.lmer, drop=TRUE)[["school:class"]]
flist <- science1.lmer@flist[["school:class"]]
privpub <- science[match(names(ranf), flist), "PrivPub"]
num <- unclass(table(flist)); numlabs <- pretty(num)
opar <- par(mfrow=c(2,2), pty="s", mgp=c(2.25,0.5,0), mar=c(3.6,3.6,2.1, 0.6))
## Panel A: Plot effect estimates vs numbers
plot(sqrt(num), ranf, xaxt="n", pch=c(1,3)[as.numeric(privpub)],
     xlab="# in class (square root scale)",
     ylab="Estimate of class effect")
lines(lowess(sqrt(num[privpub=="private"]),
             ranf[privpub=="private"], f=1.1), lty=2)
lines(lowess(sqrt(num[privpub=="public"]),
             ranf[privpub=="public"], f=1.1), lty=3)
axis(1, at=sqrt(numlabs), labels=paste(numlabs))
res <- residuals(science1.lmer)
vars <- tapply(res, INDEX=list(flist), FUN=var)*(num-1)/(num-2)
## Panel B: Within class variance estimates vs numbers
plot(sqrt(num), vars, pch=c(1,3)[unclass(privpub)])
lines(lowess(sqrt(num[privpub=="private"]),
             as.vector(vars)[privpub=="private"], f=1.1), lty=2)
lines(lowess(sqrt(num[privpub=="public"]),
             as.vector(vars)[privpub=="public"], f=1.1), lty=3)
## Panel C: Normal probability plot of site effects
qqnorm(ranf, ylab="Ordered site effects", main="")
## Panel D: Normal probability plot of residuals
qqnorm(res, ylab="Ordered w/i class residuals", main="")

plot of chunk unnamed-chunk-1

par(opar)

## ss 10.3.2: Instructive, though faulty, analyses
##                Ignoring class as the random effect
science2.lmer <- lmer(like ~ sex + PrivPub + (1 | school),
                      data = science, na.action=na.exclude)
science2.lmer
## Linear mixed model fit by REML ['lmerMod']
## Formula: like ~ sex + PrivPub + (1 | school)
##    Data: science
## REML criterion at convergence: 5584
## Random effects:
##  Groups   Name        Std.Dev.
##  school   (Intercept) 0.407   
##  Residual             1.794   
## Number of obs: 1383, groups:  school, 41
## Fixed Effects:
##   (Intercept)           sexm  PrivPubpublic  
##         4.738          0.197          0.417
## Footnote Code
##             Ignoring the random structure in the data
## Faulty analysis, using lm
science.lm <- lm(like ~ sex + PrivPub, data=science)
summary(science.lm)$coef
##               Estimate Std. Error t value   Pr(>|t|)
## (Intercept)     4.7402    0.09955  47.616 1.545e-293
## sexm            0.1509    0.09860   1.531  1.261e-01
## PrivPubpublic   0.3951    0.10511   3.759  1.779e-04
## ss 10.3.3: Predictive accuracy

## Sec 10.4: A Multi-level Experimental Design
## ss 10.4.1: The anova table
## Analysis of variance: data frame kiwishade (DAAG)
kiwishade.aov <- aov(yield ~ shade + Error(block/shade),
                     data=kiwishade)

summary(kiwishade.aov)
## 
## Error: block
##           Df Sum Sq Mean Sq F value Pr(>F)
## Residuals  2    172    86.2               
## 
## Error: block:shade
##           Df Sum Sq Mean Sq F value Pr(>F)
## shade      3   1395     465    22.2 0.0012
## Residuals  6    126      21               
## 
## Error: Within
##           Df Sum Sq Mean Sq F value Pr(>F)
## Residuals 36    439    12.2
## ss 10.4.2: Expected values of mean squares
model.tables(kiwishade.aov, type="means")
## Tables of means
## Grand mean
##       
## 96.53 
## 
##  shade 
## shade
##    none Aug2Dec Dec2Feb Feb2May 
##  100.20  103.23   89.92   92.77
## Footnote Code
## Calculate treatment means
with(kiwishade, sapply(split(yield, shade), mean))
##    none Aug2Dec Dec2Feb Feb2May 
##  100.20  103.23   89.92   92.77
## ss 10.4.3: * The analysis of variance sums of squares  breakdown
## Footnote Code
## For each plot, calculate mean, and differences from the mean
vine <- paste("vine", rep(1:4, 12), sep="")
vine1rows <- seq(from=1, to=45, by=4)
kiwivines <- unstack(kiwishade, yield ~ vine)
kiwimeans <- apply(kiwivines, 1, mean)
kiwivines <- cbind(kiwishade[vine1rows,  c("block","shade")],
                   Mean=kiwimeans, kiwivines-kiwimeans)
kiwivines <- with(kiwivines, kiwivines[order(block, shade), ])
mean(kiwimeans)      # the grand mean
## [1] 96.53
## ss 10.4.4: The variance components
## ss 10.4.5: The mixed model analysis
kiwishade.lmer <- lmer(yield ~ shade + (1|block) + (1|block:plot),
                         data=kiwishade)
  # block:shade is an alternative to block:plot

print(kiwishade.lmer, ranef.comp="Variance", digits=3)
## Linear mixed model fit by REML ['lmerMod']
## Formula: yield ~ shade + (1 | block) + (1 | block:plot)
##    Data: kiwishade
## REML criterion at convergence: 252
## Random effects:
##  Groups     Name        Variance
##  block:plot (Intercept)  2.19   
##  block      (Intercept)  4.08   
##  Residual               12.18   
## Number of obs: 48, groups:  block:plot, 12; block, 3
## Fixed Effects:
##  (Intercept)  shadeAug2Dec  shadeDec2Feb  shadeFeb2May  
##       100.20          3.03        -10.28         -7.43
##                  Residuals and estimated effects
## Footnote Code
## Simplified version of plot
xyplot(residuals(kiwishade.lmer) ~ fitted(kiwishade.lmer)|block, data=kiwishade,
                groups=shade, layout=c(3,1), par.strip.text=list(cex=1.0),
                xlab="Fitted values (Treatment + block + plot effects)",
                ylab="Residuals", pch=1:4, grid=TRUE, aspect=1,
                scales=list(x=list(alternating=FALSE), tck=0.5),
                key=list(space="top", points=list(pch=1:4),
                         text=list(labels=levels(kiwishade$shade)),columns=4))

plot of chunk unnamed-chunk-1

## Footnote Code
## Simplified version of graph that shows the plot effects
ploteff <- ranef(kiwishade.lmer, drop=TRUE)[[1]]
qqmath(ploteff, xlab="Normal quantiles", ylab="Plot effect estimates",
       aspect=1, scales=list(tck=0.5))

plot of chunk unnamed-chunk-1

## Footnote Code
## Overlaid normal probability plots of 2 sets of simulated effects
## To do more simulations, change nsim as required, and re-execute
simvals <- simulate(kiwishade.lmer, nsim=2)
simeff <- apply(simvals, 2, function(y) scale(ranef(refit(kiwishade.lmer, y),
                                                drop=TRUE)[[1]]))
simeff <- data.frame(v1=simeff[,1], v2=simeff[,2])
qqmath(~ v1+v2, data=simeff, xlab="Normal quantiles",
       ylab="Simulated plot effects\n(2 sets, standardized)",
              scales=list(tck=0.5), aspect=1)

plot of chunk unnamed-chunk-1

## ss 10.4.6: Predictive accuracy

## Sec 10.5: Within and Between Subject Effects
##                       Model fitting criteria
## ss 10.5.1: Model selection
## Change initial letters of levels of tinting$agegp to upper case
library(R.utils)
## Loading required package: R.oo
## Loading required package: R.methodsS3
## R.methodsS3 v1.6.1 (2014-01-04) successfully loaded. See ?R.methodsS3 for help.
## R.oo v1.18.0 (2014-02-22) successfully loaded. See ?R.oo for help.
## 
## Attaching package: 'R.oo'
## 
## The following objects are masked from 'package:methods':
## 
##     getClasses, getMethods
## 
## The following objects are masked from 'package:base':
## 
##     attach, detach, gc, load, save
## 
## R.utils v1.32.4 (2014-05-14) successfully loaded. See ?R.utils for help.
## 
## Attaching package: 'R.utils'
## 
## The following object is masked from 'package:utils':
## 
##     timestamp
## 
## The following objects are masked from 'package:base':
## 
##     cat, commandArgs, getOption, inherits, isOpen, parse, warnings
levels(tinting$agegp) <- capitalize(levels(tinting$agegp))
## Fit all interactions: data frame tinting (DAAG)
it3.lmer <- lmer(log(it) ~ tint*target*agegp*sex + (1 | id),
                 data=tinting, REML=FALSE)

it2.lmer <- lmer(log(it) ~ (tint+target+agegp+sex)^2 + (1 | id),
                 data=tinting, REML=FALSE)

it1.lmer <- lmer(log(it)~(tint+target+agegp+sex) + (1 | id),
                 data=tinting, REML=FALSE)

anova(it1.lmer, it2.lmer, it3.lmer)
## Data: tinting
## Models:
## it1.lmer: log(it) ~ (tint + target + agegp + sex) + (1 | id)
## it2.lmer: log(it) ~ (tint + target + agegp + sex)^2 + (1 | id)
## it3.lmer: log(it) ~ tint * target * agegp * sex + (1 | id)
##          Df   AIC  BIC logLik deviance Chisq Chi Df Pr(>Chisq)
## it1.lmer  8  1.14 26.8   7.43    -14.9                        
## it2.lmer 17 -3.74 50.7  18.87    -37.7 22.88      9     0.0065
## it3.lmer 26  8.15 91.5  21.93    -43.9  6.11      9     0.7288
## Footnote Code
## Code that gives the first four such plots, for the observed data
opar <- par(mfrow=c(2,2), pty="s", mgp=c(2.25,0.5,0), mar=c(3.6,3.6,2.1, 0.6))
interaction.plot(tinting$tint, tinting$agegp, log(tinting$it))
interaction.plot(tinting$target, tinting$sex, log(tinting$it))
interaction.plot(tinting$tint, tinting$target, log(tinting$it))
interaction.plot(tinting$tint, tinting$sex, log(tinting$it))

plot of chunk unnamed-chunk-1

par(opar)

## ss 10.5.2: Estimates of model parameters
it2.reml <- update(it2.lmer, REML=TRUE)
print(coef(summary(it2.reml)), digits=2)
##                        Estimate Std. Error t value
## (Intercept)              3.6191      0.130   27.82
## tint.L                   0.1609      0.044    3.64
## tint.Q                   0.0210      0.045    0.46
## targethicon             -0.1181      0.042   -2.79
## agegpOlder               0.4712      0.233    2.02
## sexm                     0.0821      0.233    0.35
## tint.L:targethicon      -0.0919      0.046   -2.00
## tint.Q:targethicon      -0.0072      0.048   -0.15
## tint.L:agegpOlder        0.1308      0.049    2.66
## tint.Q:agegpOlder        0.0697      0.052    1.34
## tint.L:sexm             -0.0979      0.049   -1.99
## tint.Q:sexm              0.0054      0.052    0.10
## targethicon:agegpOlder  -0.1389      0.058   -2.38
## targethicon:sexm         0.0779      0.058    1.33
## agegpOlder:sexm          0.3316      0.326    1.02
 # NB: The final column in the text, giving degrees of freedom, is not in the
 # summary output. It is our addition.

## Footnote Code

## Sec 10.6: A Generalized Linear Mixed Model
moths$transect <- 1:41  # Each row is from a different transect
moths$habitat <- relevel(moths$habitat, ref="Lowerside")
A.glmer <-  glmer(A~habitat+sqrt(meters)+(1|transect),
                  family=poisson(link=sqrt), data=moths)
print(summary(A.glmer), show.resid=FALSE, correlation=FALSE)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: poisson  ( sqrt )
## Formula: A ~ habitat + sqrt(meters) + (1 | transect)
##    Data: moths
## 
##      AIC      BIC   logLik deviance df.resid 
##    212.6    229.7    -96.3    192.6       31 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  transect (Intercept) 0.319    0.564   
## Number of obs: 41, groups:  transect, 41
## 
## Fixed effects:
##                  Estimate Std. Error z value Pr(>|z|)
## (Intercept)        1.7322     0.3513    4.93  8.2e-07
## habitatBank       -2.0415     0.9377   -2.18    0.029
## habitatDisturbed  -1.0359     0.4071   -2.54    0.011
## habitatNEsoak     -0.7319     0.4323   -1.69    0.090
## habitatNWsoak      2.6787     0.5101    5.25  1.5e-07
## habitatSEsoak      0.1178     0.3923    0.30    0.764
## habitatSWsoak      0.3900     0.5260    0.74    0.458
## habitatUpperside  -0.3135     0.7549   -0.42    0.678
## sqrt(meters)       0.0675     0.0631    1.07    0.285
## Footnote Code
##         Mixed models with a binomial error and logit link

## Sec 10.7: Repeated Measures in Time
##             The theory of repeated measures modeling
##                      *Correlation structure
##         Different approaches to repeated measures analysis
## ss 10.7.1: Example -- random variation between profiles
## Footnote Code
## Plot points and fitted lines (panel A)
library(lattice)
xyplot(o2 ~ wattsPerKg, groups=id, data=humanpower1,
        panel=function(x,y,subscripts,groups,...){
           yhat <- fitted(lm(y ~ groups*x))
           panel.superpose(x, y, subscripts, groups, pch=1:5)
           panel.superpose(x, yhat, subscripts, groups, type="l")
        },
        xlab="Watts per kilogram",
        ylab=expression("Oxygen intake ("*ml.min^{-1}*.kg^{-1}*")"))

plot of chunk unnamed-chunk-1

##           Separate lines for different athletes
## Calculate intercepts and slopes; plot Slopes vs Intercepts
## Uses the function lmList() from the lme4 package
library(lme4)
hp.lmList <- lmList(o2 ~ wattsPerKg | id, data=humanpower1)
coefs <- coef(hp.lmList)
names(coefs) <- c("Intercept", "Slope")
plot(Slope ~ Intercept, data=coefs)
abline(lm(Slope~Intercept, data=coefs))

plot of chunk unnamed-chunk-1

##                     A random coefficients model
hp.lmer <- lmer(o2 ~ wattsPerKg + (wattsPerKg | id),
                data=humanpower1)
print(summary(hp.lmer), digits=3)
## Linear mixed model fit by REML ['lmerMod']
## Formula: o2 ~ wattsPerKg + (wattsPerKg | id)
##    Data: humanpower1
## 
## REML criterion at convergence: 124.2
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -1.9947 -0.4878 -0.0835  0.4616  2.1212 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr 
##  id       (Intercept) 50.73    7.12          
##           wattsPerKg   7.15    2.67     -1.00
##  Residual              4.13    2.03          
## Number of obs: 28, groups:  id, 5
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept)     2.09       3.78    0.55
## wattsPerKg     13.91       1.36   10.23
## 
## Correlation of Fixed Effects:
##            (Intr)
## wattsPerKg -0.992
hat <- fitted(hp.lmer)
lmhat <- with(humanpower1, fitted(lm(o2 ~ id*wattsPerKg)))
panelfun <-
  function(x, y, subscripts, groups, ...){
           panel.superpose(x, hat, subscripts, groups, type="l",lty=2)
           panel.superpose(x, lmhat, subscripts, groups, type="l",lty=1)
}
xyplot(o2 ~ wattsPerKg, groups=id, data=humanpower1, panel=panelfun,
       xlab="Watts",
       ylab=expression("Oxygen intake ("*ml.min^{-1}*"."*kg^{-1}*")"))

plot of chunk unnamed-chunk-1

## Footnote Code
## Plot of residuals
xyplot(resid(hp.lmer) ~ wattsPerKg, groups=id, type="b", data=humanpower1)

plot of chunk unnamed-chunk-1

## Footnote Code
## Derive the sd from the data frame coefs that was calculated above
sd(coefs$Slope)
## [1] 3.278
## ss 10.7.2: Orthodontic measurements on chlldren
##              Preliminary data exploration
## Footnote Code
## Plot showing pattern of change for each of the 25 individuals
library(MEMSS)
## 
## Attaching package: 'MEMSS'
## 
## The following objects are masked from 'package:datasets':
## 
##     CO2, Orange, Theoph
xyplot(distance ~ age | Subject, groups=Sex, data=Orthodont,
       scales=list(y=list(log=2)), type=c("p","r"), layout=c(11,3))

plot of chunk unnamed-chunk-1

## Use lmList() to find the slopes
ab <- coef(lmList(distance ~ age | Subject, Orthodont))
names(ab) <- c("a", "b")
## Obtain the intercept at x=mean(x)
## (For each subject, this is independent of the slope)
ab$ybar <- ab$a + ab$b*11  # mean age is 11, for each subject.
sex <- substring(rownames(ab), 1 ,1)
plot(ab[, 3], ab[, 2], col=c(F="gray40", M="black")[sex],
     pch=c(F=1, M=3)[sex], xlab="Intercept", ylab="Slope")
extremes <- ab$ybar %in% range(ab$ybar) |
            ab$b %in% range(ab$b[sex=="M"]) |
            ab$b %in% range(ab$b[sex=="F"])
text(ab[extremes, 3], ab[extremes, 2], rownames(ab)[extremes], pos=4, xpd=TRUE)

plot of chunk unnamed-chunk-1

## The following makes clear M13's difference from other points
opar <- par(mfrow=c(1,2), pty="s", mgp=c(2.25,0.5,0), mar=c(4.1,3.6,2.1, 0.6))
qqnorm(ab$b, main="QQ -- Slope in lm(dist~age)")
Orthodont$logdist <- log(Orthodont$distance)
## Now repeat, with logdist replacing distance
ablog <- coef(lmList(logdist ~ age | Subject, Orthodont))
names(ablog) <- c("a", "b")
qqnorm(ablog$b, main="QQ -- Slope in lm(logist~age)")

plot of chunk unnamed-chunk-1

par(opar)

## Footnote Code
## Compare males slopes with female slopes
Orthodont$logdist <- log(Orthodont$distance)
ablog <- coef(lmList(logdist ~ age | Subject, Orthodont))
names(ablog) <- c("a", "b")
## Obtain the intercept at mean age (= 11), for each subject
## (For each subject, this is independent of the slope)
ablog$ybar <- with(ablog, a + b*11)
extreme.males <- rownames(ablog) %in% c("M04","M13")
sex <- substring(rownames(ab), 1, 1)
with(ablog,
  t.test(b[sex=="F"], b[sex=="M" & !extreme.males], var.equal=TRUE))
## 
##  Two Sample t-test
## 
## data:  b[sex == "F"] and b[sex == "M" & !extreme.males]
## t = -2.32, df = 23, p-value = 0.02957
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -0.0160529 -0.0009191
## sample estimates:
## mean of x mean of y 
##   0.02115   0.02963
  # Specify var.equal=TRUE, to allow comparison with anova output

##                A random coefficients model
keep <- !(Orthodont$Subject%in%c("M04","M13"))
orthdiff.lmer <- lmer(logdist ~ Sex * I(age-11) + (I(age-11) | Subject),
                      data=Orthodont, subset=keep, REML=FALSE)
print(summary(orthdiff.lmer), digits=3)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: logdist ~ Sex * I(age - 11) + (I(age - 11) | Subject)
##    Data: Orthodont
##  Subset: keep
## 
##      AIC      BIC   logLik deviance df.resid 
##   -247.1   -226.2    131.5   -263.1       92 
## 
## Scaled residuals: 
##    Min     1Q Median     3Q    Max 
## -3.370 -0.482  0.004  0.534  3.993 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr
##  Subject  (Intercept) 5.79e-03 0.076124     
##           I(age - 11) 7.71e-07 0.000878 1.00
##  Residual             2.31e-03 0.048109     
## Number of obs: 100, groups:  Subject, 25
## 
## Fixed effects:
##                     Estimate Std. Error t value
## (Intercept)          3.11451    0.02407   129.4
## SexMale              0.09443    0.03217     2.9
## I(age - 11)          0.02115    0.00325     6.5
## SexMale:I(age - 11)  0.00849    0.00435     2.0
## 
## Correlation of Fixed Effects:
##             (Intr) SexMal I(-11)
## SexMale     -0.748              
## I(age - 11)  0.078 -0.058       
## SxMl:I(-11) -0.058  0.078 -0.748
orthsame.lmer <- lmer(logdist ~ Sex + I(age-11) + (I(age-11) | Subject),
                     data=Orthodont, subset=keep, REML=FALSE)
print(anova(orthsame.lmer, orthdiff.lmer)[2, "Pr(>Chisq)"], digits=3)
## [1] 0.054
orthdiffr.lmer <- update(orthdiff.lmer, REML=TRUE) 
print(summary(orthdiffr.lmer), digits=3)
## Linear mixed model fit by REML ['lmerMod']
## Formula: logdist ~ Sex * I(age - 11) + (I(age - 11) | Subject)
##    Data: Orthodont
##  Subset: keep
## 
## REML criterion at convergence: -232.2
## 
## Scaled residuals: 
##    Min     1Q Median     3Q    Max 
## -3.325 -0.477  0.001  0.522  3.939 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev. Corr
##  Subject  (Intercept) 6.33e-03 0.079581     
##           I(age - 11) 8.42e-07 0.000918 1.00
##  Residual             2.38e-03 0.048764     
## Number of obs: 100, groups:  Subject, 25
## 
## Fixed effects:
##                     Estimate Std. Error t value
## (Intercept)          3.11451    0.02510   124.1
## SexMale              0.09443    0.03354     2.8
## I(age - 11)          0.02115    0.00330     6.4
## SexMale:I(age - 11)  0.00849    0.00441     1.9
## 
## Correlation of Fixed Effects:
##             (Intr) SexMal I(-11)
## SexMale     -0.748              
## I(age - 11)  0.080 -0.060       
## SxMl:I(-11) -0.060  0.080 -0.748
##                Correlation between successive times
res <- resid(orthdiffr.lmer)
Subject <- factor(Orthodont$Subject[keep])
orth.acf <- t(sapply(split(res, Subject),
                     function(x)acf(x, lag=4, plot=FALSE)$acf))
## Calculate respective proportions of Subjects for which
## autocorrelations at lags 1, 2 and 3 are greater than zero.
apply(orth.acf[,-1], 2, function(x)sum(x>0)/length(x))
## [1] 0.20 0.32 0.36
##             *The variance for the difference in slopes

## Sec 10.8: Further Notes on Multi-level and Other Models with CorrelatedErrors
## ss 10.8.1: Different sources of variance -- complication or focus of interest?
## ss 10.8.2: Predictions from models with a complex error structure
##              Consequences from assuming an overly simplistic error structure
## ss 10.8.3: An historical perspective on multi-level models
## ss 10.8.4: Meta-analysis
## ss 10.8.5: Functional data analysis
## ss 10.8.6:  Error structure in explanatory variables

## Sec 10.9: Recap

## Sec 10.10: Further Reading
##                             References
##                   References for further reading
##           Analysis of variance with multiple error terms
##              Multi-level models and repeated measures
##                           Meta-analysis
##                             References
##                   References for further reading
n.omit <- 2
take <- rep(TRUE, 48)
take[sample(1:48,2)] <- FALSE
kiwishade.lmer <- lmer(yield ~ shade + (1|block) + (1|block:plot),
                      data = kiwishade,subset=take)
vcov <- VarCorr(kiwishade.lmer)
print(vcov, comp="Variance")
##  Groups     Name        Variance
##  block:plot (Intercept)  2.42   
##  block      (Intercept)  4.00   
##  Residual               12.42
cult.lmer <- lmer(ct ~ Cultivar + Dose + factor(year) +
                  (-1 + Dose | gp), data = sorption,
                  REML=TRUE)
cultdose.lmer <- lmer(ct ~ Cultivar/Dose + factor(year) +
                      (-1 + Dose | gp), data = sorption,
                      REML=TRUE)