Skip to content

Commit b08c4f1

Browse files
committed
restructured into new Section 7.2.5 with mode/curvature matching
1 parent 1aeb427 commit b08c4f1

1 file changed

Lines changed: 153 additions & 95 deletions

File tree

vignettes/Chapter07.Rmd

Lines changed: 153 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ names(ymiss) <- names(logret)[missing]
395395
For simplicity, we employ our improper prior and sample iteratively.
396396

397397
```{r}
398-
ndraws <- 5000
398+
ndraws <- 500000
399399
nburn <- 1000
400400
ind <- missing - 2
401401
@@ -720,31 +720,15 @@ for (m in seq_len(ndraws + nburn)) {
720720
phis[m - nburn] <- phi
721721
}
722722
}
723-
res1 <- data.frame(y0 = y0s, sigma2 = sigma2s, zeta = zetas, phi = phis)
723+
stationary1 <- data.frame(zeta = zetas, phi = phis, sigma2 = sigma2s, y0 = y0s)
724724
naccepts1 <- naccepts
725725
```
726726

727-
Let us investigate traceplots and empirical autocorrelation functions of the
728-
draws. In addition, we check the percentage of accepted draws in MH-step (d).
727+
We repeat the exercise with a more informative beta prior.
729728

730729
```{r}
731-
plot.ts(res1, xlab = "Draws after burn-in",
732-
main = paste0("MH acceptance rate: ", 100 * naccepts1 / ndraws, "%"))
733-
acf(res1, ylab = "")
734-
```
735-
736-
We now repeat this exercise, but use the conditional posterior resulting
737-
from an auxiliary moment-matched prior in Step (d).
738-
739-
```{r}
740-
# Compute mean and variance of the actual prior using properties of the beta
741-
priormean <- 2 * aphi / (aphi + bphi) - 1
742-
priorvar <- 4 * (aphi * bphi) / ((aphi + bphi)^2 * (aphi + bphi + 1))
743-
744-
# Define the design matrix and y
745-
X <- matrix(NA_real_, nrow = length(y), 2)
746-
X[, 1] <- 1
747-
X[, 2] <- c(NA_real_, y[-length(y)])
730+
aphi <- 20
731+
bphi <- 1.5
748732
749733
# Allocate some space for the posterior draws and initialize the parameters:
750734
y0s <- sigma2s <- zetas <- phis <- rep(NA_real_, ndraws)
@@ -772,18 +756,15 @@ for (m in seq_len(ndraws + nburn)) {
772756
773757
# Step (d): Draw the persistence
774758
tmp <- y0^2 + sum(y[-length(y)]^2)
775-
propvar <- 1 / (1 / priorvar + tmp / sigma2)
776-
propmean <- propvar * (priormean / priorvar + (y0 * (y[1] - zeta) +
777-
sum(y[-length(y)] * (y[-1] - zeta))) / sigma2)
759+
propmean <- (y0 * (y[1] - zeta) + sum(y[-length(y)] * (y[-1] - zeta))) / tmp
760+
propvar <- sigma2 / tmp
778761
phiprop <- rnorm(1, propmean, sqrt(propvar))
779762
if (-1 < phiprop & phiprop < 1) {
780763
logR <- dbetarescaled(phiprop, aphi, bphi, log = TRUE) -
781764
dbetarescaled(phi, aphi, bphi, log = TRUE) +
782765
dnorm(y0, zeta / (1 - phiprop), sqrt(sigma2 / (1 - phiprop^2)),
783766
log = TRUE) -
784-
dnorm(y0, zeta / (1 - phi), sqrt(sigma2 / (1 - phi^2)), log = TRUE) +
785-
dnorm(phi, priormean, sqrt(priorvar), log = TRUE) -
786-
dnorm(phiprop, priormean, sqrt(priorvar), log = TRUE)
767+
dnorm(y0, zeta / (1 - phi), sqrt(sigma2 / (1 - phi^2)), log = TRUE)
787768
if (log(runif(1)) < logR) {
788769
phi <- phiprop
789770
if (m > nburn) naccepts <- naccepts + 1L
@@ -798,58 +779,111 @@ for (m in seq_len(ndraws + nburn)) {
798779
phis[m - nburn] <- phi
799780
}
800781
}
801-
res2 <- data.frame(y0 = y0s, sigma2 = sigma2s, zeta = zetas, phi = phis)
782+
stationary2 <- data.frame(zeta = zetas, phi = phis, sigma2 = sigma2s, y0 = y0s)
802783
naccepts2 <- naccepts
803784
```
804785

