---
title: "Lecture 13" 
subtitle: "Gaussian Process Models - Part 2"
date: "3/06/2018"
fontsize: 11pt
output: 
  beamer_presentation:
    theme: metropolis
    highlight: pygments
    fig_caption: false
    keep_tex: true
    latex_engine: xelatex
    includes:
      in_header: ../settings.tex
---

```{r setup, include=FALSE}
library(dplyr)
library(ggplot2)

set.seed(20180306)
```

```{r config, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  fig.width=7,
  fig.height=4.5,
  out.width="\\textwidth",
  fig.align="center",
  echo=TRUE,
  warning=FALSE
)

ggplot2::theme_set(ggplot2::theme_bw())

source("../util.R")
```

# EDA and GPs

## Variogram {.t}

When fitting a Gaussian process model, it is often difficult to fit the covariance parameters (hard to identify). Today we will discuss some EDA approaches for getting a sense of the values for the scale, range and nugget parameters.

. . .

From the spatial modeling literature the typical approach is to examine an *empirical variogram*, first we will define the *theoretical variogram* and its connection to the covariance.

\vspace{2mm}

. . .

Variogram:
$$ \begin{aligned}
2 \gamma(t_i, t_j) 
  &= Var(Y(t_i) - Y(t_j)) \\
\end{aligned}$$
where $\gamma(t_i, t_j)$ is known as the semivariogram.




## Some Properties of the theoretical Variogram / Semivariogram {.t}

* are non-negative 
\footnotesize $$\gamma(t_i, t_j) \geq 0$$ \normalsize

. . .

\vspace{2mm}

* are equal to 0 at distance 0
\footnotesize $$\gamma(t_i, t_i) = 0$$ \normalsize

. . .

\vspace{2mm}

* are symmetric 
\footnotesize $$\gamma(t_i, t_j) = \gamma(t_j, t_i)$$ \normalsize

. . .

\vspace{2mm}

* if there is no dependence then
\footnotesize $$2\gamma(t_i, t_j) = Var(Y(t_i)) + Var(Y(t_j)) \quad \text{ for all } i \ne j$$ \normalsize

. . .

\vspace{2mm}

* if the process *is not* stationary
\footnotesize
$$2\gamma(t_i, t_j) = Var\big(Y(t_i)\big) + Var\big(Y(t_j)\big) - 2 \, Cov\big(Y(t_i),Y(t_j)\big)$$

. . .

\vspace{2mm}

* if the process *is* stationary
\footnotesize
$$\begin{aligned}
2\gamma(t_i, t_j) 
  &= 2Var\big(Y(t_i)\big) - 2 \, Cov\big(Y(t_i),Y(t_j)\big)
\end{aligned}$$


## Empirical Semivariogram {.t}

We will assume that our process of interest is stationary, in which case we will parameterize the semivariagram in terms of $h = |t_i - t_j|$.

\vspace{3mm}

Empirical Semivariogram:
$$ \hat{\gamma}(h) = \frac{1}{2 \, N(h)} \sum_{|t_i-t_j| \in (h-\epsilon,h+\epsilon)} (Y(t_i) - Y(t_j))^2 $$

. . .

Practically, for any data set with $n$ observations there are ${n \choose 2} + n$ possible data pairs to examine. Each individually is not very informative, so we aggregate into bins and calculate the empirical semivariogram for each bin.  


## Connection to Covariance

<!--
$$\begin{aligned}
2\gamma(t_i, t_j) &= 2Var\big(Y(t_i)\big) - 2 \, Cov\big(Y(t_i),Y(t_j)\big) \\
\gamma(t_i, t_j) &= Var\big(Y(t_i)\big) - \, Cov\big(Y(t_i),Y(t_j)\big) \\
  &= \sigma^2 - \, \sigma^2 \exp\big(-(|t_i-t_j|\,l)^2\big)
\end{aligned}$$
-->


## Covariance vs Semivariogram - Exponential

