Assignment 10—Solution

Setting up the data

I read in the data.

> inverts<- read.table('http://www.unc.edu/courses/2006spring/ecol/145/001/data/lab11/74species.csv', sep=',', header=TRUE)
> names(inverts)
 [1] "species"      "meantemp" "temp"  "PLD" "taxon"   "climate.3"
 [7] "feeding.type" "lntemp"   "lnPLD" "n"   "PLD.15c" "lnPLD.15c"
[13] "X1000.k"      "lograte"

Question 1

I fit the two models discussed in the question.

> model2<-lme(lnPLD~lntemp, random=~lntemp|species, data=inverts, method="ML")
> model2.5<-lme(lnPLD~I(lntemp-log(15)), random=~I(lntemp-log(15))|species, data=inverts, method="ML")
> summary(model2)$tTable

                Value  Std.Error  DF   t-value      p-value
(Intercept)  7.088667 0.29554220 143  23.98529 5.647789e-52
lntemp      -1.454417 0.08465556 143 -17.18040 1.371520e-36

> summary(model2.5)$tTable
                        Value  Std.Error  DF   t-value      p-value
(Intercept)          3.150033 0.11072433 143  28.44933 9.559019e-61
I(lntemp - log(15)) -1.454417 0.08465556 143 -17.18041 1.371507e-36

The fixed effect portion of model 2 is where x is lntemp. The fixed effect portion of model 2.5 is the following.

Thus if the estimates are truly the same it must be the case that

Plugging in the coefficient estimates from model 2.5 into these formulas we obtain the following for the intercept and slope.

> #intercept
> fixef(model2.5)[1]-fixef(model2.5)[2]*log(15)
(Intercept)
7.088667

> #slope
> fixef(model2.5)[2]
I(lntemp - log(15))
-1.454417

which we see are exactly the values estimated in model 2 that are shown in the summary table above.

Question 2

I first fit all the models of interest.

Model 3a—feeding type affects only the intercept

The multilevel model we're fitting is the following.

Its composite form is shown below.

The composite form gives us the fixed and random expressions required by the lme function of R. To minimize estimation problems due to excessive correlation between the random effects that we've encountered previously, I center the log temperature variable around log(15).

> model3a<-lme(lnPLD~I(lntemp-log(15))+feeding.type, random=~I(lntemp-log(15))|species, data=inverts, method='ML')

Model 3b—feeding type affects only the slope

The multilevel model we're fitting is the following.

which in composite form is the following.

The composite form gives us the fixed and random expressions required by the lme function of R. Once again I center the log temperature variable around log(15).

> model3b<-lme(lnPLD~I(lntemp-log(15)) + I(lntemp-log(15)):feeding.type, random=~I(lntemp-log(15))|species, data=inverts, method='ML')

Model 3c—feeding type affects both the intercept and slope

The multilevel model we're fitting is the following.

which in composite form is the following.

The composite form gives us the fixed and random expressions required by the lme function of R. As explained in Lecture 44 I can use R's shortcut notation to obtain the two main effects and the interaction term. Once again I center the log temperature variable around log(15).

> model3c<-lme(lnPLD~I(lntemp-log(15))*feeding.type, random=~I(lntemp-log(15))|species, data=inverts, method='ML')

Comparing the models

For completeness I also fit a model in which feeding.type is omitted altogether.

> model3<-lme(lnPLD~I(lntemp-log(15)), random=~I(lntemp-log(15))|species, data=inverts, method='ML')

I compare the four models using AIC.

> sapply(list(model3,model3a,model3b,model3c),AIC)
[1] 210.4372 203.2484 211.8966 204.7071

Model 3a, the model in which feeding type affects only the intercept, ranks best. The only model that comes close is one in which feeding type affects both the slope and intercept. But notice that the decrease in the loglikelihood obtained with this model is not even enough to offset the increase in the penalty term.

Since they are nested we can use likelihood ratio tests to test model3a against each of model3 and model3c.

> anova(model3,model3a)
        Model df      AIC      BIC    logLik   Test  L.Ratio p-value
model3      1  6 210.4372 230.7441 -99.21859
model3a     2  7 203.2484 226.9399 -94.62421 1 vs 2 9.188761  0.0024

> anova(model3a,model3c)
        Model df      AIC      BIC    logLik   Test  L.Ratio p-value
model3a     1  7 203.2484 226.9399 -94.62421
model3c     2  8 204.7071 231.7830 -94.35353 1 vs 2 0.541343  0.4619

From the first test we see that adding feeding type to the intercept equation is a significant improvement over a model without feeding type. On the other hand, the second test tells us that there is no evidence that the magnitude of the relationship between larval development time and temperature differs for larva of different feeding types. Therefore feeding type does not modify the slope.

Question 3

The two-stage and composite versions of model 3a were shown in Question 2 and are repeated below.

Part 1

Two stage model

Part 2

Composite model

Part 3

Population-averaged (marginal) model

Subject-specific (conditional) model

Part 4.  R has set up the contrasts so that feeding.type = 0 for lecithotrophic larvae and is equal to 1 for planktotrophic larva.

> contrasts(inverts$feeding.type)
  P
L 0
P 1

