Lecture 39 (lab)—Friday, April 20, 2007

What was covered?

R functions and commands demonstrated

R function options

R packages used

Preliminaries

Fig. 1  R file menu choices

A WinBUGS example—the complete pooling model

> inverts<- read.table('http://www.unc.edu/courses/2006spring/ecol/145/001/data/lab11/74species.csv', sep=',', header=TRUE)

> x<-log(inverts$temp)-log(15)
> y<-log(inverts$PLD)

> n<-length(y)

#complete pooling
model{
for(i in 1:n) {
y[i]~dnorm(y.hat[i],tau.y)
y.hat[i]<-a+b*x[i]
}
a~dnorm(0,.0001)
b~dnorm(0,.0001)
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
}

  1. The model begins with the key word model. The actual model code is contained within the pair of curly braces { }.
  2. A for loop is used to describe the probability model for the data, the likelihood so to speak. Notice that index runs from 1 to n, the variable recording the number of observations, so this loop specifies a probability model for each observation.
  3. The dnorm function in WinBUGS differs from that of R. It has only two arguments--the first records the mean of the distribution and the second specifies what's called the precision.
    • Notice that the value of the mean argument, y.hat[i], includes an index i. This causes each observation to have a potentially different mean.
    • The value of the precision argument is given the name tau.y
  4. The second line of the loop further defines the mean, y.hat. The use of the assignment arrow means that y.hat is a derived quantity. The formula given is just the regression model: a + bx where a is the intercept and b is the slope.
  5. The definition of y.hat here appears to be redundant. It would seem that we could have made the formula a + b*x[i] the first argument of the dnorm function and skipped the definition of y.hat entirely. You should never do this! WinBUGS is easily confused when complicated expressions are used as arguments in probability functions. Regression formulas and the like should always be specified outside of probability functions just as we're doing here.
  6. The remainder of the code specifies priors for the model parameters. The next two lines place noninformative priors on the regression parameters a and b. Since a priori these could be anything, we use normal priors. The second argument is the precision and we choose this to be low. The precision is the reciprocal of the variance so we're choosing a normal distribution with a variance of 10,000 as the prior. Since the variables are on a log scale it is clear that this is an extremely wide distribution implying that we have essentially no knowledge about the true values of a and b.
  7. The next line defines the quantity tau.y, the precision of the normal distribution for the likelihood. The usual approach is to place a prior on the standard deviation rather than the precision, so the line tau.y<-pow(sigma.y,–2) serves to connect the precision to the standard deviation thus making the precision a derived quantity rather than a parameter. Notice the use of the pow (for power) function in lieu of writing this out with the exponentiation operator. This is the preferred way of writing powers in WinBUGS.
  8. Finally the last line sets a uniform prior on the standard deviation, sigma.y, uniform on the interval (0, 100). For log-transformed variables an interval such as this is more than wide enough to cover any realistic value for sigma.y.

> invert.data<-list("n","y","x")
> invert.inits<-function() {list(a=rnorm(1),b=rnorm(1),sigma.y=runif(1))}
> invert.parameters<-c("a","b","sigma.y")