```{r echo=FALSE, fig.height=4.5}
vals = expand.grid(
  d = seq(0, 2, length.out = 100),
  l = seq(1, 7, length.out = 10)
) %>%
  as.data.frame() %>%
  tbl_df()
```

```{r echo=FALSE, fig.height=3.5}
exp = rbind(
  mutate(vals, func="exp cov", y = exp_cov(d, l=l)),
  mutate(vals, func="exp semivar", y = exp_sv(d, l=l))
)

exp %>%
  mutate(l = as.factor(round(l,1))) %>%
  ggplot(aes(x=d, y=y, color=l)) +
    geom_line() +
    facet_wrap(~func, ncol=2)
```

## Covariance vs Semivariogram - Square Exponential

```{r echo=FALSE, fig.height=3.5}
sq_exp = rbind(
  mutate(vals, func="sq exp cov", y = sq_exp_cov(d, l=l)),
  mutate(vals, func="sq exp semivar", y = sq_exp_sv(d, l=l))
) 

sq_exp %>%
  mutate(l = as.factor(round(l,1))) %>%
  ggplot(aes(x=d, y=y, color=l)) +
    geom_line() +
    facet_wrap(~func, ncol=2)
```


## From last time

```{r echo=FALSE}
load(file="../Lec12/lec12_ex.Rdata")
base
```

## Empirical semivariogram - no bins / cloud

```{r echo=FALSE}
d_emp_cloud = d %>% emp_semivariogram(y,t)

d_emp_cloud %>%
  ggplot(aes(x=h, y=gamma)) +
  geom_point()
```

## Empirical semivariogram (binned)

```{r echo=FALSE}
d_emp = rbind(
  d %>% emp_semivariogram(y, t, bin=TRUE, binwidth=0.05)  %>% mutate(binwidth="binwidth=0.05"),
  d %>% emp_semivariogram(y, t, bin=TRUE, binwidth=0.075) %>% mutate(binwidth="binwidth=0.075"),
  d %>% emp_semivariogram(y, t, bin=TRUE, binwidth=0.1)   %>% mutate(binwidth="binwidth=0.1"),
  d %>% emp_semivariogram(y, t, bin=TRUE, binwidth=0.15)  %>% mutate(binwidth="binwidth=0.15")
)

d_emp %>%
  ggplot(aes(x=h, y=gamma)) +
  geom_point() +
  facet_wrap(~binwidth, nrow=2)
```


## Empirical semivariogram (binned + n)

```{r echo=FALSE}
d_emp %>%
  ggplot(aes(x=h, y=gamma, size=n)) +
  geom_point() +
  facet_wrap(~binwidth, nrow=2)
```

## Theoretical vs empirical semivariogram

After fitting the model last time we came up with a posterior median of $\sigma^2 = 1.89$ and $l=5.86$ for a square exponential covariance.

. . .

\scriptsize
$$ \begin{aligned}
Cov(h) &= \sigma^2 \exp\big(-(h\,l)^2\big) \\
\gamma(h) 
  &= \sigma^2 - \sigma^2 \exp\big(-(h\,l)^2\big) \\
  &= 1.89 - 1.89 \exp\big(-(5.86\, h)^2\big)
\end{aligned}$$

. . .

```{r echo=FALSE, fig.height=3}
d_fit = data_frame(h=seq(0,1,length.out = 100)) %>%
  mutate(gamma = 1.89 - 1.89 * exp(-(5.86*h)^2))

d_emp %>%
  filter(binwidth %in% c("binwidth=0.05", "binwidth=0.1")) %>%
  ggplot(aes(x=h, y=gamma)) +
  geom_point() +
  geom_line(data=d_fit, color='red') +
  facet_wrap(~binwidth, ncol=2)
```

## Variogram features

\begin{center}
\includegraphics[width=0.7\textwidth]{figs/variogram.png}
\end{center}


# PM2.5 Example

## FRN Data

Measured PM2.5 data from an EPA monitoring station in Columbia, NJ.