805-
Again, we investigate traceplots and empirical autocorrelation functions of the
806-
draws. In addition, we check the percentage of accepted draws in MH-step (d).
786+
To conclude, we compare the posteriors of $\phi$ under the improper prior,
787+
the posterior obtained after post-processing draws to obtain stationarity,
788+
and the posterior under the stationary-enforcing shifted beta priors.
807789

808-
```{r}
809-
plot.ts(res2, xlab = "Draws after burn-in",
810-
main = paste0("MH acceptance rate: ", 100 * naccepts2 / ndraws, "%"))
811-
acf(res2, ylab = "")
790+
```{r, echo = -c(1:2)}
791+
if (pdfplots) {
792+
pdf("7-2_12.pdf", width = 8, height = 5)
793+
}
794+
par(mfrow = c(1, 1), mar = c(2.5, 2.5, 1.5, .1), mgp = c(1.5, .5, 0), lwd = 1.5)
795+
mybreaks <- seq(floor(100 * .99 * min(stationary2$phi)) / 100,
796+
ceiling(100 * 1.01 * max(ar1draws)) / 100,
797+
by = .0025)
798+
hist(ar1draws[!nonstationary[, 1]], breaks = mybreaks, col = rgb(0, 0, 1, .2),
799+
main = "Histogram of posterior draws", xlab = expression(phi),
800+
freq = FALSE)
801+
hist(ar1draws, breaks = mybreaks, col = rgb(0, 1, 0, .2),
802+
freq = FALSE, add = TRUE)
803+
hist(stationary1$phi, breaks = mybreaks, col = rgb(1, 0, 0, .2),
804+
freq = FALSE, add = TRUE)
805+
hist(stationary2$phi, breaks = mybreaks, col = rgb(1, 1, 0, .2),
806+
freq = FALSE, add = TRUE)
807+
lines(mybreaks[mybreaks <= 1], lty = 3, lwd = 3,
808+
dbetarescaled(mybreaks[mybreaks <= 1], 1, 1))
809+
lines(mybreaks[mybreaks <= 1], lty = 2, lwd = 3,
810+
dbetarescaled(mybreaks[mybreaks <= 1], aphi, bphi))
811+
legend("topright",
812+
c("Unrestricted posterior", "Post-processed posterior",
813+
"Beta prior posterior (flat)", "Beta prior posterior (informative)"),
814+
fill = rgb(c(0, 0, 1, 1), c(1, 0, 0, 1), c(0, 1, 0, 0), .2))
815+
legend("topleft", c("Beta prior (flat)", "Beta prior (informative)"),
816+
col = 1, lty = 3:2, lwd = 3)
812817
```
813818

814-
We now compare the draws from the two samplers; they should yield
815-
draws from the same distribution, irrespective of the acceptance rate and thus
816-
the mixing of the Markov chain. We graphically
817-
check this by comparing histograms and quantiles of draws from the marginal
818-
posterior of $\phi$.
819+
## Section 7.2.5: Evaluating the Efficiency of an MCMC Sampler
820+
### Example 7.11: Improving the independence MH step
819821

820-
```{r, echo = -c(1:2), fig.width = 8, fig.height = 4}
822+
Let us investigate traceplots and empirical autocorrelation functions of the
823+
posterior draws under the informative stationarity-inducing prior.
824+
825+
```{r, echo = -c(1:2)}
821826
if (pdfplots) {
822-
pdf("7-2_11.pdf", width = 8, height = 4)
827+
pdf("7-2_13.pdf", width = 8, height = 5)
828+
}
829+
labels <- expression(zeta, phi, sigma^2, y[0])
830+
par(mfrow = c(4, 2), mar = c(2.5, 2.8, 1.5, .1), mgp = c(1.5, .5, 0), lwd = 1.5)
831+
for (i in seq_along(stationary2)) {
832+
plot.ts(stationary2[i], xlab = "Draws after burn-in", ylab = labels[i])
833+
if (i == 1) title("Traceplot")
834+
acf(stationary2[i], ylab = "", main = "ACF")
835+
if (i == 1) title("Empirical ACF")
823836
}
824-
par(mfrow = c(1, 2), mar = c(2.5, 2.5, 1.5, .1), mgp = c(1.5, .5, 0), lwd = 2)
825-
mybreaks <- seq(min(res1$phi, res2$phi), max(res1$phi, res2$phi),
826-
length.out = 50)
827-
hist(res1$phi, breaks = mybreaks, col = rgb(0, 0, 1, .3),
828-
main = "Histogram", xlab = expression(phi), freq = FALSE)
829-
hist(res2$phi, breaks = mybreaks, col = rgb(1, 0, 0, .3), freq = FALSE,
830-
add = TRUE)
831-
qqplot(res1$phi, res2$phi, xlab = "Sampler 1", ylab = "Sampler 2",
832-
main = "QQ plot")
833-
abline(0, 1, col = 2)
834837
```
835-
We can see that the draws appear to come from the same distribution. Note,
836-
however, that the first sampler gets "stuck" slightly below 0.93 for a few
837-
draws, which isn't the case for the second sampler.
838838

