Gibbs Sampling from a Bivariate Normal Distribution

Goals

This tutorial looks at one of the work horses of Bayesian estimation, the Gibbs sampler. In this tutorial, we will:

  1. Use the Gibbs sampler to generate bivariate normal draws.
  2. Create side-by-side plots of the parameter paths.
  3. Calculate the drawn distribution's mean and variance-covariance matrix.

Introduction

The Gibbs sampler draws iteratively from posterior conditional distributions rather than drawing directly from the joint posterior distribution. This makes the Gibbs Sampler particularly useful, as the joint posterior is not always easy to work with.

The Gibbs sampler works by restructuring the joint estimation problem as a series of smaller, easier estimation problems. For example, consider the case where the parameter vector can be broken into two blocks: $ \theta' = [\theta_{1}'\text{ }\theta_{2}'] $.

The Gibbs sampler steps

The bivariate general Gibbs Sampler can be broken down into simple steps:

  1. Set up sampler specifications including the number of iterations and the number of burn-ins draws.
  2. Choose a starting value $\:p(\theta_1|y,\:\theta_2^{(0)})$.
  3. Draw $\theta_2^{(r)}$ from $\:p(\theta_2|y,\:\theta_1^{(r-1)})$.
  4. Draw $\theta_1^{(r)}$ from $\:p(\theta_1|y,\:\theta_2^{(r)})$.
  5. Repeat 2-3 for desired number of iterations.
  6. Final calculations including means, standard deviations and plots.

Posterior distribution

In this tutorial, we consider a bivariate normal posterior distribution such that

$$ \begin{eqnarray*} \begin{pmatrix} \theta_1\\ \theta_2 \end{pmatrix} & \sim & N\left[\left(\begin{array}{c} 0\\ 0 \end{array}\right),\left(\begin{array}{ccc} 1 & \rho\\ \rho & 1 \end{array}\right)\right]\\ \end{eqnarray*} $$

where $\theta_1$ and $\theta_2$ are unknown parameters of the model, while $\rho$ is the known posterior correlation between $\theta_1$ and $\theta_2$.

Using the properties of the multivariate normal distribution

$$\theta_1|\theta_2,\: y \text{ ~ } N(\rho\theta_2,\: 1-\rho^2) \text{ ~ } \rho\theta_2 + \sqrt{1-\rho^2}N(0,\:1)$$

and

$$\theta_2|\theta_1,\: y \text{ ~ } N(\rho\theta_1,\: 1-\rho^2) \text{ ~ } \rho\theta_1 + \sqrt{1-\rho^2}N(0,\:1)$$

Set Gibbs sampler options

Our first step is to set the specifications of the Gibbs sampler such that:

  1. We have 10,000 total draws.
  2. We will make 1000 burn-in draws.
  3. The known $\rho$ is equal to 0.6.
/*
** Gibbs Sampler Specifications
** Known correlation parameter
*/
rho = 0.6;      

// Burn-in for the Gibbs sampler
burn_in = 1000;        

// Draws to keep from sampler
keep_draws = 10000;      

Pre-initialize vectors to hold the draws

Next, we need to initialize the storage matrices for our parameters, $\theta_1$ and $\theta_2$. The GAUSS function zeros allows us to build zero matrices with specified numbers of rows and columns.

// Initialize the variables 
theta_1_do = zeros(burn_in + keep_draws, 1);        
theta_2_do = zeros(burn_in + keep_draws, 1);

Set initial values

The last step before starting our Gibbs sampler is to set our initial values for our parameters. In this example, we will start with $\theta_1^{(1)} = \theta_2^{(1)} = 0$. As we have already set our matrices to zero for all iterations, we can effectively set our initial values by starting our loop at r = 2.

Run Gibbs sampler using a for loop

The for loop offers a method for looping which, in general, is the preferred method for situations like the Gibbs sampler. The for loop often provides a fast, compact for iterative computations.

The for loop repeats a set of commands for a specified number of iterations based on a start, stop and step specification.

/*
** General anatomy of a 'for' loop
** 'i' is the loop counter
** Do not run this code snippet
*/
for i(start, stop, step);
    // loop body work

endfor;

Using this structure to run our Gibbs sampler:

// Initialize the variables for the loop to zero again
theta_1 = zeros(burn_in + keep_draws, 1);        
theta_2 = zeros(burn_in + keep_draws, 1);

// Set random seed for repeatable random numbers
rndseed 45352;

// Start for loop
for i(2, burn_in + keep_draws, 1);

    // Draw theta_2 given theta_1
    theta_2[i, 1] = ((1 - rho^2)^0.5)*rndn(1, 1) 
                     + rho*theta_1[i-1, 1];

    // Draw theta_1 given theta_2
    theta_1[i, 1] = ((1 - rho^2)^0.5)*rndn(1, 1) 
                     + rho*theta_2[i, 1];

endfor;

print "The first 5 values of 'theta_1' and 'theta_2' are:";
print theta_1[1:5]~theta_2[1:5];

The output reads:

The first 5 values of 'theta_1' and 'theta_2' are:
       0.0000000        0.0000000
     -0.65117043      -0.15106836
     -0.32630327       -1.6688055
     -0.98322799      -0.62352043
     -0.45213979      -0.24999669 

Plot the variable paths

To check the behavior of our Gibbs sampler, we can plot the path of our variables. We will first set the plot appearance using a GAUSS control structure.

// Declare 'myGibbsPlot` to be a plotControl structure
struct plotControl myGibbsPlot;

