See some more multi-level models.
Take a look at shinystan
for exploring our models.
Learn two methods for dealing with Stan issues as they arise.
data(chimpanzees, package = "rethinking")
Chimps <- chimpanzees
rm(chimpanzees)
The data include two experimental conditions, prosoc_left
and condition
, each of which has two levels. This results in four combinations.
Chimps %>%
distinct(prosoc_left, condition) %>%
mutate(description = c("Two food items on right and no partner",
"Two food items on left and no partner",
"Two food items on right and partner present",
"Two food items on left and partner present")) %>%
knitr::kable()
condition | prosoc_left | description |
---|---|---|
0 | 0 | Two food items on right and no partner |
0 | 1 | Two food items on left and no partner |
1 | 0 | Two food items on right and partner present |
1 | 1 | Two food items on left and partner present |
Chimps <-
Chimps %>%
mutate(treatment = 1 + prosoc_left + 2 * condition) %>%
rename(block_idx = block) %>%
mutate(
label = factor(treatment,
levels = 1:4,
labels = c("R/N", "L/N", "R/P", "L/P")))
mosaic::tally(treatment ~ block_idx + actor, data = Chimps) %>% pander()
actor | 1 | 2 | 3 | 4 | 5 | 6 | 7 | ||
treatment | block_idx | ||||||||
1 | 1 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | |
2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
4 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
5 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
6 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
2 | 1 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | |
2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
4 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
5 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
6 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | ||
3 | 1 | 5 | 6 | 3 | 4 | 3 | 3 | 2 | |
2 | 3 | 2 | 1 | 1 | 3 | 2 | 3 | ||
3 | 2 | 2 | 3 | 3 | 3 | 3 | 3 | ||
4 | 4 | 4 | 5 | 5 | 3 | 3 | 5 | ||
5 | 2 | 1 | 1 | 1 | 4 | 3 | 2 | ||
6 | 2 | 3 | 5 | 4 | 2 | 4 | 3 | ||
4 | 1 | 1 | 0 | 3 | 2 | 3 | 3 | 4 | |
2 | 3 | 4 | 5 | 5 | 3 | 4 | 3 | ||
3 | 4 | 4 | 3 | 3 | 3 | 3 | 3 | ||
4 | 2 | 2 | 1 | 1 | 3 | 3 | 1 | ||
5 | 4 | 5 | 5 | 5 | 2 | 3 | 4 | ||
6 | 4 | 3 | 1 | 2 | 4 | 2 | 3 |
Notes:
block
is a reserved word in Stan, so we can’t use a variable with that name in our models.
Both block
and actor
are already sequential integers starting at 1. If that were not the case, we would want to recode them using, for example, mutate(block_idx = as.integer(factor(block))
.
In R, it often doesn’t matter whether a number is considered an integer or just a number. But Stan worries about these things, so be sure that your indices are coded as integers. You can put a capital L after a number to force it to be an integer.
is.integer(1)
## [1] FALSE
is.integer(1L)
## [1] TRUE
Time for some models. I’m going to switch up the names of things a bit here and there, but otherwise, these are just like in the text.
u13.4 <-
ulam(
data = Chimps %>% select(actor, block_idx, pulled_left, treatment),
alist(
pulled_left ~ dbinom(1, p),
logit(p) <- a[actor] + b[block_idx] + t[treatment],
a[actor] ~ dnorm(mu_a, sigma_a),
b[block_idx] ~ dnorm(0, sigma_b),
t[treatment] ~ dnorm(0, 0.5),
mu_a ~ dnorm(0, 1.5),
sigma_a ~ dexp(1),
sigma_b ~ dexp(1)
),
chains = 4, cores = 4, log_lik = TRUE,
file = "fits/u13.4"
)
u13.5 <-
ulam(
data = Chimps %>% select(actor, block_idx, pulled_left, treatment),
alist(
pulled_left ~ dbinom(1, p),
logit(p) <- a[actor] + t[treatment],
a[actor] ~ dnorm(mu_a, sigma_a),
t[treatment] ~ dnorm(0, 0.5),
mu_a ~ dnorm(0, 1.5),
sigma_a ~ dexp(1)
),
chains = 4, cores = 4, log_lik = TRUE,
file = "fits/u13.5"
)
u13.6 <-
ulam(
data = Chimps %>% select(actor, block_idx, pulled_left, treatment),
alist(
pulled_left ~ dbinom(1, p),
logit(p) <- a[actor] + b[block_idx] + t[treatment],
a[actor] ~ dnorm(mu_a, sigma_a),
b[block_idx] ~ dnorm(0, sigma_b),
t[treatment] ~ dnorm(0, sigma_t),
mu_a ~ dnorm(0, 1.5),
sigma_a ~ dexp(1),
sigma_b ~ dexp(1),
sigma_t ~ dexp(1)
),
chains = 4, cores = 4, log_lik = TRUE,
file = "fits/u13.6"
)
Multi-level models can arise in several ways, and we handle them basically the same, but you will see people refer to hierarchical and cross-classified models. The chimp models above are cross-classified – the blocks are not nested in the actors or vise versa. In hierarchical models, the different levels are nested. For example, we might have a model where each person belongs to a city/town, each city/town to a state/region, and each state/region to a country.
The Bayesian model is happy to deal with either type of structure. But some software (like brms
) has special short-cuts for describing nested/hierarchical structures.
When these models were originally fit, Stan issued several warnings. These warnings are not reissued when the models are read from the files.
Here are some of the warning messages produced for the last model:
Warning messages:
1: There were 8 divergent transitions after warmup. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
2: Examine the pairs() plot to diagnose sampling problems
3: Bulk Effective Samples Size (ESS) is too low, indicating posterior means and medians may be unreliable.
Running the chains for more iterations may help. See
http://mc-stan.org/misc/warnings.html#bulk-ess
4: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
Running the chains for more iterations may help. See
http://mc-stan.org/misc/warnings.html#tail-ess
Divergent transitions occur when Stan’s check that the simulation conserves total energy fails. When this happens, the proposed new posterior sample is rejected. Divergent transitions can indicate two things:
The algorithm is less efficient because it is discarding attempts to find new posterior samples.
The algorithm is having a hard time sampling is some region of the posterior.
The first of these is less of a problem than the second. More iterations (and more patience) handle the first. But if sampling is not working well in a region of the posterior, then our approxmate posterior won’t be an accurate representation of the true posterior.
Effective sample size is something we have seen before and it is a measure of how efficiently Stan is sampling and how reliable our estimates will be. Stan actually reports two kinds of effective sample size. “Bulk” is what we have seen as n_eff
in ulam()
output. “Tail” is about the lower density portions of the distribution. Since those are sampled less frequently (by design), estiamates based on those (like HDIs) are potentially less reliable, so Stan does a separate estimated effective sample size for the tails.
More on addressing those shortly. For now, let’s compare our models.
You can learn more about Stan’s warnings at https://mc-stan.org/misc/warnings.html
We should really address the warning first and then look, but…
coeftab(u13.4, u13.5, u13.6) %>% plot()
The models are remarkably similar. Why? because block
and treatment
don’t have much of an influence, so whether we include them, and whether we use adaptive priors/multi-level models or not, we get essentially the same story. Also, the adaptive priors for converged to nearly the same values as the non-adaptive priors we used, so our adaptive prior approach didn’t do much either.
If you issue the following command in your console, you will launch an interactive web app that lets you inspect your model.
u13.6 %>% stanfit() %>% launch_shinystan()
If you want to remove all those log_lik
items, you can do
u13.6 %>% stanfit() %>%
as.shinystan() %>%
drop_parameters("log_lik") %>%
launch_shinystan()
Do not exectute these commands in your R Markdown document.
The plots produced with shinystan
can be reproduced using various mcmc_
functions. For example:
mcmc_nuts_divergence(
u13.6 %>% stanfit() %>% nuts_params(),
u13.6 %>% stanfit() %>% log_posterior()
)
Some of the summary tables do not appear to have a public-facing function. (I’m checking on this with Jonah Sol Gabry, one of the authors of shinystan.) So for the moment, I don’t know an easy way to produce this table in your R Markdown document:
There are two basic approaches
Adjust tuning parameters for stan.
Here are some things that can help.
increase the number of iterations (to boost effective sample sizes, for example)
increase the warm up (to let Stan take more time to auto-tune itself)
increaese adapt_delta
(default in ulam()
is 0.95) to tell Stan to take smaller steps when approximating paths of the sliding puck. This will make the approximations better, but slower. The unit here is desired proportion of attempts that are accepted, so you could bump this up to 0.97 or 0.99. This is done using the control
argument of ulam()
: control = list(adapt_delta - 0.99)
, for example.
Reparameterize the model.
Sometimes two mathematically equivalent versions of a model are not equivalent numerically and one of them may help Stan perform better.
Sometimes adjusting Stan’s tuning parameters is all you need to take care of problems, but often this will not be sufficient and we need to try repararameterizing our model.
Here we will trying using standardized versions of our prior distributions.
Instead of a[actor] ~ dnorm(mu_a, sigma_a)
, we will use mu_a + za[actor] * sigma_a
where za[actor] ~ dnorm(0, 1)
Instead of b[block_idx] ~ dnorm(0, sigma_b)
, we will use zb[block_idx] * sigma_b
where zb[block_idx] ~ dnorm(0, 1)
Instead of t[treatment] ~ dnorm(0, sigma_t)
, we will use zb[treatment] * sigma_t
where zt[treatment] ~ dnorm(0, 1)
u13.6r <-
ulam(
data = Chimps %>% select(actor, block_idx, pulled_left, treatment),
alist(
pulled_left ~ dbinom(1, p),
logit(p) <-
mu_a + za[actor] * sigma_a +
zb[block_idx] * sigma_b +
zt[treatment] * sigma_t,
za[actor] ~ dnorm(0, 1),
zb[block_idx] ~ dnorm(0, 1),
zt[treatment] ~ dnorm(0, 1),
mu_a ~ dnorm(0, 1.5),
sigma_a ~ dexp(1),
sigma_b ~ dexp(1),
sigma_t ~ dexp(1),
gq> vector[actor]: a <<- mu_a + za * sigma_a,
gq> vector[block_idx]: b <<- zb * sigma_b,
gq> vector[treatment]: t <<- zt * sigma_t
),
chains = 4, cores = 4, log_lik = TRUE,
file = "fits/u13.6r"
)
Did it help? Well, there were no warnings. Also, ESS increased for nearly every parameter.
as.data.frame.precis <- function(x, row.names = NULL, optional = FALSE, ...){
x@.Data %>%
as.data.frame() %>%
setNames(x@names) %>%
mutate(param = x@row.names) %>%
select(param, everything())
}
left_join(
precis(u13.6, depth = 2) %>% as.data.frame() %>% mutate(model = "u13.6"),
precis(u13.6r, depth = 2) %>% as.data.frame() %>% mutate(model = "u13.6r"),
by = "param"
) %>%
gf_text(n_eff.y ~ n_eff.x, label = ~ param, alpha = 0.6, angle = 30, size = 3) %>%
gf_abline(slope = ~1, intercept = ~ 0, inherit = FALSE,
linetype = "dotted", color = "red") %>%
gf_labs(x = "ESS (u13.6)", y = "ESS (u13.6r)")
In our particular case, things were not too bad (effective sample sizes were smallish, but not terribly small, the number of divergent transitions was also pretty small), so very little changes when we reparameterize:
coeftab(u13.6, u13.6r) %>% plot()
But if the problems are more severe, reparameterizing can make all the difference.
Here are two easy reparameterizations that involve distributions we have seen frequently.
\(\beta \sim {\sf Norm}(\mu, \sigma) \to z_\beta \sim {\sf Norm}(0, 1); \beta = \mu + z_\beta \sigma\)
\(\beta \sim {\sf Exp}(\lambda) \to \tilde \beta \sim {\sf Exp}(1); \beta = \tilde \beta / \lambda\)
Chimp2 <-
expand_grid(
actor = 2:3, treatment = 1:4, block_idx = 1
)
AvgChimp <-
u13.6r %>%
link(data = Chimp2) %>%
apply(2, mean_hdci) %>%
bind_rows() %>%
bind_cols(Chimp2)
AvgChimp %>%
gf_pointrange(y + ymin + ymax ~ treatment) %>%
gf_facet_grid( ~ actor, labeller = label_both) %>%
gf_lims(y = c(0,1))
AvgChimp %>% pander()
y | ymin | ymax | .width | .point | .interval | actor | treatment |
---|---|---|---|---|---|---|---|
0.9791 | 0.9404 | 1 | 0.95 | mean | hdci | 2 | 1 |
0.9869 | 0.9606 | 1 | 0.95 | mean | hdci | 2 | 2 |
0.9715 | 0.9197 | 1 | 0.95 | mean | hdci | 2 | 3 |
0.9855 | 0.956 | 1 | 0.95 | mean | hdci | 2 | 4 |
0.2845 | 0.1472 | 0.4201 | 0.95 | mean | hdci | 3 | 1 |
0.3936 | 0.2284 | 0.5543 | 0.95 | mean | hdci | 3 | 2 |
0.2259 | 0.1064 | 0.3536 | 0.95 | mean | hdci | 3 | 3 |
0.369 | 0.2221 | 0.5353 | 0.95 | mean | hdci | 3 | 4 |
block_idx |
---|
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
We have several ways to access the posterior samples and the format of the R object differs from one to another.
Predict the output of each of these:
Post13.6r <- extract.samples(u13.6r)
Post13.6r %>% str()
## List of 10
## $ za : num [1:2000, 1:7] -0.586 -0.712 -0.138 0.113 -0.246 ...
## $ zb : num [1:2000, 1:6] 0.154 0.101 -1.553 -1.578 -1.923 ...
## $ zt : num [1:2000, 1:4] 0.158 0.339 0.679 -0.443 -1.008 ...
## $ mu_a : num [1:2000(1d)] 0.484 0.742 0.185 -0.392 0.784 ...
## $ sigma_a: num [1:2000(1d)] 1.77 2.08 2.96 2.89 2.35 ...
## $ sigma_b: num [1:2000(1d)] 0.365 0.101 0.151 0.2 0.121 ...
## $ sigma_t: num [1:2000(1d)] 0.507 0.722 0.394 0.272 0.946 ...
## $ t : num [1:2000, 1:4] 0.0802 0.2449 0.2679 -0.1202 -0.9532 ...
## $ b : num [1:2000, 1:6] 0.056 0.0101 -0.2344 -0.316 -0.2331 ...
## $ a : num [1:2000, 1:7] -0.5549 -0.7398 -0.2239 -0.0664 0.2054 ...
## - attr(*, "source")= chr "ulam posterior: 2000 samples from object"
Post13.6r %>% as.data.frame() %>% str()
## 'data.frame': 2000 obs. of 38 variables:
## $ za.1 : num -0.586 -0.712 -0.138 0.113 -0.246 ...
## $ za.2 : num 2.242 2.164 0.978 1.844 1.806 ...
## $ za.3 : num -0.9207 -0.9509 -0.4592 -0.0595 -0.3578 ...
## $ za.4 : num -0.60007 -0.66059 -0.35398 -0.00327 -0.47199 ...
## $ za.5 : num -0.6696 -0.6127 -0.2289 0.0206 -0.3812 ...
## $ za.6 : num -0.04965 0.01172 -0.02351 0.45914 0.00248 ...
## $ za.7 : num 0.691 0.36 0.691 1.037 0.758 ...
## $ zb.1 : num 0.154 0.101 -1.553 -1.578 -1.923 ...
## $ zb.2 : num 1.301 1.227 1.626 -0.862 -0.364 ...
## $ zb.3 : num 0.432 0.242 0.564 -1.136 1.501 ...
## $ zb.4 : num 0.7425 1.24 -0.0715 -1.6753 0.3141 ...
## $ zb.5 : num 0.714 1.037 1.959 -0.534 -0.602 ...
## $ zb.6 : num -0.154 1.081 0.838 0.894 1.283 ...
## $ zt.1 : num 0.158 0.339 0.679 -0.443 -1.008 ...
## $ zt.2 : num 0.4842 1.0478 0.7026 0.8071 -0.0782 ...
## $ zt.3 : num -0.702 -0.818 -0.572 -2.575 -0.754 ...
## $ zt.4 : num 0.4967 0.401 0.7138 0.7623 0.0418 ...
## $ mu_a : num 0.484 0.742 0.185 -0.392 0.784 ...
## $ sigma_a: num 1.77 2.08 2.96 2.89 2.35 ...
## $ sigma_b: num 0.365 0.101 0.151 0.2 0.121 ...
## $ sigma_t: num 0.507 0.722 0.394 0.272 0.946 ...
## $ t.1 : num 0.0802 0.2449 0.2679 -0.1202 -0.9532 ...
## $ t.2 : num 0.2456 0.7561 0.2771 0.2191 -0.0739 ...
## $ t.3 : num -0.356 -0.59 -0.226 -0.699 -0.714 ...
## $ t.4 : num 0.2519 0.2894 0.2816 0.207 0.0395 ...
## $ b.1 : num 0.056 0.0101 -0.2344 -0.316 -0.2331 ...
## $ b.2 : num 0.4746 0.1237 0.2454 -0.1727 -0.0441 ...
## $ b.3 : num 0.1574 0.0244 0.0851 -0.2275 0.182 ...
## $ b.4 : num 0.2708 0.125 -0.0108 -0.3355 0.0381 ...
## $ b.5 : num 0.26 0.105 0.296 -0.107 -0.073 ...
## $ b.6 : num -0.0562 0.109 0.1265 0.179 0.1556 ...
## $ a.1 : num -0.5549 -0.7398 -0.2239 -0.0664 0.2054 ...
## $ a.2 : num 4.46 5.24 3.08 4.93 5.03 ...
## $ a.3 : num -1.1495 -1.236 -1.1732 -0.5634 -0.0569 ...
## $ a.4 : num -0.581 -0.632 -0.862 -0.401 -0.325 ...
## $ a.5 : num -0.704 -0.533 -0.492 -0.332 -0.112 ...
## $ a.6 : num 0.396 0.766 0.116 0.934 0.789 ...
## $ a.7 : num 1.71 1.49 2.23 2.6 2.56 ...
u13.6r %>% stanfit() %>% as.data.frame() %>% select(-matches("log_lik")) %>% str()
## 'data.frame': 2000 obs. of 39 variables:
## $ za[1] : num -0.259 -0.286 -0.16 -0.743 -0.538 ...
## $ za[2] : num 3.02 2.88 3.36 3.11 2.93 ...
## $ za[3] : num -0.929 -0.981 -0.766 -0.677 -0.846 ...
## $ za[4] : num -1.022 -1.065 -0.418 -1.051 -0.56 ...
## $ za[5] : num 0.04192 -0.00887 -0.6948 -0.26507 -0.6049 ...
## $ za[6] : num 0.2471 0.2842 0.0136 0.2514 -0.2844 ...
## $ za[7] : num 1.365 1.442 1.565 1.017 0.879 ...
## $ zb[1] : num -1.249 -1.305 -0.405 -1.582 -1.562 ...
## $ zb[2] : num 0.0576 0.13 0.6005 -0.3592 -0.887 ...
## $ zb[3] : num 0.5207 0.5506 0.0678 0.2078 -1.286 ...
## $ zb[4] : num 0.983 0.984 0.874 -0.658 0.201 ...
## $ zb[5] : num -0.444 -0.387 -1.107 -0.126 -0.678 ...
## $ zb[6] : num -0.44 -0.305 0.797 0.298 -0.473 ...
## $ zt[1] : num -0.311 -0.156 0.221 0.189 -0.204 ...
## $ zt[2] : num 0.656 0.667 0.547 0.96 0.843 ...
## $ zt[3] : num -0.454 -0.647 -0.256 -0.303 -0.395 ...
## $ zt[4] : num 0.56 0.608 0.626 0.921 0.363 ...
## $ mu_a : num 0.381 0.197 -0.102 0.301 0.671 ...
## $ sigma_a: num 1.23 1.31 1.13 1 1.66 ...
## $ sigma_b: num 0.318 0.275 0.303 0.463 0.285 ...
## $ sigma_t: num 0.618 0.783 1.123 0.582 0.802 ...
## $ t[1] : num -0.192 -0.122 0.249 0.11 -0.164 ...
## $ t[2] : num 0.405 0.522 0.614 0.559 0.676 ...
## $ t[3] : num -0.28 -0.506 -0.288 -0.176 -0.317 ...
## $ t[4] : num 0.346 0.476 0.703 0.536 0.291 ...
## $ b[1] : num -0.398 -0.359 -0.122 -0.733 -0.445 ...
## $ b[2] : num 0.0183 0.0358 0.1817 -0.1664 -0.2524 ...
## $ b[3] : num 0.1658 0.1517 0.0205 0.0962 -0.366 ...
## $ b[4] : num 0.3131 0.2711 0.2644 -0.3049 0.0572 ...
## $ b[5] : num -0.1414 -0.1066 -0.3349 -0.0583 -0.1931 ...
## $ b[6] : num -0.1401 -0.0839 0.2412 0.1379 -0.1347 ...
## $ a[1] : num 0.061 -0.178 -0.283 -0.442 -0.222 ...
## $ a[2] : num 4.1 3.98 3.68 3.42 5.53 ...
## $ a[3] : num -0.765 -1.089 -0.965 -0.376 -0.732 ...
## $ a[4] : num -0.88 -1.198 -0.573 -0.75 -0.257 ...
## $ a[5] : num 0.4323 0.1851 -0.8846 0.0362 -0.3317 ...
## $ a[6] : num 0.6854 0.5691 -0.0869 0.553 0.1995 ...
## $ a[7] : num 2.06 2.09 1.66 1.32 2.13 ...
## $ lp__ : num -277 -274 -274 -274 -272 ...
Post13.6r %>% as.data.frame() %>%
mutate(
logit_p_t1 =
mu_a + za.2 * sigma_a + zb.1 * sigma_b + zt.1 * sigma_t,
logit_p_t1a = a.2 + b.1 + zt.1, # alternative to previous line
p = ilogit(logit_p_t1)
) %>%
pull(p) %>%
mean_hdi()
## y ymin ymax .width .point .interval
## 1 0.9790522 0.9404348 0.999986 0.95 mean hdi
my_link <-
function(post, actor = 1, treatment = 1, block_idx = 1) {
logodds <-
with(post, a[, actor] + b[, block_idx] + t[, treatment])
ilogit(logodds)
}
my_link(Post13.6r, actor = 2, treatment = 1, block_idx = 1) %>%
mean_hdci()
## y ymin ymax .width .point .interval
## 1 0.9790522 0.9404348 0.999986 0.95 mean hdci
my_link2 <-
function(post, treatment = 1) {
logodds <-
with(post, mu_a + 0 + t[, treatment])
ilogit(logodds)
}
my_link2(Post13.6r, treatment = 1) %>%
mean_hdci()
## y ymin ymax .width .point .interval
## 1 0.5931986 0.2715195 0.8743485 0.95 mean hdci
1:4 %>% purrr::map_dfr(~ my_link2(Post13.6r, treatment = .x) %>% mean_hdci())
## y ymin ymax .width .point .interval
## 1 0.5931986 0.2715195 0.8743485 0.95 mean hdci
## 2 0.6955318 0.4196862 0.9410139 0.95 mean hdci
## 3 0.5226647 0.2108311 0.8469232 0.95 mean hdci
## 4 0.6746721 0.3910939 0.9339129 0.95 mean hdci
1:4 %>% purrr::map_dfr(~ my_link2(Post13.6r, treatment = .x) %>% mean_hdci()) %>%
mutate(treatment = 1:4) %>%
gf_line(y ~ treatment) %>%
gf_pointrange(y + ymin + ymax ~ treatment) %>%
gf_labs(title = "Average actor")
set.seed(12345)
Post13.6r$random_a <-
with(Post13.6r, rnorm(1:length(mu_a), mu_a, sigma_a))
Post13.6r %>% str()
## List of 11
## $ za : num [1:2000, 1:7] -0.586 -0.712 -0.138 0.113 -0.246 ...
## $ zb : num [1:2000, 1:6] 0.154 0.101 -1.553 -1.578 -1.923 ...
## $ zt : num [1:2000, 1:4] 0.158 0.339 0.679 -0.443 -1.008 ...
## $ mu_a : num [1:2000(1d)] 0.484 0.742 0.185 -0.392 0.784 ...
## $ sigma_a : num [1:2000(1d)] 1.77 2.08 2.96 2.89 2.35 ...
## $ sigma_b : num [1:2000(1d)] 0.365 0.101 0.151 0.2 0.121 ...
## $ sigma_t : num [1:2000(1d)] 0.507 0.722 0.394 0.272 0.946 ...
## $ t : num [1:2000, 1:4] 0.0802 0.2449 0.2679 -0.1202 -0.9532 ...
## $ b : num [1:2000, 1:6] 0.056 0.0101 -0.2344 -0.316 -0.2331 ...
## $ a : num [1:2000, 1:7] -0.5549 -0.7398 -0.2239 -0.0664 0.2054 ...
## $ random_a: num [1:2000] 1.523 2.217 -0.138 -1.701 2.207 ...
## - attr(*, "source")= chr "ulam posterior: 2000 samples from object"
my_sim <-
function(post) {
logodds <-
with(post, random_a + 0 + t[, 1:4])
ilogit(logodds)
}
Sim13.6r <- my_sim(Post13.6r)
Sim13.6r %>% str()
## num [1:2000, 1:4] 0.832 0.921 0.532 0.139 0.778 ...
Sim13.6r %>%
apply(2, mean_hdci) %>%
bind_rows()
## y ymin ymax .width .point .interval
## 1 0.5684042 0.04021216 0.9998147 0.95 mean hdci
## 2 0.6397878 0.06197481 0.9998708 0.95 mean hdci
## 3 0.5201288 0.02662308 0.9997084 0.95 mean hdci
## 4 0.6242883 0.06160885 0.9998533 0.95 mean hdci
Sim13.6r %>%
apply(2, mean_hdci) %>%
bind_rows() %>%
mutate(treatment = 1:4) %>%
gf_pointrange(y + ymin + ymax ~ treatment)
expand_grid(chimp = 1:100, treatment = 1:4) %>%
mutate(pulled_left = purrr::map2_dbl(chimp, treatment, ~ Sim13.6r[.x, .y])) %>%
gf_line(pulled_left ~ treatment, group = ~ chimp) %>%
gf_labs(x = "treatment", title = "100 simulated chimps")