3 Rethinking: Chapter 2
Small Worlds and Large Worlds
by Richard McElreath, building on the Summary by Solomon Kurz
3.1 Counting possibilities
<- tibble(p1 = 0,
d p2 = rep(1:0, times = c(1, 3)),
p3 = rep(1:0, times = c(2, 2)),
p4 = rep(1:0, times = c(3, 1)),
p5 = 1)
p1 | p2 | p3 | p4 | p5 |
---|---|---|---|---|
0 | 1 | 1 | 1 | 1 |
0 | 0 | 1 | 1 | 1 |
0 | 0 | 0 | 1 | 1 |
0 | 0 | 0 | 0 | 1 |
%>%
d mutate(turn = 1:4)%>%
pivot_longer(p1:p5,
names_to = "prob",
values_to = "realization") %>%
arrange(prob, turn) %>%
mutate(prob = factor(prob, levels = c("p5", "p4", "p3", "p2", "p1")),
marble = c("white", "dark")[realization + 1]) %>%
ggplot( aes( x = turn, y = prob ) ) +
geom_point(shape = 21, size = 4,
aes( fill = marble, color = after_scale(clr_darken(fill)))) +
geom_text(data = tibble(x = rep(c(.65, 4.45), each = 5),
y = rep(str_c("p",1:5), 2),
label = rep(c("[", "]"), each = 5), vjust = .7),
aes( x = x, y = y, label = label), family = fnt_sel, size = 6)+
scale_fill_manual(values = c(white = clr0, dark = clrd)) +
theme(legend.position = "bottom")
tibble(draws = 1:3,
marbles = 4) %>%
mutate(possibilities = marbles ^ draws) %>%
::flextable() flextable
draws | marbles | possibilities |
1 | 4 | 4 |
2 | 4 | 16 |
3 | 4 | 64 |
<- function(round = 1, n = 4, angle = 360, start_angle = 0, p = .5, round_prefix = ""){
layout_round <- n ^ round
n_round tibble(idx_round = 1:n_round,
idx_round_sacaled = scales::rescale(idx_round,
from = c(.5, n_round+.5),
to = c(0, 1) * angle/360 + start_angle/360),
idx_draw = rep(1:n, n_round/n),
idx_parent = ((idx_round - 1 ) %/% n) + 1,
name_parent = str_c(round_prefix, round - 1, "_", idx_parent),
name = str_c(round_prefix, round, "_", idx_round),
x = sin(idx_round_sacaled * 2 * pi) * round,
y = cos(idx_round_sacaled * 2 * pi) * round) %>%
mutate(marble = c("white", "dark")[1 + ((idx_draw/n) <= p)],
round_prefix = round_prefix,
round = round)
}
<- function(round = 1, n = 4, round_prefix = ""){
links_round <- n ^ round
n_round <- n ^ (round - 1)
n_prev tibble(idx_round = 1:n_round,
idx_parent = ((idx_round - 1 ) %/% n) + 1,
from = str_c(round_prefix, round - 1, "_", idx_parent),
to = str_c(round_prefix,round, "_", idx_round),
round_prefix = round_prefix)
}
<- origin_round <- function(round_prefix = ""){
round_origin tibble(idx_round = 0,
idx_round_sacaled = 0,
idx_draw = 0,
name = str_c(round_prefix, "0_1"),
x = 0,
y = 0,
marble = NA)
}
<- function(n_rounds = 3, n_draws = 4, angle = 360,start_angle = 0, p = .5, round_prefix = ""){
marble_graph tbl_graph(nodes = 1:n_rounds %>% map_dfr(layout_round,
n = n_draws, angle = angle, start_angle = start_angle,
p = p, round_prefix = round_prefix) %>%
bind_rows(round_origin(round_prefix = round_prefix), .),
edges = 1:n_rounds %>% map_dfr(links_round, round_prefix = round_prefix)) %E>%
mutate(marble = .N()$marble[to],
to_name = .N()$name[to],
from_name = .N()$name[from])
}
marble_graph(p = .25, n_rounds = 3, angle = 180, start_angle = -90) %>%
ggraph( layout = "manual",
x = . %N>% as_tibble() %>% .$x,
y = . %N>% as_tibble() %>% .$y) +
geom_node_point(aes(fill = marble, color = marble, size = -round), shape = 21) +
geom_edge_link(aes(color = marble),
start_cap = circle(2, 'mm'),
end_cap = circle(2, 'mm')) +
coord_equal() +
scale_color_manual(values = clr_darken(c(white = clr0, dark = clrd)),
na.value = "transparent", guide = "none") +
scale_fill_manual(values = c(white = clr0, dark = clrd), na.value = "transparent") +
scale_edge_color_manual(values = c(white = clr_darken(clr0, .2), dark = clrd), guide = "none") +
scale_size_continuous(range = c(1.5,3), guide = "none") +
guides(fill = guide_legend(override.aes = list(size = 4)), edge_alpha = "none") +
labs(title = "p = 0.25") +
theme(legend.position = "bottom")
<- 3
n_deviders <- 3
n_rounds <- tibble(x = rep(0,n_deviders),
dividers y = x,
tau = seq(from = 0, to = 2*pi, length.out = n_deviders + 1)[2:(n_deviders+1)],
xend = sin(tau) * (n_rounds + .1),
yend = cos(tau) * (n_rounds + .1))
<- c(.25, .5, .75)
p_trials <- tibble(start_angle = c(0, 120, 240),
all_conjectures round_prefix = c("r1_" ,"r2_", "r3_"),
p = p_trials) %>%
pmap(marble_graph, angle = 120) %>%
reduce(bind_graphs)
<- function(x){x[is.na(x)] <- FALSE; x}
na_to_false <- function(x){x[is.na(x)] <- TRUE; x}
na_to_true <- function(x){x$name[x$r1_right]}
tester
<- c("white", "dark")[c(2,1,2)]
trial_sequence
<- all_conjectures %N>%
selectors mutate(r1_right = (round == 1 & marble == trial_sequence[1]) %>% na_to_true(),
r2_still_in = name_parent %in% name[r1_right],
r2_right = r2_still_in & (round == 2 & marble == trial_sequence[2]),
r3_still_in = name_parent %in% name[r2_right],
r3_right = r3_still_in & (round == 3 & marble == trial_sequence[3]),
on_path = r1_right | r2_right |r3_right) %>%
as_tibble() %>%
filter(on_path)
<- selectors %>%
selector_results filter(round == n_rounds) %>%
group_by(round_prefix) %>%
count() %>%
ungroup() %>%
mutate(tau = seq(from = 0, to = 2*pi,
length.out = n_rounds + 1)[2:(n_rounds+1)] - (2*pi)/(n_rounds * 2),
x = sin(tau) * (n_rounds + .5),
y = cos(tau) * (n_rounds + .5))
%>%
all_conjectures ggraph( layout = "manual",
x = . %N>% as_tibble() %>% .$x,
y = . %N>% as_tibble() %>% .$y) +
geom_node_point(aes(fill = marble, color = marble,
size = -round, alpha = name %in% selectors$name),
shape = 21) +
geom_edge_link(aes(color = marble, alpha = to_name %in% selectors$name),
start_cap = circle(2, 'mm'),
end_cap = circle(2, 'mm')) +
geom_segment(data = dividers, aes(x = x, y = y, xend = xend, yend = yend),
color = clr_darken("white",.10)) +
geom_text(data = selector_results, aes( x = x, y = y, label = n), family = fnt_sel, size = 6) +
coord_equal() +
scale_color_manual(values = clr_darken(c(white = clr0, dark = clrd)),
na.value = "transparent", guide = "none") +
scale_fill_manual(values = c(white = clr0, dark = clrd), na.value = "transparent") +
scale_edge_color_manual(values = c(white = clr_darken(clr0, .2), dark = clrd),
guide = "none") +
scale_size_continuous(range = c(1.5,3), guide = "none") +
scale_alpha_manual(values = c(`TRUE` = 1, `FALSE` = .2), guide = "none") +
guides(fill = guide_legend(override.aes = list(size = 4)), edge_alpha = "none") +
labs(caption = str_c(trial_sequence,collapse = "-")) +
theme(legend.position = "bottom")
<- c( glue("<span style='color:{clr0};filter:drop-shadow(0px 0px 1px black)'>\U2B24</span>"),
html_marbles glue("<span style='color:{clr1l};filter:drop-shadow(0px 0px 1px black)'>\U2B24</span>"))
<- function(x){
html_conjecture str_c("[ ",str_c(html_marbles[c(x)+1], collapse = " ")," ]")
}
tibble(conjectures = list(rep(0,4),
rep(1:0, c(1,3)),
rep(1:0, c(2,2)),
rep(1:0, c(3,1)),
rep(1,4)),
conjecture = map_chr(conjectures, html_conjecture),
ways = map_dbl(conjectures, sum),
p = c(0, p_trials, 1),
`ways data/prior counts` = c(0, selector_results$n, 0),
`new count` = map2_chr( `ways data/prior counts`, ways, .f = function(x,y){glue("{x} $\\times$ {y} = {x * y}")}),
plausibility = (`ways data/prior counts` / sum(`ways data/prior counts`)) %>% round(digits = 2)
%>%
) rename(`ways to produce <span style='color:#85769EFF;filter:drop-shadow(0px 0px 1px black)'>⬤</span>` = "ways") %>%
::select(-conjectures) %>%
dplyr::kable() knitr
conjecture | ways to produce ⬤ | p | ways data/prior counts | new count | plausibility |
---|---|---|---|---|---|
[ ⬤ ⬤ ⬤ ⬤ ] | 0 | 0.00 | 0 | 0 \(\times\) 0 = 0 | 0.00 |
[ ⬤ ⬤ ⬤ ⬤ ] | 1 | 0.25 | 3 | 3 \(\times\) 1 = 3 | 0.15 |
[ ⬤ ⬤ ⬤ ⬤ ] | 2 | 0.50 | 8 | 8 \(\times\) 2 = 16 | 0.40 |
[ ⬤ ⬤ ⬤ ⬤ ] | 3 | 0.75 | 9 | 9 \(\times\) 3 = 27 | 0.45 |
[ ⬤ ⬤ ⬤ ⬤ ] | 4 | 1.00 | 0 | 0 \(\times\) 4 = 0 | 0.00 |
3.2 Building a Model
<- tibble(toss = c("w", "l", "w", "w", "w", "l", "w", "l", "w"),
d n_trials = 1:9,
sequence = n_trials %>% map_chr(.f = function(x, chr){str_c(chr[1:x], collapse = "")}, chr = toss),
n_success = cumsum(toss == "w"),
lag_n_trials = lag(n_trials, default = 0),
lag_n_success = lag(n_success, default = 0))
<- 50
sequence_length %>%
d expand(nesting(n_trials, toss, n_success),
p_water = seq(from = 0, to = 1, length.out = sequence_length)) %>%
group_by(p_water) %>%
# you can learn more about lagging here: https://www.rdocumentation.org/packages/stats/versions/3.5.1/topics/lag or here: https://dplyr.tidyverse.org/reference/lead-lag.html
mutate(lagged_n_trials = lag(n_trials, k = 1),
lagged_n_success = lag(n_success, k = 1))
#> # A tibble: 450 × 6
#> # Groups: p_water [50]
#> n_trials toss n_success p_water lagged_n_trials lagged_n_success
#> <int> <chr> <int> <dbl> <int> <int>
#> 1 1 w 1 0 NA NA
#> 2 1 w 1 0.0204 NA NA
#> 3 1 w 1 0.0408 NA NA
#> 4 1 w 1 0.0612 NA NA
#> 5 1 w 1 0.0816 NA NA
#> 6 1 w 1 0.102 NA NA
#> 7 1 w 1 0.122 NA NA
#> 8 1 w 1 0.143 NA NA
#> 9 1 w 1 0.163 NA NA
#> 10 1 w 1 0.184 NA NA
#> # … with 440 more rows
<- function(n_trials, n_success, lag_n_trials, lag_n_success, sequence, ...){
stat_binom if(n_trials == 1) {
<- stat_function(data = tibble(n_trials = n_trials, sequence = sequence),
g_lag fun = function(x){1}, xlim = c(0,1), linetype = 3)
else {
} <- stat_function(data = tibble(n_trials = n_trials, sequence = sequence),
g_lag fun = function(x){
<- function(x){
f dbinom(x = lag_n_success, size = lag_n_trials, prob = x)}
f(x)/ integrate(f = f,lower = 0, upper = 1)[[1]]},
xlim = c(0,1), n = 500, linetype = 3)
}
<- stat_function(data = tibble(n_trials = n_trials, sequence = sequence),
g_current fun = function(x){
<- function(x){
f dbinom(x = n_success, size = n_trials, prob = x)}
f(x)/ integrate(f = f,lower = 0,upper = 1)[[1]]},
xlim = c(0,1),n = 500)
list( g_lag, g_current)
}
ggplot() +
%>% pmap(stat_binom) %>% unlist()) +
(d facet_wrap(str_c(n_trials,": ", sequence) ~ .)
3.3 Making the model go / Bayes’ Theorem
\[ Pr(\textit{p} | W, L) = \frac{Pr(W, L | \textit{p}) ~ Pr(\textit{p})}{Pr(W,L)}\\ Posterior = \frac{Probability~of~the~Data \times Prior}{ Average~probability~of~the~Data} \]
<- function(f_porior, f_like){ function(x){ f_porior(x) * f_like(x)} }
f_posterior_unscaled
<- c("prior", "likelihood", "posterior")
f_parts
<- function(f_porior, f_like, comp = 1){
gg_posterior list(
stat_function(data = tibble(part = factor("prior", levels = f_parts),
comp = comp),
fun = f_porior,
xlim = c(0,1), n = 500, geom = "area", color = clr2, fill = fll2),
stat_function(data = tibble(part = factor("likelihood", levels = f_parts),
comp = comp),
fun = f_like,
xlim = c(0,1), n = 500, geom = "area", color = clr2, fill = fll2),
stat_function(data = tibble(part = factor("posterior", levels = f_parts),
comp = comp),
fun = f_posterior_unscaled(f_porior = f_porior, f_like = f_like),
xlim = c(0,1), n = 500, geom = "area", color = clr2, fill = fll2)
) }
<- function(f){
scale_fun # marginal likelihood
function(x){f(x) / integrate(f = f, lower = 0, upper = 1)[[1]]}
}
<- function(x){dbeta(x = x, shape1 = 8, shape2 = 5)}
f_like_in <- function(x){1}
f_uni <- function(x){if_else(x < .5, 0, 1)}
f_step <- function(x){if_else(x < .5, (x * 2)^3, ((1 - x) * 2)^3)} f_peak
ggplot() +
gg_posterior(f_porior = f_uni, f_like = f_like_in) +
gg_posterior(f_porior = f_step, f_like = f_like_in, comp = 2) +
gg_posterior(f_porior = f_peak, f_like = f_like_in, comp = 3) +
facet_wrap(comp ~ part, scales = "free_y")
3.4 Motors: Grid Approximation
<- function(n_grid = 20, W = 6, L = 3, prior = function(x){rep(1, length(x))}){
grid_approx tibble(p_grid = seq(0, 1, length.out = n_grid),
prior = prior(p_grid),
likelihood = dbinom(W, size = W + L, prob = p_grid),
posterior_unstand = likelihood * prior,
posterior = posterior_unstand / sum(posterior_unstand))
}
<- function(data){
plot_grid_approx %>%
data ggplot(aes(x = p_grid, y = posterior, color = posterior)) +
::geom_link2() +
ggforcegeom_point(shape = 21, fill = "white", size = 2.5) +
scale_color_gradientn(colours = c(clr_darken(clr0), clr2), guide = "none") +
labs(title = glue("{length(data$p_grid)} grid points"))
}
c(4, 7, 15, 60) %>%
map(grid_approx) %>%
map(plot_grid_approx) %>%
wrap_plots(nrow = 1) &
scale_x_continuous(breaks = c(0, .5, 1))
Note how the y scale depends on the number of grid points: the peak reaches ~0.75 for 4 points, but only ~ 0.043 for 60 points.
3.5 Quadratic Approximation
library(rethinking)
<- purrr::map
map <- function(w_in, l_in){
compare_qa <- quap(
globe_qa alist(
~ dbinom( W + L, p ), # binomial likelihood
W ~ dunif( 0, 1 ) # uniform prior
p
),data = list( W = w_in, L = l_in )
)
<- precis(globe_qa) %>%
qa_results as_tibble() %>%
mutate(qa = glue("W: {w_in}, L: {l_in}"))
ggplot() +
stat_function(fun = function(x){ dbeta( shape1 = w_in + 1, shape2 = l_in + 1, x = x ) /
integrate(f = function(x){ dbeta( shape1 = w_in + 1, shape2 = l_in + 1, x = x )},
lower = 0, upper = 1)[[1]] },
xlim = c(0,1), n = 500, geom = "area", color = clr0d, fill = fll0)+
stat_function(fun = function(x){ dnorm(x = x, mean = qa_results$mean, sd = qa_results$sd )},
xlim = c(0,1), n = 500, geom = "line", color = clr2, linetype = 3) +
labs(title = glue("W: {w_in}, L: {l_in}, n = {w_in + l_in}"),
y = "density", x = "proportion water")
}
compare_qa(w_in = 6, l_in = 3) +
compare_qa(w_in = 12, l_in = 6) +
compare_qa(w_in = 24, l_in = 12)
3.6 Marcov Chain Monte Carlo (MCMC)
<- 1e4
n_samples <- rep( NA, n_samples )
p_init 1] <- .5
p_init[
<- function(p, W = 6, L = 3){
manual_mcmc for ( i in 2:n_samples ) {
<- rnorm( n = 1, mean = p[ i - 1], sd = 0.1)
p_new if ( p_new < 0 ){ p_new <- abs( p_new ) }
if ( p_new > 1 ){ p_new <- 2 - p_new }
<- dbinom( W, W + L, p[ i - 1 ] )
q0 <- dbinom( W, W + L, p_new )
q1 <- if_else( runif(1) < q1 / q0, p_new, p[i - 1] )
p[i]
}
p
}
<- manual_mcmc(p_init)
p <- manual_mcmc(p_init, W = 24, L = 12)
p_36
<- tibble(x = p) %>%
p_p ggplot() +
stat_function(fun = function(x){ dbeta( shape1 = 6 + 1, shape2 = 3 + 1, x = x ) /
integrate(f = function(x){ dbeta( shape1 = 6 + 1, shape2 = 3 + 1, x = x )},
lower = 0, upper = 1)[[1]] },
xlim = c(0,1), n = 500, geom = "area",
aes(color = "posterior", fill = after_scale(clr_alpha(color))))+
geom_density(aes(x = x, color = "MCMC")) +
scale_color_manual("approach", values = c(posterior = clr0, MCMC = clr2)) +
labs(y = "density", x = "proportion water", title = "n = 12") +
theme(legend.position = "bottom")
<- tibble(x = p_36) %>%
p_p36 ggplot() +
stat_function(fun = function(x){ dbeta( shape1 = 24 + 1, shape2 = 12 + 1, x = x ) /
integrate(f = function(x){ dbeta( shape1 = 24 + 1, shape2 = 12 + 1, x = x )},
lower = 0, upper = 1)[[1]] },
xlim = c(0,1), n = 500, geom = "area",
aes(color = "posterior", fill = after_scale(clr_alpha(color))))+
geom_density(aes(x = x, color = "MCMC")) +
scale_color_manual("approach", values = c(posterior = clr0, MCMC = clr2)) +
labs(y = "density", x = "proportion water", title = "n = 36") +
theme(legend.position = "bottom")
+ p_p36 p_p
library(rlang)
<- env(
chapter2_models compare_qa = compare_qa
)
write_rds(chapter2_models, "envs/chapter2_models.rds")
3.7 Homework
M1
<- function(data){
plot_grid_approx %>%
data ggplot(aes(x = p_grid, y = posterior, color = posterior)) +
::geom_link2() +
ggforcegeom_point(shape = 21, fill = "white", size = 2.5) +
scale_color_gradientn(colours = c(clr_darken(clr0), clr2), guide = "none") +
labs(title = glue("{length(data$p_grid)} grid points"))
}
tibble(n_grid = rep(c(4,7,20), 3),
L = rep(c(0,1,2), each = 3),
W = rep(c(3,3,5), each = 3)) %>%
pmap(grid_approx) %>%
map(plot_grid_approx) %>%
wrap_plots(nrow = 3) &
scale_x_continuous(breaks = c(0, .5, 1))
M2
tibble(n_grid = rep(c(4,7,20), 3),
L = rep(c(0,1,2), each = 3),
W = rep(c(3,3,5), each = 3)) %>%
pmap(grid_approx, prior = f_step ) %>%
map(plot_grid_approx) %>%
wrap_plots(nrow = 3) &
scale_x_continuous(breaks = c(0, .5, 1))
M3
\[ Pr(Earth | Land) = \frac{Pr(Land | Earth) \times Pr(Earth)}{Pr(Land)} \]
<- .3
p_l_on_earth <- 1
p_l_on_mars <- .5
p_earth <- .5 * (p_l_on_earth + p_l_on_mars)
average_p_l <- p_l_on_earth * p_earth / average_p_l) (p_earth_on_l
#> [1] 0.2307692
M4 & M5
<- c("C1b1|C1b2", "C2b1|C2w2", "C3w1|C3w2" )
cards
<- c("C1b1|C1b2", "C1b2|C1b1",
conjectures "C2b1|C2w2", "C2w2|C2b1",
"C3w1|C3w2", "C3w2|C3w1")
tibble(cards = c("C1", "C2", "C3"),
ways = c("C1b1|C1b2 & C1b2|C1b1", "C2b1|C2w2", ""),
ways_tor_produce_data = c(2, 1, 0),
plausibility = (ways_tor_produce_data / sum(ways_tor_produce_data)) %>% round(digits = 3),
other_side = c("b", "w", ""),
prior = c(2,1,1),
posterior = ((plausibility * prior) / sum(plausibility * prior)) %>% round(digits = 3))%>%
::kable() knitr
cards | ways | ways_tor_produce_data | plausibility | other_side | prior | posterior |
---|---|---|---|---|---|---|
C1 | C1b1|C1b2 & C1b2|C1b1 | 2 | 0.667 | b | 2 | 0.8 |
C2 | C2b1|C2w2 | 1 | 0.333 | w | 1 | 0.2 |
C3 | 0 | 0.000 | 1 | 0.0 |
M6
tibble(cards = c("C1", "C2", "C3"),
ways = c("C1b1|C1b2 & C1b2|C1b1", "C2b1|C2w2", ""),
ways_tor_produce_data = c(2, 1, 0),
plausibility = (ways_tor_produce_data / sum(ways_tor_produce_data)) %>% round(digits = 3),
other_side = c("b", "w", ""),
prior = c(1,2,3),
posterior = ((plausibility * prior) / sum(plausibility * prior)) %>% round(digits = 3))%>%
::kable() knitr
cards | ways | ways_tor_produce_data | plausibility | other_side | prior | posterior |
---|---|---|---|---|---|---|
C1 | C1b1|C1b2 & C1b2|C1b1 | 2 | 0.667 | b | 1 | 0.5 |
C2 | C2b1|C2w2 | 1 | 0.333 | w | 2 | 0.5 |
C3 | 0 | 0.000 | 3 | 0.0 |
M7
tibble(cards = c("C1", "C2", "C3"),
ways = c("C1b1>C2w2 & C1b1>C3w1 & C1b1>C3w2 & C2b1>C2w2 & C2b1>C3w1 & C2b1>C3w2",
"C2b1>C3w1 & C2b1>C3w2", ""),
ways_tor_produce_data = c(6, 2, 0),
plausibility = (ways_tor_produce_data / sum(ways_tor_produce_data)) %>% round(digits = 3))%>%
::kable() knitr
cards | ways | ways_tor_produce_data | plausibility |
---|---|---|---|
C1 | C1b1>C2w2 & C1b1>C3w1 & C1b1>C3w2 & C2b1>C2w2 & C2b1>C3w1 & C2b1>C3w2 | 6 | 0.75 |
C2 | C2b1>C3w1 & C2b1>C3w2 | 2 | 0.25 |
C3 | 0 | 0.00 |
H1
\[ Pr(twin | spec_a) = 0.2 \\ Pr(twin | spec_b) = 0.1 \\ Pr(twin) = 0.15 \]
\[ Pr(spec_a | twin) = \frac{Pr(spec_a) \times Pr(twin | spec_a)}{Pr(twin)} \\ Pr(spec_b | twin) = \frac{Pr(spec_b) \times Pr(twin | spec_b)}{Pr(twin)} \]
<- .1
pr_twn_on_a <- .2
pr_twn_on_b <- (pr_twn_on_a + pr_twn_on_b) /2
pr_twn
<- .5
prior_a
<- (prior_a * pr_twn_on_a) / pr_twn
pr_a_on_twn <- ((1 - prior_a) * pr_twn_on_b) / pr_twn
pr_b_on_twn
<- pr_a_on_twn * pr_twn_on_a + pr_b_on_twn * pr_twn_on_b) %>%
(p_next_twn round(digits = 3)
#> [1] 0.167
H2
\[ Pr(spec_a | twin) = \frac{1}{3} \]
pr_a_on_twn
#> [1] 0.3333333
H3
\[ Pr(single | spec_a) = Pr(\neg twin | spec_a) = 1 - Pr(twin | spec_a) \]
\[ Pr( spec_a | single) = \frac{Pr(single|spec_a)Pr(spec_a)}{Pr(single)} \]
<- 1 - pr_twn_on_a
pr_sgl_on_a <- 1 - pr_twn_on_b
pr_sgl_on_b <- weighted.mean(x = c(pr_sgl_on_a, pr_sgl_on_b),
pr_sgl w = c(pr_a_on_twn, 1- pr_a_on_twn))
<- pr_a_on_twn
prior_a
<- (prior_a * pr_sgl_on_a) / pr_sgl
pr_a_on_sgl <- ((1 - prior_a) * pr_sgl_on_b) / pr_sgl
pr_b_on_sgl
tibble(pr_a_on_sgl = pr_a_on_sgl, pr_b_on_sgl = pr_b_on_sgl,
control = pr_a_on_sgl + pr_b_on_sgl) %>%
round(digits = 4) %>%
::kable() knitr
pr_a_on_sgl | pr_b_on_sgl | control |
---|---|---|
0.36 | 0.64 | 1 |
H4
\[ Pr(spec_a | test ) = 0.8 \\ Pr(spec_b | test ) = 0.65 \\ Pr(spec_a | test ) = \frac{Pr( test | spec_a ) \times Pr(spec_a)}{Pr(test_positive)} \]
<- .8
pr_testa_on_a <- .65
pr_testb_on_b <- 1 - pr_testb_on_b
pr_testa_on_b
<- .5
prior_a
<- (prior_a * pr_testa_on_a) + ((1- prior_a) * pr_testa_on_b)
pr_testa
tibble(pr_on_testa = c("A","B"),
genetic_test = c((prior_a * pr_testa_on_a) / pr_testa,
1 - prior_a) * pr_testa_on_b) / pr_testa)) ((
#> # A tibble: 2 × 2
#> pr_on_testa genetic_test
#> <chr> <dbl>
#> 1 A 0.696
#> 2 B 0.304
<- pr_a_on_sgl
prior_a_updated <- (prior_a_updated * pr_testa_on_a) + ((1- prior_a_updated) * pr_testa_on_b)
pr_testa_updated
tibble(pr_on_testa = c("A","B"),
genetic_test = c((prior_a_updated * pr_testa_on_a) / pr_testa_updated,
1 - prior_a_updated) * pr_testa_on_b) / pr_testa_updated)) ((
#> # A tibble: 2 × 2
#> pr_on_testa genetic_test
#> <chr> <dbl>
#> 1 A 0.562
#> 2 B 0.438
3.8 {brms} section
<- brm( data = list(w = 24),
brms_c2_36_tosses family = binomial(link = "identity"),
| trials(36) ~ 0 + Intercept,
w prior(beta(1, 1), class = b, lb = 0, ub = 1),
seed = 42,
file = "brms/brms_c2_36_tosses" )
%>% summary() brms_c2_36_tosses
#> Family: binomial
#> Links: mu = identity
#> Formula: w | trials(36) ~ 0 + Intercept
#> Data: list(w = 24) (Number of observations: 1)
#> 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.66 0.08 0.49 0.80 1.00 1123 1813
#>
#> 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).
posterior_summary(brms_c2_36_tosses) %>%
round(digits = 3) %>%
::kable() knitr
Estimate | Est.Error | Q2.5 | Q97.5 | |
---|---|---|---|---|
b_Intercept | 0.657 | 0.078 | 0.492 | 0.798 |
lp__ | -3.994 | 0.744 | -6.055 | -3.465 |
as_draws_df(brms_c2_36_tosses) %>%
as_tibble() %>%
ggplot(aes(x = b_Intercept)) +
geom_density(color = clr1, fill = fll1) +
scale_x_continuous(limits = c(0, 1), expand = c(0, 0)) +
labs(y = "density", x = "proportion water",
title = "{brms} posterior probability (n = 36)")
3.9 pymc3 section
Taken from from pymc-devs…
import arviz as az
import matplotlib.pyplot as plt
import numpy as np
import pymc3 as pm
import scipy.stats as stats
import seaborn as sns
import matplotlib
from matplotlib.colors import ListedColormap
import matplotlib.font_manager
#matplotlib.font_manager.rebuild()
= np.array([0, 3, 8, 9, 0])
ways / ways.sum() ways
#> array([0. , 0.15, 0.4 , 0.45, 0. ])
\[ Pr(w | n, p) = \frac{n!}{w! (n - w)!}p^{w}(1 - p)^{n-w} \]
6, n = 9, p = 0.5) stats.binom.pmf(
#> 0.16406250000000003
Computing the posterior using a grid approximation inside a python
function.
def posterior_grid_approx(grid_points = 5, success = 6, tosses = 9):
""""""
# define grid
= np.linspace(0, 1, grid_points)
p_grid
# define prior
= np.repeat(5, grid_points) # uniform
prior # prior = (p_grid >= 0.5).astype(int) # truncated
# prior = np.exp(- 5 * abs(p_grid - 0.5)) # double exp
# compute likelihood at each point in the grid
= stats.binom.pmf(success, tosses, p_grid)
likelihood
# compute product of likelihood and prior
= likelihood * prior
unstd_posterior
# standardize the posterior, so it sums to 1
= unstd_posterior / unstd_posterior.sum()
posterior return p_grid, posterior
= 6, 9
w, n
= plt.subplots(1, 3, figsize = (12, 4))
fig, ax
= (5, 10, 20)
points for idx, ps in enumerate(points):
= posterior_grid_approx(ps, w, n)
p_grid, posterior "o-", color = r.clr3, label = f"successes = {w}\ntosses = {n}")
ax[idx].plot(p_grid, posterior, "probability of water")
ax[idx].set_xlabel("posterior probability")
ax[idx].set_ylabel(f"{ps} points")
ax[idx].set_title(= 0)
ax[idx].legend(loc 'left'].set_visible(False)
ax[idx].spines['right'].set_visible(False)
ax[idx].spines['top'].set_visible(False)
ax[idx].spines['bottom'].set_visible(False)
ax[idx].spines[= 'dotted') ax[idx].grid(linestyle
0, 1), (3, 6)) np.repeat((
#> array([0, 0, 0, 1, 1, 1, 1, 1, 1])
= np.repeat((0, 1), (3, 6))
data
with pm.Model() as normal_approximation:
= pm.Uniform("p", 0, 1) # uniform priors
p = pm.Binomial("w", n=len(data), p=p, observed=data.sum()) # binomial likelihood
w = pm.find_MAP()
mean_q = ((1 / pm.find_hessian(mean_q, vars=[p])) ** 0.5)[0]
std_q
# display summary of quadratic approximation
#> █
print("Mean\tStandard deviation\np {:.2}\t{:.2}".format(mean_q["p"], std_q[0]))
#> Mean Standard deviation
#> p 0.67 0.16
# Compute the 89% percentile interval
= stats.norm(mean_q, std_q)
norm = 0.89
prob = stats.norm.ppf([(1 - prob) / 2, (1 + prob) / 2])
z = mean_q["p"] + std_q * z
pi print("5.5%, 94.5% \n{:.2}, {:.2}".format(pi[0], pi[1]))
#> 5.5%, 94.5%
#> 0.42, 0.92
# analytical calculation
= 6, 9
w, n = np.linspace(0, 1, 100)
x + 1, n - w + 1), label="True posterior", color = r.clr0d)
plt.plot(x, stats.beta.pdf(x, w
# quadratic approximation
"p"], std_q), label="Quadratic approximation", color = r.clr3)
plt.plot(x, stats.norm.pdf(x, mean_q[= 0)
plt.legend(loc
f"n = {n}")
plt.title("Proportion water");
plt.xlabel('left'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().spines[= 'dotted')
plt.grid(linestyle plt.show()
= np.linspace(0, 1, 100)
x = [6, 12, 24], [9, 18, 36]
w, n
= plt.subplots(1, 3, figsize=(12, 4))
fig, ax
for idx, ps in enumerate(zip(w, n)):
= np.repeat((0, 1), (ps[1] - ps[0], ps[0]))
data with pm.Model() as normal_approximation:
= pm.Uniform("p", 0, 1) # uniform priors
p = pm.Binomial("w", n=len(data), p=p, observed=data.sum()) # binomial likelihood
w = pm.find_MAP()
mean_q = ((1 / pm.find_hessian(mean_q, vars=[p])) ** 0.5)[0]
std_q
0] + 1, ps[1] - ps[0] + 1), label = "True posterior", color = r.clr0d)
ax[idx].plot(x, stats.beta.pdf(x, ps["p"], std_q), label = "Quadratic approximation", color = r.clr3)
ax[idx].plot(x, stats.norm.pdf(x, mean_q["probability of water")
ax[idx].set_xlabel("density")
ax[idx].set_ylabel(r"$n={}$".format(ps[1]))
ax[idx].set_title(="upper left")
ax[idx].legend(loc'left'].set_visible(False)
ax[idx].spines['right'].set_visible(False)
ax[idx].spines['top'].set_visible(False)
ax[idx].spines['bottom'].set_visible(False)
ax[idx].spines[= 'dotted') ax[idx].grid(linestyle
= 1000
n_samples = np.zeros(n_samples)
p 0] = 0.5
p[= 6
W = 3
L
for i in range(1, n_samples):
= stats.norm(p[i - 1], 0.1).rvs(1)
p_new if p_new < 0:
= -p_new
p_new if p_new > 1:
= 2 - p_new
p_new = stats.binom.pmf(W, n = W + L, p = p[i - 1])
q0 = stats.binom.pmf(W, n = W + L, p = p_new)
q1 if stats.uniform.rvs(0, 1) < q1 / q0:
= p_new
p[i] else:
= p[i - 1] p[i]
= "Metropolis approximation", plot_kwargs = {"color": r.clr3})
az.plot_kde(p, label = np.linspace(0, 1, 100)
x + 1, L + 1), "C1", label = "True posterior", color = r.clr0d)
plt.plot(x, stats.beta.pdf(x, W
plt.legend()'left'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().spines[= 'dotted')
plt.grid(linestyle plt.show()