> library(arm)
> invert.3<-bugs(invert.data, invert.inits, invert.parameters, "cpmodel.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=10, debug=T)

Fig. 2  WinBUGS report on the model

Fig. 3  Additional information in the WinBUGS log window

> invert.3<-bugs(invert.data, invert.inits, invert.parameters, "cpmodel.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=500, debug=T)

Fig. 4  Mixing of the individual chains for the standard deviation of the response

> invert.3
Inference for Bugs model at "cpmodel.txt", fit using winbugs,
3 chains, each with 500 iterations (first 250 discarded)
n.sims = 750 iterations saved
          mean  sd  2.5%   25%   50%   75%  97.5% Rhat n.eff
a          3.0 0.1   2.8   2.9   3.0   3.0   3.1     1   750
b         -0.7 0.1  -0.9  -0.8  -0.7  -0.6  -0.5     1   440
sigma.y    0.9 0.0   0.8   0.8   0.9   0.9   0.9     1   750
deviance 550.0 2.4 547.3 548.3 549.5 551.1 556.4     1   740

For each parameter, n.eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor (at convergence, Rhat=1).

pD = 2.9 and DIC = 553.0 (using the rule, pD = Dbar-Dhat)
DIC is an estimate of expected predictive error (lower deviance is better).

> names(invert.3)
 [1] "n.chains"      "n.iter"      "n.burnin"   "n.thin"
 [5] "n.keep"        "n.sims"      "sims.array" "sims.list"
 [9] "sims.matrix"   "summary"     "mean"       "sd"
[13] "median"        "root.short"  "long.short" "dimension.short"
[17] "indexes.short" "last.values" "pD"         "DIC"
[21] "DICbyR"        "model.file"  "is.DIC"     "program"

> plot(c(151,250), range(invert.3$sims.array[151:250, , 'a']), type='n', ylab='a', xlab='iteration')
> for(i in 1:3) {
lines(151:250, invert.3$sims.array[151:250, i, 'a'], col=i)}

> sapply(invert.3$sims.list,function(x) x[1:5])
       a     b sigma.y deviance
[1,] 2.8 -0.50    0.83      555
[2,] 2.8 -0.60    0.94      556
[3,] 2.9 -0.48    0.85      551
[4,] 3.0 -0.78    0.83      548
[5,] 2.9 -0.65    0.86      548

> invert.3$sims.list$a[1:5]
[1] 2.8 2.8 2.9 3.0 2.9

> invert.3$sims.matrix[1:10,]
        a     b sigma.y deviance
 [1,] 2.8 -0.50    0.83      555
 [2,] 2.8 -0.60    0.94      556
 [3,] 2.9 -0.48    0.85      551
 [4,] 3.0 -0.78    0.83      548
 [5,] 2.9 -0.65    0.86      548
 [6,] 2.9 -0.82    0.82      551
 [7,] 2.9 -0.65    0.85      548
 [8,] 2.9 -0.74    0.81      549
 [9,] 3.0 -0.53    0.83      550
[10,] 3.1 -0.66    0.90      552

> invert.3$sims.matrix[1:5,'a']
[1] 2.8 2.8 2.9 3.0 2.9

> apply(invert.3$sims.matrix,2,mean)
     a       b sigma.y deviance
2.9560 –0.7049  0.8566 550.0477

> lm(y~x)->out
> summary(out)

Call:
lm(formula = y ~ x)

Residuals:
   Min     1Q Median    3Q   Max
-3.057 -0.505  0.172 0.569 2.133

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept)   2.9559     0.0588   50.25 < 2e-16 ***
x            -0.7045     0.1112   -6.33 1.4e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.852 on 216 degrees of freedom
Multiple R-Squared: 0.157, Adjusted R-squared: 0.153
F-statistic: 40.1 on 1 and 216 DF, p-value: 1.37e-09

Fitting a separate intercept model using WinBUGS

> J<-length(unique(inverts$species))
> species<-as.numeric(inverts$species)

Common pooling model Separate intercepts model
model{
for(i in 1:n) {
y[i]~dnorm(y.hat[i],tau.y)
y.hat[i]<-
a+b*x[i]
}
a~dnorm(0,.0001)
b~dnorm(0,.0001)
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
}

model{
  for(i in 1:n) {
    y[i]~dnorm(y.hat[i],tau.y)
    y.hat[i]<-a[species[i]]+b*x[i]
}
b~dnorm(0,.0001)
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
   a[j]~dnorm(0,.0001)
}

}

> invert.data<-list("n","J","y","species","x")
> invert.inits<-function() {
  list(a=rnorm(J),b=rnorm(1),sigma.y=runif(1))}
> invert.parameters<-c("a", "b", "sigma.y")
> invert.3b<-bugs(invert.data, invert.inits, invert.parameters, "sepints.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=10, debug=T)

Random intercepts model

Separate intercepts model Random intercepts model
model{
  for(i in 1:n) {
    y[i]~dnorm(y.hat[i],tau.y)
    y.hat[i]<-a[species[i]]+b*x[i]
}
b~dnorm(0,.0001)
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
   a[j]~dnorm(0,.0001)
}
}
model{
  for(i in 1:n) {
    y[i]~dnorm(y.hat[i],tau.y)
    y.hat[i]<-a[species[i]]+b*x[i]
}
b~dnorm(0,.0001)
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
   a[j]~dnorm(a.hat[j],tau.a)
   a.hat[j]<-mu.a
}
mu.a~dnorm(0,.0001)

tau.a<-pow(sigma.a,-2)
sigma.a~dunif(0,100)

}

Actually in the model above we suppress the existence of the terms and work directly with the distribution of the .

