library(ggplot2)
Customize the 2D Gaussian Target
means <- c(x=1, y=1)
vars <- c(x=1, y=1)
corr <- -0.6
coordinates <- c("x", "y") # names of the coordinates, should match the names in the means and vars vectors.
Define the Marginal Distributions
Since the marginal distributions of x and y are both normal, a single
function does the job.
# to sample the target p(x|y=1), use sample_marginal("x", 1)
sample_marginal <- function(sampled_coor = "x", other_value = 1){
other_coor <- coordinates[coordinates != sampled_coor] # "y" if sampled_var is "x"
marginal_mean <- means[sampled_coor] +
corr *
sqrt(vars[sampled_coor]/vars[other_coor]) *
(other_value - means[other_coor])
marginal_var <- vars[sampled_coor] * (1-corr^2)
return(rnorm(n=1, mean=marginal_mean, sd=sqrt(marginal_var)))
}
Define Each Iteration of Gibbs Sampling
# systematic scan Gibbs, simulates transition from prev to new
sys_gibbs_step <- function(prev, order = coordinates){
new <- prev
for (coordinate in order){
remaining_coord <- order[order != coordinate] # the coord. not being updated
new[coordinate] <- sample_marginal(sampled_var = coordinate,
other_value = prev[remaining_coord])
}
return(new)
}
# random scan Gibbs
random_gibbs_step <- function(prev){
new <- prev
coordinate <- sample(coordinates, size=1) # sample the coordinate to be updated
remaining_coor <- coordinates[coordinates != coordinate]
new[coordinate] <- sample_marginal(sampled_coor = coordinate,
other_value = prev[remaining_coor])
return(new)
}
Running Gibbs Sampling
# initialize
n <- 50000 # takes a while... use a smaller sample size if your computer dies.
samples <- data.frame(x = numeric(length = n),
y = numeric(length = n)) # each realization is 2d.
previous_state <- c(x = xmean, y = ymean)
for (k in 1:n){
previous_state <- random_gibbs_step(previous_state)
# previous_state <- sys_gibbs_step(previous_state)
samples[k,] <- previous_state
}
Plotting the Samples
Beware that this first plot looks like crap if your sample size is
small.
ggplot(data = samples, aes(x=x, y=y)) +
geom_bin2d(bins = sqrt(n)) +
scale_fill_continuous(type = "viridis") +
theme_minimal()

So use this instead.
ggplot(data = samples, aes(x=x, y=y)) +
geom_point(alpha = 5/sqrt(n)) +
theme_minimal()

