A Tutorial on Gibbs Sampling Software for Ecological Modelers

advertisement
A Tutorial on Gibbs Sampling
Software for Ecological Modelers
N. T. Hobbs
NR 575, Systems Ecology, Spring 2010
Aim
5
Introducing MCMC Samplers
5
Introducing BUGS
6
Installing Gibbs Sampling Software
7
For Windows users: Installing WinBugs
8
For Mac users: Installing JAGS
8
Implementing the logistic example to estimate r, K, and τ
8
Examining the Code
8
Technical notes
8
The model statement ................................................................................8
for loops ....................................................................................................9
Precision as an argument to the normal density function (and the
lognormal) ...............................................................................................10
The <- operator .......................................................................................11
Coding and saving the BUG’s script
11
Saving the program file: JAGS ..............................................................11
Saving the program file: WinBUGS .......................................................11
The interface between BUGS and R
11
Interface between R and BUGS: JAGS
13
Interface between R and BUGS: WinBUGS
14
Getting simple output: JAGS and WinBUGS
16
Complete R script for JAGS (to this point)
18
Complete R script for WinBUGS
19
Exercise I: courtesy of McCarthy, M. A. 2007. Bayesian Methods for
Ecology. Cambridge University Press, Cambridge, U. K.
20
Using the summary() list
20
2
Estimating Quantities of Interest Derived from Model Parameters
23
Exercise 2
27
Comparing Parameter Values Between Populations
27
Using the MCMC List
29
Exercise 3
34
Checking for Convergence
35
Checking Syntax
37
WinBUGS
37
JAGS
38
Some common error messages
39
JAGS
39
WinBUGS
42
made use of undefined node—
42
multiple definitions of node [x]
43
incompatible copy
43
Splus data 100
43
Cannot Bracket Slice for Nod
43
Shape parameter of gamma r too small.
43
Differences Between JAGS and WinBUGS
44
Bibliography
45
Appendix 1: Reference material for JAGS
46
Distributions
46
Functions and operators
47
3
Reference Material for WinBUGS
49
Distributions
49
Functions and Operators
53
Appendix 2: Answers to Exercises
55
Exercise 1
55
Exercise 2
56
Exercise 3
59
4
Aim
The purpose of this Primer is to teach the programming skills needed to estimate the
posterior distributions of parameters and derived quantities of interest in ecological
models using software implementing Mote Carlo Markov chain methods. Along the
way, I will reinforce some of the ideas and principals that we have learned in lecture. My
approach to teaching this topic is quite different than you are likely to find in other
sources because I will integrate the programming you need to learn in R with the
programming needed in the MCMC software.
The Primer is organized primarily as a tutorial, but it contains a modicum of reference
material as well. Although I think this Primer offers a great start for learning JAGS and
WinBUGS, it is not intended to be comprehensive. So, some Sunday afternoon when
you find yourself with time on your hands, you should read through the WinBUGS and
JAGS manual as well.
Introducing MCMC Samplers
WinBugs, OpenBUGS, and JAGS are three systems of software that implement Monte
Carlo Markov Chain sampling using the BUGS language.1 BUGS stands for Bayesian
Analysis Using Gibbs Sampling, so you can get an idea what this language does from
its name. Imagine that you took the MCMC code you wrote for a Gibbs sampler and
tried to turn it into a general R function for building chains of parameter estimates.
Actually, you know enough now to construct a very general tool that would do this.
However, you are probably delighted to know that all of this work has been done for you
in the BUGS language.
The BUGS language is currently implemented in three flavors of software: OpenBUGS,
WinBUGS, and JAGS. OpenBUGS and WinBUGS run on Windows operating systems,
while JAGS was specifically constructed to run on Mac OS and Unix.2 Although all
three use essentially the same syntax3, OpenBUGS and WinBUGS run in an elaborate
graphical user interface, while JAGS only runs from the command line of a Unix shell or
from R. However, all three can be easily called from R, and this is the approach I will
teach. In the past, I have taught students how to use the graphical version, but they
quickly learned, as I have, that the GUI involves far to much tedious pointing and
clicking and doesn't’ provide the flexibility that is needed for high level work. So, I have
decided to abandon any detailed instruction on the GUI, but you will see it in print
elsewhere. If you would like to look at past tutorials that include the GUI, I would be
happy to provide them. That said, there is a bit we need to know about the interface,
mainly as a syntax checker, which I will cover later. It is important for you to understand
that when I talk about a BUGS program, it can be run in either JAGS or WinBUGS.
1
There is also software called GeoBUBS that is specifically developed for spatial models, but I know
virtually nothing about it. However, if you are interested in landscape ecology otherwise have an interest
in spatial modeling, I urge you to look into it after completing this tutorial.
2 JAGS will also run under Windows and you may like it better than WinBUGS.
3 JAGS has a few notable departures from the others. These will be discussed later. If I forget to cover
these differences, Mac Users, please remind me!
5
When I am talking about distinct features of these programs and their implementation in
R, I will be sure to point that out.
Introducing BUGS
This tutorial will use a simple example of regression as a starting point for teaching the
BUGS language and associated R commands. Although the problem starts simply, it
builds to include some fairly sophisticated analysis. The model that we will use is the a
linear relationship between the per-capita rate of population growth and the density of a
population, which, as you know is the starting point for deriving the logistic equation.
For the ecosystem scientists among you, this problem is easily recast as the mass
specific rate of accumulation of nitrogen in the soil; see for example, Knops and Tillman
(2000)4. Happily, both the population example and the ecosystem example can use the
symbol N to represent the state variable of interest. Consider the model,
dN 1
r
r N
dt N
K
which, of course gives the equation for a line with intercept r and slope =
r
(Figure 1)
K
Figure 1. Simulated data for regression problem.
Note that these quantities enjoy a sturdy biological interpretation; r is the intrinsic rate of
r
increase,
is the strength of the feedback from population size to population growth
K
rate, and K is the carrying capacity, that is, the population size (o.k., o.k., the gm N per
dN 1
 0 . Presume we have some data consisting of observations of
gm soil) at which
dt N
4
Remember, ecological modelers have a rather small bag of tricks.
6
per capita rate of growth of N paired with observations of N. The vector y contains
dN i 1
, xi  N i .
values for the rate and the vector x contains aligned data on N5, i.e. yi 
dt N i
A simple Bayesian model might reasonably specify the posterior distributions of r, K,
and τ as
i  r 
rxi
K
n
P r, K,  y, x    P yi | i ,  P r P K P  
in
n
P r, K,  y, x    Normal yi | i ,  Normal 0,.0011Gamma .001,.001Gamma .001,.001
in
where the priors are uninformative. Now, I have full, abiding confidence that with a
couple of hours worth of work, perhaps less, you could knock out a Gibbs sampler to
estimate r, K, and τ. However, I am all for doing things nimbly in 15 minutes that might
otherwise take a sweaty hour of hard labor, so, consider the code below.
##Logistic example for Primer
model{
#priors
K~dgamma(.001,.001)
r~dnorm(0.,.001)
tau~ dgamma(.001,.001)
sigma<-1/sqrt(tau)
#likelihood
for(i in 1:n){
mu[i] <- r - r/K * x[i]
y[i] ~ dnorm(mu[i],tau)
}
} #end of model
This code above implements the Bayesian model, providing full MCMC chains for each
parameter, chains that form the basis for estimating their posterior distributions and
associated statistics, i.e., means, medians, standard deviations, and credible intervals.
As we will soon learn, it becomes child’s play to derive chains other quantities of interest
and their posterior distributions, for example, K/2 (What is K/2?), N as a function of time
or dN/dt as a function of N. It is easy to construct comparisons between of the growth
parameters of two populations or among ten of them. If this seems as if it might be
useful to you, you should continue reading.
Installing Gibbs Sampling Software
5
This could also be easily done with a matrix data structure, but I will leave that for a later topic.
7
For Windows users: Installing WinBugs
WinBUGS is free software maintained by the Medical Research Council Biostatistics
Unit at the University of Cambridge. It can be downloaded from their web site,
http://www.mrc-bsu.cam.ac.uk/bugs/winbugs/contents.shtml.
For Mac users: Installing JAGS
For those of you using MAC OS, JAGS (for Just Another Gibbs Sampler) is available at
http://www-fis.iarc.fr/~martyn/software/jags/. You should click on JAGS-1.0.4u.dmg to get
the disk image. There is a readme file with instructions. It will be very helpful to be able
to run WinBUGS on your Mac as well, if for no other reason, to use its syntax checker
for BUGS models. There are two ways to do that. You can, of course, create a
Windows virtual machine using Parallels or VM Fusion and simply download WinBUGS
onto it. There is also free software called Wine (for Windows Emulation) that allows
Winbugs to run on the Mac without Parallels etc--go to
idiom.ucsd.edu/~rlevy/winbugsonmacosx.pdf and follow the instructions. It may take a little
work to get it set up. Let me know if you need help.
Implementing the logistic example to estimate r, K,
and τ
Examining the Code
Study the relationship between the numerator of Bayes law and the code. The similarity
should be pretty obvious, but there are a few things to point out. Priors and likelihoods
are specified using the ~ notation that we have seen in class. In so doing, remember
that the following notation is equivalent:
yi ~ Normal i ,  
is the same as
Normal yi | i ,  
So, it is easy to see the correspondence between the fully conditional distribution (i.e.,
the numerator of Bayes law) and the code. I chose uninformative Gamma priors for K
and τ because they must be positive. I chose an uninformative normal for r because it
can be positive or negative.
Technical notes
The model statement
Your entire model must be enclosed in
model{
.
.
.
.
8
} #end of model
For JAGS users, it is critical that you put a hard return (a blank line) after the } #end of
model statement. If you fail to do so, you will get the message #syntax error,
unexpected NAME, expecting $end.
for loops
n
Notice that the for loop replaces the