839-
We now re-run the second sampler with a more informative beta prior.
839+
We now compute an estimate of the effective sample size (ESS) and the
840+
inefficiency factor (IF) use the coda package.
840841

841842
```{r}
842-
aphi <- 20
843-
bphi <- 1.5
843+
library(coda)
844+
ess1 <- effectiveSize(data.frame(zeta = res2[[1]]$betas[, 1],
845+
phi = res2[[1]]$betas[, 2],
846+
sigma2 = res2[[1]]$sigma2s))
847+
ess2 <- effectiveSize(data.frame(zeta = res2[[1]]$betas[!nonstationary[, 1], 1],
848+
phi = res2[[1]]$betas[!nonstationary[, 1], 2],
849+
sigma2 = res2[[1]]$sigma2s[!nonstationary[, 1]]))
850+
ess3 <- effectiveSize(stationary1)
851+
ess4 <- effectiveSize(stationary2)
852+
ess <- rbind(unrestricted = c(ess1, y0 = NA),
853+
postprocessed = c(ess2, y0 = NA),
854+
betapriorflat = ess3,
855+
betapriorinformative = ess4)
856+
knitr::kable(round(ess))
857+
knitr::kable(round(ndraws / ess, 2))
858+
```
844859

845-
# Compute mean and variance of the actual prior using properties of the beta
846-
priormean <- 2 * aphi / (aphi + bphi) - 1
847-
priorvar <- 4 * (aphi * bphi) / ((aphi + bphi)^2 * (aphi + bphi + 1))
860+
We now repeat the above exercise, but use the conditional posterior resulting
861+
from an auxiliary moment-matched prior in Step (d).
848862

