5 Rethinking: Chapter 4
Geocentric Models
by Richard McElreath, building on the Summaries by Solomon Kurz and Jake Thompson.
5.1 Why normal distributions are normal
5.1.1 Normal by addition
<- 1e3
n_people <- crossing(person = 1:n_people,
position step = 0:16) %>%
mutate(deviation = map_dbl(step, ~if_else(. == 0, 0, runif(1, -1, 1)))) %>%
group_by(person) %>%
mutate(position = cumsum(deviation)) %>%
ungroup()
<- position %>%
p_all_steps ggplot(aes(x = step, y = position, group = person)) +
geom_line(aes(color = person == n_people)) +
geom_point(data = position %>% filter(person == n_people), aes(color = "TRUE"), size = 1) +
geom_vline(data = tibble(step = c(4, 8, 16)),
aes(xintercept = step), linetype = 3, color = rgb(0,0,0,.5)) +
scale_color_manual(values = c(`TRUE` = clr2, `FALSE` = clr_alpha(clr0d, .05)), guide = "none") +
scale_x_continuous(breaks = c(0,4,8,16)) +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank())
<- function(step_nr, data = position, add_ideal = FALSE){
plot_steps <- data %>%
data_step filter(step == step_nr)
<- data_step %>%
p ggplot(aes(x = position)) +
geom_density(adjust = .2, color = clr0d, fill = fll0) +
scale_x_continuous(limits = c(-6, 6)) +
labs(title = glue("{step_nr} steps"))
if(add_ideal){p <- p +
stat_function(fun = function(x){dnorm(x, mean = 0, sd = sd(data_step$position))},
n = 501, color = clr2, linetype = 3)}
p
}/
p_all_steps plot_steps(step_nr = 4) + plot_steps(step_nr = 8) + plot_steps(step_nr = 16, add_ideal = TRUE)) (
5.2 Normal by multiplication and by log-multiplication
<- function(effect_size = 0.1,
normal_by_multiplication x_scale = ggplot2::scale_x_continuous(),
x_lab = "normal"){
tibble(person = 1:n_people,
growth = replicate(length(person), prod(1 + runif(12, 0, effect_size)))) %>%
ggplot(aes(x = growth)) +
geom_density(color = clr0d, fill = fll0) +
labs(title = glue("effect size: {effect_size}"), x = glue("growth ({x_lab})")) +
x_scale
}
normal_by_multiplication(effect_size = .01) +
normal_by_multiplication(effect_size = .1) +
normal_by_multiplication(effect_size = .5)+
normal_by_multiplication(effect_size = .5, x_scale = scale_x_log10(), x_lab = "log10") +
plot_layout(nrow = 1)
5.2.1 using the Gaussian distribution
- part of the exponential family
- probability density function
- \(\mu\): mean
- \(\sigma\): standard deviation
- \(\tau\): precision
\[ p( y | \mu, \sigma) = \frac{1}{\sqrt{2\pi\sigma^2}} exp \left( \frac{(y-\mu)^2}{2\sigma^2} \right)\\ \tau = 1 / \sigma^2 \\ p( y | \mu, \tau) = \sqrt{\frac{\tau}{2\pi}}exp(-\tfrac{1}{2}\tau(y - \mu)^2) \]
5.3 A language for describing models
The first line defines the likelihood used in Bayes’ theorem, the other lines describe the priors used. The tilde means that the relationships are stochastic.
re-describing the globe-toss model:
The count \(W\) is distributed binomially with a sample size \(N\) and the probabiliy \(p\). The prior for \(p\) is assumed to be uniform between zero and one
\[ W \sim Binomial(N, p)\\ p \sim Uniform(0, 1) \]
Substituting in Bayes’ theorem:
\[ Pr(p | w, n) = \frac{Binomial(w|n,p)~Uniform(p|0,1)}{\int Binomial(w|n,p)~Uniform(p|0,1) dp} \]
<- 6
w <- 9
n <- tibble(p_grid = seq(0,1, length.out = 101),
grid_data likelihood = dbinom(w, n, p_grid),
prior = dunif(p_grid, 0, 1),
posterior_unstand = likelihood * prior,
posterior = posterior_unstand / sum(posterior_unstand))
%>%
grid_data pivot_longer(cols = c(prior, likelihood, posterior),
names_to = "bayes_part",
values_to = "p") %>%
mutate(bayes_part = factor(bayes_part, levels = names(clr_bayes))) %>%
ggplot(aes(x = p_grid)) +
geom_area(aes(y = p, color = bayes_part, fill = after_scale(clr_alpha(color)))) +
scale_color_manual(values = clr_bayes, guide = "none") +
facet_wrap(bayes_part ~ ., scales = "free_y")
5.3.1 Gaussian model of height
5.3.1.1 The data
library(rethinking)
data(Howell1)
<- as_tibble(Howell1)) %>%
(data precis() %>% as_tibble(rownames = NA) %>% knitr::kable()
|
|
|
|
|
<- data %>% filter(age >= 18)) %>%
(data_adults precis() %>% as_tibble(rownames = NA) %>% knitr::kable()
|
|
|
|
|
%>%
data_adults ggplot(aes(x = height)) +
geom_density(adjust = .5, color = clr0d, fill = fll0) +
scale_x_continuous(limits = c(130,185))
5.3.1.2 The model
\[ \begin{array}{cccr} h_i & \stackrel{iid}{\sim} & Normal(\mu, \sigma) & \textrm{[likelihood]}\\ \mu & \sim & Normal(178, 20) & \textrm{[$\mu$ prior]}\\ \sigma & \sim & Uniform(0,50) & \textrm{[$\sigma$ prior]} \end{array} \]
where, \(iid\) means “independent and identically distributed”.
Prior predictive simulation
(‘what does the model think before seeing the data?’)
<- 1e4
n_samples <- tibble(
prior_simulation sample_mu = rnorm(n_samples, 178, 20),
sample_sigma = runif(n_samples, 0, 50),
prior_h = rnorm(n_samples, sample_mu, sample_sigma),
bad_mu = rnorm(n_samples, 178, 100),
bad_prior = rnorm(n_samples, bad_mu, sample_sigma)
)
<- ggplot() +
p_mu stat_function(fun = function(x){dnorm(x = x, mean = 178, sd = 20)},
xlim = c(100,250), color = clr0d, fill = fll0, geom = "area") +
labs(title = glue("*\U03BC* {mth('\U007E')} dnorm( 178, 20 )"),
y = "density", x = "*\U03BC*")
<- ggplot() +
p_sigma stat_function(fun = function(x){dunif(x = x, min = 0, max = 50)},
xlim = c(-5, 55),
color = clr1, fill = fll1, geom = "area") +
labs(title = glue("*{mth('\U03C3')}* {mth('\U007E')} dunif( 0, 50 )"),
y = "density", x = glue("*{mth('\U03C3')}*"))
<- prior_simulation %>%
p_prior_sim ggplot(aes(x = prior_h)) +
geom_density(color = clr2, fill = fll2, adjust = .4) +
scale_x_continuous(limits = c(0,356), breaks = c(0,73,178,283)) +
labs(title = glue("*h<sub>i</sub>* {mth('\U007E')} dnorm( *\U03BC*, {mth('\U03C3')} )"),
x = "height")
<- prior_simulation %>%
p_bad_prior ggplot(aes(x = bad_prior)) +
geom_density(color = clr2, fill = fll2, adjust = .4) +
scale_x_continuous(limits = c(-222,578),
breaks = c(-128,0,178,484), expand = c(0,0)) +
geom_vline(data = tibble(h = c(0,272)), aes(xintercept = h), linetype = 3)+
labs(title = glue("*h<sub>i</sub>* {mth('\U007E')} dnorm( *\U03BC*, {mth('\U03C3')} )<br>*\U03BC* {mth('\U007E')} dnorm( 178, 100 )"),
x = "height")
+ p_sigma +
p_mu + p_bad_prior &
p_prior_sim theme(plot.title = element_markdown(),
axis.title.x = element_markdown())
5.3.1.3 grid approximation of the posterior distribution
<- 101
n_grid
<- cross_df(list(mu = seq(from = 152, to = 157, length.out = n_grid),
grid_data sigma = seq(from = 6.5, to = 9, length.out = n_grid))) %>%
mutate(log_likelihood = map2_dbl(.x = mu, .y = sigma, .f = function(x, y){
dnorm(x = data_adults$height, mean = x, sd = y, log = TRUE) %>% sum()
}),prior_mu = dnorm(mu, mean = 178, sd = 20, log = TRUE),
prior_sigma = dunif(sigma, min = 0, max = 50, log = TRUE),
product = log_likelihood + prior_mu + prior_sigma,
probability = exp(product - max(product)))
%>%
grid_data ggplot(aes(x = mu, y = sigma, z = probability)) +
geom_raster(aes(fill = probability)) +
geom_contour(color = rgb(1,1,1,.1)) +
coord_cartesian(xlim = range(grid_data$mu),
ylim = range(grid_data$sigma)) +
scale_fill_gradientn(colours = clr_grd5 %>% clr_alpha(alpha = .8),
limits = c(0,1)) +
coord_cartesian(xlim = range(grid_data$mu),
ylim = range(grid_data$sigma),
expand = 0) +
guides(fill = guide_colorbar(title.position = "top",
barwidth = unit(.9,"npc"),
barheight = unit(5, "pt"))) +
labs(x = " *\U03BC*", y = glue("*{mth('\U03C3')}*"))+
theme(legend.position = "bottom",
axis.title.x = element_markdown(),
axis.title.y = element_markdown())
Sampling from the posterior distribution
<- 1e4
n_posterior_sample <- grid_data %>%
samples slice_sample(n = n_posterior_sample, weight_by = probability, replace = TRUE)
<- samples %>%
p_samples group_by(mu, sigma) %>%
count() %>%
ungroup() %>%
ggplot(aes(x = mu, y = sigma, color = n)) +
geom_point(size = .4) +
scale_color_gradientn(colours = clr_grd5 %>% clr_alpha(alpha = .8)) +
coord_cartesian(xlim = buffer_range(grid_data$mu),
ylim = buffer_range(grid_data$sigma),
expand = 0) +
guides(color = guide_colorbar(title.position = "top",
barwidth = unit(.2,"npc"),
barheight = unit(5, "pt"))) +
labs(x = " *\U03BC*", y = glue("*{mth('\U03C3')}*"))+
theme(legend.position = "bottom",
axis.title.x = element_markdown(),
axis.title.y = element_markdown())
<- samples %>%
p_mu_dens ggplot(aes(x = mu)) +
geom_density(color = clr0d, fill = fll0) +
scale_x_continuous(limits = buffer_range(grid_data$mu), expand = c(0, 0)) +
labs(y = "marginal<br>density") +
theme(axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.title.y = element_markdown())
<- samples %>%
p_sigma_dens ggplot(aes(x = sigma)) +
geom_density(color = clr0d, fill = fll0) +
scale_x_continuous(limits = buffer_range(grid_data$sigma), expand = c(0, 0)) +
labs(y = "marginal density") +
coord_flip() +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank())
+ patchwork::guide_area() +
p_mu_dens + p_sigma_dens +
p_samples plot_layout(guides = "collect", widths = c(1,.3), heights = c(.3,1))
Exploration of long tail for \(\sigma\) when original sample size is small:
<- sample(data_adults$height, size = 20)
heights_subset <- cross_df(list(mu = seq(from = 145, to = 165, length.out = n_grid),
grid_data_subset sigma = seq(from = 4.5, to = 16, length.out = n_grid))) %>%
mutate(log_likelihood = map2_dbl(.x = mu, .y = sigma, .f = function(x, y){
dnorm(x = heights_subset, mean = x, sd = y, log = TRUE) %>% sum()
}),prior_mu = dnorm(mu, mean = 178, sd = 20, log = TRUE),
prior_sigma = dunif(sigma, min = 0, max = 50, log = TRUE),
product = log_likelihood + prior_mu + prior_sigma,
probability = exp(product - max(product)))
<- grid_data_subset %>%
samples_subset slice_sample(n = n_posterior_sample, weight_by = probability, replace = TRUE)
<- samples_subset %>%
p_samples group_by(mu, sigma) %>%
count() %>%
ungroup() %>%
ggplot(aes(x = mu, y = sigma, color = n)) +
geom_point(size = .4) +
scale_color_gradientn(colours = clr_grd4 %>% clr_alpha(alpha = .8)) +
coord_cartesian(xlim = buffer_range(grid_data_subset$mu),
ylim = buffer_range(grid_data_subset$sigma),
expand = 0) +
guides(color = guide_colorbar(title.position = "top",
barwidth = unit(.2,"npc"),
barheight = unit(5, "pt"))) +
labs(x = " *\U03BC*", y = glue("*{mth('\U03C3')}*"))+
theme(legend.position = "bottom",
axis.title.x = element_markdown(),
axis.title.y = element_markdown())
<- samples_subset %>%
p_mu_dens ggplot(aes(x = mu)) +
geom_density(color = clr0d, fill = fll0) +
scale_x_continuous(limits = buffer_range(grid_data_subset$mu), expand = c(0, 0)) +
labs(y = "marginal<br>density") +
theme(axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.title.y = element_markdown())
<- samples_subset %>%
p_sigma_dens ggplot(aes(x = sigma)) +
geom_density(color = clr0d, fill = fll0) +
scale_x_continuous(limits = buffer_range(grid_data_subset$sigma), expand = c(0, 0)) +
labs(y = "marginal density") +
coord_flip() +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank())
+ patchwork::guide_area() +
p_mu_dens + p_sigma_dens +
p_samples plot_layout(guides = "collect", widths = c(1,.3), heights = c(.3,1))
5.3.1.4 Quadratic approximation of the posterior distribution
\[ \begin{array}{cccr} h_i & \stackrel{iid}{\sim} & Normal(\mu, \sigma) & \verb|height ~ dnorm(mu, sigma)|\\ \mu & \sim & Normal(178, 20) & \verb|mu ~ dnorm(178, 20)|\\ \sigma & \sim & Uniform(0,50) & \verb|sigma ~ dunif(0, 50)| \end{array} \]
<- alist(
model_spec ~ dnorm(mu, sigma),
height ~ dnorm(178, 20),
mu ~ dunif(0, 50)
sigma
)
# "maximum a priori estimate"
<- list(
map_starting_points mu = mean(data_adults$height),
sigma = sd(data_adults$height)
)
<- quap(flist = model_spec,
model_heights_quap_weak_prior data = data_adults,
start = map_starting_points)
precis(model_heights_quap_weak_prior) %>%
as_tibble(rownames = NA) %>%
round(digits = 2) %>%
::kable() knitr
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
mu | 154.61 | 0.41 | 153.95 | 155.27 |
sigma | 7.73 | 0.29 | 7.27 | 8.20 |
Comparing how a stronger prior for \(\mu\) (narrower distribution) forces a larger estimate of \(\sigma\) to compensate for this.
quap(
flist = alist(
~ dnorm( mu , sigma ),
height ~ dnorm( 178, 0.1 ),
mu ~ dunif( 0, 50 )
sigma
),data = data_adults,
start = map_starting_points) %>%
precis() %>%
as_tibble(rownames = NA) %>%
round(digits = 2) %>%
::kable() knitr
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
mu | 177.86 | 0.10 | 177.70 | 178.02 |
sigma | 24.52 | 0.93 | 23.03 | 26.00 |
The variance-covariance matrix of the quadratic aprroximation for sampling the multi-dimensional gaussian distribution:
<- vcov(model_heights_quap_weak_prior)
vcov_mod_heights %>%
vcov_mod_heights round(digits = 6) %>%
::kable() knitr
mu | sigma | |
---|---|---|
mu | 0.169740 | 0.000218 |
sigma | 0.000218 | 0.084906 |
diag(vcov_mod_heights)
#> mu sigma
#> 0.16973961 0.08490582
round(cov2cor(vcov_mod_heights), digits = 5)
\[\begin{bmatrix} 1 &0.00182 \\0.00182 &1 \\ \end{bmatrix}\]
sampling from the multi-dimensional posterior distribution
<- extract.samples(model_heights_quap_weak_prior, n = 1e4) %>%
posterior_sample as_tibble()
precis(posterior_sample) %>%
as_tibble() %>%
::kable() knitr
|
|
|
|
|
5.4 Linear Prediction
ggplot(data_adults,
aes(height, weight)) +
geom_point(shape = 21, size = 1.5, color = clr1, fill = fll1)
\[ \begin{array}{cccr} h_i & {\sim} & Normal(\mu, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \beta (x_i - \bar{x}) & \textrm{[linear model]}\\ % alternatively \overline{x} \alpha & \sim & Normal(178, 20) & \textrm{[$\alpha$ prior]}\\ \beta & \sim & Normal(0, 10) & \textrm{[$\beta$ prior]}\\ \sigma & \sim & Uniform(0,50) & \textrm{[$\sigma$ prior]} \end{array} \]
The current prior for \(\beta\) is a bad choice, because it allows negative as well as unreasonably high and low dependencies of \(h\) (height) on \(x\) (weight):
set.seed(2971)
<- 100
N <- tibble(n = 1:N,
linear_priors alpha = rnorm( n = N, mean = 178, sd = 20 ),
beta_1 = rnorm( n = N, mean = 0, sd = 10),
beta_2 = rlnorm( n = N, mean = 0, sd = 1)) %>%
expand(nesting(n, alpha, beta_1, beta_2), weight = range(data_adults$weight)) %>%
mutate(height_1 = alpha + beta_1 * (weight - mean(data_adults$weight)),
height_2 = alpha + beta_2 * (weight - mean(data_adults$weight)))
<- ggplot(linear_priors, aes(x = weight, y = height_1, group = n)) +
p_lin_pr labs(title = glue("{mth('*\U03B2* ~')} Normal(0, 10)"), y = "height")
<- ggplot(linear_priors, aes(x = weight, y = height_2, group = n)) +
p_log_pr labs(title = glue("{mth('*\U03B2* ~')} Log-Normal(0, 1)"), y = "height")
<- ggplot() +
p_lnorm stat_function(fun = function(x){dlnorm(x = x, meanlog = 0, sdlog = 1)},
xlim = c(0,5), geom = "area", color = clr2, fill = fll2, n = 501) +
labs(title = "Log-Norm(0, 0.1)", y = "density")
+ p_log_pr &
(p_lin_pr geom_hline(data = tibble(height = c(0, 272), type = 1:2),
aes(yintercept = height, linetype = factor(type)), size = .4) &
geom_line(color = clr2, alpha = .25) &
scale_linetype_manual(values = c(`1` = 3, `2` = 1), guide = "none") &
coord_cartesian(xlim = range(data_adults$weight), ylim = c(-100, 400)) &
theme(plot.title = element_markdown())) +
p_lnorm
The log-normal prior seems more sensible, so we update the model priors as such:
\[ \begin{array}{cccr} \beta & \sim & Log-Normal(0, 1) & \textrm{[$\beta$ prior]}\\ \end{array} \]
5.4.1 Finding the posterior Distribution
<- mean(data_adults$weight)
xbar <- quap(
model_hight flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta * ( weight - xbar ),
mu ~ dnorm( 178, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta ~ dunif( 0, 50)
sigma
),data = data_adults
)
Table of marginal distributions of the parameters after training the model on the data
<- precis(model_hight) %>%
centered_remember_hw round(digits = 3) %>%
as_tibble(rownames = NA)
%>%
centered_remember_hw ::kable() knitr
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
alpha | 154.602 | 0.270 | 154.170 | 155.034 |
beta | 0.903 | 0.042 | 0.836 | 0.970 |
sigma | 5.072 | 0.191 | 4.766 | 5.377 |
We also need thevariance-covariance matrix to fully describe the audratic approximation completely:
%>%
model_hight vcov() %>%
round(digits = 2) %>%
as.data.frame(row.names = row.names(.)) %>%
::kable() knitr
alpha | beta | sigma | |
---|---|---|---|
alpha | 0.07 | 0 | 0.00 |
beta | 0.00 | 0 | 0.00 |
sigma | 0.00 | 0 | 0.04 |
<- extract.samples(model_hight) %>%
model_hight_smp as_tibble()
<- model_hight_smp %>%
model_hight_smp_mean summarise(across(.cols = everything(), mean))
%>%
model_hight_smp ggpairs(
lower = list(continuous = wrap(ggally_points, colour = clr1, size = .2, alpha = .1)),
diag = list(continuous = wrap("densityDiag", fill = fll1, color = clr1, adjust = .5)),
upper = list(continuous = wrap(ggally_cor, size = 5, color = "black", family = "Josefin sans")))
Plotting the posterior distribution against the data
ggplot(data_adults, aes(x = weight, y = height)) +
geom_point(color = clr0d) +
stat_function(fun = function(x){model_hight_smp_mean$alpha + model_hight_smp_mean$beta * (x - xbar)},
color = clr2, n = 2)
A demonstration of the the effect of sample size on the uncertainty of the linear fit
<- function(N = 10){
sub_model <- data_adults[1:N,]
data_inner <- mean(data_inner$weight)
xbar
<- quap(
model_hight_inner flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta * ( weight - xbar ),
mu ~ dnorm( 178, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta ~ dunif( 0, 50)
sigma
),data = data_inner
)
<- extract.samples(model_hight_inner) %>%
model_hight_smp_inner as_tibble() %>%
sample_n(20)
ggplot(data_inner, aes(x = weight, y = height)) +
geom_point(color = clr0d) +
::map(1:20, function(i){stat_function(
(purrrfun = function(x){model_hight_smp_inner$alpha[i] + model_hight_smp_inner$beta[i] * (x - xbar)},
color = clr2, n = 2, alpha = .1)})) +
labs(title = glue("N: {N}"))
}
sub_model(10) + sub_model(50) +
sub_model(150) + sub_model(352)
adding intervals
<- model_hight_smp %>%
mu_at_50 mutate(mu_at_50 = alpha + beta * (50 - xbar))
<- mu_at_50 %>%
p_density ggplot(aes(x = mu_at_50)) +
geom_density(adjust = .5, color = clr0d, fill = fll0) +
stat_function(fun = function(x){demp(x, obs = mu_at_50$mu_at_50, density.arg.list = list(adjust = .5))},
xlim = mu_at_50$mu_at_50 %>% PI(), geom = "area", fill = fll2, color = clr2) +
geom_vline(data = tibble(weights = mu_at_50$mu_at_50 %>% PI()), aes(xintercept = weights), linetype = 3)+
scale_x_continuous(glue("{mth('*\U03BC*')} | weight = 50"), limits = c(157.7, 160.8)) +
theme(axis.title.x = element_markdown())
$mu_at_50 %>% PI() mu_at_50
#> 5% 94%
#> 158.5857 159.6717
<- seq(from = 25, to = 70, by = 1)
weight_seq <- link(model_hight, data = data.frame(weight = weight_seq)) %>%
model_hight_mu as_tibble() %>%
set_names(nm = weight_seq) %>%
pivot_longer(cols = everything(), names_to = "weight", values_to = "height") %>%
mutate(weight = as.numeric(weight))
<- model_hight_mu %>%
p_dots ggplot(aes(x = weight, y = height)) +
geom_point(aes(color = weight == 50), alpha = .1, size = .3) +
scale_color_manual(values = c(`TRUE` = clr2, `FALSE` = clr0d), guide = "none")
<- model_hight_mu %>%
model_hight_mu_interval group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup()
<- model_hight_mu_interval %>%
p_interval ggplot(aes(x = weight)) +
geom_point(data = data_adults, aes(y = height), color = clr0, size = .3) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr2, alpha = .35) +
geom_line(aes(y = mean))
+ p_dots + p_interval p_density
Prediction intervals
<- sim(model_hight, data = data.frame(weight = weight_seq), n = 1e4) %>%
model_hight_sd as_tibble() %>%
set_names(nm = weight_seq) %>%
pivot_longer(cols = `25`:`70`, names_to = "weight", values_to = "height") %>%
mutate(weight = as.numeric(weight))
%>%
model_hight_sd group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup() %>%
ggplot(aes(x = weight)) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data_adults, aes(y = height), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = model_hight_mu_interval,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_hight_mu_interval,
aes(y = mean))
5.5 Curves from lines
The full data (including kids) is clearly curved in shape:
ggplot(data = data, aes(x = weight, y = height)) +
geom_point(color = clr0d)
We will work on standardized \(x\) values to prevent “numerical glitches” by transforming \(x\) via \(x_s = (\frac{x - \bar{x}}{sd(x)})\):
quadratic polynomial fit \[ \begin{array}{cccr} h_i & {\sim} & Normal(\mu, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \beta_1 x_i + \beta_2 x_i ^ 2& \textrm{[linear model]}\\ % alternatively \overline{x} \alpha & \sim & Normal(178, 20) & \textrm{[$\alpha$ prior]}\\ \beta_1 & \sim & Log-Normal(0, 1) & \textrm{[$\beta_1$ prior]}\\ \beta_2 & \sim & Normal(0, 1) & \textrm{[$\beta_2$ prior]}\\ \sigma & \sim & Uniform(0,50) & \textrm{[$\sigma$ prior]} \end{array} \]
cubic polynomial fit \[ \begin{array}{cccr} h_i & {\sim} & Normal(\mu, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \beta_1 x_i + \beta_2 x_i ^ 2 + \beta_3 x_i ^ 3 & \textrm{[linear model]}\\ % alternatively \overline{x} \alpha & \sim & Normal(178, 20) & \textrm{[$\alpha$ prior]}\\ \beta_1 & \sim & Log-Normal(0, 1) & \textrm{[$\beta_1$ prior]}\\ \beta_2 & \sim & Normal(0, 1) & \textrm{[$\beta_2$ prior]}\\ \beta_3 & \sim & Normal(0, 1) & \textrm{[$\beta_3$ prior]}\\ \sigma & \sim & Uniform(0,50) & \textrm{[$\sigma$ prior]} \end{array} \]
<- function(mod, data,
plot_model_intervals weight_seq = list(weight_s = seq(from = min(data_model$weight_s),
to = max(data_model$weight_s),
length.out = 70))){
<- link(mod, data = weight_seq) %>%
model_hight_mu_interval as_tibble() %>%
set_names(nm = weight_seq$weight_s ) %>%
pivot_longer(cols = everything(), names_to = "weight_s", values_to = "height") %>%
mutate(weight_s = as.numeric(weight_s)) %>%
group_by(weight_s) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup()
<- sim(mod, data = weight_seq, n = 1e4) %>%
model_hight_sd as_tibble() %>%
set_names(nm = weight_seq$weight_s) %>%
pivot_longer(cols = everything(), names_to = "weight_s", values_to = "height") %>%
mutate(weight_s = as.numeric(weight_s))
%>%
model_hight_sd group_by(weight_s) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup() %>%
ggplot(aes(x = weight_s)) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data, aes(y = height), color = rgb(0,0,0,.25), size = .4) +
geom_ribbon(data = model_hight_mu_interval,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_hight_mu_interval,
aes(y = mean))
}
<- data %>%
data_model mutate(weight_s = (weight - mean(weight))/sd(weight),
weight_s2 = weight_s ^ 2,
weight_s3 = weight_s ^ 3)
<- quap(
model_hight_s1 flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta * weight_s ,
mu ~ dnorm( 178, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta ~ dunif( 0, 50)
sigma
),data = data_model
)
<- quap(
model_hight_s2 flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta1 * weight_s + beta2 * weight_s2,
mu ~ dnorm( 178, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta1 ~ dnorm( 0, 1 ),
beta2 ~ dunif( 0, 50)
sigma
),data = data_model
)
<- quap(
model_hight_s3 flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta1 * weight_s + beta2 * weight_s2 + beta3 * weight_s3,
mu ~ dnorm( 178, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta1 ~ dnorm( 0, 1 ),
beta2 ~ dnorm( 0, 1 ),
beta3 ~ dunif( 0, 50)
sigma
),data = data_model
)
plot_model_intervals(model_hight_s1, data_model) +
plot_model_intervals(model_hight_s2, data_model,
weight_seq = tibble(weight_s = seq(from = min(data_model$weight_s),
to = max(data_model$weight_s),
length.out = 70),
weight_s2 = weight_s ^ 2)) +
plot_model_intervals(model_hight_s3, data_model,
weight_seq = tibble(weight_s = seq(from = min(data_model$weight_s),
to = max(data_model$weight_s),
length.out = 70),
weight_s2 = weight_s ^ 2,
weight_s3 = weight_s ^ 3))
plot_model_intervals(model_hight_s3, data_model,
weight_seq = tibble(weight_s = seq(from = min(data_model$weight_s),
to = max(data_model$weight_s),
length.out = 70),
weight_s2 = weight_s ^ 2,
weight_s3 = weight_s ^ 3)) +
scale_x_continuous("weight [kg]",
breaks = (seq(5,65, length.out = 5) - mean(data_model$weight)) / sd(data_model$weight),
labels = seq(5,65, length.out = 5)) +
labs(y = "height [cm]")
5.6 Splines
Loading the Hanami data (花見), containing the historical dates of first annual cherry tree blossom.
data(cherry_blossoms)
precis(cherry_blossoms) %>% as_tibble() %>% knitr::kable()
|
|
|
|
|
%>%
cherry_blossoms ggplot(aes(x = year, y = doy)) +
geom_point(color = clr2, alpha = .3) +
labs(y = "Day of first blossom")
<- cherry_blossoms %>%
data_cherry filter(complete.cases(doy)) %>%
as_tibble()
<- 15
n_knots <- quantile(data_cherry$year, probs = seq(0, 1, length.out = n_knots)) knot_list
library(splines)
<- bs(data_cherry$year,
b_spline_cherry knots = knot_list[-c(1, n_knots)],
degree = 3,
intercept = TRUE)
<- b_spline_cherry %>%
b_spline_tib as_tibble() %>%
set_names(nm = str_pad(1:17, width = 2, pad = 0)) %>%
bind_cols(select(data_cherry, year)) %>%
pivot_longer(cols = -year, names_to = "bias_function", values_to = "bias")
ggplot() +
geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = "black") +
geom_line(data = b_spline_tib, aes(x = year, y = bias,
color = as.numeric(bias_function)
group = bias_function),
, size = 1, alpha = .75) +
scale_color_gradientn(colours = c("black", clr0d, clr2), guide = "none")+
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
B-spline model:
\[ \begin{array}{cccr} D_i & \sim & Normal( \mu_i, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \sum_{k=1}^K w_k B_{k,i} & \textrm{[linear model]}\\ \alpha & \sim & Normal(100, 10) & \textrm{[$\alpha$ prior]}\\ w_i & \sim & Normal(0, 10) & \textrm{[w prior]}\\ \sigma & \sim & Exponential(1) & \textrm{[$\sigma$ prior]} \end{array} \]
<- quap(
model_cherry alist(
~ dnorm(mu, sigma),
D <- a + B %*% w,
mu ~ dnorm(100, 10),
a ~ dnorm(0, 10),
w ~ dexp(1)
sigma
),data = list(D = data_cherry$doy, B = b_spline_cherry),
start = list(w = rep(0, ncol(b_spline_cherry)))
)
precis(model_cherry, depth = 2) %>% round(digits = 2) %>% as_tibble(rownames = NA) %>% knitr::kable()
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
w[1] | -3.02 | 3.86 | -9.19 | 3.15 |
w[2] | -0.83 | 3.87 | -7.01 | 5.36 |
w[3] | -1.06 | 3.58 | -6.79 | 4.67 |
w[4] | 4.85 | 2.88 | 0.25 | 9.44 |
w[5] | -0.84 | 2.87 | -5.43 | 3.76 |
w[6] | 4.32 | 2.91 | -0.33 | 8.98 |
w[7] | -5.32 | 2.80 | -9.79 | -0.84 |
w[8] | 7.85 | 2.80 | 3.37 | 12.33 |
w[9] | -1.00 | 2.88 | -5.61 | 3.60 |
w[10] | 3.04 | 2.91 | -1.61 | 7.69 |
w[11] | 4.67 | 2.89 | 0.05 | 9.29 |
w[12] | -0.15 | 2.87 | -4.74 | 4.43 |
w[13] | 5.56 | 2.89 | 0.95 | 10.18 |
w[14] | 0.72 | 3.00 | -4.08 | 5.51 |
w[15] | -0.80 | 3.29 | -6.06 | 4.46 |
w[16] | -6.96 | 3.38 | -12.36 | -1.57 |
w[17] | -7.67 | 3.22 | -12.82 | -2.52 |
a | 103.35 | 2.37 | 99.56 | 107.13 |
sigma | 5.88 | 0.14 | 5.65 | 6.11 |
<- extract.samples(model_cherry) %>%
cherry_samples as.data.frame() %>%
as_tibble() %>%
set_names(nm = c("a", "sigma", str_pad(1:17, 2,pad = 0)))
<- cherry_samples %>%
cherry_samples_mu summarise(across(everything(), mean)) %>%
pivot_longer(cols = everything(),
names_to = "bias_function", values_to ="weight")
ggplot() +
geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = "black") +
geom_line(data = b_spline_tib %>% left_join(cherry_samples_mu),
aes(x = year, y = bias * weight,
color = as.numeric(bias_function)
group = bias_function),
, size = 1, alpha = .75) +
scale_color_gradientn(colours = c("black", clr0d, clr2), guide = "none") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
<- link(model_cherry) %>%
model_cherry_samples as_tibble()%>%
set_names(nm = data_cherry$year) %>%
pivot_longer(cols = everything(), names_to = "year", values_to = "doy") %>%
mutate(year = as.numeric(year)) %>%
arrange(year)
<- model_cherry_samples %>%
model_cherry_stats group_by(year) %>%
nest() %>%
mutate(mean = map_dbl(data, function(data){mean(data$doy)}),
PI_lower = map_dbl(data, function(data){PI(data$doy)[1]}),
PI_upper = map_dbl(data, function(data){PI(data$doy)[2]}))
%>%
model_cherry_stats ggplot(aes(x = year)) +
geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = "black") +
geom_point(data = cherry_blossoms, aes(y = doy), color = clr2, alpha = .3) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr2, alpha = .35) +
geom_line(aes(y = mean)) +
labs(y = "Day of first blossom") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
library(rlang)
<- env(
chapter4_models data_adults = data_adults,
model_heights_quap_weak_prior = model_heights_quap_weak_prior,
model_hight = model_hight,
data_model = data_model,
model_hight_s1 = model_hight_s1,
model_hight_s2 = model_hight_s2,
model_hight_s3 = model_hight_s3,
data_cherry = data_cherry,
b_spline_cherry = b_spline_cherry,
model_cherry = model_cherry
)
write_rds(chapter4_models, "envs/chapter4_models.rds")
5.7 Homework
E1
\[ \begin{array}{cccr} y_i & \sim & Normal( \mu, \sigma) & \textrm{[likelihood]}\\ \mu & \sim & Normal(0, 10) & \textrm{[$\mu$ prior]}\\ \sigma & \sim & Exponential(1) & \textrm{[$\sigma$ prior]} \end{array} \]
E2
There are two parameters:
- \(\mu\)
- \(\sigma\)
E3
\[ \begin{array}{rcl} Pr( \mu, \sigma | y ) & = & \frac{Normal( y | \mu, \sigma ) Pr(y)}{Pr(\mu, \sigma)} \\ Pr( \mu, \sigma | y ) & = & \frac{\prod_i Pr( y_i | \mu, \sigma) Normal( \mu | 0, 10) Exponential(\sigma | 1) }{ \int\int\prod_i Pr( y_i | \mu, \sigma) Normal( \mu | 0, 10) Exponential(\sigma | 1)d\mu d\sigma} \end{array} \]
E4
\[ \begin{array}{cccr} y_i & \sim & Normal( \mu, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \beta x_i & \textrm{[linear model]}\\ \alpha & \sim & Normal(0, 10) & \textrm{[$\alpha$ prior]}\\ \beta & \sim & Normal(0, 1) & \textrm{[$\beta$ prior]}\\ \sigma & \sim & Exponential(2) & \textrm{[$\sigma$ prior]} \end{array} \]
E5
There are three parameters
- \(\alpha\)
- \(\beta\)
- \(\sigma\)
M1
\[ \begin{array}{cccr} y_i & \sim & Normal( \mu, \sigma) & \textrm{[likelihood]}\\ \mu & \sim & Normal(0, 10) & \textrm{[$\mu$ prior]}\\ \sigma & \sim & Exponential(1) & \textrm{[$\sigma$ prior]} \end{array} \]
Using grid approximation
<- 5e3
n <- tibble(y = rnorm(n = n, # sample size
sample_data mean = rnorm(n = n, mean = 0, sd = 10), # mu prior
sd = rexp(n = n, rate = 1))) # sigma prior
%>%
sample_data ggplot(aes(x = y)) +
geom_density(color = clr0d, fill = fll0) +
stat_function(fun = function(x){dnorm(x = x, mean = 0, sd = 10 )}, geom = "line", linetype = 3, color = clr1) +
scale_x_continuous(limits = c(-50, 50)) +
labs(y = "density")
M2
<- alist(
quap_formula ~ dnorm(mu, sigma), # likelihood
y ~ dnorm(0, 10), # mu prior
mu ~ exp(1) # sigma prior
sigma )
M3
\[ \begin{array}{cccr} y_i & \sim & Normal( \mu, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \beta x_i & \textrm{[linear model]}\\ \alpha & \sim & Normal(0, 10) & \textrm{[$\alpha$ prior]}\\ \beta & \sim & Uniform(0, 1) & \textrm{[$\beta$ prior]}\\ \sigma & \sim & Exponential(1) & \textrm{[$\sigma$ prior]} \end{array} \]
M4
\[ \begin{array}{cccr} h_i & \sim & Normal( \mu, \sigma) & \textrm{[likelihood]} \\ \mu_i & = & \alpha + \beta h_i & \textrm{[linear model]} \\ \alpha & \sim & Normal(150, 5) & \textrm{[$\alpha$ prior, starting size]} \\ \beta & \sim & Uniform(0, 10) & \textrm{[$\beta$ prior, yearly growth]} \\ \sigma & \sim & Normal(0, 8) & \textrm{[$\sigma$ prior, size variation]} \end{array} \]
M5
No, the chosen prior for \(\beta\) already covers this information: \(\beta \sim Uniform(0, 10)\) is always positive, forcing a positive growth per year.
M6
Limiting the variance of height to 64cm could be done in different ways: by choosing a uniform prior with fixed boundaries [eg. \(Uniform(0,64)\)], or by limiting the variance of an unbound distribution [eg. \(\sigma\) for a normal distribution. 99.7% of the mass is within \(3 \sigma\), so \(Normal(32, 10)\) would do as well].
M7
<- quap(
model_uncentered flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta * weight,
mu ~ dnorm( 178, 20),
alpha ~ dlnorm( 0, 1 ),
beta ~ dunif( 0, 50 )
sigma
),data = data_adults
)
precis(model_uncentered) %>%
round(digits = 3) %>%
as_tibble(rownames = NA) %>%
::kable() knitr
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
alpha | 114.534 | 1.898 | 111.501 | 117.567 |
beta | 0.891 | 0.042 | 0.824 | 0.957 |
sigma | 5.073 | 0.191 | 4.767 | 5.378 |
%>%
model_uncentered vcov() %>%
round(digits = 2) %>%
as.data.frame(row.names = row.names(.)) %>%
::kable() knitr
alpha | beta | sigma | |
---|---|---|---|
alpha | 3.60 | -0.08 | 0.01 |
beta | -0.08 | 0.00 | 0.00 |
sigma | 0.01 | 0.00 | 0.04 |
compare to the centered version:
%>%
centered_remember_hw ::kable() knitr
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
alpha | 154.602 | 0.270 | 154.170 | 155.034 |
beta | 0.903 | 0.042 | 0.836 | 0.970 |
sigma | 5.072 | 0.191 | 4.766 | 5.377 |
%>%
model_hight vcov() %>%
round(digits = 2) %>%
as.data.frame(row.names = row.names(.)) %>%
::kable() knitr
alpha | beta | sigma | |
---|---|---|---|
alpha | 0.07 | 0 | 0.00 |
beta | 0.00 | 0 | 0.00 |
sigma | 0.00 | 0 | 0.04 |
The un-centered model shows higher covariances between \(\alpha\) and all other parameters.
<- link(model_uncentered, data = data.frame(weight = weight_seq)) %>%
model_uncentered_mu as_tibble() %>%
set_names(nm = weight_seq) %>%
pivot_longer(cols = everything(), names_to = "weight", values_to = "height") %>%
mutate(weight = as.numeric(weight))
<- model_uncentered_mu %>%
model_uncentered_mu_interval group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup()
<- sim(model_uncentered, data = data.frame(weight = weight_seq), n = 5e3) %>%
model_uncentered_sd as_tibble() %>%
set_names(nm = weight_seq) %>%
pivot_longer(cols = `25`:`70`, names_to = "weight", values_to = "height") %>%
mutate(weight = as.numeric(weight))
<- model_uncentered_sd %>%
p_1 group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup() %>%
ggplot(aes(x = weight)) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data_adults, aes(y = height), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = model_uncentered_mu_interval,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_uncentered_mu_interval,
aes(y = mean))+
labs(title = " uncentered")
<- model_hight_sd %>%
p_2 group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup() %>%
ggplot(aes(x = weight)) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data_adults, aes(y = height), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = model_hight_mu_interval,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_hight_mu_interval,
aes(y = mean))+
labs(title = " centered")
+ p_2 p_1
Hmm 🤔`: I can’t see a difference - maybe that is the point?
M8
<- function(n_knots = 15, inner = TRUE){
spline_check <- quantile(data_cherry$year, probs = seq(0, 1, length.out = n_knots))
knot_list
<- bs(data_cherry$year,
b_spline_cherry knots = knot_list[-c(1, n_knots)],
degree = 3,
intercept = TRUE)
<- b_spline_cherry %>%
b_spline_tib as_tibble() %>%
set_names(nm = str_pad(1:(n_knots+2), width = 2, pad = 0)) %>%
bind_cols(select(data_cherry, year)) %>%
pivot_longer(cols = -year, names_to = "bias_function", values_to = "bias")
<- quap(
model_cherry alist(
~ dnorm(mu, sigma),
D <- a + B %*% w,
mu ~ dnorm(100, prior_sd_a),
a ~ dnorm(0, prior_sd_w),
w ~ dexp(1)
sigma
),data = list(D = data_cherry$doy, B = b_spline_cherry),
start = list(w = rep(0, ncol(b_spline_cherry)))
)
<- extract.samples(model_cherry) %>%
cherry_samples as.data.frame() %>%
as_tibble() %>%
set_names(nm = c("a", "sigma", str_pad(1:(n_knots+2), 2,pad = 0)))
<- cherry_samples %>%
cherry_samples_mu summarise(across(everything(), mean)) %>%
pivot_longer(cols = everything(),
names_to = "bias_function", values_to ="weight")
<- link(model_cherry) %>%
model_cherry_samples as_tibble()%>%
set_names(nm = data_cherry$year) %>%
pivot_longer(cols = everything(), names_to = "year", values_to = "doy") %>%
mutate(year = as.numeric(year)) %>%
arrange(year)
<- model_cherry_samples %>%
model_cherry_stats group_by(year) %>%
nest() %>%
mutate(mean = map_dbl(data, function(data){mean(data$doy)}),
PI_lower = map_dbl(data, function(data){PI(data$doy)[1]}),
PI_upper = map_dbl(data, function(data){PI(data$doy)[2]}))
<- ggplot() +
p_splines_pure geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = rgb(0,0,0,.5)) +
geom_line(data = b_spline_tib %>% left_join(cherry_samples_mu),
aes(x = year, y = bias * weight,
color = as.numeric(bias_function)
group = bias_function),
, size = .3, alpha = .75) +
scale_color_gradientn(colours = c("black", clr0d, clr1), guide = "none") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_blank()) +
labs(title = glue("{n_knots} kn, sd a: {prior_sd_a}, sd w: {prior_sd_w}"))
<- model_cherry_stats %>%
p_splines_fitted ggplot(aes(x = year)) +
geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = rgb(0,0,0,.5)) +
geom_point(data = cherry_blossoms, aes(y = doy), color = clr1, alpha = .1, size = .2) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .65) +
geom_line(aes(y = mean)) +
labs(y = "Day of first blossom") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
axis.title.x = element_blank(),
axis.text.x = element_blank())
if(inner){
<- p_splines_pure + theme(axis.title.y = element_blank())
p_splines_pure <- p_splines_fitted + theme(axis.title.y = element_blank())
p_splines_fitted
}
+ p_splines_fitted + plot_layout(ncol = 1, heights = c(.5, 1))
p_splines_pure
}
set.seed(14)
= 10
prior_sd_a <- 10
prior_sd_w <- spline_check(n_knots = 3, inner = FALSE)
p1
<- 50
prior_sd_w <- spline_check(n_knots = 3, inner = TRUE)
p2 set.seed(42)
<- 100
prior_sd_w <- spline_check(n_knots = 3, inner = TRUE)
p3
| p2 | p3 p1
set.seed(41)
<- 10
prior_sd_w <- spline_check(n_knots = 10, inner = FALSE)
p1
<- 50
prior_sd_w <- spline_check(n_knots = 10, inner = TRUE)
p2
<- 100
prior_sd_w <- spline_check(n_knots = 10, inner = TRUE)
p3
| p2 | p3 p1
set.seed(42)
<- 10
prior_sd_w <- spline_check(n_knots = 30, inner = FALSE)
p1
<- 50
prior_sd_w <- spline_check(n_knots = 30, inner = TRUE)
p2
<- 100
prior_sd_w <- spline_check(n_knots = 30, inner = TRUE)
p3
| p2 | p3 p1
They control the division of data and the initial scale for the weighting
H1
%>%
model_hight_smp mutate(mu_at_50 = alpha + beta * (50 - xbar),
`46.95` = alpha + beta * (46.95 - xbar),
`43.72` = alpha + beta * (43.72 - xbar),
`64.78` = alpha + beta * (64.78 - xbar),
`32.59` = alpha + beta * (32.59 - xbar),
`54.63` = alpha + beta * (54.63 - xbar)) %>%
::select(`46.95`:`54.63` ) %>%
dplyrpivot_longer(cols = everything(), names_to = "weight", values_to = "height") %>%
group_by(weight) %>%
nest() %>%
ungroup() %>%
mutate(median_weight = map_dbl(data, function(x){median(x$height)}),
mean_weight = map_dbl(data, function(x){mean(x$height)}),
lower_89 = map_dbl(data, function(x){PI(x$height)[[1]]}),
upper_89 = map_dbl(data, function(x){PI(x$height)[[2]]}),
individual = 1:5) %>%
::select(individual, weight,median_weight:upper_89) %>%
dplyrmutate(across(everything(), .fns = ~ round(as.numeric(.x), digits = 5))) %>%
::kable() knitr
individual | weight | median_weight | mean_weight | lower_89 | upper_89 |
---|---|---|---|---|---|
1 | 46.95 | 156.3742 | 156.3739 | 155.9302 | 156.8173 |
2 | 43.72 | 153.4580 | 153.4585 | 153.0147 | 153.8970 |
3 | 64.78 | 172.4641 | 172.4671 | 171.0703 | 173.8520 |
4 | 32.59 | 143.4179 | 143.4127 | 142.4567 | 144.3474 |
5 | 54.63 | 163.3039 | 163.3058 | 162.5306 | 164.0879 |
H2
<- data %>%
data_children filter(age < 18)
ggplot(data_children,
aes(weight, height)) +
geom_point(shape = 21, size = 1.5, color = clr1, fill = fll1)
<- mean(data_children$weight)
xbar_children <- quap(
model_hight_children flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta * ( weight - xbar_children ),
mu ~ dnorm( 178, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta ~ dunif( 0, 50)
sigma
),data = data_children
)
precis(model_hight_children) %>%
round(digits = 3) %>%
as_tibble(rownames = NA)
#> # A tibble: 3 × 4
#> mean sd `5.5%` `94.5%`
#> * <dbl> <dbl> <dbl> <dbl>
#> 1 108. 0.609 107. 109.
#> 2 2.72 0.068 2.61 2.83
#> 3 8.44 0.431 7.75 9.13
%>%
model_hight_children vcov() %>%
cov2cor() %>%
round(digits = 2) %>%
as.data.frame(row.names = row.names(.)) %>%
::kable() knitr
alpha | beta | sigma | |
---|---|---|---|
alpha | 1.00 | 0.00 | 0.01 |
beta | 0.00 | 1.00 | -0.01 |
sigma | 0.01 | -0.01 | 1.00 |
<- extract.samples(model_hight_children) %>%
model_hight_smp_children as_tibble()
<- model_hight_smp_children %>%
model_hight_smp_mean_children summarise(across(.cols = everything(), mean))
ggplot(data_children, aes(x = weight, y = height)) +
geom_point(color = clr0d) +
stat_function(fun = function(x){model_hight_smp_mean_children$alpha + model_hight_smp_mean_children$beta * (x - xbar_children)},
color = clr2, n = 2)
%>%
model_hight_smp_mean_children ::kable() knitr
alpha | beta | sigma |
---|---|---|
108.3763 | 2.716742 | 8.44416 |
A child get 27.1674225128912 cm taller per 10 kg weight.
<- seq(from = 2, to = 45, by = 1)
weight_seq_children <- link(model_hight_children, data = data.frame(weight = weight_seq_children)) %>%
model_hight_mu_children as_tibble() %>%
set_names(nm = weight_seq_children) %>%
pivot_longer(cols = everything(), names_to = "weight", values_to = "height") %>%
mutate(weight = as.numeric(weight))
<- model_hight_mu_children %>%
model_hight_mu_interval_children group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup()
<- sim(model_hight_children, data = data.frame(weight = weight_seq_children), n = 1e4) %>%
model_hight_sd_children as_tibble() %>%
set_names(nm = weight_seq_children) %>%
pivot_longer(cols = `2`:`45`, names_to = "weight", values_to = "height") %>%
mutate(weight = as.numeric(weight))
%>%
model_hight_sd_children group_by(weight) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup() %>%
ggplot(aes(x = weight)) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data_children, aes(y = height), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = model_hight_mu_interval_children,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_hight_mu_interval_children,
aes(y = mean))
The model seems to be systematically overestimate the height for the more extreme weights (very light and rather heavy). The relationship does not appear to be linear in the first place, so a non-lnear fit would be better - ideally one that is biologically motivated.
H3
<- data %>%
data_log mutate(weight_log = log10(weight))
<- mean(data_log$weight_log)
xbar_log <- quap(
model_hight_log flist = alist(
~ dnorm( mu, sigma ),
height <- alpha + beta * ( weight_log - xbar_log ),
mu ~ dnorm( 179, 20 ),
alpha ~ dlnorm( 0, 1 ),
beta ~ dunif( 0, 50)
sigma
),data = data_log
)
precis(model_hight_log) %>%
round(digits = 3) %>%
as_tibble(rownames = NA)
#> # A tibble: 3 × 4
#> mean sd `5.5%` `94.5%`
#> * <dbl> <dbl> <dbl> <dbl>
#> 1 138. 0.22 138. 139.
#> 2 108. 0.881 107. 110.
#> 3 5.14 0.156 4.89 5.38
%>%
model_hight_log vcov() %>%
cov2cor() %>%
round(digits = 2) %>%
as.data.frame(row.names = row.names(.)) %>%
::kable() knitr
alpha | beta | sigma | |
---|---|---|---|
alpha | 1 | 0 | 0 |
beta | 0 | 1 | 0 |
sigma | 0 | 0 | 1 |
<- extract.samples(model_hight_log) %>%
model_hight_smp_log as_tibble()
<- model_hight_smp_log %>%
model_hight_smp_mean_log summarise(across(.cols = everything(), mean))
ggplot(data_log, aes(x = weight_log, y = height)) +
geom_point(color = clr0d) +
stat_function(fun = function(x){model_hight_smp_mean_log$alpha + model_hight_smp_mean_log$beta * (x - xbar_log)},
color = clr2, n = 2)
<- log10(seq(from = 2, to = 70, by = 1))
weight_seq_log <- link(model_hight_log, data = data.frame(weight_log = weight_seq_log)) %>%
model_hight_mu_log as_tibble() %>%
set_names(nm = weight_seq_log) %>%
pivot_longer(cols = everything(), names_to = "weight_log", values_to = "height") %>%
mutate(weight_log = as.numeric(weight_log))
<- model_hight_mu_log %>%
model_hight_mu_interval_log group_by(weight_log) %>%
summarise(mean = mean(height),
PI_lower = PI(height, prob = .97)[1],
PI_upper = PI(height, prob = .97)[2]) %>%
ungroup()
<- sim(model_hight_log, data = data.frame(weight_log = weight_seq_log), n = 1e4) %>%
model_hight_sd_log as_tibble() %>%
set_names(nm = weight_seq_log) %>%
pivot_longer(cols = everything(), names_to = "weight_log", values_to = "height") %>%
mutate(weight_log = as.numeric(weight_log))
%>%
model_hight_sd_log group_by(weight_log) %>%
summarise(mean = mean(height),
PI_lower = PI(height, prob = .97)[1],
PI_upper = PI(height, prob = .97)[2]) %>%
ungroup() %>%
ggplot(aes(x = 10^(weight_log))) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data, aes(x = weight, y = height), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = model_hight_mu_interval_log,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_hight_mu_interval_log,
aes(y = mean), linetype = 3)
H4
<- 25
N <- tibble(n = 1:N,
cubic_priors alpha = rnorm( n = N, mean = -128, sd = 20 ),
beta_1 = rnorm( n = N, mean = 11, sd = .1),
beta_2 = rnorm( n = N, mean = -.1, sd = .01)) %>%
expand(nesting(n, alpha, beta_1, beta_2),
weight = range(data_adults$weight)) %>%
mutate(height = alpha +
* (weight - mean(data_adults$weight)) +
beta_1 * (weight - mean(data_adults$weight))^2)
beta_2
ggplot(cubic_priors, aes(x = weight, y = height, group = n)) +
pmap(cubic_priors, function(alpha, beta_1, beta_2, ...){
stat_function(fun = function(x){alpha + beta_1 * x + beta_2 * x^2},
color = fll1, alpha = .1,
n = 100, lwd = .2, geom = "line")
+
}) geom_hline(data = tibble(height = c(0, 272), type = 1:2),
aes(yintercept = height, linetype = factor(type)), size = .4) +
scale_linetype_manual(values = c(`1` = 3, `2` = 1), guide = "none") +
coord_cartesian(xlim = range(data_adults$weight), ylim = c(-100, 400)) +
theme(plot.title = element_markdown())
H5
<- cherry_blossoms %>%
cherry_blossoms_tib as_tibble() %>%
filter(!is.na(temp) & !is.na(doy)) %>%
mutate(temp_s = (temp - mean(temp, na.rm = TRUE))/sd(temp, na.rm = TRUE))
<- mean(cherry_blossoms_tib$temp, na.rm = TRUE)
temp_bar <- sd(cherry_blossoms_tib$temp, na.rm = TRUE)
temp_sd
%>%
cherry_blossoms_tib as_tibble() %>%
ggplot(aes(x = temp, y = doy)) +
geom_point(size = 1.2, color = fll2)
\[ \begin{array}{cccr} d_i & \sim & Normal( \mu, \sigma) & \textrm{[likelihood]}\\ \mu_i & = & \alpha + \beta x_i & \textrm{[linear model]}\\ \alpha & \sim & Normal(105, 10) & \textrm{[$\alpha$ prior]}\\ \beta & \sim & Normal(0, 10) & \textrm{[$\beta$ prior]}\\ \sigma & \sim & Exponential(1) & \textrm{[$\sigma$ prior]} \end{array} \]
<- quap(
model_temp flist = alist(
~ dnorm( mu, sigma ),
doy <- alpha + beta * temp_s,
mu ~ dnorm( 105, 10 ),
alpha ~ dnorm( 0, 10 ),
beta ~ dexp( 1 )
sigma
),data = cherry_blossoms_tib
)
<- (seq(from = 4.5, to = 8.4, by = .1) - temp_bar) / temp_sd
temp_seq
<- link(model_temp, data = data.frame(temp_s = temp_seq)) %>%
model_temp_mu as_tibble() %>%
set_names(nm = temp_seq) %>%
pivot_longer(cols = everything(), names_to = "temp_s", values_to = "doy") %>%
mutate(temp_s = as.numeric(temp_s),
temp = temp_s * temp_sd + temp_bar)
<- model_temp_mu %>%
model_temp_mu_interval group_by(temp) %>%
summarise(mean = mean(doy),
PI_lower = PI(doy)[1],
PI_upper = PI(doy)[2]) %>%
ungroup()
<- sim(model_temp, data = data.frame(temp_s = temp_seq), n = 1e4) %>%
model_temp_sd as_tibble() %>%
set_names(nm = temp_seq) %>%
pivot_longer(cols = everything(), names_to = "temp_s", values_to = "doy") %>%
mutate(temp_s = as.numeric(temp_s),
temp = temp_s * temp_sd + temp_bar)
%>%
model_temp_sd group_by(temp) %>%
summarise(mean = mean(doy),
PI_lower = PI(doy)[1],
PI_upper = PI(doy)[2]) %>%
ungroup() %>%
ggplot(aes(x = temp)) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = cherry_blossoms_tib, aes(y = doy), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = model_temp_mu_interval,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(data = model_temp_mu_interval,
aes(y = mean))
H6
<- 15
n_knots <- quantile(data_cherry$year, probs = seq(0, 1, length.out = n_knots))
knot_list
<- bs(data_cherry$year,
b_spline_cherry knots = knot_list[-c(1, n_knots)],
degree = 3,
intercept = TRUE)
<- function(n = 100, prior_w = function(knots){rnorm(n = knots + 2, 0, 10)}){
prior_predictive tibble(.draw = 1:n,
alpha = rnorm(n, 100, 10),
w = purrr::map(seq_len(n), # random weighting of knots
function(x, knots){
<- prior_w(knots)
w
w
},knots = n_knots)) %>%
mutate(mu = purrr::map2(alpha, w, .f = function(alpha, w, b){
<- alpha + b %*% w
mu %>%
mu as_tibble(.name_repair = ~"mu") %>%
mutate(year = data_cherry$year, .before = 1)
b = b_spline_cherry)) %>%
}, unnest(cols = mu)
}
<- prior_predictive(n = 50) %>%
p1 ggplot(aes(x = year, y = mu)) +
labs(title = "weight sd: 10")
<- prior_predictive(n = 50, prior_w = function(knots){rnorm(n = knots + 2, mean = 0, sd = .3)}) %>%
p2 ggplot(aes(x = year, y = mu)) +
labs(title = "weight sd: 0.3")
+ p2 &
p1 geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = "black") &
geom_line(aes(group = .draw, color = .draw), alpha = .2) &
scale_color_gradientn(colours = c("black", clr0d, clr2), guide = "none") &
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
H7
This one is missing…
H8
set.seed(42)
<- quap(
model_cherry2 alist(
~ dnorm(mu, sigma),
D <- B %*% w,
mu ~ dnorm(0, 10),
w ~ dexp(1)
sigma
),data = list(D = data_cherry$doy, B = b_spline_cherry),
start = list(w = rep(0, ncol(b_spline_cherry))),
control = list(maxit = 5000)
)
precis(model_cherry2, depth = 2) %>% round(digits = 2) %>% as_tibble(rownames = NA) %>% knitr::kable()
mean | sd | 5.5% | 94.5% | |
---|---|---|---|---|
w[1] | 92.84 | 3.22 | 87.69 | 97.98 |
w[2] | 102.76 | 3.09 | 97.83 | 107.69 |
w[3] | 100.43 | 2.75 | 96.03 | 104.83 |
w[4] | 108.34 | 1.64 | 105.71 | 110.96 |
w[5] | 101.56 | 1.68 | 98.88 | 104.24 |
w[6] | 106.97 | 1.74 | 104.19 | 109.75 |
w[7] | 97.56 | 1.53 | 95.12 | 99.99 |
w[8] | 110.64 | 1.53 | 108.19 | 113.08 |
w[9] | 101.68 | 1.68 | 99.00 | 104.36 |
w[10] | 105.75 | 1.73 | 102.99 | 108.52 |
w[11] | 107.37 | 1.70 | 104.66 | 110.08 |
w[12] | 102.67 | 1.65 | 100.02 | 105.31 |
w[13] | 108.15 | 1.69 | 105.44 | 110.86 |
w[14] | 103.77 | 1.87 | 100.78 | 106.75 |
w[15] | 101.31 | 2.34 | 97.57 | 105.05 |
w[16] | 95.98 | 2.44 | 92.08 | 99.87 |
w[17] | 92.12 | 2.30 | 88.45 | 95.80 |
sigma | 5.95 | 0.15 | 5.71 | 6.18 |
<- extract.samples(model_cherry2) %>%
cherry_samples as.data.frame() %>%
as_tibble() %>%
set_names(nm = c("sigma", str_pad(1:17, 2,pad = 0)))
<- cherry_samples %>%
cherry_samples_mu summarise(across(everything(), mean)) %>%
pivot_longer(cols = everything(),
names_to = "bias_function", values_to ="weight")
<- link(model_cherry2) %>%
model_cherry_samples as_tibble()%>%
set_names(nm = data_cherry$year) %>%
pivot_longer(cols = everything(), names_to = "year", values_to = "doy") %>%
mutate(year = as.numeric(year)) %>%
arrange(year)
<- model_cherry_samples %>%
model_cherry_stats group_by(year) %>%
nest() %>%
mutate(mean = map_dbl(data, function(data){mean(data$doy)}),
PI_lower = map_dbl(data, function(data){PI(data$doy)[1]}),
PI_upper = map_dbl(data, function(data){PI(data$doy)[2]}))
%>%
model_cherry_stats ggplot(aes(x = year)) +
geom_vline(data = tibble(year = knot_list),
aes(xintercept = year),
linetype = 3, color = "black") +
geom_point(data = cherry_blossoms, aes(y = doy), color = clr2, alpha = .3) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr2, alpha = .35) +
geom_line(aes(y = mean)) +
labs(y = "Day of first blossom") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
5.8 {brms} section
5.8.1 linear model of adult height
finding the posterior with {brms}
<- brm(data = data_adults,
brms_c4_adult_heights family = gaussian,
~ 1,
height prior = c(prior(normal(178, 20), class = Intercept),
prior(uniform(0, 50), class = sigma)),
iter = 31000,
warmup = 30000,
chains = 7,
cores = 7,
seed = 4,
file = "brms/brms_c4_adult_heights")
posterior_summary(brms_c4_adult_heights,probs = c(.055, .945)) %>%
round(digits = 3) %>%
::kable() knitr
Estimate | Est.Error | Q5.5 | Q94.5 | |
---|---|---|---|---|
b_Intercept | 154.604 | 0.418 | 153.947 | 155.275 |
sigma | 7.742 | 0.304 | 7.276 | 8.250 |
lp__ | -1226.924 | 1.058 | -1228.772 | -1225.927 |
<- function(mod, n_chains = 4){
brms_summary_plot <- bayesplot::mcmc_areas_data(mod, prob = .95) %>%
bayes_data filter(parameter != "lp__")
<- bayesplot::mcmc_trace_data(mod) %>%
bayes_chains_data filter(parameter != "lp__")
<- bayes_data %>%
p_dens filter(interval == "outer") %>%
ggplot(aes(x = x, y = scaled_density)) +
geom_area(color = clr0d, fill = fll0) +
geom_area(data = bayes_data %>%
filter(interval == "inner"), color = clr2, fill = fll2) +
facet_wrap(parameter ~ . , scales = "free", ncol = 1)
<- bayes_chains_data %>%
p_chains ggplot(aes(x = iteration, y = value, group = chain)) +
geom_line(aes(color = chain), alpha = .6) +
facet_wrap(parameter ~ . , scales = "free", ncol = 1) +
scale_color_manual(values = scales::colour_ramp(colors = c("black",clr0d,clr2))(seq(0,1,length.out = n_chains)))
+ p_chains
p_dens
}
brms_summary_plot(brms_c4_adult_heights)
sampling from the posterior
# equivalent to `rethinking::extract.samples()`
<- as_draws_df(brms_c4_adult_heights) %>%
brms_post as_tibble()
head(brms_post)
#> # A tibble: 6 × 6
#> b_Intercept sigma lp__ .chain .iteration .draw
#> <dbl> <dbl> <dbl> <int> <int> <int>
#> 1 154. 8.07 -1227. 1 1 1
#> 2 154. 7.84 -1227. 1 2 2
#> 3 155. 8.21 -1228. 1 3 3
#> 4 154. 7.99 -1227. 1 4 4
#> 5 155. 7.77 -1226. 1 5 5
#> 6 154. 7.93 -1227. 1 6 6
select(brms_post, b_Intercept:sigma) %>%
cov() %>%
cov2cor()
\[\begin{bmatrix} 1 &-0.0192241582730429 \\-0.0192241582730429 &1 \\ \end{bmatrix}\]
%>%
brms_post ::select(-(lp__:.draw)) %>%
dplyrpivot_longer(cols = everything()) %>%
group_by(name) %>%
summarise(quantiles = list(tibble(quant = quantile(value, probs = c(.5, .025, .75)),
perc = str_c("q",names(quant)))) )%>%
unnest(quantiles) %>%
pivot_wider(names_from = perc, values_from = quant) %>%
mutate(across(.cols = -name, ~ round(.x, digits = 2))) %>%
::kable() knitr
name | q50% | q2.5% | q75% |
---|---|---|---|
b_Intercept | 154.59 | 153.80 | 154.89 |
sigma | 7.73 | 7.18 | 7.94 |
posterior_summary(brms_post)
\[\begin{bmatrix} 154.603681717344 &0.417698657902289 &153.797143321477 &155.411872045528 &7.7417412324536 &0.304409842943225 \\7.17548730321122 &8.38470302045445 &-1226.92435306886 &1.05809371424477 &-1229.68670608237 &-1225.89312495532 \\2.5 &1.11817376920787 &1 &4 &500.5 &288.711081398222 \\25.975 &975.025 &2000.5 &1154.84486692658 &100.975 &3900.025 \\ \end{bmatrix}\]
the height model with a predictor
<- data_adults %>%
data_adults mutate(weight_centered = weight - mean(weight))
<- brm(data = data_adults,
brms_c4_heights_x family = gaussian,
~ 1 + weight_centered,
height prior = c(prior(normal(178, 20), class = Intercept),
prior(lognormal(0, 1), class = b),
prior(uniform(0, 50), class = sigma)),
iter = 28000, warmup = 27000,
chains = 7, cores = 7,
seed = 42,
file = "brms/brms_c4_heights_x")
brms_summary_plot(brms_c4_heights_x, n_chains = 12)
Logs and exps (m4.3b
)
<- brm(data = data_adults,
brms_c4_heights_x_log family = gaussian,
bf(height ~ alpha + exp(logbeta) * weight_centered,
~ 1,
alpha ~ 1,
logbeta nl = TRUE),
prior = c(prior(normal(178, 20), class = b, nlpar = alpha),
prior(normal(0, 1), class = b, nlpar = logbeta),
prior(uniform(0, 50), class = sigma)),
iter = 31000, warmup = 30000,
chains = 7, cores = 7,
seed = 42,
file = "brms/brms_c4_heights_x_log")
posterior_summary(brms_c4_heights_x)[1:3, ] %>%
round(digits = 2)%>%
as.data.frame()
#> Estimate Est.Error Q2.5 Q97.5
#> b_Intercept 154.60 0.27 154.08 155.12
#> b_weight_centered 0.90 0.04 0.82 0.99
#> sigma 5.11 0.20 4.74 5.52
vcov(brms_c4_heights_x) %>%
cov2cor() %>%
round(3) %>%
as.data.frame()
#> Intercept weight_centered
#> Intercept 1.000 -0.003
#> weight_centered -0.003 1.000
<- as_draws_df(brms_c4_heights_x) %>%
brms_posterior_samples as_tibble() %>%
select(-(lp__:.draw))
%>%
brms_posterior_samples cov() %>%
cov2cor() %>%
round(digits = 3)%>%
as.data.frame()
#> b_Intercept b_weight_centered sigma
#> b_Intercept 1.000 -0.003 0.006
#> b_weight_centered -0.003 1.000 0.000
#> sigma 0.006 0.000 1.000
ggpairs(brms_posterior_samples,
lower = list(continuous = wrap(ggally_points, colour = clr1, size = .3, alpha = .1)),
diag = list(continuous = wrap("densityDiag", fill = fll1, color = clr1, adjust = .5)),
upper = list(continuous = wrap(ggally_cor, size = 5, color = "black", family = "Josefin sans")))
<- tibble(weight = 25:70) %>%
weight_seq mutate(weight_centered = weight - mean(data_adults$weight))
<- fitted(brms_c4_heights_x,
brms_model_hight_mu summary = FALSE,
newdata = weight_seq) %>%
as_tibble() %>%
set_names(nm = weight_seq$weight_centered) %>%
pivot_longer(cols = everything(), names_to = "weight_centered", values_to = "height") %>%
mutate(weight_centered = as.numeric(weight_centered))
<- brms_model_hight_mu %>%
brms_model_hight_mu_interval group_by(weight_centered) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup()
<- predict(brms_c4_heights_x,
brms_model_hight_samples summary = FALSE,
newdata = weight_seq) %>%
as_tibble() %>%
set_names(nm = weight_seq$weight_centered) %>%
pivot_longer(cols = everything(), names_to = "weight_centered", values_to = "height") %>%
mutate(weight_centered = as.numeric(weight_centered))
<- brms_model_hight_samples %>%
brms_model_hight_samples_interval group_by(weight_centered) %>%
summarise(mean = mean(height),
PI_lower = PI(height)[1],
PI_upper = PI(height)[2]) %>%
ungroup()
%>%
data_adults ggplot(aes(x = weight_centered, y = height)) +
geom_point(shape = 21, size = 2, color = clr1, fill = fll1) +
geom_abline(intercept = fixef(brms_c4_heights_x)[1],
slope = fixef(brms_c4_heights_x)[2])
%>%
brms_model_hight_mu_interval ggplot(aes(x = weight_centered)) +
geom_ribbon(data = brms_model_hight_samples_interval,
aes(ymin = PI_lower, ymax = PI_upper), fill = clr0d, alpha = .35) +
geom_point(data = data_adults, aes(y = height), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(aes(ymin = PI_lower, ymax = PI_upper), fill = clr1, alpha = .35) +
geom_line(aes(y = mean))
<- brm(data = data_model,
brms_c4_curve_x family = gaussian,
~ 1 + weight_s + weight_s2,
height prior = c(prior(normal(178, 20), class = Intercept),
prior(lognormal(0, 1), class = b, coef = "weight_s"),
prior(normal(0, 1), class = b, coef = "weight_s2"),
prior(uniform(0, 50), class = sigma)),
iter = 30000, warmup = 29000,
chains = 4, cores = 4,
seed = 4,
file = "brms/brms_c4_curve_x")
brms_summary_plot(brms_c4_curve_x, n_chains = 4)
<- tibble(weight_s = seq(from = -2.5, to = 2.5, length.out = 30)) %>%
weight_seq mutate(weight_s2 = weight_s^2)
<- fitted(brms_c4_curve_x,
fitd_quad newdata = weight_seq,
probs = c(.055, .955)) %>%
data.frame() %>%
bind_cols(weight_seq) %>%
as_tibble()
<- predict(brms_c4_curve_x,
pred_quad newdata = weight_seq,
probs = c(.055, .955)) %>%
data.frame() %>%
bind_cols(weight_seq) %>%
as_tibble()
ggplot(data = data_model,
aes(x = weight_s)) +
geom_ribbon(data = pred_quad,
aes(ymin = Q5.5, ymax = Q95.5), fill = clr0d, alpha = .35) +
geom_smooth(data = fitd_quad,
aes(y = Estimate, ymin = Q5.5, ymax = Q95.5),
stat = "identity", color = clr1, fill = fll1, alpha = .35, size = .2) +
geom_point(aes(y = height), color = rgb(0,0,0,.5), size = .6)
<- 15
num_knots <- quantile(data_cherry$year, probs = seq(from = 0, to = 1, length.out = num_knots))
knot_list <- bs(data_cherry$year,
B knots = knot_list[-c(1, num_knots)],
degree = 3,
intercept = TRUE)
<- data_cherry %>% mutate(B = B)
data_cherry_B %>% glimpse() data_cherry_B
#> Rows: 827
#> Columns: 6
#> $ year <int> 812, 815, 831, 851, 853, 864, 866, 869, 889, 891, 892, 894,…
#> $ doy <int> 92, 105, 96, 108, 104, 100, 106, 95, 104, 109, 108, 106, 10…
#> $ temp <dbl> NA, NA, NA, 7.38, NA, 6.42, 6.44, NA, 6.83, 6.98, 7.11, 6.9…
#> $ temp_upper <dbl> NA, NA, NA, 12.10, NA, 8.69, 8.11, NA, 8.48, 8.96, 9.11, 8.…
#> $ temp_lower <dbl> NA, NA, NA, 2.66, NA, 4.14, 4.77, NA, 5.19, 5.00, 5.11, 5.5…
#> $ B <bs[,17]> <bs[26 x 17]>
<- brm(data = data_cherry_B,
brms_c4_cherry_spline family = gaussian,
~ 1 + B,
doy prior = c(prior(normal(100, 10), class = Intercept),
prior(normal(0, 10), class = b),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000, chains = 4, cores = 4,
seed = 4,
file = "brms/brms_c4_cherry_spline")
<- as_draws_df(brms_c4_cherry_spline)
brms_posterior_samples <- tibble(year = seq(from = min(data_cherry$year), to = max(data_cherry$year), by = 10))
years_seq
<- fitted(brms_c4_cherry_spline,
fitd_quad probs = c(.055, .955)) %>%
data.frame() %>%
bind_cols(data_cherry_B) %>%
as_tibble()
<- predict(brms_c4_cherry_spline,
pred_quad probs = c(.055, .955)) %>%
data.frame() %>%
bind_cols(data_cherry_B) %>%
as_tibble()
ggplot(data = data_cherry,
aes(x = year)) +
geom_vline(data = tibble(year = knot_list), aes(xintercept = year),
linetype = 3, color = rgb(0,0,0,.4)) +
geom_point(aes(y = doy), color = rgb(0,0,0,.5), size = .6) +
geom_ribbon(data = pred_quad,
aes(ymin = Q5.5, ymax = Q95.5), fill = clr0d, alpha = .35) +
geom_smooth(data = fitd_quad,
aes(y = Estimate, ymin = Q5.5, ymax = Q95.5),
stat = "identity", color = clr1, fill = fll1, alpha = .35, size = .2) +
geom_hline(yintercept = fixef(brms_c4_cherry_spline)[1, 1], color = clr1, linetype = 2) +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
get_prior(data = data_cherry,
family = gaussian,
~ 1 + s(year)) doy
#> prior class coef group resp dpar nlpar bound
#> (flat) b
#> (flat) b syear_1
#> student_t(3, 105, 5.9) Intercept
#> student_t(3, 0, 5.9) sds
#> student_t(3, 0, 5.9) sds s(year)
#> student_t(3, 0, 5.9) sigma
#> source
#> default
#> (vectorized)
#> default
#> default
#> (vectorized)
#> default
Using a thin plate spline
<- brm(data = data_cherry,
brms_c4_cherry_smooth family = gaussian,
~ 1 + s(year),
doy prior = c(prior(normal(100, 10), class = Intercept),
prior(normal(0, 10), class = b),
prior(student_t(3, 0, 5.9), class = sds),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000,
chains = 4, cores = 4,
seed = 4,
control = list(adapt_delta = .99),
file = "brms/brms_c4_cherry_smooth")
fitted(brms_c4_cherry_smooth,
probs = c(.055, .945)) %>%
data.frame() %>%
bind_cols(select(data_cherry, year, doy)) %>%
as_tibble() %>%
ggplot(aes(x = year, y = doy, ymin = Q5.5, ymax = Q94.5)) +
geom_vline(data = tibble(year = knot_list), aes(xintercept = year),
linetype = 3, color = rgb(0,0,0,.4)) +
geom_point(aes(y = doy), color = rgb(0,0,0,.5), size = .6) +
geom_smooth(aes(y = Estimate, ymin = Q5.5, ymax = Q94.5),
stat = "identity", color = clr1, fill = fll1, alpha = .35, size = .2) +
geom_hline(yintercept = fixef(brms_c4_cherry_smooth)[1, 1], color = clr1, linetype = 2) +
labs(subtitle = "brms smooth using s(year) (thin plate)",
y = "day in year") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
<- brm(data = data_cherry,
brms_c4_cherry_smooth2 family = gaussian,
~ 1 + s(year, bs = "bs", k = 19),
doy prior = c(prior(normal(100, 10), class = Intercept),
prior(normal(0, 10), class = b),
prior(student_t(3, 0, 5.9), class = sds),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000, chains = 4, cores = 4,
seed = 4,
control = list(adapt_delta = .99),
file = "brms/brms_c4_cherry_smooth2")
fitted(brms_c4_cherry_smooth2,
probs = c(.055, .945)) %>%
data.frame() %>%
bind_cols(select(data_cherry, year, doy)) %>%
as_tibble() %>%
ggplot(aes(x = year, y = doy, ymin = Q5.5, ymax = Q94.5)) +
geom_vline(data = tibble(year = knot_list), aes(xintercept = year),
linetype = 3, color = rgb(0,0,0,.4)) +
geom_point(aes(y = doy), color = rgb(0,0,0,.5), size = .6) +
geom_smooth(aes(y = Estimate, ymin = Q5.5, ymax = Q94.5),
stat = "identity", color = clr1, fill = fll1, alpha = .35, size = .2) +
geom_hline(yintercept = fixef(brms_c4_cherry_smooth2)[1, 1], color = clr1, linetype = 2) +
labs(subtitle = "brms smooth using s(year) (B-spline)",
y = "day in year") +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank())
Add-on about matrix-columns (4.7 Second bonus)
<- 100
n
# how many continuous x predictor variables would you like?
<- 10
k
# simulate a dichotomous dummy variable for z
# simulate an n by k array for X
set.seed(4)
<- tibble(z = sample(0:1, size = n, replace = T),
data_matrix_columns X = array(runif(n * k, min = 0, max = 1), dim = c(n, k)))
# set the data-generating parameter values
<- 1
a <- 5
theta <- 1:k
b <- 2
sigma
# simulate the criterion
<- data_matrix_columns %>%
data_matrix_columns mutate(y = as.vector(a + X %*% b + theta * z + rnorm(n, mean = 0, sd = sigma)))
# data_matrix_columns %>% glimpse()
# data_matrix_columns$X[1,]
<- brm(data = data_matrix_columns,
brms_c4_matrix_column family = gaussian,
~ 1 + z + X,
y prior = c(prior(normal(0, 2), class = Intercept),
prior(normal(0, 10), class = b),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000, chains = 4, cores = 4,
seed = 4,
file = "brms/brms_c4_matrix_column")
summary(brms_c4_matrix_column)
#> Family: gaussian
#> Links: mu = identity; sigma = identity
#> Formula: y ~ 1 + z + X
#> Data: data_matrix_columns (Number of observations: 100)
#> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
#> total post-warmup draws = 4000
#>
#> Population-Level Effects:
#> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
#> Intercept 0.95 1.19 -1.47 3.34 1.00 6928 3291
#> z 4.74 0.42 3.91 5.56 1.00 7389 3032
#> X1 0.57 0.75 -0.86 2.04 1.00 6798 3312
#> X2 0.90 0.69 -0.47 2.26 1.00 6421 2949
#> X3 3.41 0.75 1.96 4.90 1.00 7375 3214
#> X4 2.81 0.73 1.36 4.25 1.00 6712 3469
#> X5 5.74 0.72 4.32 7.12 1.00 6621 3588
#> X6 6.40 0.73 4.97 7.82 1.00 6552 3228
#> X7 8.49 0.73 7.06 9.90 1.00 7098 3335
#> X8 8.40 0.69 7.04 9.76 1.00 8718 3363
#> X9 8.82 0.81 7.27 10.39 1.00 8378 3273
#> X10 9.32 0.73 7.88 10.78 1.00 8260 2894
#>
#> Family Specific Parameters:
#> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
#> sigma 1.99 0.16 1.71 2.32 1.00 5722 3433
#>
#> Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
#> and Tail_ESS are effective sample size measures, and Rhat is the potential
#> scale reduction factor on split chains (at convergence, Rhat = 1).