```{r echo=FALSE}
load("../Lec01/data/frn_example.Rdata")

pm25 = pm25 %>%
  mutate(Date = lubridate::mdy(Date)) %>%
  mutate(day  = (Date - lubridate::mdy("1/1/2007") + 1) %>% as.integer()) %>% 
  select(-POC) %>%
  setNames(., tolower(names(.)))

ggplot(pm25, aes(x=date, y=pm25)) +
  geom_line() +
  geom_point()
```

## FRN Data

\footnotesize
```{r echo=FALSE}
pm25 %>% select(-day,-date, date, day) %>% head(n=20) %>% knitr::kable()
```

## Mean Model

```{r echo=FALSE, fig.height=3.5}
ggplot(pm25, aes(x=day, y=pm25)) +
  geom_line() +
  geom_point() +
  geom_smooth(method="lm", formula=y~x+I(x^2), se=FALSE, color="red", fullrange=TRUE)

(l=lm(pm25~day+I(day^2), data=pm25))
l
```

## Detrended Residuals

```{r echo=FALSE}
pm25 = pm25 %>%
  modelr::add_residuals(l)

ggplot(pm25, aes(x=day, y=resid)) +
  geom_line() +
  geom_point() + 
  labs(title="Residuals")
```

## Empirical Variogram

```{r echo=FALSE}
rbind(
  pm25 %>% emp_semivariogram(resid, day, bin=TRUE, binwidth=3) %>% mutate(bw = "binwidth=3"),
  pm25 %>% emp_semivariogram(resid, day, bin=TRUE, binwidth=6) %>% mutate(bw = "binwidth=6")
) %>%
  ggplot(aes(x=h, y=gamma, size=n)) +
    geom_point(alpha=0.5) +
    facet_wrap(~bw, ncol=2)
```

## Empirical Variogram

```{r echo=FALSE}
rbind(
  pm25 %>% emp_semivariogram(resid, day, bin=TRUE, binwidth=3, range_max=180) %>% mutate(bw = "binwidth=6"),
  pm25 %>% emp_semivariogram(resid, day, bin=TRUE, binwidth=6, range_max=180) %>% mutate(bw = "binwidth=9")
) %>%
  ggplot(aes(x=h, y=gamma, size=n)) +
    geom_point(alpha=0.5) +
    ylim(0,NA) +
    facet_wrap(~bw, ncol=2)
```

## Model {.t}

What does the model we are trying to fit actually look like?

. . .

\vspace{2mm}

$$ y(t) = \mu(t) + w(t) + \epsilon(t) $$
where

$$\begin{aligned}
\symbf{\mu(t)} &= \beta_0 + \beta_1\, \symbf{t} +\beta_2\, \symbf{t}^2\\
\symbf{w(t)} &\sim \mathcal{GP}(0, \symbf{\Sigma}) \\
\epsilon(t) &\sim \mathcal{N}(0, \sigma^2_w)
\end{aligned}$$

 


## JAGS Model

\scriptoutput
```{r}
gp_exp_model = "model{
  y ~ dmnorm(mu, inverse(Sigma))

  for (i in 1:N) {
    mu[i] <- beta[1]+ beta[2] * x[i] + beta[3] * x[i]^2
  }
  
  for (i in 1:(N-1)) {
    for (j in (i+1):N) {
      Sigma[i,j] <- sigma2 * exp(- pow(l*d[i,j],2))
      Sigma[j,i] <- Sigma[i,j]
    }
  }

  for (k in 1:N) {
    Sigma[k,k] <- sigma2 + sigma2_w
  }

  for (i in 1:3) {
    beta[i] ~ dt(0, 2.5, 1)
  }
  sigma2_w ~ dnorm(10, 1/25) T(0,)
  sigma2   ~ dnorm(10, 1/25) T(0,)
  l        ~ dt(0, 2.5, 1) T(0,) 
}"
```