849-
# Define the design matrix and y
850-
X <- matrix(NA_real_, nrow = length(y), 2)
851-
X[, 1] <- 1
852-
X[, 2] <- c(NA_real_, y[-length(y)])
863+
```{r}
864+
method <- "fancy"
865+
if (method == "simple") {
866+
# Add .5 to aphi and bphi (from the determinant of the initial state prior)
867+
aphiprop <- aphi + .5
868+
bphiprop <- bphi + .5
869+
870+
# Compute mean and variance of the proposal using properties of the beta
871+
priormean <- 2 * aphiprop / (aphiprop + bphiprop) - 1
872+
priorvar <- 4 * (aphiprop * bphiprop) /
873+
((aphiprop + bphiprop)^2 * (aphiprop + bphiprop + 1))
874+
875+
} else if (method == "fancy") {
876+
877+
# Alternatively, mode-and-curvature-match:
878+
dprior <- function(phi, zeta, sigma2, y0, aphi, bphi, log = FALSE) {
879+
inside <- phi <= 1 & phi >= -1
880+
logdens <- -Inf
881+
logdens[inside] <- dbetarescaled(phi[inside], aphi, bphi, log = TRUE) +
882+
dnorm(y0, zeta / (1 - phi[inside]), sqrt(sigma2 / (1 - phi[inside]^2)),
883+
log = TRUE)
884+
if (log) logdens else exp(logdens)
885+
}
886+
}
853887
854888
# Allocate some space for the posterior draws and initialize the parameters:
855889
y0s <- sigma2s <- zetas <- phis <- rep(NA_real_, ndraws)
@@ -876,10 +910,21 @@ for (m in seq_len(ndraws + nburn)) {
876910
zeta <- rnorm(1, bT, sqrt(BT))
877911
878912
# Step (d): Draw the persistence
913+
914+
if (method == "fancy") {
915+
mode <- optimize(dprior, c(-1, 1), zeta = zeta, sigma2 = sigma2, y0 = y0,
916+
aphi = aphi, bphi = bphi, log = TRUE, maximum = TRUE)$maximum
917+
dd <- numDeriv::hessian(dprior, mode, zeta = zeta, sigma2 = sigma2, y0 = y0,
918+
aphi = aphi, bphi = bphi)
919+
priormean <- mode
920+
priorvar <- -1 / as.numeric(dd)
921+
}
922+
879923
tmp <- y0^2 + sum(y[-length(y)]^2)
880924
propvar <- 1 / (1 / priorvar + tmp / sigma2)
881-
propmean <- propvar * (priormean / priorvar +
882-
(y0 * (y[1] - zeta) + sum(y[-length(y)] * (y[-1] - zeta))) / sigma2)
925+
propmean <- propvar * (priormean / priorvar + (y0 * (y[1] - zeta) +
926+
sum(y[-length(y)] * (y[-1] - zeta))) / sigma2)
927+
883928
phiprop <- rnorm(1, propmean, sqrt(propvar))
884929
if (-1 < phiprop & phiprop < 1) {
885930
logR <- dbetarescaled(phiprop, aphi, bphi, log = TRUE) -
@@ -903,43 +948,56 @@ for (m in seq_len(ndraws + nburn)) {
903948
phis[m - nburn] <- phi
904949
}
905950
}
906-
res3 <- data.frame(y0 = y0s, sigma2 = sigma2s, zeta = zetas, phi = phis)
951+
stationary3 <- data.frame(zeta = zetas, phi = phis, sigma2 = sigma2s, y0 = y0s)
907952
naccepts3 <- naccepts
908953
```
909954

910-
To conclude, we compare the posteriors of $\phi$ under the improper prior,
911-
the posterior obtained after post-processing draws to obtain stationarity,
912-
and the posterior under the stationary-enforcing shifted beta priors.
955+
Again, we investigate traceplots and empirical autocorrelation functions of the
956+
draws. In addition, we check the percentage of accepted draws in MH-step (d).
913957