in the likelihood. Recall that when we
i 1
specify a total likelihood, we ask, what is the probability that we would obtain this data
point conditional on the value of the parameter(s) of interest? The total likelihood is the
product of the individual likelihoods. Recall in the Excel example that we labored
through that you had an entire column of likelihoods adjacent to a column of
deterministic predictions of our model. If you were to duplicate these “columns” in
WinBUGS, or JAGS you would write
mu[1] <- r - r/K * x[1]
y[1] ~ dnorm(mu[1],tau)
mu[2] <- r - r/K * x[2]
y[2] ~ dnorm(mu[3],tau)
mu[3] <y[3]
.
.
.
mu[n] <y[n]
r - r/K * x[3]
~ dnorm(mu[3],tau)
r - r/K * x[n]
~ dnorm(mu[n],tau)
Well, presuming that you have something better to do with your time that to write out
statements like this for every observation in your data set, you may substitute
for(i in 1:n){
mu[i] <- r - r/K * x[i]
y[i] ~ dnorm(mu[i],tau)
}
for the line by line specification of the likelihood. Thus, the for loop specifies the
elements in the product of the likelihoods.
Note however, that the for structure in the BUGS language is subtly different from what
you have learned in R. For example the following would be legal in R but not in the
BUGS language:
9
#WRONG!!!
for(i in 1:n){
mu <- r - r/K * x[i]
y[i] ~ dnorm(m,tau)
}
If you write something like this in BUGS you will get a message that complains about
multiple definitions of node mu. If you think about what the for loop is doing, you can
see the reason for this complaint; the incorrect syntax translates to
mu <- r - r/K * x[1]
y[1] ~ dnorm(mu,tau)
mu <- r - r/K * x[2]
y[2] ~ dnorm(mu,tau)
mu <- r - r/K * x[3]
y[3] ~ dnorm(mu,tau)
.
.
.
mu <- r - r/K * x[n]
y[n] ~ dnorm(mu,tau),
which is nonsense if you are specifying a likelihood. One more thing about the for
construct. If you have two product symbols in the fully conditional distribution with
K  J

different indexes, i.e. something like    expresssion k expression j  , then you would

k 1  j 1
implement this in BUGS as nested for loops, e.g.,
for(k in 1:K){
expression[k]
for(j in 1:J){
expression[j]
} #end of j loop
} #end of k loop
Precision as an argument to the normal density function (and the lognormal)
Note that in the code, the second argument to the Normal density function is tau, which
1
is the precision, defined as the reciprocal of the variance, 2 . This means that we