```{r echo=FALSE}
if (file.exists("gp_jags.Rdata"))
{
  load(file="gp_jags.Rdata")
} else {
  m = rjags::jags.model(
    textConnection(gp_exp_model), 
    data = list(
      y = pm25$pm25,
      x = pm25$day,
      d = dist(pm25$day) %>% as.matrix(),
      N = nrow(pm25)
    ),
    quiet = TRUE
  )

  update(m, n.iter=5000)#, progress.bar="none")

  exp_cov_coda = rjags::coda.samples(
    m, variable.names=c("beta", "sigma2", "l", "sigma2_w"),
    n.iter=10000, thin=10#, progress.bar="none"
  )
  save(exp_cov_coda, file="gp_jags.Rdata")
}
```

## Posterior - Betas

```{r echo=FALSE}
betas = tidybayes::gather_samples(exp_cov_coda, beta[i]) %>%
  ungroup() %>%
  mutate(term = paste0(term, "[",i,"]")) %>%
  select(-i)

betas %>%
  group_by(term) %>%
  slice(seq(1,n(),length.out=500)) %>%
  ggplot(aes(x=.iteration, y=estimate, color=term)) +
    geom_line() +
    facet_grid(term~., scales = "free_y")
```


## Posterior - Covariance Parameters

```{r echo=FALSE}
params = tidybayes::gather_samples(exp_cov_coda, sigma2, l, sigma2_w)

params %>%
  slice(seq(1,n(),length.out=500)) %>%
  ggplot(aes(x=.iteration, y=estimate, color=term)) +
    geom_line() +
    facet_grid(term~., scales="free_y")
```

## Posterior - Covariance Parameters

```{r echo=FALSE}
params %>%
  slice(seq(1,n(),length.out=500)) %>%
  ggplot(aes(x=estimate, fill=term)) +
    geom_density() +
    facet_wrap(~term, scales="free")
```

## Posterior 

```{r echo=FALSE}
post = bind_rows(betas, params) %>%
  group_by(term) %>%
  summarize(
    post_mean = mean(estimate),
    post_med  = median(estimate),
    post_lower = quantile(estimate, probs = 0.025),
    post_upper = quantile(estimate, probs = 0.975)
  )

knitr::kable(post, digits = 3)
```


## Empirical + Fitted Variogram 

```{r echo=FALSE, fig.height=4}
l = post %>% filter(term == 'l') %>% pull(post_med)
sigma2 = post %>% filter(term == 'sigma2') %>% pull(post_med)
sigma2_w = post %>% filter(term == 'sigma2_w') %>% pull(post_med)

beta0 = post %>% filter(term == 'beta[1]') %>% pull(post_med)
beta1 = post %>% filter(term == 'beta[2]') %>% pull(post_med)
beta2 = post %>% filter(term == 'beta[3]') %>% pull(post_med)

pm25 = pm25 %>% mutate(bayes_resid = pm25 - beta0 - beta1 * day - beta2 * day^2)

d_fit = data_frame(h=1:180) %>% mutate(gamma = sq_exp_sv(h, sigma2, l, sigma2_w))

rbind(
  pm25 %>% emp_semivariogram(bayes_resid, day, bin=TRUE, binwidth=3, range_max=180) %>% mutate(bw = "binwidth=3"),
  pm25 %>% emp_semivariogram(bayes_resid, day, bin=TRUE, binwidth=6, range_max=180) %>% mutate(bw = "binwidth=6")
) %>%
  ggplot(aes(x=h, y=gamma)) +
    geom_point() +
    ylim(0,NA) +
    geom_line(data=d_fit, color='red') +
    facet_wrap(~bw, ncol=2) 
```


## Fitted Model + Predictions