LS0tCnRpdGxlOiAiR2liYnMgU2FtcGxpbmcgaW4gUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIEN1c3RvbWl6ZSB0aGUgMkQgR2F1c3NpYW4gVGFyZ2V0CmBgYHtyfQptZWFucyA8LSBjKHg9MSwgeT0xKQp2YXJzIDwtIGMoeD0xLCB5PTEpCmNvcnIgPC0gLTAuNgpjb29yZGluYXRlcyA8LSBjKCJ4IiwgInkiKSAjIG5hbWVzIG9mIHRoZSBjb29yZGluYXRlcywgc2hvdWxkIG1hdGNoIHRoZSBuYW1lcyBpbiB0aGUgbWVhbnMgYW5kIHZhcnMgdmVjdG9ycy4KYGBgCgojIERlZmluZSB0aGUgTWFyZ2luYWwgRGlzdHJpYnV0aW9ucwpTaW5jZSB0aGUgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucyBvZiB4IGFuZCB5IGFyZSBib3RoIG5vcm1hbCwgYSBzaW5nbGUgZnVuY3Rpb24gZG9lcyB0aGUgam9iLgpgYGB7cn0KIyB0byBzYW1wbGUgdGhlIHRhcmdldCBwKHh8eT0xKSwgdXNlIHNhbXBsZV9tYXJnaW5hbCgieCIsIDEpCnNhbXBsZV9tYXJnaW5hbCA8LSBmdW5jdGlvbihzYW1wbGVkX2Nvb3IgPSAieCIsIG90aGVyX3ZhbHVlID0gMSl7CiAgb3RoZXJfY29vciA8LSBjb29yZGluYXRlc1tjb29yZGluYXRlcyAhPSBzYW1wbGVkX2Nvb3JdICAjICJ5IiBpZiBzYW1wbGVkX3ZhciBpcyAieCIKICBtYXJnaW5hbF9tZWFuIDwtIG1lYW5zW3NhbXBsZWRfY29vcl0gKyAKICAgIGNvcnIgKiAKICAgIHNxcnQodmFyc1tzYW1wbGVkX2Nvb3JdL3ZhcnNbb3RoZXJfY29vcl0pICoKICAgIChvdGhlcl92YWx1ZSAtIG1lYW5zW290aGVyX2Nvb3JdKQogIAogIG1hcmdpbmFsX3ZhciA8LSB2YXJzW3NhbXBsZWRfY29vcl0gKiAoMS1jb3JyXjIpCiAgCiAgcmV0dXJuKHJub3JtKG49MSwgbWVhbj1tYXJnaW5hbF9tZWFuLCBzZD1zcXJ0KG1hcmdpbmFsX3ZhcikpKQp9CmBgYAoKIyBEZWZpbmUgRWFjaCBJdGVyYXRpb24gb2YgR2liYnMgU2FtcGxpbmcKYGBge3J9CiMgc3lzdGVtYXRpYyBzY2FuIEdpYmJzLCBzaW11bGF0ZXMgdHJhbnNpdGlvbiBmcm9tIHByZXYgdG8gbmV3IApzeXNfZ2liYnNfc3RlcCA8LSBmdW5jdGlvbihwcmV2LCBvcmRlciA9IGNvb3JkaW5hdGVzKXsKICBuZXcgPC0gcHJldgogIGZvciAoY29vcmRpbmF0ZSBpbiBvcmRlcil7CiAgICByZW1haW5pbmdfY29vcmQgPC0gb3JkZXJbb3JkZXIgIT0gY29vcmRpbmF0ZV0gICMgdGhlIGNvb3JkLiBub3QgYmVpbmcgdXBkYXRlZAogICAgbmV3W2Nvb3JkaW5hdGVdIDwtIHNhbXBsZV9tYXJnaW5hbChzYW1wbGVkX3ZhciA9IGNvb3JkaW5hdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJfdmFsdWUgPSBwcmV2W3JlbWFpbmluZ19jb29yZF0pCiAgfQogIHJldHVybihuZXcpCn0KCiMgcmFuZG9tIHNjYW4gR2liYnMKcmFuZG9tX2dpYmJzX3N0ZXAgPC0gZnVuY3Rpb24ocHJldil7CiAgbmV3IDwtIHByZXYKICBjb29yZGluYXRlIDwtIHNhbXBsZShjb29yZGluYXRlcywgc2l6ZT0xKSAjIHNhbXBsZSB0aGUgY29vcmRpbmF0ZSB0byBiZSB1cGRhdGVkCiAgcmVtYWluaW5nX2Nvb3IgPC0gY29vcmRpbmF0ZXNbY29vcmRpbmF0ZXMgIT0gY29vcmRpbmF0ZV0KICBuZXdbY29vcmRpbmF0ZV0gPC0gc2FtcGxlX21hcmdpbmFsKHNhbXBsZWRfY29vciA9IGNvb3JkaW5hdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJfdmFsdWUgPSBwcmV2W3JlbWFpbmluZ19jb29yXSkKICByZXR1cm4obmV3KQp9CmBgYAoKIyBSdW5uaW5nIEdpYmJzIFNhbXBsaW5nCmBgYHtyfQojIGluaXRpYWxpemUKbiA8LSA1MDAwMCAgIyB0YWtlcyBhIHdoaWxlLi4uIHVzZSBhIHNtYWxsZXIgc2FtcGxlIHNpemUgaWYgeW91ciBjb21wdXRlciBkaWVzLgpzYW1wbGVzIDwtIGRhdGEuZnJhbWUoeCA9IG51bWVyaWMobGVuZ3RoID0gbiksCiAgICAgICAgICAgICAgICAgICAgICB5ID0gbnVtZXJpYyhsZW5ndGggPSBuKSkgICMgZWFjaCByZWFsaXphdGlvbiBpcyAyZC4KcHJldmlvdXNfc3RhdGUgPC0gYyh4ID0geG1lYW4sIHkgPSB5bWVhbikKCmZvciAoayBpbiAxOm4pewogIHByZXZpb3VzX3N0YXRlIDwtIHJhbmRvbV9naWJic19zdGVwKHByZXZpb3VzX3N0YXRlKSAgCiAgIyBwcmV2aW91c19zdGF0ZSA8LSBzeXNfZ2liYnNfc3RlcChwcmV2aW91c19zdGF0ZSkKICBzYW1wbGVzW2ssXSA8LSBwcmV2aW91c19zdGF0ZQp9CmBgYAoKIyBQbG90dGluZyB0aGUgU2FtcGxlcwpCZXdhcmUgdGhhdCB0aGlzIGZpcnN0IHBsb3QgbG9va3MgbGlrZSBjcmFwIGlmIHlvdXIgc2FtcGxlIHNpemUgaXMgc21hbGwuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHNhbXBsZXMsIGFlcyh4PXgsIHk9eSkpICsKICBnZW9tX2JpbjJkKGJpbnMgPSBzcXJ0KG4pKSArCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKHR5cGUgPSAidmlyaWRpcyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgClNvIHVzZSB0aGlzIGluc3RlYWQuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHNhbXBsZXMsIGFlcyh4PXgsIHk9eSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gNS9zcXJ0KG4pKSArCiAgdGhlbWVfbWluaW1hbCgpIApgYGA=