10
must calculate sigma from tau if we want a posterior distribution on sigma. Be very
careful about this. For the lognormal, it is the precision on the log scale.
The <- operator
Note that, unlike R, you do not have the option in BUGS to use the = sign in an
assignment statement. You must use <-.
Coding and saving the BUG’s script
Carefully write out all of the code in the Logistic example (above) into program window
in R.
Saving the program file: JAGS
For JAGS users, you may save this code in any directory that you like and may name it anything
you like. I use names like logisticBUGS.R which lets me know that the file contains BUGS
code. Using an R extension allows me to search these files easily with Spotlight.
Alternatively, you can embed the BUGS code in you R script using:
sink("logisticBUG.R") #This is the file name for the bugs code
cat(" model{
K~dgamma(.001,.001)
r~dnorm(0.,.001)
tau~ dgamma(.001,.001)
sigma<-1/sqrt(tau)
#likelihood
for(i in 1:n){
mu[i] <- r - r/K * x[i]
y[i] ~ dnorm(mu[i],tau)
}
} #end of model
",fill=TRUE)
sink()
Saving the program file: WinBUGS
For Windows users, you must put the code in a directory immediately off of the root
directory with a short name (something like C:\BUGS) and name the file with < 8
characters, i.e., something like logis.bugs or logis.R. If you get a screen full of gibberish
and the rather unhelpful message “INCOMPATIBLE COPY” at the top, the pathname to
your file has too many characters. I know, this is cumbersome.
The interface between BUGS and R
11
Consider the following code:
rm(list=ls())
pop.data=(read.csv("Logistic Data"))
names(pop.data)=c("Year","Population Size", "Growth Rate")
inits=list(
list(K=1500, r=.5, tau=.05)
)
n.xy = nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population Size"),
y=as.real(pop.data$"Growth Rate"))
The first two lines bring the growth rate data into R as a data frame. Next we specify
the initial conditions for the MCMC chain. This is exactly the same thing as you did
when you wrote you MCMC code and assigned a guess to the first element in the chain.
There are two important things to notice about this statement. First, this must be
composed as as “list of lists”, as you see above. If you use
#WRONG
inits=
list(K=1500, r=.5, tau=.05)
your code will not run. Second, this statement allows you to set up multiple chains,
which are needed to assure convergence6. For example, if you want three chains, you
would use something like:
inits=list(
list(K=1500, r=.5, tau=.05), #chain 1
list(K=1000, r=.1, tau=.03), #chain 2
list(K=900, r=.7, tau=.07) #chain 3
) #end of inits list
Which variables in your BUGS code require initialization? Anything you are estimating
must be initialized.7 So, this is everything that has a prior, but also, any variable that is
6
Jennifer Hoeting taught me to start my work with a single chain. Once everything seems to be running,
you add additional ones. Note, it sometimes happens that adding a second or third or fourth chain will
cause the model to fail because one of its initial conditions causes undefined real results etc. Thus, you
should add them one at a time for complex models.
7 Actually, if you fail to specify initial values, JAG and WinBUGS will choose them for you, but they often
way off. At best, this means that your chains will take a long time to converge; at worst, your model won’t
12
a random effect, which is a bit of an advanced topic now, but I would like for you to tuck
that away for later. Think about it this way. When you were writing your own Gibbs
sampler, every chain required a value as the first element in the vector holding the
chain. That is what you are doing when you specify initial conditions here.
The next couple of statements,
n.xy = nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population Size"),
y=as.real(pop.data$"Growth Rate"))
specify the data that will be used by your BUGS program. Notice that you can assign
data vectors on the R side to different names on the BUGS side. For example, the bit
that reads
x=as.real(pop.data$"Population Size")
says that the x vector in your BUGS program (see the code above) is composed of the
column in your data frame called Population Size and the bit that reads
y=as.real(pop.data$"Growth Rate")
the y vector required by the BUGS program is composed of a column in your data frame
called Growth Rate (pretty cool, I think). It is important for you to understand that the lhs
of the = in corresponds to the what the data are called in the BUGS program and the rhs
of the = is what they are called in R. Also, note that because pop.data is a data frame I
used as.real( ) to be sure that BUGS received real numbers instead of characters or
factors, as can happen with data frames. This can be particularly important if you have
missing data in the data matrix. The n is required in the BUGS program to index the for
loop (see the code) and it must be read as data in this statement. By the way, you don’t
need to call this list “data”---it could be anything (“apples”, “bookshelves”, “xy” etc.)
Interface between R and BUGS: JAGS
This section is for JAGS users. WinBUGS users should go to the next section.
Now that you have a list of data and initial values (which I include below for clarity) for
the MCMC chain you make calls to JAGS using the following:
setwd("/Users/Tom/Documents/Ecological Modeling Course/WinBUGS
manual/Primer of MCMC Sampling Software for Ecologists/")
rm(list=ls())
pop.data=(read.csv("Logistic Data"))
names(pop.data)=c("Year","Population Size", "Growth Rate")
run. My rule is, always specify good initial conditions based on your best knowledge of what the
parameter values should be..
13
inits=list(
list(K=1500, r=.5, tau=.05)
)
n.xy = nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population Size"),
y=as.real(pop.data$"Growth Rate"))
library(rjags)
##call to JAGS
jm=jags.model("Logistic example
BUGS.R",data=data,inits,n.chains=length(inits), n.adapt = 10000)
zm=coda.samples(jm,variable.names=c("K", "r", "sigma"),
n.iter=50000, n.thin=1)
The first statement (jm = ....) creates and initializes then MCMC chain. Its first
argument is the name of the file containing the BUGS code. Note that in this case, the
file resided in the default working directory, which I specified at the top of the file.
Otherwise, you would need to specify the full path name. The next two expressions
specify where the data come from, where to get the initial values, and how many chains
to create (i.e., the length of the list inits). Finally, it specifies the “burn-in” how many
samples to throw away before beginning to save values in the chain. Thus, in this
case, we will throw away the first 10,000 values.
The second statement (zm=coda.samples...) creates the chains. The first argument
(jm) is the name of the jags model you created in the first step. The second argument
(variable.names) tells JAGS which variables to “monitor.” These are the variables for
which you want output. Finally, n.iter=50000 says we want 50000 elements in each
chain and n.thin specifies which of these to keep. For example, if n.thin = 10, we would
store every 10th element. Sometimes setting n.thin > 1 is a good idea to reduce
autocorrelation and to reduce the size of the data files that you will analyze.
And that’s it. If you have been thinking pure thoughts and paying attention to your
coding, your model will run. If not (more likely, I am afraid), error messages will appear
on the R console. Do some sleuthing using the section on trouble shooting, below.
Interface between R and BUGS: WinBUGS
Now that you have a list of data and initial values (which I include below for clarity) for
the MCMC chain you make calls to WinBUGS using the following:
##call to WinBUGS
rm(list=ls())
pop.data=(read.csv("Logistic Data"))
names(pop.data)=c("Year","Population Size", "Growth Rate")
inits=list(
14
list(K=1500, r=.5, tau=.05)
)
n.xy = nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population
Size"),y=as.real(pop.data$"Growth Rate"))
##call to Winbugs
library(R2WinBUGS)
zm.bugs = bugs(
data=data,
parameters.to.save = c("r","K","sigma"),
inits=inits,
n.thin = 1,
n.burnin = 10000,
model.file = "/BUGS/Logis.R",
n.chains = length(inits),
n.iter=50000,
codaPkg = TRUE,
debug=TRUE
) #end of bugs statement
zm=read.bugs(zm.bugs)
Consider the long statement with zm.bugs on the left hand side. Here is what it does,
part by part:
data=data tells WinBUGS about the data it needs for the BUGS code. If you had
named the data list “bookshelf”, then this statement would read data=bookshelf.
parameters.to.save lists the variables for which you want posterior distributions (i.e. the
variables you want “monitored”)
inits names the list of lists containing the initial values
n.burnin specifies how may iterations to throw away. This is your estimate of the
number of iterations required to assure convergence. More about that later
model.file gives the location of the BUGS code. Remember to keep this pathname
brief. This is for a very good reason for brevity, discovered only after substantial
suffering. There is a quirk with WinBUGS and R (also when you use WinBUGS scripts
if you ever do) that produces an unintelligible error message “INCOMPATIBLE COPY.”
Hmmm. Well, what this means is that the path name to your files and to the working
directory are too long (isn’t that intuitive). If you have a terse, single level directory
structure with folder names like “\ltr”, well you won’t need to worry about this. But if you
are like me, you will need to set the working directory for R to something with a brief
name (i.e., “C:/BUG”) and store your WinBUGS code there (not your R code). You also
need to keep your file names short. You only need to do this until you get the code
debugged and running. After that, you can store it anywhere because the error only
occurs when WinBUGS is trying to send you an error message. However, it is vital to
remember to copy your debugged file to a logical directory or to paste it to the bottom of
the R script. Otherwise, you have files all over the place. One more thing. When you
15
use file.choose() to get a path name, the path is specified using \\. These must be
manually changed to /, as illustrated above. No whining. Live with it.
n.chains specifies the number of different chains you want to run. To do any good,
these must have different initial conditions as specified in the init list. Multiple chains
are needed for convergence diagnostics, which are discussed below.
coda.Pkg = TRUE tells BUGS to produce “coda” style output, which is the type we will
use in this course. If you set coda.Pkg = FALSE you will get BUGS style output, which
you can learn about by doing ?R2WinBUGS.
debug = TRUE means that you want to pause to use the WinBUGS graphical interface
to see error diagnostics. This will become apparent when you do it.
When you run R2WinBUGS with the debug option set to TRUE, then the WinBUGS gui
appears when the iterations are complete. You must press the close button before
R will function again. Sometimes it takes quite a long time (2 minutes?) for R to come
back after a WinBUGS run where you monitor lots of variables. Be patient. You would
set debug = FALSE when your code is running without errors if you wanted the interface
to close automatically without displaying output. You might want this, for example if you
were doing multiple runs of your model with calls to WinBUGS embedded in an R for
loop.
If you are running WinBUGS, you must execute the statement
zm=read.bugs(zm.bugs)
before you execute the statements below. This creates an MCMC list from a bugs
object--more about this later.
Getting simple output: JAGS and WinBUGS
Three statements are very useful for summarizing the output of your chains:
summary(zm)
produces the following at the console:
16
If you enter traceplot(zm) you get a plot of the values of the chains across
iterations, one per window, e.g.,
Entering densityplot(zm) gives plots of the posterior distributions:
17
If you want single plots, you use syntax like
densityplot(zm)[1]
where 1 corresponds to the row number in the summary table (above). This becomes
very handy if you have lots of output variables, as in the next example. You may not
want to see density plots of everything. Unfortunately, this syntax doesn’t seem to
work for traceplot().
Complete R script for JAGS (to this point)
So, the complete R program for JAGS is
setwd("/Users/Tom/Documents/Ecological Modeling Course/WinBUGS
manual/Primer of MCMC Sampling Software for Ecologists/")
rm(list=ls())
pop.data=(read.csv("Logistic Data"))
names(pop.data)=c("Year","Population Size", "Growth Rate")
inits=list(
list(K=1500, r=.5, tau=.05)
)
n.xy = nrow(pop.data)
18
data=list(n=n.xy, x=as.real(pop.data$"Population
Size"),y=as.real(pop.data$"Growth Rate"))
library(rjags)
##call to JAGS
jm=jags.model("Logistic example
BUGS.R",data=data,inits,n.chains=length(inits), n.adapt = 10000)
zm=coda.samples(jm,variable.names=c("K", "r", "sigma"),
n.iter=50000, n.thin=1)
summary(zm)
traceplot(zm)
densityplot(zm)
Complete R script for WinBUGS
rm(list=ls())
pop.data=(read.csv("Logistic Data"))
names(pop.data)=c("Year","Population Size", "Growth Rate")
inits=list(
list(K=1500, r=.5, tau=.05)
)
n.xy = nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population
Size"),y=as.real(pop.data$"Growth Rate"))
##call to Winbugs
library(R2WinBUGS)
zm.bugs <-bugs(
data=data,
parameters.to.save = c("r","K","sigma"),
inits=inits,
n.thin = 1,
n.burnin = 10000,
model.file = "/BUGS/Logis.R",
n.chains = length(inits),
n.iter=50000,
codaPkg = TRUE,
debug=TRUE
) #end of bugs statement
zm=read.bugs(zm.bugs)
summary(zm)
traceplot(zm)
densityplot(zm)
19
Exercise I: courtesy of McCarthy, M. A. 2007. Bayesian Methods for Ecology.
Cambridge University Press, Cambridge, U. K.
Polis et. al. (1998) analyzed the probability of occupancy of islands (p) by lizards that as
a function of the ratio of the islands’ perimeter to area (PA). The data from this
investigation are available in islands.csv. The response data, as you will see, are 0-1, 0
if there were no lizards found on the island, 1 if there lizards were found. Please
construct a simple Bayesian model that represents the probability of occupancy as
logit(p) = a + b*PA showing what you have learned about logit functions and the
Bernoulli distribution. Write out the Bayesian model. Estimate the posterior distribution
of a and b using WinBUGS or JAGS. For now, you may ignore the problem of detection
probability. You may assume uninformative priors.
Using the summary() list
Congratulations on successfully coding your first BUGS program Continuing with our
logistic example, what if you wanted to plot the model predictions against the
observations, which, frankly, you should always do. To accomplish this, you would do
the following. Modify your R statement by adding mu to the list of variables for which
you want output, i.e.:
#For JAGS
zm=coda.samples(jm,variable.names=c("K", "r", "sigma", “mu”),
n.iter=50000, n.thin=1)
#For Windows
zm.bugs <-bugs(
data=data,
parameters.to.save = c("r","K","sigma", “mu”),
inits=inits,
n.thin = 1,
n.burnin = 10000,
model.file = "/BUGS/Logis.R",
n.chains = length(inits),
n.iter=50000,
codaPkg = TRUE,
debug=TRUE
) #end of bugs statement
#add mu here
Now, rerun the statements above and consider the following. The statement
summary(zm)
20
now returns this:
Iterations = 10001:60000
Thinning interval = 1
Number of chains = 1
Sample size per chain = 50000
1. Empirical mean and standard deviation for each variable,
plus standard error of the mean:
Mean
SD Naive SE Time-series SE
K
1.209e+03 1.444e+02 6.460e-01
1.417e+00
mu[1] 1.931e-01 2.108e-02 9.428e-05
1.492e-04
mu[2] 1.562e-01 1.538e-02 6.880e-05
9.278e-05
mu[3] 1.478e-01 1.424e-02 6.367e-05
8.063e-05
mu[4] 1.258e-01 1.177e-02 5.264e-05
5.320e-05
mu[5] 1.183e-01 1.116e-02 4.993e-05
4.659e-05
mu[6] 8.788e-02 1.045e-02 4.674e-05
5.112e-05
mu[7] 8.580e-02 1.052e-02 4.703e-05
5.320e-05
mu[8] 6.607e-02 1.179e-02 5.273e-05
7.770e-05
mu[9] 4.548e-02 1.409e-02 6.300e-05
1.077e-04
mu[10] 3.129e-02 1.603e-02 7.171e-05
1.294e-04
mu[11] 1.138e-02 1.907e-02 8.528e-05
1.606e-04
r
2.052e-01 2.311e-02 1.033e-04
1.683e-04
sigma 3.318e-02 8.932e-03 3.994e-05
5.217e-05
2. Quantiles for each variable:
2.5%
25%
50%
75% 97.5%
K
1.000e+03 1.117e+03 1.187e+03 1.271e+03 1.556e+03
mu[1] 1.497e-01 1.802e-01 1.932e-01 2.063e-01 2.345e-01
mu[2] 1.245e-01 1.469e-01 1.563e-01 1.659e-01 1.863e-01
mu[3] 1.186e-01 1.391e-01 1.479e-01 1.567e-01 1.755e-01
mu[4] 1.020e-01 1.186e-01 1.258e-01 1.332e-01 1.488e-01
mu[5] 9.569e-02 1.115e-01 1.184e-01 1.254e-01 1.402e-01
mu[6] 6.676e-02 8.142e-02 8.791e-02 9.442e-02 1.087e-01
mu[7] 6.450e-02 7.933e-02 8.585e-02 9.239e-02 1.068e-01
mu[8] 4.232e-02 5.873e-02 6.613e-02 7.348e-02 8.977e-02
mu[9] 1.694e-02 3.666e-02 4.560e-02 5.437e-02 7.377e-02
mu[10] -1.075e-03 2.118e-02 3.141e-02 4.140e-02 6.335e-02
mu[11] -2.703e-02 -6.485e-04 1.153e-02 2.344e-02 4.944e-02
r
1.576e-01 1.911e-01 2.054e-01 2.197e-01 2.507e-01
sigma 2.084e-02 2.695e-02 3.154e-02 3.754e-02 5.539e-02
21
The summary() function returns a list consisting of two matrices, one that contains the
statistics and the other that contains the quantiles for all of the variables that you asked
BUGS to monitor (i.e., listed in parameters.to.save() or variable.names()). This list is
extremely useful so you really must learn how to manipulate it. To extract the individual
matrices from the list, you would use
zm$stat or zm$quantile
You now have a matrix that can be manipulated like any other. Try the following:
b=zm$stat
b[1:2,]
b[1,1]
To complete this plotting example, study the following code. It contains some cool new
tricks for your R locker [i.e., grep(), rownames()]. You may want to do a ? on
commands that are not familiar to you.
#get the quantile part of the list
zm.quantile=summary(zm)$quantile
#make a vector with the rownumbers of rows containing mu in the
row name
mu.i=grep("mu", rownames(zm.quantile))
#make some easy notation for Population Size
x=pop.data$"Population Size"
#plot the median--could also use matplot here
plot(x,zm.quantile[mu.i,3],typ='l', xlab = "N", main = "Per
Capita Growth Rate", ylab = "dN/dt * 1/N")
#plot the data
points(x,pop.data$"Growth Rate")
#plot the upper credible interval
lines(x,zm.quantile[mu.i,5], lty="dashed", col="red")
lines(x,zm.quantile[mu.i,1], lty="dashed", col="red")
Talk to me or to one of the TA’s if you don’t thoroughly understand what is going on in
this code.
If you get it right, you will produce, tad-dah--
22
Discuss with you lab mates where these credible intervals are coming from. What do
they have to do with the MCMC chain?
Estimating Quantities of Interest Derived from Model
Parameters
A common difficulty when using frequentist statistics is how to calculate quantities of
interest from estimates that you obtain from your analysis. Continuing our example, if
we know estimate the means and variances of r and K from a regression analysis, then
dN
dN
r 

 N  r  N  ? This is particularly problematic for
how do we estimate
where

dt
dt
K 
frequentist approaches because this function is nonlinear and the variance of its output
can be only approximated. Alternatively, using MCMC to estimate the posterior
dN
distribution of
is easy and the posterior distribution forms the basis for estimating
dt
variances, confidence envelopes, etc. Using MCMC, any quantity that is a function of a
random variable becomes a random variable as you learned when you did the half life
problem in the MCMC lab exercise.
Consider two quantities of interest that are functions of our estimates of the random
K
variables r and K: 1) the maximum rate of population growth
, and 2) the rate of
2
dN
r 

 N  r  N  . We want to estimate the posterior distributions of
population growth,

dt
K 
each of these quantities. This requires only minor modification to our BUGS code:
##Logistic example for Primer
model{
#priors
K~dgamma(.001,.001)
23
r~dnorm(0.,.001)
tau~ dgamma(.001,.001)
sigma<-1/sqrt(tau)
#likelihood
for(i in 1:n){
mu[i] <- r - r/K * x[i]
y[i] ~ dnorm(mu[i],tau)
}
#Calculate maximum growth rate
Kover2 <- K/2
#Calculate growth rate over range of N values. These
#must be read in as data
for(j in 1:J){
dNdt[j] <- N[j]* (r - r/K *N[j])
}
} #end of model
Notice two changes. First is the single line to calculate Kover2. Now take a look at the
loop at the bottom of the code:
#Calculate growth rate over range of N values. These
#must be read in as data
for(j in 1:J){
dNdt[j] <- N[j]* (r - r/K *N[j])
}
I knew that dNdt was a curve and I wasn’t sure that the 11 data points would be
sufficient to estimate that curve smoothly. So, I created a vector for N that took on
values between 1 and 1500 in steps of 10 (well, actually steps of 10 for all except 1-10)
and added the N vector to the data list in the R script. I also added the length of this
vector as the scalar, J to the data list. Thus, the vector N and the scalar J in the BUGS
code are data. The lovely plot below, shown in comparison with the original linear plot,
is produced by the modification of the BUGS file (above) and the the R code beneath
the plot.
There is some very important biology here. Notice that for a linear function our
uncertainty remains relatively constant over the range of the function. Although
uncertainty is greater for extreme values at the ends of the line (left panel), it is not
markedly different. Not so for the nonlinear function (right panel). Uncertainty when the
population size is large is enormous--for example, when the population 1100
24
individuals, it could be growing by almost 60 animals per year or declining annually by
24. This graph has fundamental implications for planning sustainable harvest in the
face of uncertainty.
for JAGS:
pop.data=read.csv("Logistic Data")
names(pop.data)=c("Year","Population Size", "Growth Rate")
inits=list(
list(K=1500, r=.5, tau=.05)
)
#create vector of N's for calculating dNdt
N=seq(0,1500,10)
#substitute 1 for 0 in the N vector
N[N==0]=1
n.xy=nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population Size"),
y=as.real(pop.data$"Growth Rate"), N=N, J=length(N))
library(rjags)
##call to JAGS
jm=jags.model("Logistic example
BUGSII.R",data=data,inits,n.chains=length(inits), n.adapt =
10000)
25
zm=coda.samples(jm,variable.names=c("K", "r", "sigma", "mu",
"dNdt", "Kover2"), n.iter=50000, n.thin=1)
for WinBUGS:
pop.data=read.csv("Logistic Data")
names(pop.data)=c("Year","Population Size", "Growth Rate")
inits=list(
list(K=1500, r=.5, tau=.05)
)
#create vector of N's for calcuating dNdt
N=seq(0,1500,10)
#substitute 1 for 0 in the N vector
N[N==0]=1
n.xy=nrow(pop.data)
data=list(n=n.xy, x=as.real(pop.data$"Population Size"),
y=as.real(pop.data$"Growth Rate"), N=N, J=length(N))
##call to Winbugs
library(R2WinBUGS)
zm.bugs <-bugs(
data=data,
parameters.to.save = c("r","K","mu", "Kover2", "dNdt"),
inits=inits,
n.thin = 1,
n.burnin = 10000,
model.file = "/BUGS/LogisII.R",
n.chains = length(inits),
n.iter=50000,
codaPkg = TRUE,
debug=TRUE
) #end of bugs statement
zm=read.bugs(zm.bugs)
Output code for both:
#make a couple of summary matrices
zm.quantile=summary(zm)$quantile
zm.stat=summary(zm)$stat
#set up 2 panel plots
par(mfrow=c(1,2))
#get the number of rows to make the next steps easy
rows = nrow(zm.stat)
#get some statistics you would like
26
zm.stat[c(1,2,rows, rows-1),]
zm.quantile[c(1,2,rows, rows-1),]
##make a vecor with the rownumbers of rows containing mu in the
row name
mu.i=grep("mu", rownames(zm.quantile))
#make some easy notation for Population Size
x=pop.data$"Population Size"
#plot the median--could also use matplot here
plot(x,zm.quantile[mu.i,3],typ='l', xlab = "N", main = "Per
Capita Growth Rate", ylab = "dN/dt * 1/N")
#plot the data
points(x,pop.data$"Growth Rate")
#plot the upper credible interval
lines(x,zm.quantile[mu.i,5], lty="dashed", col="red")
#plot the lower credible interval
lines(x,zm.quantile[mu.i,1], lty="dashed", col="red")
legend(400,.20,legend=c("median", "95% Credible interval"),
lty=c("solid","dashed"), col=c("black", "red"), bty="n")
#get the row numbers for dNdt
dNdt.i=grep("dNdt", rownames(zm.quantile))
#make some easy notation
dNdt=zm.quantile[dNdt.i,]
plot(N,dNdt[,3], typ="l",ylim=c(0,110), xlab="N", ylab="dN/dt",
main="Population Growth Rate")
lines(N,dNdt[,1], lty="dashed", col="red")
lines(N,dNdt[,5], lty="dashed", col="red")
legend(5,110,legend=c("median", "95% Credible interval"),
lty=c("solid","dashed"), col=c("black", "red"), bty="n")
Exercise 2
Modify the model that you constructed in exercise 1 to allow you to output the individual
predictions (i.e., the p[i]). Also, create a new variable that allow you to plot the
probability of occurrence as a function of island PA ranging from 1-60. Make point plots
of the median p[i] at each value of PA with points for the upper and lower 95% credible
intervals. Also make a smooth line plot of the predictions across the range of 1-60 and
a smooth plot of the upper and lower 95% credible intervals.
Comparing Parameter Values Between Populations
Imagine that you had data on two populations and you wanted to compare the strength
of density dependent feedbacks to population growth rate between the two populations.
We ask, which population has stronger feedbacks? Or we might hypothesize that
27
population 2 has stronger feedbacks than population 1. We define the strength of
density dependence as the slope of the line between per-capita population growth rate
and population size (e.g., Wang et al. 2006). The data now look like this:
Think carefully about this problem. We now have a vector of parameters, i.e., K = K1,K2
and r = r1,r2. We also have a matrix of data with columns for population size and
population growth rate for each population an rows containing values for each year.
Although we are doing the simplest thing first (i.e., 2 populations) this example could
easily be extended to more than 2 populations. Moreover, it could be modified to apply
to any comparisons of parameter values between “populations,” where populations is
used in its broadest sense to mean what we sample from. Please try to think broadly as
you see what we are doing here, musing, “How could I modify this example to fit my
research problem?”
Our simple Bayesian model is now:
i, j  ri 
ri xi, j
Ki
2
 n

P r, K,  1 ,  2 y, x      P yi, j | i ,  i P ri P K i P  i 

j 1  i 1


2
 n

P r, K,  1 ,  2 y, x      Normal yi, j | i ,  i Normal ri | 0,.001Gamma K i | .001,.001Gamma  i | .001,.001

j 1  i 1


Think about this model, being particularly careful to understand the subscripts.
Now, consider the implementation of this model in BUGS code below:
##Logistic III example for Primer
model{
#priors
for(i in 1:n.pop){
K[i]~dgamma(.001,.001)
r[i]~dnorm(0.,.001)
tau[i]~ dgamma(.001,.001)
sigma[i]<-1/sqrt(tau[i])
28
}
#likelihood
for(j in 1:n.pop){
for(i in 1:n){
mu[i,j] <- r[j] - r[j]/K[j] * x[i,j]
y[i,j] ~ dnorm(mu[i,j],tau[j])
} #end of i loop
} # end of j loop
# calculate strength of feedbacks and their ratio
s[1]<-r[1]/K[1]
s[2]<-r[2]/K[2]
ratio <- s[2]/s[1]
} #end of model
Notice that the code is virtually the same as for our first model, except that now we
index the parameters and the observations. The really cool trick comes at the end. The
quantity ratio tells us about the relative strength of density dependence. (It could be the
relative value of any parameter relative to another--use you imagination here). Given
that the credible interval on the ratio does not overlap 1, we can be confident that
density dependent feedbacks in population 2 (i.e., s[2]) are stronger than feedbacks in
population 1 (i.e. s[2]):
> summary(zm)$quantile
2.5%
25%
50%
75%
97.5%
K[1] 1.002190e+03 1.115655e+03 1.185166e+03 1.270009e+03 1.557576e+03
K[2] 6.206618e+02 6.753259e+02 7.063284e+02 7.431181e+02 8.664173e+02
r[1] 1.588349e-01 1.908177e-01 2.052687e-01 2.196932e-01 2.506633e-01
r[2] 1.728313e-01 2.094595e-01 2.250574e-01 2.404328e-01 2.736518e-01
ratio 1.093860e+00 1.565790e+00 1.835414e+00 2.166422e+00 3.183662e+00
s[1] 1.072269e-04 1.526994e-04 1.731944e-04 1.936909e-04 2.379542e-04
s[2] 2.099020e-04 2.862172e-04 3.184726e-04 3.503766e-04 4.192468e-04
sigma[1] 2.092391e-02 2.701965e-02 3.158954e-02 3.747498e-02 5.507619e-02
sigma[2] 2.086381e-02 2.687533e-02 3.145780e-02 3.739067e-02 5.539449e-02
However, we might want to be more precise in our statements. We might want to say
something about the probability that s[2] > s[1] or perhaps the probability that s[2] is
twice as great as s[1]. I cover how to do this in the next section.
Using the MCMC List
Much of the analytical power in the approach you are learning comes directly from the
complete MCMC chains. The zm object that we have been producing in the examples
above is a MCMC list. For example, lets say for illustration purposes that you only did
10 iterations after a burn-in of 500. What would the zm object look like? For the
previous example,
29
So, the list appears to have a single element, a matrix, with the iterations as rows and
the variables as columns. To prove to yourself that is is correct, do something like
a=zm[[1]] # note the double brackets used to extract elements
of a list
a[1,] #the first row of the matrix
However, recall earlier that I showed you how to run more than one chain by including
more than one set of initial conditions in the inits list. So, if we had specified 3 lists of
initial conditions using something like:
inits=list(
list(K=c(1500,1500), r=c(.5,.5), tau=c(.05,.05)),
list(K=c(1000,1000), r=c(.4,.2), tau=c(.01,.07)),
list(K=c(1300,1700), r=c(.2,.6), tau=c(.08,.01))
)
30
Then our zm object would be a list of 3 matrices that looks like this:
We can put these matrices together in a way that is really useful. Presume you
produced a zm object as above with three chains. Consider the following code:
#make a data frame containing all three chains
c = as.data.frame(rbind(zm[[1]],zm[[2]], zm[[3]])) #note double
brackets
plot(density(c$ratio), xlab = "Relative Strength of Density
Dependence", xlim=c(0,5), main="Posterior Distribution of Ratio
of Slopes")
plot(density(c$"K[1]"), xlab = "Carrying Capacity",
xlim=c(400,1700), main="Posterior Distribution of K",
ylim=c(0,.010))
lines(density(c$"K[2]"), col = "red")
legend(900,.009, legend=c("Population 1", "Population 2"),
col=c("black", "red"), lty=c("solid", "solid"))
1-ecdf(c$ratio)(1.0) #probability that ratio > 1
31
1-ecdf(c$ratio)(1.5) #probability that ratio > 1.5
hist(c$ratio, breaks=500, xlim=c(0,5), freq=FALSE)
abline(v=1.0, col="red")
Let’s look at some key bits to understand what’s going on. First,
c = as.data.frame(rbind(zm[[1]],zm[[2]], zm[[3]]))
brackets
#note double
This statement takes the chains from the zm MCMC list and combines them into a
single data frame with the number of columns = number of variables and number of
rows = 3 x the number of iterations. (Note that we should only do this if the chains are
nicely mixed, that is, if they have converged on the same posterior distribution. We will
learn more about how to test for that in the next section.) We now have a data frame
that we can work with the same way we work with any data frame.
Most often, we create a data frame like this to serve two purposes. First, we may want
publication quality plots of the posterior distributions; densityplot( ) is fine for
quick looks a many variables with one command, but it doesn’t produce high quality
output that we can control. The following code uses the data frame c to create some
nice plots of the posterior densities.
plot(density(c$ratio), xlab = "Relative Strength of Density
Dependence", xlim=c(0,5), main="Posterior Distribution of Ratio
of Slopes")
plot(density(c$"K[1]"), xlab = "Carrying Capacity",
xlim=c(400,1700), main="Posterior Distribution of K",
ylim=c(0,.010))
lines(density(c$"K[2]"), col = "red") #note quotes needed for [
]
legend(900,.009, legend=c("Population 1", "Population 2"),
col=c("black", "red"), lty=c("solid", "solid"))
32
The other reason we might use a data frame like c is to calculate the probabilities. Take
a look at the lines that read:
#caclulcate probabilites for ratio exceeding 1 or 1.5
1-ecdf(c$ratio)(1.0) #probability that ratio >
1-ecdf(c$ratio)(1.5) #probability that ratio > 1.5
The console returns:
> #caclulcate probabilites for ratio exceeding 1 or 1.5
> 1-ecdf(c$ratio)(1.0) #probability that ratio > 1
[1] 0.9875
> 1-ecdf(c$ratio)(1.5) #probability that ratio > 1.5
[1] 0.8041733
This code provides a hypothesis test that the strength of density dependence is greater
in population 2 compared with population 1. So we can be 99% sure that the
dependence is stronger. We can only be 80% certain that it is 50% stronger. I think
you can see how flexible this kind of statement could be in analyzing your data.
Essentially, it allow us to set up any kind of comparison of interest and attach a
probability statement to it.
So, given the utility of this function, you should understand it deeply. Here is what is
going on. ecdf stands for “empirical cumulative density function.” It is the same as a
cumulative density function we learned about in lecture (remember those?) but it is,
well, empirical--that is, we look at a vector, in this case a chain, and we calculate the
proportion of values that are less than or equal to the argument to the function. So
33
ecdf(v)(x) gives the proportion of values of v that are less than or equal to x and 1ecdf(v)(x) gives the proportion of elements of v that exceed x. If you wanted the
proportion of values that were between x1 and x2, how would you do it?
So, what this function does is show graphically below. The histogram was created from
a 50,000 element chain for ratio. The statement ecdf(c$ratio)(1.0) estimates
the area to the left of the vertical red line (actually, the number of elements in the chain
that are < 1.0 divided by 50,000) and 1-ecdf(c$ratio)(1.0) estimates the area to
the right of the line.
Exercise 3
Assume you are interested in 2 islands, 1 that has a perimeter to area ratio of 10, the
other that has a perimeter to area ratio of 20. What is the 95% confidence interval on
the difference in the probability of occupancy of the two islands based on the analysis
you did above? What is the problbailty that the p[i] of the larger island is twice as great
at the p[i] of the smaller one?
34
Checking for Convergence
I have emphasized several times that chains must reach what is called a stationary
distribution to allow reliable inferences on posterior distributions. You were able to
graphically observed convergence in the MCMC lab exercise. There are three primary
ways that we will evaluate convergence. As I mentioned above, my working habit is to
get my model running using a single chain and then add others for “production” runs.
The reasons for this is simple: two chains requires twice as much time to run as a single
one. So, running 4 or 5 chains can be time consuming, even for simple models.
The first thing we do to evaluate convergence is to examine the chains themselves,
which we can do with a traceplot() function. This is possible even with a single chain,
as we saw above, but with multiple chains, we can evaluate mixing. Continuing the
previous example above, we obtain the following traceplot, which is one of several that
we obtain using the function traceplot(zm):
The different colors represent the different chains. They are well mixed and the
horizontal line show that the mean of the distribution is not increasing or declining.
The corresponding density plot looks like
35
It may be hard to see, but there are 3 different colors in the lines here, showing that all
three chains have converged on the same posterior distribution.
Examples of chains that have not converged but that arrive at convergence are shown
below.
36
Finally, there is a less artistic approach offered in the Gelman Diagnostic, which we
obtain using the function:
#test for convergence
gelman.diag(zm)
which produces the output
Potential scale reduction factors:
Point est. 97.5% quantile
K[1]
1.02
1.02
K[2]
1.00
1.00
r[1]
1.00
1.00
r[2]
1.00
1.00
ratio
1.01
1.01
s[1]
1.00
1.00
s[2]
1.00
1.00
sigma[1]
1.00
1.00
sigma[2]
1.00
1.00
Each of the values for the upper confidence limit must be close to 1 if we are to be
confident of convergence. If that value is much above 1 (say, 1.1) we should run more
iterations. For detail on the Gelman statistic, do ?gelman.diag at the R prompt.
There are also diagnostics that will work on a single chain, see gweke.diag() and
gweke.plot().
Checking Syntax
If you do much work with this software, you will learn the syntax and will be able to
sleuth syntax errors quickly and easily. But, in the beginning, this can be, well, tedious.
Here are a few tips on syntax checking in general, followed by a list of common errors
for both WinBUGS and JAGS.
WinBUGS
If you have been thinking pure thoughts, you will get no syntax errors. However, this
never happens to me. Common syntax errors include using = where you needed <-,
mismatching parentheses, misspelling distribution or function names, failing to close
}’s, leaving out commas, and so on. A surprising one is that the ^ for exponentiation is
illegal. If you need to square sigma, then you must use (sigma*sigma) or
37
pow(sigma,2) If you generate an error, WinBUGS stops at the GUI (if you have set
debug = TRUE) and shows, in the lower left corner of the screen, a characteristically
unintuitive message in text requiring 2 x glasses to read. The location of the offending
syntax within your code is given by a wispy |. Believe me, the | is hard to see, but it is
there, somewhere.
JAGS
JAGS has many things to recommend it, but one of them is not the way it
communicates syntax errors. There are a couple of ways to get around this--actually
after a while it is not a problem because the syntax is simple and you will get a nose for
what’s wrong. Until then, I strongly urge you to build up your code slowly, in sensible
blocks with a few statements at a time. You must fill in the { } within for loops, but other
than that, you can add code bit by bit, running the ....=jags.model(.... statement each
time to assure your syntax is correct. If you have a syntax error you simply can’t find,
then I suggest you 1) open WinBUGS under an emulator 2) Click File, new to open a
program window, 3) Paste the offending JAGS code into the window, 4) Click on Model
then specification, then check model. The error should be shown with a wispy I and
there will a not terribly intuitive message in the lower left hand side of the scree.
These are reported at the R console with language something like:
Error in jags.model("Logistic example BUGSIII.R", data = data, inits, :
Parse error on line 1
syntax error, unexpected $undefined, expecting ARROW or '~'
Pay no attention to the line number; it is meaningless. The statement itself can be
helpful. Here are a few common ones. The parenthetical statements are mine:
syntax error, unexpected $undefined, expecting ARROW or '~'
(you used a = instead of a <-)
Error in jags.model("Logistic example BUGSIII.R", data = data, inits, :
RUNTIME ERROR:
Unknown distribution: dmorm
(dnorm was spelled incorrectly, dmorm)
syntax error, unexpected $end, expecting NAME or FUNC or FOR or '}'
You have mismatched [], () or {}
38
#syntax error, unexpected NAME, expecting $end
The closing bracket for your model statement looks like this:
} #end of model
You must add a hard return after this line.
Some common error messages
JAGS
From my notebook:
Never use _ as a part of a variable name, it reads as if it weren’t there—i.e. N_[i,j] reads
as N[i,j] producing potential errors.
var is a reserved keyword, so variables cannot be name var.
Let Y[i] be data. If you want to calculate a quantity based on the data, this cannot be
done in the main body of code the way it is in Winbugs.
Error in jags.model("test.R", data = data, inits, n.chains = 1, n.adapt = 10000) :
RUNTIME ERROR:
Unable to resolve parameter O[38,1:2] (one of its ancestors may be undefined)
If you change:
[33,]
[34,]
[35,]
[36,]
[37,]
[38,]
[39,]
[40,]
61 447
79 351
54 175
47 260
53 287
NA NA
29 231
25 404
39
to
[33,] 61 447
[34,] 79 351
[35,] 54 175
[36,] 47 260
[37,] 53 287
[38,] 9999 9999
[39,] 29 231
[40,] 25 404
and loop around it, the model runs. It appears to be treating the NA as a parameter because you
loop around it—it never appears on the lhs of ~
Warning message:
In readLines(file) : incomplete final line found on 'SS2.R'
will occur when you don’t have a hard return after the last } for the model.
error
syntax error, unexpected '}', expecting $end
occurs when there are mismatched parens
error JAGS does not allow model names:
model state_space_logistic {
must be
model { #state_space_logistic
error
syntax error, unexpected DOUBLE, expecting FUNC
happens when you have a ~ and mean a <i.e.,
40
Wrong:
tau.s[t] ~ 1/(sigma.s[t]*sigma.s[t])
right
tau.s[t] <- 1/(sigma.s[t]*sigma.s[t])
Error in setParameters(init.values[[i]], i) :
Error in node sigma.s[1]
Attempt to set value of non-variable node
You get this error when you have a variable in your init list that is not a variable in the model
When jags doesn’t run, i.e., you get:
> jm=jags.model("obs.model.jags.R", data=data, inits,n.chains=length(inits),n.adapt=n.adapt)
Compiling data graph
Declaring variables
Resolving undeclared variables
Allocating nodes
Initializing
Reading data back into data table
Compiling model graph
Declaring variables
Resolving undeclared variables
Allocating nodes
Graph Size: 258
and nothing happens, try switching uninformative gamma prior to uniform
If you are going to use a uniform for tau or sigma, it is better to put it on tau, i.e.,
tau ~ dunif(0,5)
sigma <- 1/sqrt(tau)
rather than
sigma ~duinf(0,500)
tau <- 1/(sigma*sigma)
This is because the former formulation is sensitive to your choice on the upper bound of the
uniform if the data are sparse. The second form is not sensitive.
Error:
41
Error: Error in node x[3,5,193]
All possible values have probability zero
caused by uninitialized values for the array x.
Error in setParameters(init.values[[i]], i) : Error in node sigma
Attempt to set value of non-variable node
when sigma is defined by <- instead of ~ and you have an init for sigma
Error in jags.model("Logistic example BUGSIII.R", data = data, inits, :
RUNTIME ERROR:
Unable to evaluate upper index of counter i
The value for the upper range of the loop was omitted from the data statement
Error in jags.model("IslandBugIII.R", data = data, inits, n.chains = length(inits), :
RUNTIME ERROR:
Unknown parameter P20
the parameter name was misspelled. Should have been p20
WinBUGS
Syntax errors are usually pretty easy to figure out. However, once you model is
syntactically correct, you are not out of the woods. Here are some errors that occur on
compilation or
made use of undefined node—
This means you have variables in your code that are not data or stochastic quantities to
be estimated (i.e., don’t have priors) and are not derive from data or stochastic
quantities. It usually means a typo in variable name. Check spelling of the suspect
variable. This can also happen when you have a quantity that is defined in terms of
stochastic nodes that themselves don’t have priors or if you have a stochastic node that
is outside of the model statement.
this chain contains uninitialized variables
You have not initialized all variables. Remember, anything on the lhs of a ~ that is not
data must be initialized.
42
multiple definitions of node [x]
You have a variable that should be indexed in a loop, and is not indexed.
incompatible copy
path names are too long when writing scripts (not recommended) or when using
R2WinBUGS
array index is greater than array upper bound
An index to a loop in WinBUGS exceeds the number of data items indexed.
WinBUGS hangs at the compile step
You probably have a structure that is something like this:
for(t in 2:n){
w[t] = some function of w[t-1]
w[t]~dnorm(w[t], tau.p)
}
WinBUGS cannot recursively update arrays. You need code something like this:
for(t in 2:n){
w.hat[t] = some function of w[t-1]
w[t]~dnorm(what[t], tau.p)
}
Splus data 100
You have a vector of initial conditions that is the wrong length. I know, I know, this is
not a terribly informative message.
Cannot Bracket Slice for Nod
Shape parameter of gamma r too small.
Chances are this occurs when all stochastic nodes (all) have not been initialized. If the
gen ints( ) is not greyed out, you have not initialized everything. This can occur when
you have arrays of initial values that go from 1-N, but you loop over 2:N. You then get a
message that the first value is not stochastic, and WinBUGS generates initial values for
you, whether you want them or not. In this case, you need to put an NA for the first
element of the initial value array.
One more thing:
Never use 2 periods in notation, for example
43
y.s.neg should be ys.neg or y.neg but never y.s.neg. For reasons unknown, the double
. produces bad results but no error. Be warned.
Differences Between JAGS and WinBUGS
There are a couple of important differences between JAGS and WinBUGS that are
covered nicely in the JAGS manual. In short, these have to do with data transformation,
which must be done in a data statement. Quoting the JAGS manual:
There are also some operations that are not found in the WinBUGS implementation,
notably matrix multiplication, exponentiation via ^, and the ability to write user-defined
operators.
44
Bibliography
Knops, J. M. H., and D. Tilman. 2000. Dynamics of soil nitrogen and carbon
accumulation for 61 years after agricultural abandonment. Ecology 81:88-98.
Polis, G. A., S. D. Hurd, C. T. Jackson, and F. Sanchez-Pinero. 1998. Multifactor
population limitation: Variable spatial and temporal control of spiders on Gulf of
California islands. Ecology 79:490-502.
Wang, G. M., N. T. Hobbs, R. B. Boone, A. W. Illius, I. J. Gordon, J. E. Gross, and K. L.
Hamlin. 2006. Spatial and temporal variability modify density dependence in
populations of large herbivores. Ecology 87:95-102.
45
Appendix 1: Reference material for JAGS
Distributions
46
Functions and operators
47
48
Reference Material for WinBUGS
Distributions
49
50
51
52
Functions and Operators
Note that the ^ for exponentiation is not a permitted operation in WinBUGS. You must
use the pow( ) function below.
53
54
Appendix 2: Answers to Exercises
Exercise 1
log it pi   a  bxi
19
P a,b,| y, x    Bernoulli yi | pi Normal a | 0,.000001Normal b | 0,.000001
i 1
BUGS code
model
{
a ~ dnorm(0, 1.0E-6)
b ~ dnorm(0, 1.0E-6)
# uninformative intercept term
# uninformative effect of PA
for (i in 1:n) # for each island
{
logit(p[i]) <- a + b*PA[i] # logit(p) a function of PA
y[i] ~ dbern(p[i]) # observed occurrence drawn from
Bernoulli dist'n
}
} #end of model
R code:
setwd("/Users/Tom/Documents/Ecological Modeling Course/WinBUGS
manual/Primer of MCMC Sampling Software for Ecologists")
rm(list=ls())
islands=read.csv("island data.csv")
data=list(PA=islands[,1], y=islands[,2], n=nrow(islands) )
inits=list(
list(a=0,b=0)
)
jm=jags.model("IslandBug.R",data=data,inits,n.chains=length(init
s), n.adapt = 10000)
zm=coda.samples(jm,variable.names=c("a","b"), n.iter=50000,
n.thin=1)
#for WinBUGS
library(R2WinBUGS)
zm.bugs <-bugs(
55
data=data,
parameters.to.save = c("a","b"),
inits=inits,
n.thin = 1,
n.burnin = 10000,
model.file = "/BUGS/IslandI.R",
n.chains = length(inits),
n.iter=50000,
codaPkg = TRUE,
debug=TRUE
) #end of bugs statement
zm=read.bugs(zm.bugs)
traceplot(zm)
densityplot(zm)
summary(zm)
Exercise 2
56
Bugs code
model
{
a ~ dnorm(0, 1.0E-6)
# uninformative intercept term
57
b ~ dnorm(0, 1.0E-6)
# uninformative effect of PA
for (i in 1:n) # for each island
{
logit(p[i]) <- a + b*PA[i] # logit(p) a function of PA
y[i] ~ dbern(p[i]) # observed occurrence drawn from
Bernoulli dist'n
}
for(j in 1:n.pred){
#p.hat[j] <- ilogit(a + b*x[j]) # for use with JAGS
p.hat[j] <- exp(a + b*x[j]) / (1 + exp(a + b*x[j])) #for use
with JAGS or WinBUGS
}
} #end of model
R code
setwd("/Users/Tom/Documents/Ecological Modeling Course/WinBUGS
manual/Primer of MCMC Sampling Software for Ecologists")
rm(list=ls())
islands=read.csv("island data.csv")
#make a sequence to get predicted values
x=seq(1,60)
data=list(PA=islands[,1], y=islands[,2], n=nrow(islands), x=x,
n.pred=length(x))
inits=list(
list(a=0,b=0)
)
#for JAGS
library(rjags)
jm=jags.model("IslandsBugII.R",data=data,inits,n.chains=length(i
nits), n.adapt = 10000)
zm=coda.samples(jm,variable.names=c("a","b", "p", "p.hat"),
n.iter=50000, n.thin=1)
#for WinBUGS
library(R2WinBUGS)
zm.bugs <-bugs(
data=data,
parameters.to.save = c("a","b", "p", "p.hat"),
inits=inits,
n.thin = 1,
58
n.burnin = 10000,
model.file = "/BUGS/IslandII.R",
n.chains = length(inits),
n.iter=50000,
codaPkg = TRUE,
debug=TRUE
) #end of bugs statement
zm=read.bugs(zm.bugs)
#get the quantiles
s=summary(zm)$quantile
#find the predictions at data points. Note fixed = TRUE
p1=grep("p[", rownames(s), fixed=TRUE)
#plot median and credible intervals
plot(islands[,1],s[p1,3], xlab="Perimeter to Area Ratio", ylab=
"Probability Occupied")
points(islands[,1],s[p1,5], pch=18)
points(islands[,1],s[p1,1], pch=18)
#find the predictions over 1-60
p2=grep("p.hat", rownames(s), fixed=TRUE)
#plot median and credible intervals
plot(x,s[p2,3], xlab="Perimeter to Area Ratio", ylab=
"Probability Occupied", typ='l')
lines(x,s[p2,5], lty="dashed")
lines(x,s[p2,1], lty="dashed")
Exercise 3
95% CI = .19 - .85
Probability that the larger island (smaller PA) is more than twice as likely to have a
lizard as the smaller island = .76
59
Download