```{r echo=FALSE}
reps=1000

x = pm25$day
y = pm25$pm25
x_pred = 1:365 + rnorm(365, 0.01)

mu = beta0 + beta1*x + beta2*x^2
mu_pred = beta0 + beta1*x_pred + beta2*x_pred^2

dist_o = fields::rdist(x)
dist_p = fields::rdist(x_pred)
dist_op = fields::rdist(x, x_pred)
dist_po = t(dist_op)
  
cov_o  = sq_exp_cov(dist_o,  sigma2 = sigma2, l = l, sigma2_w = sigma2_w)
cov_p  = sq_exp_cov(dist_p,  sigma2 = sigma2, l = l, sigma2_w = sigma2_w)
cov_op = sq_exp_cov(dist_op, sigma2 = sigma2, l = l, sigma2_w = sigma2_w)
cov_po = sq_exp_cov(dist_po, sigma2 = sigma2, l = l, sigma2_w = sigma2_w)

cond_cov = cov_p - cov_po %*% solve(cov_o) %*% cov_op
cond_mu  = mu_pred + cov_po %*% solve(cov_o) %*% (y - mu)
  
pred = cond_mu %*% matrix(1, ncol=reps) + t(chol(cond_cov)) %*% matrix(rnorm(length(x_pred)*reps), ncol=reps)

pred_df = pred %>% t() %>% post_summary() %>% mutate(day=x_pred)

ggplot(pm25, aes(x=day, y=pm25)) +
  geom_line() +
  geom_point() +
  geom_ribbon(data=pred_df, aes(ymin=post_lower,ymax=post_upper, x=day, y=post_med), fill="red", alpha=0.1)+
  geom_line(data=pred_df, aes(y=post_mean), color='red', size=1)
```


## Empirical Variogram Model

```{r echo=FALSE, fig.height=4}
beta0 = 12.9644351       
beta1 = -0.0724639
beta2 = 0.0001751

pm25 = pm25 %>% mutate(bayes_resid = pm25 - beta0 - beta1 * day - beta2 * day^2)

rbind(
  pm25 %>% emp_semivariogram(bayes_resid, day, bin=TRUE, binwidth=3, range_max=180) %>% mutate(bw = "binwidth=3"),
  pm25 %>% emp_semivariogram(bayes_resid, day, bin=TRUE, binwidth=6, range_max=180) %>% mutate(bw = "binwidth=6")
) %>%
  ggplot(aes(x=h, y=gamma)) +
    geom_point() +
    ylim(0,NA) +
    facet_wrap(~bw, ncol=2) 
```

## Fit

```{r echo=FALSE}
sigma2_w_emp = 8
sigma2_emp = 4
l_emp = sqrt(3)/5

d_fit_gp = data_frame(h=1:180) %>% mutate(gamma = sq_exp_sv(h, sigma2, l, sigma2_w))
d_fit_emp = data_frame(h=1:180) %>% mutate(gamma = sq_exp_sv(h, sigma2_emp, l_emp, sigma2_w_emp))

rbind(
  pm25 %>% emp_semivariogram(bayes_resid, day, bin=TRUE, binwidth=3, range_max=180) %>% mutate(bw = "binwidth=3"),
  pm25 %>% emp_semivariogram(bayes_resid, day, bin=TRUE, binwidth=6, range_max=180) %>% mutate(bw = "binwidth=6")
) %>%
  ggplot(aes(x=h, y=gamma)) +
    geom_point() +
    ylim(0,NA) +
    geom_line(data=d_fit_emp, color='red') +
    facet_wrap(~bw, ncol=2) 
```

## Predictions

```{r echo=FALSE}
reps=1000

x = pm25$day
y = pm25$pm25
x_pred = 1:365 + rnorm(365, 0.01)

mu = beta0 + beta1*x + beta2*x^2
mu_pred = beta0 + beta1*x_pred + beta2*x_pred^2

dist_o = fields::rdist(x)
dist_p = fields::rdist(x_pred)
dist_op = fields::rdist(x, x_pred)
dist_po = t(dist_op)
  
cov_o  = sq_exp_cov(dist_o,  sigma2 = sigma2, l = l, sigma2_w = sigma2_w)
cov_p  = sq_exp_cov(dist_p,  sigma2 = sigma2, l = l, sigma2_w = sigma2_w)
cov_op = sq_exp_cov(dist_op, sigma2 = sigma2, l = l, sigma2_w = sigma2_w)
cov_po = sq_exp_cov(dist_po, sigma2 = sigma2, l = l, sigma2_w = sigma2_w)

cond_cov = cov_p - cov_po %*% solve(cov_o) %*% cov_op
cond_mu  = mu_pred + cov_po %*% solve(cov_o) %*% (y - mu)
  
pred = cond_mu %*% matrix(1, ncol=reps) + t(chol(cond_cov)) %*% matrix(rnorm(length(x_pred)*reps), ncol=reps)

pred_df_emp = pred %>% t() %>% post_summary() %>% mutate(day=x_pred)

ggplot(pm25, aes(x=day, y=pm25)) +
  geom_line() +
  geom_point() +
  geom_line(data=pred_df, aes(y=post_mean), color='red', size=1) +
  geom_line(data=pred_df_emp, aes(y=post_mean), color='blue', size=1) +
  geom_ribbon(data=pred_df, aes(ymin=post_lower,ymax=post_upper, x=day, y=post_med), fill="blue", alpha=0.1)+
  geom_ribbon(data=pred_df_emp, aes(ymin=post_lower,ymax=post_upper, x=day, y=post_med), fill="red", alpha=0.1)
``` 