// Fill 'myGibbsPlot' with 'XY' defaults
myGibbsPLot = plotGetDefaults("xy");

// Set the text interpreter to LaTex for axis labels only
plotSetTextInterpreter(&myGibbsPlot, "latex", "axes");

// Set plot title
plotSetTitle(&myGibbsPlot, "Bivariate Normal Gibbs Sampler", "Arial", 18);

// Set axis labels
plotSetYLabel(&myGibbsPlot, "\\theta_1", "Arial", 14);

We will again combine two separate plots, one for $\theta_1$ and one for $\theta_2$ using the plotLayout command. In our first cell, we will plot the pay of $\theta_1$ against the iteration number:

/*
** Change plot layout to 2 x 1 grid and place
** next plot in 1st cell
*/
plotLayout(2, 1, 1);

// Create a sequential vector counting iteration numbers
x_plot = seqa(1, 1, burn_in + keep_draws);

// Plot theta_1
plotXY(myGibbsPlot, x_plot, theta_1);

Next, plot $\theta_2$ next to the graph of $\theta_1$:

// Turn off title for second plot
plotSetTitle(&myGibbsPlot, "");

// Set axis labels
plotSetYLabel(&myGibbsPlot, "\\theta_2", "Arial", 14);

/*
** Change plot layout to 2 x 1 grid and place
** next plot in 2nd cell
*/
plotLayout(2, 1, 2);

// Plot theta_1
plotXY(myGibbsPlot, x_plot, theta_2);

// Turn off plot grid for next plot
plotClearLayout();

The above code should produce a plot similar to the following:

Plot of bivariate normal gibbs sampler variable paths.

Finally, we finish our Gibbs sampler by examining the mean and variance-covariance matrix of the Gibbs sampler distribution.

// Output matrix concatenating theta_1 and theta_2
ygs = theta_1~theta_2;
ygs = ygs[burn_in+1:burn_in+keep_draws, .];

"---------------------------------------------------";
"Variance-Covariance Matrix from Gibbs Sampler";
"---------------------------------------------------";
"Sample Mean: ";; meanc(ygs);
"Sample Variance-Covariance Matrix: ";
vcx(ygs);

The above code should print the following output:

---------------------------------------------------
Variance-Covariance Matrix from Gibbs Sampler
---------------------------------------------------
Sample Mean:
    0.0060170250
    0.0032377421
Sample Variance-Covariance Matrix:

      0.97698091       0.58179239
      0.58179239       0.98180547

Conclusion

Congratulations! You have:

  • Used the Gibbs sampler to generate bivariate normal draws.
  • Created side-by-side plots of the parameter paths.
  • Calculated the drawn distribution's mean and variance-covariance matrix.

The next tutorial introduces the Metroplis Hastings sampler.

For convenience, the full program text is reproduced below.

/*
** Gibbs Sampler Specifications
** Known correlation parameter
*/
rho = 0.6;      

// Burn-in for the Gibbs sampler
burn_in = 1000;        

// Draws to keep from sampler
keep_draws = 10000;  

// Initialize the variables for the loop to zero again
theta_1 = zeros(burn_in + keep_draws, 1);        
theta_2 = zeros(burn_in + keep_draws, 1);

// Set random seed for repeatable random numbers
rndseed 45352;

// Start for loop
for i(2, burn_in + keep_draws, 1);

    // Draw theta_2 given theta_1
    theta_2[i, 1] = ((1 - rho^2)^0.5)*rndn(1, 1) 
                     + rho*theta_1[i-1, 1];

    // Draw theta_1 given theta_2
    theta_1[i, 1] = ((1 - rho^2)^0.5)*rndn(1, 1) 
                     + rho*theta_2[i, 1];

endfor;

print "The first 5 values of 'theta_1' and 'theta_2' are:";
print theta_1[1:5]~theta_2[1:5];

// Declare 'myGibbsPlot` to be a plotControl structure
struct plotControl myGibbsPlot;

// Fill 'myGibbsPlot' with 'XY' defaults
myGibbsPLot = plotGetDefaults("xy");

// Set the text interpreter to LaTex for axis labels only
plotSetTextInterpreter(&myGibbsPlot, "latex", "axes");

// Set plot title
plotSetTitle(&myGibbsPlot, "Bivariate Normal Gibbs Sampler", "Arial", 18);

// Set axis labels
plotSetYLabel(&myGibbsPlot, "\\theta_1", "Arial", 14);

/*
** Change plot layout to 2 x 1 grid and place
** next plot in 1st cell
*/
plotLayout(2, 1, 1);

// Create a sequential vector counting iteration numbers
x_plot = seqa(1, 1, burn_in + keep_draws);

// Plot theta_1
plotXY(myGibbsPlot, x_plot, theta_1);

/*
** Change plot layout to 2 x 1 grid and place
** next plot in 2nd cell
*/
plotLayout(2, 1, 2);

// Plot theta_1
plotXY(myGibbsPlot, x_plot, theta_2);

// Turn off plot grid for next plot
plotClearLayout();

// Output matrix concatenating theta_1 and theta_2
ygs = theta_1~theta_2;
ygs = ygs[burn_in+1:burn_in+keep_draws, .];

"---------------------------------------------------";
"Variance-Covariance Matrix from Gibbs Sampler";
"---------------------------------------------------";
"Sample Mean: ";; meanc(ygs);
"Sample Variance-Covariance Matrix: ";
vcx(ygs);

Have a Specific Question?

Get a real answer from a real person

Need Support?

Get help from our friendly experts.