Marginal models

Conditional models

Question 4

To obtain a pseudo-R2 we compare the variance component of the random slopes and intercepts model (model3) with its corresponding value in model 3a.

> VarCorr(model3)
species = pdLogChol(I(lntemp - log(15)))
                    Variance   StdDev    Corr
(Intercept)         0.85672246 0.9255930 (Intr)
I(lntemp - log(15)) 0.28109623 0.5301851 -0.49
Residual            0.02310587 0.1520062

> VarCorr(model3a)
species = pdLogChol(I(lntemp - log(15)))
                    Variance   StdDev    Corr
(Intercept)         0.75255594 0.8674998 (Intr)
I(lntemp - log(15)) 0.28135382 0.5304280 -0.479
Residual            0.02303037 0.1517576

Observe that except for , labeled (Intercept) in the output, the remaining components have changed hardly at all. This is good because it means the pseudo-R2 can be interpreted more like a real R2. The formula is

> (as.numeric(VarCorr(model3)[1,1]) - as.numeric(VarCorr(model3a)[1,1])) / as.numeric(VarCorr(model3)[1,1])
[1] 0.1215872

So roughly 12% of the variability in intercepts between species is explained by their feeding type.

Question 5

From the marginal and conditional equations obtained in Question 3, we see that the two feeding types differ only in their intercept. From a printout of the fixed effects we observe the estimate for β2 is positive.

> fixef(model3a)
(Intercept) I(lntemp - log(15)) feeding.typeP
  2.6374682          -1.4489413     0.6773972

Fig. 1   Population-average model for each feeding type

Thus at any fixed temperature planktotrophic larvae have a larval duration time that is longer than that of lecithotrophic larvae. A plot of the population-averaged models for each feeding type is shown below superimposed on the raw data. I use the curve function to simplify the plotting. With the curve function it is not necessary to specify a range for the x-variable; it is determined automatically from the plot function when you use the add=TRUE option of curve.

> plot(inverts$lntemp, inverts$lnPLD, pch= (as.numeric(inverts$feeding.type) -1)*18 + 2, xlab='log(temperature)', ylab='log(PLD)')
> curve(fixef(model3a)[1] + fixef(model3a)[2] * (x-log(15)), col=2, add=TRUE)
> curve(fixef(model3a)[1] + fixef(model3a)[3] + fixef(model3a)[2] * (x-log(15)), col=4, add=TRUE)
> legend(1,.8, c('lecithotrophic', 'planktotrophic'), col=c(2,4), lty=c(1,1), cex=c(.9,.9), bty='n')

Fig. 1 is a bit dishonest in that it suggests we are fitting lines to the raw data. That's not correct. Instead the displayed lines are the population averaged lines for two families of subject-specific lines. A more honest picture is one in which the subject-specific lines are also displayed. The next bit of code carries this out.

> plot(inverts$lntemp, inverts$lnPLD, pch=(as.numeric(inverts$feeding.type)-1)*18+2, xlab='log(temperature)', ylab='log(PLD)')
#get list of species names for each feeding type
> unique(inverts[inverts$feeding.type=='L', "species"])->lecitho
> unique(inverts[inverts$feeding.type=='P', "species"])->plankto
> random.effs<-ranef(model3a)
#separate random effects by feeding type
> lecitho.ran<-random.effs[rownames(random.effs)%in%lecitho,]
> plankto.ran<-random.effs[rownames(random.effs)%in%plankto,]
#plot subject specific curves--planktotrophic
> apply(cbind(plankto.ran[,1]+fixef(model3a)[1]+fixef(model3a)[3],
plankto.ran[,2]+fixef(model3a)[2]),1,
function(y) curve(y[1]+y[2]*(x-log(15)), col='skyblue', add=TRUE, lty=2))
#plot subject specific curves--lecithotrophic
> apply(cbind(lecitho.ran[,1]+fixef(model3a)[1],
lecitho.ran[,2]+fixef(model3a)[2]),1,
function(y) curve(y[1]+y[2]*(x-log(15)), col='pink', add=TRUE))
#plot population-averaged curves
> curve(fixef(model3a)[1] + fixef(model3a)[2] * (x-log(15)), col=2, add=TRUE, lwd=2)
> curve(fixef(model3a)[1] + fixef(model3a)[3] + fixef(model3a)[2] * (x-log(15)), col=4, lty=2, add=TRUE, lwd=2)
> legend(1,.8, c('lecithotrophic', 'planktotrophic'), col=c(2,4), lty=c(1,2), cex=c(.9,.9), bty='n')


Fig. 2  Conditional and marginal models for feeding types

Observe that there is one clearly deviant lecithotrophic species (top right corner) and perhaps three or four truly deviant planktotrophic species (these have subject-specific lines that are far below the lecithotrophic population-averaged line)

Course Home Page


Jack Weiss
Phone: (919) 962-5930
E-Mail: jack_weiss@unc.edu
Address: Curriculum in Ecology, Box 3275, University of North Carolina, Chapel Hill, 27516
Copyright © 2006
Last Revised--April 22, 2006
URL: http://www.unc.edu/courses/2006spring/ecol/145/001/docs/solutions/assign10.htm