> invert.data<-list("n","J","y","species","x")
> invert.inits<-function() {list(a=rnorm(J), b=rnorm(1), sigma.y=runif(1), mu.a=rnorm(1), sigma.a=runif(1))}
> invert.parameters<-c("a", "b", "sigma.y", "mu.a", "sigma.a")
> invert.3b<-bugs(invert.data, invert.inits, invert.parameters, "randomints.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=10, debug=T)

Random slopes and intercepts model (slopes uncorrelated with intercepts)

random intercepts model random slopes and intercepts (uncorrelated)
model{
  for(i in 1:n) {
   y[i]~dnorm(y.hat[i],tau.y)    y.hat[i]<-a[species[i]]+
b*x[i]
}
b~dnorm(0,.0001)
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
   a[j]~dnorm(a.hat[j],tau.a)
   a.hat[j]<-mu.a
}
mu.a~dnorm(0,.0001)
tau.a<-pow(sigma.a,-2)
sigma.a~dunif(0,100)
}
model{
for(i in 1:n) {
  y[i]~dnorm(y.hat[i],tau.y)
  y.hat[i]<-a[species[i]]+
b[species[i]]*x[i]
}
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
  a[j]~dnorm(a.hat[j],tau.a)

  b[j]~dnorm(b.hat[j],tau.b)
  a.hat[j]<-mu.a
  b.hat[j]<-mu.b
}
mu.a~dnorm(0,.0001)
mu.b~dnorm(0,.0001)
tau.a<-pow(sigma.a,-2)
sigma.a~dunif(0,100)

tau.b<-pow(sigma.b,-2)
sigma.b~dunif(0,100)

}

Random slopes and intercepts model

random slopes and intercepts (uncorrelated) random slopes and intercepts
model{
for(i in 1:n) {
  y[i]~dnorm(y.hat[i],tau.y)
  y.hat[i]<-a[species[i]]+ b[species[i]]*x[i]
}
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
  a[j]~dnorm(a.hat[j],tau.a)
  b[j]~dnorm(b.hat[j],tau.b)
  a.hat[j]<-mu.a
  b.hat[j]<-mu.b

}
mu.a~dnorm(0,.0001)
mu.b~dnorm(0,.0001)
tau.a<-pow(sigma.a,-2)
sigma.a~dunif(0,100)
tau.b<-pow(sigma.b,-2)
sigma.b~dunif(0,100)

}

model{
for(i in 1:n) {
y[i]~dnorm(y.hat[i],tau.y)
y.hat[i]<-a[species[i]]+ b[species[i]]*x[i]
}
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){

a[j]<-B[j,1]
b[j]<-B[j,2]
B[j,1:2]~dmnorm(B.hat[j,], Tau.B[,])
B.hat[j,1]<-mu.a
B.hat[j,2]<-mu.b

}
mu.a~dnorm(0,.0001)
mu.b~dnorm(0,.0001)

Tau.B[1:2,1:2]<-inverse(Sigma.B[,])
Sigma.B[1,1]<-pow(sigma.a,2)
sigma.a~dunif(0,100)
Sigma.B[2,2]<-pow(sigma.b,2)
sigma.b~dunif(0,100)
#correlation
Sigma.B[1,2]<-rho*sigma.a*sigma.b
Sigma.B[2,1]<-Sigma.B[1,2]
rho~dunif(-1,1)

}

> invert.data<-list("n","J","y","species","x")
> invert.inits<-function() {
list(B=array(rnorm(2*J),c(J,2)), sigma.y=runif(1), mu.a=rnorm(1), sigma.a=runif(1), mu.b=rnorm(1), sigma.b=runif(1), rho=runif(1))}
> invert.parameters<-c("a", "b", "sigma.y", "mu.a", "sigma.a", "mu.b", "sigma.b", "rho")
> invert.3c<-bugs(invert.data, invert.inits, invert.parameters, "randomslopeint.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=10, debug=T)

Random slopes and intercepts model with a level-2 predictor

f<-as.numeric(tapply(inverts$feeding.type, inverts$species, function(x) x[1])-1)

random slopes and intercepts random slopes and intercepts (with a level-2 predictor)
model{
for(i in 1:n) {
y[i]~dnorm(y.hat[i],tau.y)
y.hat[i]<-a[species[i]]+ b[species[i]]*x[i]
}
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
a[j]<-B[j,1]
b[j]<-B[j,2]
B[j,1:2]~dmnorm(B.hat[j,], Tau.B[,])
B.hat[j,1]<-mu.a
B.hat[j,2]<-mu.b
}
mu.a~dnorm(0,.0001)
mu.b~dnorm(0,.0001)

Tau.B[1:2,1:2]<-inverse(Sigma.B[,])
Sigma.B[1,1]<-pow(sigma.a,2)
sigma.a~dunif(0,100)
Sigma.B[2,2]<-pow(sigma.b,2)
sigma.b~dunif(0,100)
#correlation
Sigma.B[1,2]<-rho*sigma.a*sigma.b
Sigma.B[2,1]<-Sigma.B[1,2]
rho~dunif(-1,1)
}