# Full Posterior Predictive Distribution

## Plug in Prediction {.t} 

\scriptoutput
```{r eval=FALSE}
l        = filter(post, term == 'l')        %>% pull(post_med)
sigma2   = filter(post, term == 'sigma2')   %>% pull(post_med)
sigma2_w = filter(post, term == 'sigma2_w') %>% pull(post_med)
beta0    = filter(post, term == 'beta[1]') %>% pull(post_med)
beta1    = filter(post, term == 'beta[2]') %>% pull(post_med)
beta2    = filter(post, term == 'beta[3]') %>% pull(post_med)
   
reps=1000

x = pm25$day
y = pm25$pm25
x_pred = 1:365 + rnorm(365, 0.01)

mu = beta0 + beta1*x + beta2*x^2
mu_pred = beta0 + beta1*x_pred + beta2*x_pred^2

dist_o = fields::rdist(x)
dist_p = fields::rdist(x_pred)
dist_op = fields::rdist(x, x_pred)
dist_po = t(dist_op)
  
cov_o  = sq_exp_cov(dist_o,  sigma2 = sigma2, l = l) + nugget_cov(dist_o, sigma2 = sigma2_w)
cov_p  = sq_exp_cov(dist_p,  sigma2 = sigma2, l = l) + nugget_cov(dist_p, sigma2 = sigma2_w)
cov_op = sq_exp_cov(dist_op, sigma2 = sigma2, l = l) + nugget_cov(dist_op, sigma2 = sigma2_w)
cov_po = sq_exp_cov(dist_po, sigma2 = sigma2, l = l) + nugget_cov(dist_po, sigma2 = sigma2_w)

inv = solve(cov_o, cov_op)
cond_cov = cov_p - cov_po %*% inv
cond_mu  = mu_pred + t(inv) %*% (y - mu)
  
pred = cond_mu %*% matrix(1, ncol=reps) + t(chol(cond_cov)) %*% matrix(rnorm(length(x_pred)*reps), ncol=reps)
```

## Full Posterior Predictive Distribution {.t}

Our posterior consists of samples from
$$ l, \sigma^2, \sigma^2_w, \beta_0, \beta_1, \beta_2 ~|~ \symbf{y} $$

and for the purposes of generating the posterior predictions we sampled
$$ \symbf{y}_{pred} ~|~ l^{(m)}, {\sigma^2}^{(m)}, {\sigma^2_w}^{(m)}, {\beta_0}^{(m)}, {\beta_1}^{(m)}, {\beta_2}^{(m)}, \symbf{y} $$
where $l^{(m)}, \ldots, {\beta_2}^{(m)}$, etc. are the posterior median of that parameter. 

. . .

\vspace{5mm}

In practice we should instead be sampling 

$$ \symbf{y}^{(i)}_{pred} ~|~ l^{(i)}, {\sigma^2}^{(i)}, {\sigma^2_w}^{(i)}, {\beta_0}^{(i)}, {\beta_1}^{(i)}, {\beta_2}^{(i)}, \symbf{y} $$
since this takes into account the additional uncertainty in the model parameters.

