En esta parte veremos cómo correr y diagnosticar en Stan varios ejemplos vistos en clase. Para instalar cmdstanr y Stan, puedes ver aquí. En python puedes usar pystan, por ejemplo.
# install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos"))) library(cmdstanr) library(posterior) library(tidyverse)
Escribimos el código para el modelo en un archivo modelo-1.stan, y compilamos:
archivo_stan <- file.path("stan/modelo-1.stan") # compilar mod <- cmdstan_model(archivo_stan)
mod
Pasamos datos y muestreamos
datos_lista <- list(n = 30, y = 19) ajuste <- mod$sample( data = datos_lista, seed = 1234, chains = 4, parallel_chains = 4, refresh = 500)
Checamos diagnósticos:
ajuste$cmdstan_diagnose()
Si no hay problemas, podemos ver el resumen:
ajuste$summary()
Donde verificamos que el tamaño de muestra efectivo (ess) y el diagnóstico de $\hat{R}$ son apropiados.
Podemos ver las cadenas de la siguiente forma:
theta_tbl <- ajuste$draws(c("theta", "theta_inicial")) %>% as_draws_df() ggplot(theta_tbl, aes(x = .iteration, y = theta)) + geom_line() + facet_wrap(~.chain, ncol = 1)
Y replicamos la gráfica de las notas haciendo:
sims_tbl <- theta_tbl %>% pivot_longer(theta:theta_inicial, names_to = "dist", values_to = "theta") ggplot(sims_tbl, aes(x = theta, fill = dist)) + geom_histogram(aes(x = theta), bins = 30, alpha = 0.5, position = "identity")
Tomamos el ejemplo de los boletos de lotería
loteria_tbl <- read_csv("../data/nums_loteria_avion.csv", col_names = c("id", "numero")) %>% mutate(numero = as.integer(numero)) set.seed(334) muestra_loteria <- sample_n(loteria_tbl, 25) %>% mutate(numero = numero/1000)
archivo_stan <- file.path("stan/modelo-2.stan") # compilar mod <- cmdstan_model(archivo_stan)
mod
Pasamos datos y muestreamos
datos_lista <- list(n = nrow(muestra_loteria), y = muestra_loteria$numero) ajuste <- mod$sample( data = datos_lista, seed = 1234, chains = 4, iter_warmup = 5000, iter_sampling = 20000, parallel_chains = 4, refresh = 5000)
Checamos diagnósticos:
ajuste$cmdstan_diagnose()
Si no hay problemas, podemos ver el resumen:
resumen <- ajuste$summary() resumen
El intervalo 95% que obtenemos es:
ajuste$draws("theta") %>% as_draws_df() %>% summarise(theta_inf = quantile(theta, 0.025), theta_mediana = quantile(theta, 0.5), theta_sup = quantile(theta, 0.975))
Podemos ahora intentar con la inicial gamma que nos pareció más intuitiva, aún cuando el modelo no es conjugado:
archivo_stan <- file.path("stan/modelo-3.stan") # compilar mod <- cmdstan_model(archivo_stan)
mod
Pasamos datos y muestreamos
datos_lista <- list(n = nrow(muestra_loteria), y = muestra_loteria$numero) ajuste <- mod$sample( data = datos_lista, seed = 1234, chains = 4, iter_sampling = 10000, parallel_chains = 4, refresh = 2000)
Checamos diagnósticos:
ajuste$cmdstan_diagnose()
Si no hay problemas, podemos ver el resumen:
resumen <- ajuste$summary() resumen
El intervalo 95% que obtenemos es:
ajuste$draws("theta") %>% as_draws_df() %>% summarise(theta_inf = quantile(theta, 0.025), theta_mediana = quantile(theta, 0.5), theta_sup = quantile(theta, 0.975))
Y la posterior se ve como sigue:
theta_post_sim <- ajuste$draws("theta") %>% as.numeric qplot(theta_post_sim)
Haremos el ejemplo no conjugado de estaturas de cantantes, con verificación predictiva posterior.
archivo_stan <- file.path("stan/modelo-cantantes.stan") # compilar mod <- cmdstan_model(archivo_stan)
mod
Pasamos datos y muestreamos
set.seed(3413) cantantes <- lattice::singer %>% mutate(estatura_cm = (2.54 * height)) %>% filter(str_detect(voice.part, "Tenor")) %>% sample_n(20) datos_lista <- list(n = nrow(cantantes), y = cantantes$estatura_cm) ajuste <- mod$sample( data = datos_lista, seed = 1234, chains = 4, iter_warmup = 4000, iter_sampling = 4000, refresh = 2000)
Checamos diagnósticos:
ajuste$cmdstan_diagnose()
Si no hay problemas, podemos ver el resumen:
resumen <- ajuste$summary() resumen
El intervalo 95% que obtenemos es:
ajuste$draws(c("mu", "sigma")) %>% as_draws_df() %>% ggplot(aes(x = mu, y = sigma)) + geom_point(alpha = 0.1) + coord_equal()
Y ahora extraemos algunas replicaciones de la posterior predictiva:
y_sim_tbl <- ajuste$draws("y_sim") %>% as_draws_df() %>% pivot_longer(cols = starts_with("y_sim"), "nombre") %>% separate(nombre, c("nombre", "n_obs", "vacio"), "[\\[\\]]") %>% select(-nombre, -vacio) %>% filter(.chain == 1, .iteration < 12) %>% select(.iteration, value) %>% bind_rows(tibble(.iteration = 12, value = round(cantantes$estatura_cm, 0))) ggplot(y_sim_tbl, aes(sample = value)) + geom_qq() + facet_wrap(~ .iteration)
sim_formas <- function(p_azar, p_corr){ tipo <- rbinom(1, 1, 1 - p_azar) if(tipo==0){ # al azar x <- rbinom(1, 10, 1/5) } else { # no al azar x <- rbinom(1, 10, p_corr) } x } set.seed(12) muestra_1 <- map_dbl(1:200, ~ sim_formas(0.35, 0.5))
archivo_stan <- file.path("stan/modelo-examenes.stan") # compilar mod_informado <- cmdstan_model(archivo_stan)
mod_informado
Pasamos datos y muestreamos
set.seed(3413) datos_lista <- list(n = length(muestra_1), y = muestra_1) ajuste <- mod_informado$sample( data = datos_lista, seed = 1234, chains = 4, iter_warmup = 4000, iter_sampling = 4000, refresh = 2000)
Checamos diagnósticos:
ajuste$cmdstan_diagnose()
Si no hay problemas, podemos ver el resumen:
resumen <- ajuste$summary() resumen
sims_theta_tbl <- ajuste$draws(c("theta_azar", "theta_corr")) %>% as_draws_df() ggplot(sims_theta_tbl, aes(x = theta_azar, y = theta_corr)) + geom_point(alpha = 0.1)
En este ejemplo cambiamos los parámetros de la simulación y ponemos iniciales poco informativas.
set.seed(12) muestra_2 <- map_dbl(1:200, ~ sim_formas(0.35, 0.21))
archivo_stan <- file.path("stan/modelo-examenes-no-inf.stan") # compilar mod_no_inf <- cmdstan_model(archivo_stan)
mod_no_inf
Pasamos datos y muestreamos
set.seed(3413) datos_lista <- list(n = length(muestra_2), y = muestra_2) ajuste <- mod_no_inf$sample( data = datos_lista, seed = 1234, chains = 4, iter_warmup = 4000, iter_sampling = 4000, refresh = 2000)
Y obtenemos mensajes de advertencia (divergencias), lo que implica que los diagnósticos indican que es posible que las cadenas no hayan explorado suficientemente la posterior
ajuste$cmdstan_diagnose()
sims_theta_tbl <- ajuste$draws(c("theta_azar", "theta_corr")) %>% as_draws_df() ggplot(sims_theta_tbl, aes(x = theta_azar, y = theta_corr)) + geom_point(alpha = 0.1)
Donde vemos que el problema es serio: cuando $\theta_{azar}$ es chico, los datos son consistentes con valores de $\theta_{corr}$ cercanos a 0.2. Pero también es posible que $\theta_{azar}$ sea grande, y en ese caso tenemos poca información acerca de $\theta_corr$.
Si corremos el modelo informado con la muestra de esta población, obtenemos:
set.seed(3413) datos_lista <- list(n = length(muestra_2), y = muestra_2) ajuste <- mod_informado$sample( data = datos_lista, seed = 1234, chains = 4, iter_warmup = 4000, iter_sampling = 4000, refresh = 2000)
No tenemos problemas numéricos, y la posterior se ve como sigue:
sims_theta_tbl <- ajuste$draws(c("theta_azar", "theta_corr")) %>% as_draws_df() ggplot(sims_theta_tbl, aes(x = theta_azar, y = theta_corr)) + geom_point(alpha = 0.1)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.