914-
```{r, echo = -c(1:2)}
958+
```{r}
959+
par(mfrow = c(4, 2), mar = c(2.5, 2.8, 1.5, .1), mgp = c(1.5, .5, 0), lwd = 1.5)
960+
for (i in seq_along(stationary3)) {
961+
plot.ts(stationary3[i], xlab = "Draws after burn-in", ylab = labels[i])
962+
if (i == 1) title("Traceplot")
963+
acf(stationary3[i], ylab = "", main = "ACF")
964+
if (i == 1) title("Empirical ACF")
965+
}
966+
```
967+
968+
We now compare the draws from the two samplers; they should yield
969+
draws from the same distribution, irrespective of the acceptance rate and thus
970+
the mixing of the Markov chain. We graphically
971+
check this by comparing histograms and quantiles of draws from the marginal
972+
posterior of $\phi$.
973+
974+
```{r, echo = -c(1:2), fig.width = 8, fig.height = 4}
915975
if (pdfplots) {
916-
pdf("7-2_12.pdf", width = 8, height = 5)
976+
pdf("7-2_14.pdf", width = 8, height = 4)
917977
}
918-
par(mfrow = c(1, 1), mar = c(2.5, 2.5, 1.5, .1), mgp = c(1.5, .5, 0), lwd = 1.5)
919-
mybreaks <- seq(floor(100 * .99 * min(res2$phi)) / 100,
920-
ceiling(100 * 1.01 * max(ar1draws)) / 100,
921-
by = .0025)
922-
hist(ar1draws[!nonstationary[, 1]], breaks = mybreaks, col = rgb(0, 0, 1, .2),
923-
main = "Histogram of posterior draws", xlab = expression(phi),
924-
freq = FALSE)
925-
hist(ar1draws, breaks = mybreaks, col = rgb(0, 1, 0, .2),
926-
freq = FALSE, add = TRUE)
927-
hist(res2$phi, breaks = mybreaks, col = rgb(1, 0, 0, .2),
928-
freq = FALSE, add = TRUE)
929-
hist(res3$phi, breaks = mybreaks, col = rgb(1, 1, 0, .2),
930-
freq = FALSE, add = TRUE)
931-
lines(mybreaks[mybreaks <= 1], lty = 3, lwd = 3,
932-
dbetarescaled(mybreaks[mybreaks <= 1], 1, 1))
933-
lines(mybreaks[mybreaks <= 1], lty = 2, lwd = 3,
934-
dbetarescaled(mybreaks[mybreaks <= 1], aphi, bphi))
935-
legend("topright",
936-
c("Unrestricted posterior", "Post-processed posterior",
937-
"Beta prior posterior (flat)", "Beta prior posterior (informative)"),
938-
fill = rgb(c(0, 0, 1, 1), c(1, 0, 0, 1), c(0, 1, 0, 0), .2))
939-
legend("topleft", c("Beta prior (flat)", "Beta prior (informative)"),
940-
col = 1, lty = 3:2, lwd = 3)
978+
par(mfrow = c(1, 2), mar = c(2.5, 2.5, 1.5, .1), mgp = c(1.5, .5, 0), lwd = 2)
979+
mybreaks <- seq(min(stationary2$phi, stationary3$phi),
980+
max(stationary2$phi, stationary3$phi),
981+
length.out = 50)
982+
hist(stationary2$phi, breaks = mybreaks, col = rgb(0, 0, 1, .3),
983+
main = "Histogram", xlab = expression(phi), freq = FALSE)
984+
hist(stationary3$phi, breaks = mybreaks, col = rgb(1, 0, 0, .3), freq = FALSE,
985+
add = TRUE)
986+
qqplot(stationary1$phi, stationary3$phi, xlab = "Sampler 1", ylab = "Sampler 2",
987+
main = "QQ plot")
988+
abline(0, 1, col = 2)
989+
```
990+
We can see that the draws appear to come from the same distribution. Note,
991+
however, that the first sampler gets "stuck" slightly below 0.93 for a few
992+
draws, which isn't the case for the second sampler.
993+
994+
```{r}
995+
library(coda)
996+
ess <- effectiveSize(stationary3)
997+
knitr::kable(rbind(ESS = round(ess), IF = round(ndraws / ess, 2)))
941998
```
942999

1000+
9431001
# Section 7.3: Some Extensions
9441002
## Section 7.3.1: AR Models with a Unit Root
9451003

0 commit comments

Comments
 (0)