library(ggplot2)
<- function(x_muestras, x_grid, p_grid) {
graficar_distribucion # Graficar histograma de las muestras con la curva de densidad teórica superpuesta.
<- ggplot() +
plt geom_histogram(
aes(x = x, y = after_stat(density)),
fill = "grey60",
bins = 50,
data = data.frame(x = x_muestras)
+
) geom_line(
aes(x = x, y = y),
color = "#3b78b0",
linewidth = 1,
data = data.frame(x = x_grid, y = p_grid)
+
) labs(y = NULL) +
theme_bw() +
theme(
panel.grid.major = element_blank(),
axis.ticks.y = element_blank(),
axis.text.y = element_blank(),
legend.position = "none"
)
return(plt)
}
<- function(x_muestras) {
graficar_traza # Generar un _traceplot_ para una cadena de Markov.
<- data.frame(x = seq_along(x_muestras), y = x_muestras) |>
plt ggplot() +
geom_line(aes(x = x, y = y), color = "grey30") +
labs(x = "Paso", y = "x") +
theme_bw()
theme(
panel.grid.major = element_blank(),
axis.ticks.y = element_blank(),
axis.text.y = element_blank(),
legend.position = "none"
)
return(plt)
}
set.seed(12) # para que los resultados sean reproducibles
11 - Metropolis-Hastings en 1 dimensión
En este recurso se implementa el algoritmo de Metropolis-Hastings para obtener muestras de distribuciones de variables aleatorias unidimensionales utilizando una distribución de propuesta normal. Se muestra como usar el algoritmo con ejercicio Muestreo utilizando el algoritmo de Metropolis-Hastings (I) de la Práctica 3.
Funciones auxiliares
Implementación
<- function(n, x, p, sigma, verbose = FALSE) {
metropolis_hastings_normal # Algortimo de Metropolis Hastings en una dimensión.
# La distribución de propuesta es normal.
#
# Parámetros
# ------------------------------------------------------------------------
# | n | Cantidad de muestras a obtener. |
# | x | Posición inicial del algoritmo. |
# | p | Función de densidad objetivo, normalizada o sin normalizar. |
# | sigma | Desvío estándar de la distribución de propuesta normal. |
# | verbose | Indica si se muestran mensajes con información del |
# | algoritmo en cada paso. |
# ------------------------------------------------------------------------
#
# Salida
# ------------------------------------------------------------------------
# | muestras | Vector con las muestras obtenidas. |
# ------------------------------------------------------------------------
<- numeric(n)
muestras 1] <- x
muestras[
# Iterar desde i = 1 hasta i = n - 1
for (i in seq_len(n - 1)) {
# Paso 1: Proponer un nuevo valor
<- muestras[i]
x_actual <- rnorm(1, mean = x_actual, sd = sigma)
x_propuesto
# Paso 2: Calcular probabilidad de aceptación
<- p(x_actual)
p_actual <- p(x_propuesto)
p_propuesto
# Corrección en base a la densidad de la distribución de propuesta
<- dnorm(x_propuesto, mean = x_actual, sd = sigma) # q(x_propuesto | x_actual)
q_propuesto <- dnorm(x_actual, mean = x_propuesto, sd = sigma) # q(x_actual | x_propuesto)
q_actual
<- min(1, (p_propuesto / p_actual) * (q_actual / q_propuesto))
alpha
# Paso 3: Dedicir si se acepta el valor propuesto
<- runif(1)
u <- u < alpha
aceptar
# Guardar posición
if (aceptar) {
+ 1] <- x_propuesto
muestras[i else {
} + 1] <- x_actual
muestras[i
}
# Si 'verbose' es TRUE, mostrar valores de variables relevantes
if (verbose) {
cat("-----------------------\n")
cat("x_actual: ", x_actual, "x_propuesto", x_propuesto, "\n")
cat("p_actual: ", p_actual, "p_propuesto", p_propuesto, "\n")
cat("q_propuesto: ", q_propuesto, "q_actual", q_actual, "\n")
cat("alpha:", alpha, "u:", u, "aceptar:", aceptar, "\n")
}
}
return(muestras)
}
Normal
\[ X \sim \text{Normal}(\mu = 3, \sigma = 6) \]
<- function(x) dnorm(x, mean = 3, sd = 6)
densidad <- seq(-15, 21, length.out = 500)
x_grid <- densidad(x_grid) p_grid
Propuesta: \(\sigma = 0.1\)
<- metropolis_hastings_normal(n = 5000, x = 2, p = densidad, sigma = 0.1)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
Propuesta: \(\sigma = 2\)
<- metropolis_hastings_normal(n = 5000, x = 2, p = densidad, sigma = 2)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
T de Student
\[ X \sim \text{StudentT}(\nu = 5) \]
<- function(x) dt(x, df = 5)
densidad <- seq(-6, 6, length.out = 500)
x_grid <- densidad(x_grid) p_grid
Propuesta: \(\sigma = 0.1\)
<- metropolis_hastings_normal(n = 5000, x = 0, p = densidad, sigma = 0.1)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
Propuesta: \(\sigma = 1\)
<- metropolis_hastings_normal(n = 5000, x = 0, p = densidad, sigma = 1)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
Mezcla de normales
\[ X \sim \frac{2}{3}\text{Normal}(\mu = 0, \sigma = 0.5) + \frac{1}{3}\text{Normal}(\mu = 3, \sigma = 2) \]
<- function(x) {
densidad 2 / 3) * dnorm(x, mean = 0, sd = 0.5) + (1 / 3) * dnorm(x, mean = 3, sd = 2)
(
}<- seq(-5, 10, length.out = 500)
x_grid <- densidad(x_grid) p_grid
Propuesta: \(\sigma = 0.1\)
<- metropolis_hastings_normal(n = 5000, x = 0, p = densidad, sigma = 0.1)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
Propuesta: \(\sigma = 1.2\)
<- metropolis_hastings_normal(n = 5000, x = 0, p = densidad, sigma = 1.2)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
Comentarios
En ninguno de los casos resulta adecuado el desvío estándar inicialmente asignado a la distribución de propuesta. El valor inicial de \(\sigma\) es bajo y las cadenas se desplazan lentamente por el soporte de \(X\). Los traceplots muestran que las muestras obtenidas con \(\sigma=0.1\) presentan una alta autocorrelación. Además, al comparar las muestras con las densidades teóricas, se observa una notable discrepancia.
En cambio, en todos los ejemplos, bastó con incrementar el valor de \(\sigma\) para que la distribución de propuesta generara saltos más largos. Las distribuciones empíricas obtenidas se ajustan mejor a las teóricas, y los traceplots evidencian una autocorrelación considerablemente menor.
Bonus
¿Cómo se ve la trayectoria cuando el punto inicial no se encuentra en el typical set de la distribución?
Ejemplo 1
<- function(x) dnorm(x, mean = 3, sd = 6)
densidad <- seq(-15, 21, length.out = 500)
x_grid <- densidad(x_grid) p_grid
<- metropolis_hastings_normal(n = 5000, x = 60, p = densidad, sigma = 3)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)
Ejemplo 2
<- function(x) {
densidad 2 / 3) * dnorm(x, mean = 0, sd = 0.5) + (1 / 3) * dnorm(x, mean = 3, sd = 2)
(
}<- seq(-5, 10, length.out = 500)
x_grid <- densidad(x_grid) p_grid
<- metropolis_hastings_normal(n = 5000, x = -20, p = densidad, sigma = 1)
muestras graficar_distribucion(x_muestras = muestras, x_grid = x_grid, p_grid = p_grid)
graficar_traza(x_muestras = muestras)