model{
for(i in 1:n) {
y[i]~dnorm(y.hat[i],tau.y)
y.hat[i]<-a[species[i]]+ b[species[i]]*x[i]
}
tau.y<-pow(sigma.y,-2)
sigma.y~dunif(0,100)
for(j in 1:J){
a[j]<-B[j,1]
b[j]<-B[j,2]
B[j,1:2]~dmnorm(B.hat[j,], Tau.B[,])
B.hat[j,1]<-mu.a + a.1*f[j]
B.hat[j,2]<-mu.b
}
mu.a~dnorm(0,.0001)
mu.b~dnorm(0,.0001)
a.1~dnorm(0,.0001)
Tau.B[1:2,1:2]<-inverse(Sigma.B[,])
Sigma.B[1,1]<-pow(sigma.a,2)
sigma.a~dunif(0,100)
Sigma.B[2,2]<-pow(sigma.b,2)
sigma.b~dunif(0,100)
#correlation
Sigma.B[1,2]<-rho*sigma.a*sigma.b
Sigma.B[2,1]<-Sigma.B[1,2]
rho~dunif(-1,1)
}

> invert.data<-list("n","J","y","species","x", "f")
> invert.inits<-function() {list(B=array(rnorm(2*J), c(J,2)), sigma.y=runif(1), mu.a=rnorm(1), a.1=rnorm(1), sigma.a=runif(1), mu.b=rnorm(1), sigma.b=runif(1), rho=runif(1))}
> invert.parameters<-c("a", "b", "sigma.y", "mu.a", "sigma.a", "mu.b", "sigma.b", "rho", "a.1")
> invert.3d<-bugs(invert.data, invert.inits, invert.parameters, "finalmodel.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=10, debug=T)
# repeat with 500 iterations per chain
> invert.3d<-bugs(invert.data, invert.inits, invert.parameters, "finalmodel.txt", n.chains=3, bugs.directory = "D:/Program Files/WinBUGS14/", n.iter=500, debug=T)

Comparing Bayesian and Frequentist Results

> library(nlme)
> lme(log(PLD)~I(log(temp)-log(15))+feeding.type, random=~I(log(temp)-log(15))|species, data=inverts, method='ML')->out

#frequentist values
> fixef(out)
(Intercept) I(log(temp) - log(15)) feeding.typeP
2.6375 -1.4489 0.6774

#Bayesian means
> invert.3d$mean[c("mu.a","mu.b","a.1")]
$mu.a
[1] 2.631

$mu.b
[1] -1.443

$a.1
[1] 0.6851

#frequentist variance components
> VarCorr(out)
species = pdLogChol(I(log(temp) - log(15)))
                       Variance StdDev   Corr
(Intercept)             0.75256 0.8675 (Intr)
I(log(temp) - log(15))  0.28135 0.5304 -0.479
Residual                0.02303 0.1518

#Bayesian variance components (posterior means)
> invert.3d$mean[c("sigma.a","sigma.b","sigma.y","rho")]
$sigma.a
[1] 0.896

$sigma.b
[1] 0.5422

$sigma.y
[1] 0.1556

$rho
[1] -0.4440

> invert.3e$mean[c("b")]
$b
 [1] -1.5131 -1.7768 -1.3042 -1.4879 -1.7267 -2.2108 -1.5651 -1.3281 -1.7297 -1.5817
[11] -1.2255 -1.6738 -1.6516 -1.2867 -1.2916 -1.2602 -1.1192 -0.6581 -1.7243 -1.3585
[21] -1.4672 -1.4815 -1.5592 -1.3679 -1.4006 -1.2816 -1.7486 -0.7824 -1.5324 -1.4147
[31] -1.7609 -1.3958 -1.5897 -1.8182 -1.1379 -1.1969 -0.6043 -3.1154 -1.1434 -1.6132
[41] -1.6632 -1.4185 -1.6377 -1.4604 -1.1114 -1.7542 -1.5703 -1.2947 -0.9866 -1.8999
[51] -1.1519 -1.7177 -1.3753 -1.6963 -2.0192 -1.5475 -0.9388 -1.5048 -0.9855 -1.6285
[61] -1.9230 -1.2068 -1.7785 -0.9031 -2.1305 -0.7184 -1.0747 -0.8558 -0.9051 -1.4644
[71] -1.5930 -0.9411 -1.4179 -1.4266

> coef(out)[,2]
 [1] -1.5291 -1.7789 -1.3108 -1.4901 -1.7416 -2.2453 -1.5802 -1.3316 -1.7338 -1.5938
[11] -1.2302 -1.6909 -1.6725 -1.2929 -1.2994 -1.2665 -1.1185 -0.6435 -1.7471 -1.3797
[21] -1.4773 -1.4841 -1.5791 -1.3684 -1.4329 -1.2816 -1.7574 -0.7792 -1.5533 -1.4260
[31] -1.7825 -1.4052 -1.5919 -1.8386 -1.1407 -1.1863 -0.6000 -3.1658 -1.1396 -1.6258
[41] -1.6602 -1.4189 -1.6325 -1.4790 -1.1158 -1.7506 -1.5964 -1.2980 -0.9817 -1.9240
[51] -1.1554 -1.7335 -1.3813 -1.7192 -2.0553 -1.5656 -0.9350 -1.5142 -0.9822 -1.6457
[61] -1.9378 -1.2060 -1.7992 -0.8974 -2.1454 -0.7138 -1.0753 -0.8491 -0.9011 -1.4596
[71] -1.6020 -0.9341 -1.4202 -1.4439

#find maximum difference between the two sets of estimates
> max(abs(invert.3e$mean[c("b")]$b-coef(out)[,2]))

[1] 0.05037

> coef(out)[,1]
 [1] 2.5468 3.6734 2.2109 1.7286 1.8461 1.8098 2.8159 2.6335 3.2397 2.8106
[11] 2.8251 3.0223 3.0639 2.6332 2.8451 2.4693 1.7579 0.5197 2.9303 2.5899
[21] 3.2657 2.8229 1.0205 3.4077 3.5302 1.8626 3.4411 2.3351 1.7219 1.9237
[31] 1.6488 2.5302 3.1612 3.1614 2.9885 1.4778 1.6214 5.4582 3.0875 3.2099
[41] 3.2572 2.7917 3.8079 2.3639 3.1863 2.3364 2.6165 3.2145 1.7107 2.9872
[51] 2.9670 3.1763 2.6377 3.3110 2.2412 3.1127 2.3110 3.2095 2.8632 3.5682
[61] 3.6175 2.4185 3.0040 2.8714 3.4201 1.6942 3.1227 1.8006 -0.6312 3.2298
[71] 2.9375 0.9286 2.8944 2.5456

> invert.3d$mean["a"]$a-invert.3d$mean["a.1"]$a.1*f
 [1] 2.5405 3.6719 2.1972 1.7191 1.8367 1.7963 2.8089 2.6323 3.2395 2.8068
[11] 2.8189 3.0062 3.0551 2.6167 2.8414 2.4627 1.7597 0.4992 2.9171 2.5809
[21] 3.2629 2.8171 1.0186 3.4024 3.5133 1.8528 3.4330 2.3224 1.7234 1.9236
[31] 1.6482 2.5308 3.1497 3.1527 2.9809 1.4703 1.6128 5.4356 3.0905 3.2001
[41] 3.2440 2.7947 3.7891 2.3726 3.1822 2.3284 2.5920 3.2009 1.7056 2.9818
[51] 2.9585 3.1670 2.6191 3.2823 2.2340 3.1021 2.3154 3.1952 2.8685 3.5501
[61] 3.6060 2.4154 2.9786 2.8617 3.4083 1.6868 3.1095 1.8008 -0.6431 3.2277
[71] 2.9176 0.9252 2.8730 2.5397

> coef(out)[,1]->freq.int
> invert.3d$mean["a"]$a-invert.3d$mean["a.1"]$a.1*f->Bayes.int
#find maximum difference between the two sets of estimates
> max(abs(Bayes.int-freq.int))

[1] 0.02873

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 © 2007
Last Revised--August 7, 2008
URL: http://www.unc.edu/courses/2007spring/enst/562/001/docs/lectures/lecture39.htm