```{r echo=FALSE}
if (!file.exists("gp_pred.Rdata")) {
  x = pm25$day
  y = pm25$pm25
  
  n_post_samp = 1000
  
  x_pred = 1:365 + rnorm(365, 0, 0.01)
  y_pred = matrix(NA, nrow=n_post_samp, ncol=length(x_pred))
  colnames(y_pred) = paste0("Y_pred[", round(x_pred,0), "]")
  
  dist_o  = fields::rdist(x)
  dist_p  = fields::rdist(x_pred)
  dist_op = fields::rdist(x, x_pred)
  dist_po = t(dist_op)

  p = progress_estimated(n_post_samp)
  
  for(i in 1:n_post_samp) {
    l        = filter(params, .iteration == i, term == 'l')        %>% pull(estimate)
    sigma2   = filter(params, .iteration == i, term == "sigma2")   %>% pull(estimate)
    sigma2_w = filter(params, .iteration == i, term == "sigma2_w") %>% pull(estimate)
    beta0    = filter(betas,  .iteration == i, term == "beta[1]")  %>% pull(estimate)
    beta1    = filter(betas,  .iteration == i, term == "beta[2]")  %>% pull(estimate)
    beta2    = filter(betas,  .iteration == i, term == "beta[3]")  %>% pull(estimate)
    
    #cat(l,sigma2, sigma2_w, beta0, beta1, beta2,"\n", sep=", ")
    
    mu = beta0 + beta1*x + beta2*x^2
    mu_pred = beta0 + beta1*x_pred + beta2*x_pred^2
    
    cov_o  = sq_exp_cov(dist_o,  sigma2 = sigma2, l = l) + nugget_cov(dist_o,  sigma2 = sigma2_w)
    cov_p  = sq_exp_cov(dist_p,  sigma2 = sigma2, l = l) + nugget_cov(dist_p,  sigma2 = sigma2_w)
    cov_op = sq_exp_cov(dist_op, sigma2 = sigma2, l = l) + nugget_cov(dist_op, sigma2 = sigma2_w)
    cov_po = sq_exp_cov(dist_po, sigma2 = sigma2, l = l) + nugget_cov(dist_po, sigma2 = sigma2_w)
    
    inv = solve(cov_o, cov_op)
    cond_cov = cov_p - cov_po %*% inv
    cond_mu  = mu_pred + t(inv) %*% (y - mu)
      
    y_pred[i,] = cond_mu + t(chol(cond_cov)) %*% matrix(rnorm(length(x_pred)), ncol=1)
    
    p$tick()$print()
  }
  
  y_pred_df = y_pred %>% post_summary() %>% mutate(x_pred = round(x_pred,0))
  save(x_pred, y_pred, y_pred_df, file="gp_pred.Rdata")
} else {
  load("gp_pred.Rdata")
}
```

## Full Posterior Predictive Distribution - Plots

```{r echo=FALSE}
draws = data.frame(
  x_pred = round(x_pred,0), 
  t(y_pred[1:6,]) %>% as.data.frame() %>%setNames(paste("Iter", 1:6))
) %>%
  tidyr::gather(iter, y_pred, -x_pred)

ggplot(pm25, aes(x=day, y=pm25)) +
  geom_point() +
  geom_line() +
  geom_line(data=draws, aes(x=x_pred, y=y_pred, col=iter), alpha=0.75) +
  geom_line(data=y_pred_df, aes(x=x_pred, y=post_mean), color='red', size=1) +
  facet_wrap(~iter)
```


## Full Posterior Predictive Distribution - Median + CI

```{r echo=FALSE}
ggplot(pm25, aes(x=day, y=pm25)) +
  geom_line() +
  geom_point() +
  geom_ribbon(data=y_pred_df, aes(ymin=post_lower, ymax=post_upper, x=x_pred, y=post_mean), fill="red", alpha=0.1)+
  geom_line(data=y_pred_df, aes(x=x_pred, y=post_med), color='red', size=1)
```




