La regla de Bayes dice que
\[ p(\theta|y) = \frac{p(y| \theta) p(\theta)}{p(y)} \]
Donde \(p(y)\) es la probabilidad total (o marginal) de los datos, que es algo que podemos calcular así:
\[ p(y) = \int p(y|\theta) p(\theta) d \theta \]
Esa es la distribución predictiva previa es decir, una distribución que predice la probabilidad de datos que no hemos observado en base al modelo de datos (likelihood) y las previas. Si prestamos atención a esta distribución o a ciertas propiedades de ella, podemos evaluar si las previas que elegimos tienen sentido, aún sin haber colectado ningún dato.
Por ejemplo, si que queremos modelar la distribución de pesos de monito del monte adultos, podemos asumir que tienen una distribución normal, caracterizada por un valor de media y cierto desvío estándar. Una opción de previas no-informativas sería, por ejemplo \(\mu \sim N(0, 100)\) y \(\sigma \sim U(0,100)\):
= 0
m = 100
s
= par(mfrow = c(1,2), bty = "l", las = 1)
op curve(dnorm(x, m, s), xlim = c(-400, 400),
xlab = expression(mu), ylab = "densidad")
curve(dunif(x, 0, 100), xlim = c(-1, 101),
xlab = expression(sigma), ylab = "densidad")
par(op)
¿Qué valores de pesos obtendríamos con estas previas? Para responder a eso, podemos simular “datos” a partir de ellas y del modelo de datos.
= rnorm(1e4, m, s)
mus = runif(1e4, 0, 100)
sds = rnorm(1e4, mus, sds)
psims plot(density(psims), main = "", xlab = "peso simulado")
Claramente, estos valores son absurdos. El peso de los monitos adultos es variable pero en mi experiencia de trabajo con esta especie en Llao Llao, puedo decir que nunca vi un monito adulto de menos de 16 gramos o de más de 55 gramos. Entonces, para poner una previa más razonable, podríamos usar una normal con \(\mu = 30\) y \(\sigma = 5\). Para el desvío estándar podemos usar como previa una uniforme entre \(0\) y \(5\).
= 30
m = 5
s
= par(mfrow = c(1,2), bty = "l", las = 1)
op curve(dnorm(x, m, s), xlim = c(10, 55))
abline(v = m - 3*s, lty = 3)
abline(v = m + 3*s, lty = 3)
curve(dunif(x, 0, 5), xlim = c(-1, 6))
par(op)
Veamos cómo queda nuestra prior predictive ahora…
= rnorm(1e4, m, s)
mus = runif(1e4, 0, 5)
sds = rnorm(1e4, mus, sds)
lsims plot(density(lsims), main = "", xlab = "peso simulado")
Vemos que estos valores son razonables para estos datos y que permiten valores extremos pero quizás no imposibles.
Consideremos ahora el caso de una regresión para el peso de los animales en función de su largo. Asumiendo una relación linear estándar, nuestro modelo de datos es:
\[ \text{peso} \sim N \left(\alpha + \beta \times \text{largo} , \sigma \right) \] En general, nos conviene trabajar con variables predictoras centradas, de manera que la ordenada al origen (\(\alpha\)) represente el valor de la variable de respuesta para valores promedios de la predictora. Reemplazando a “peso” por \(y\) y a “largo” por \(x\) de manera de generalizar la notación, tenemos:
\[ y \sim N \left(\alpha + \beta \left(x - \bar{x} \right), \sigma \right) \] Ahora tenemos que definir previas para \(\alpha, \beta\) y \(\sigma\). Veamos qué pasa con la regresión si usamos \(\alpha \sim N(30, 5)\) y una previa poco informativa para \(\beta\): \(\beta \sim N(0, 100)\)
Carguemos los datos y veamos qué dicen estas previas
= read.table("https://github.com/jmmorales/cursoMyD/raw/master/Data/peso.txt",
datos header = TRUE)
= rnorm(100, m, s)
a = rnorm(100, 0, 100)
b
plot( NULL , xlim = range(datos$largo) ,
ylim = c(-30, 100) ,
xlab = "largo",
ylab = "peso" )
abline( h = 16, lty = 2, lwd = 2, col = rgb(0,0,0, 0.5) )
abline( h = 55, lty = 2, lwd = 2, col = rgb(0,0,0, 0.5) )
<- mean(datos$largo)
xbar for ( i in 1:100 ){
curve( a[i] + b[i] * (x - xbar),
from = min(datos$largo),
to=max(datos$largo),
add=TRUE,
col=rgb(0,0,0,0.2) )
}
Con estas previas vemos que las líneas de regresión son muy poco realistas.
Probemos entonces con \(\beta \sim N(0, 3)\)
= rnorm(100, m, s)
a = rnorm(100, 0, 3)
b
plot( NULL , xlim = range(datos$largo) ,
ylim = c(-30, 100) ,
xlab = "largo",
ylab = "peso" )
abline( h = 16, lty = 2, lwd = 2, col = rgb(0,0,0, 0.5) )
abline( h = 55, lty = 2, lwd = 2, col = rgb(0,0,0, 0.5) )
<- mean(datos$largo)
xbar for ( i in 1:100 ){
curve( a[i] + b[i] * (x - xbar),
from = min(datos$largo),
to=max(datos$largo),
add=TRUE,
col=rgb(0,0,0,0.2) )
}
Ahora ajustemos el modelo a los datos. Para eso escribimos el modelo en lenguaje de BUGS
cat(file = "ml.bug",
"
model {
# Función de likelihood
for( i in 1:n){
y[i] ~ dnorm (mu[i], tau)
mu[i] <- a + b * x[i]
}
# Previas
a ~ dnorm(21, 0.25)
b ~ dnorm(0, tau_b)
tau <- 1/(sigma*sigma)
sigma ~ dunif(0, 5)
}
")
Ahora cargamos los datos y usamos el paquete jagsUI
para correr el análisis. Vamos a comparar los resultados con las alternativas de previas para \(\beta\)
library(jagsUI)
# defino las variables
<- datos$largo - mean(datos$largo)
x <- datos$peso
y
# vector con los parámetros para que JAGS guarde los valores de las cadenas Markovianas
<- c("a", "b", "sigma")
params
# función para generar valores iniciales de los parámetros (no confundir con las previas!)
<- function() list(a = runif(1, 16, 27),
inits b = runif(1, 0, 1),
sigma = runif(1, 0, 5))
Ahora llamamos a JAGS
para que genere las cadenas Markovianas para las dos opciones de previas
<- list(x = x,
dat y = y,
n = length(datos$largo),
tau_b = (1/100)^2
)<- jags(data = dat,
m100.sim inits = inits,
parameters.to.save = params,
model.file = "ml.bug",
n.chains = 3,
n.iter = 1000)
##
## Processing function input.......
##
## Done.
##
## Compiling model graph
## Resolving undeclared variables
## Allocating nodes
## Graph information:
## Observed stochastic nodes: 139
## Unobserved stochastic nodes: 3
## Total graph size: 329
##
## Initializing model
##
## Adaptive phase.....
## Adaptive phase complete
##
## No burn-in specified
##
## Sampling from joint posterior, 1000 iterations x 3 chains
##
##
## Calculating statistics.......
##
## Done.
<- list(x = x,
dat y = y,
n = length(datos$largo),
tau_b = (1/3)^2
)
<- jags(data = dat,
m3.sim inits = inits,
parameters.to.save = params,
model.file = "ml.bug",
n.chains = 3,
n.iter = 1000)
##
## Processing function input.......
##
## Done.
##
## Compiling model graph
## Resolving undeclared variables
## Allocating nodes
## Graph information:
## Observed stochastic nodes: 139
## Unobserved stochastic nodes: 3
## Total graph size: 329
##
## Initializing model
##
## Adaptive phase.....
## Adaptive phase complete
##
## No burn-in specified
##
## Sampling from joint posterior, 1000 iterations x 3 chains
##
##
## Calculating statistics.......
##
## Done.
y vemos los resultados
print(m100.sim)
## JAGS output for model 'ml.bug', generated by jagsUI.
## Estimates based on 3 chains of 1000 iterations,
## adaptation = 100 iterations (sufficient),
## burn-in = 0 iterations and thin rate = 1,
## yielding 3000 total samples from the joint posterior.
## MCMC ran for 0.005 minutes at time 2021-12-07 23:06:32.
##
## mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
## a 30.165 0.417 29.338 30.167 30.954 FALSE 1 1.003 852
## b 4.244 0.486 3.320 4.239 5.218 FALSE 1 1.001 2739
## sigma 4.967 0.033 4.883 4.976 4.999 FALSE 1 1.001 3000
## deviance 968.845 3.218 964.819 968.110 976.939 FALSE 1 1.001 2052
##
## Successful convergence based on Rhat values (all < 1.1).
## Rhat is the potential scale reduction factor (at convergence, Rhat=1).
## For each parameter, n.eff is a crude measure of effective sample size.
##
## overlap0 checks if 0 falls in the parameter's 95% credible interval.
## f is the proportion of the posterior with the same sign as the mean;
## i.e., our confidence that the parameter is positive or negative.
##
## DIC info: (pD = var(deviance)/2)
## pD = 5.2 and DIC = 974.023
## DIC is an estimate of expected predictive error (lower is better).
print(m3.sim)
## JAGS output for model 'ml.bug', generated by jagsUI.
## Estimates based on 3 chains of 1000 iterations,
## adaptation = 100 iterations (sufficient),
## burn-in = 0 iterations and thin rate = 1,
## yielding 3000 total samples from the joint posterior.
## MCMC ran for 0.008 minutes at time 2021-12-07 23:06:32.
##
## mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
## a 30.170 0.407 29.387 30.157 30.979 FALSE 1 1.000 3000
## b 4.115 0.472 3.177 4.117 5.022 FALSE 1 1.001 1445
## sigma 4.964 0.034 4.875 4.974 4.999 FALSE 1 1.008 352
## deviance 968.938 3.177 964.875 968.258 976.602 FALSE 1 1.002 1035
##
## Successful convergence based on Rhat values (all < 1.1).
## Rhat is the potential scale reduction factor (at convergence, Rhat=1).
## For each parameter, n.eff is a crude measure of effective sample size.
##
## overlap0 checks if 0 falls in the parameter's 95% credible interval.
## f is the proportion of the posterior with the same sign as the mean;
## i.e., our confidence that the parameter is positive or negative.
##
## DIC info: (pD = var(deviance)/2)
## pD = 5 and DIC = 973.978
## DIC is an estimate of expected predictive error (lower is better).
En este caso no parece haber mucha diferencia entre las dos previas. Esto se debe a que tenemos bastantes datos y pocas variables predictoras
plot(density(m100.sim$sims.list$b), main = "",
xlab = expression(beta), bty = "l", ylim = c(0,1))
lines(density(m3.sim$sims.list$b), col=2)
Veamos qué pasa con una muestra más chica:
= 10
n set.seed(1234)
= sample(1:length(datos$largo), n)
idx
<- list(x = x[idx],
dat y = y[idx],
n = n,
tau_b = (1/100)^2
)<- jags(data = dat,
m100r.sim inits = inits,
parameters.to.save = params,
model.file = "ml.bug",
n.chains = 3,
n.iter = 10000)
##
## Processing function input.......
##
## Done.
##
## Compiling model graph
## Resolving undeclared variables
## Allocating nodes
## Graph information:
## Observed stochastic nodes: 10
## Unobserved stochastic nodes: 3
## Total graph size: 47
##
## Initializing model
##
## Adaptive phase.....
## Adaptive phase complete
##
## No burn-in specified
##
## Sampling from joint posterior, 10000 iterations x 3 chains
##
##
## Calculating statistics.......
##
## Done.
<- list(x = x[idx],
dat y = y[idx],
n = n,
tau_b = (1/3)^2
)<- jags(data = dat,
m3r.sim inits = inits,
parameters.to.save = params,
model.file = "ml.bug",
n.chains = 3,
n.iter = 10000)
##
## Processing function input.......
##
## Done.
##
## Compiling model graph
## Resolving undeclared variables
## Allocating nodes
## Graph information:
## Observed stochastic nodes: 10
## Unobserved stochastic nodes: 3
## Total graph size: 47
##
## Initializing model
##
## Adaptive phase.....
## Adaptive phase complete
##
## No burn-in specified
##
## Sampling from joint posterior, 10000 iterations x 3 chains
##
##
## Calculating statistics.......
##
## Done.
plot(density(m100r.sim$sims.list$b), main = "",
xlab = expression(beta), bty = "l", ylim = c(0,0.4))
lines(density(m3r.sim$sims.list$b), col=2)
print(m100r.sim)
## JAGS output for model 'ml.bug', generated by jagsUI.
## Estimates based on 3 chains of 10000 iterations,
## adaptation = 100 iterations (sufficient),
## burn-in = 0 iterations and thin rate = 1,
## yielding 30000 total samples from the joint posterior.
## MCMC ran for 0.016 minutes at time 2021-12-07 23:06:33.
##
## mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
## a 28.408 1.236 26.015 28.403 30.819 FALSE 1 1.000 30000
## b 6.208 1.617 3.036 6.198 9.410 FALSE 1 1.000 15119
## sigma 4.820 0.166 4.388 4.867 4.995 FALSE 1 1.002 1836
## deviance 84.582 4.923 76.787 83.967 95.750 FALSE 1 1.000 23088
##
## Successful convergence based on Rhat values (all < 1.1).
## Rhat is the potential scale reduction factor (at convergence, Rhat=1).
## For each parameter, n.eff is a crude measure of effective sample size.
##
## overlap0 checks if 0 falls in the parameter's 95% credible interval.
## f is the proportion of the posterior with the same sign as the mean;
## i.e., our confidence that the parameter is positive or negative.
##
## DIC info: (pD = var(deviance)/2)
## pD = 12.1 and DIC = 96.702
## DIC is an estimate of expected predictive error (lower is better).
print(m3r.sim)
## JAGS output for model 'ml.bug', generated by jagsUI.
## Estimates based on 3 chains of 10000 iterations,
## adaptation = 100 iterations (sufficient),
## burn-in = 0 iterations and thin rate = 1,
## yielding 30000 total samples from the joint posterior.
## MCMC ran for 0.008 minutes at time 2021-12-07 23:06:34.
##
## mean sd 2.5% 50% 97.5% overlap0 f Rhat n.eff
## a 28.552 1.223 26.143 28.557 30.949 FALSE 1 1.000 30000
## b 4.814 1.432 2.017 4.819 7.591 FALSE 1 1.000 30000
## sigma 4.821 0.165 4.390 4.868 4.995 FALSE 1 1.001 4649
## deviance 84.549 4.949 76.827 83.923 95.918 FALSE 1 1.000 30000
##
## Successful convergence based on Rhat values (all < 1.1).
## Rhat is the potential scale reduction factor (at convergence, Rhat=1).
## For each parameter, n.eff is a crude measure of effective sample size.
##
## overlap0 checks if 0 falls in the parameter's 95% credible interval.
## f is the proportion of the posterior with the same sign as the mean;
## i.e., our confidence that the parameter is positive or negative.
##
## DIC info: (pD = var(deviance)/2)
## pD = 12.2 and DIC = 96.795
## DIC is an estimate of expected predictive error (lower is better).
Como dice Richard McElreath, las previas no-informativas permiten que el modelo “se entusiasme” con los datos que observa de manera que es probable que estemos ajustando “ruido” y tengamos poca capacidad predictiva. Por eso es que previas más informativas y que producen predicciones previas razonables a veces se las llama “regularizadoras”.
La distribución posterior predictiva es una distribución de datos que podríamos obtener considerando que el modelo de datos es el correcto y teniendo en cuenta la incertidumbre en los parámetros una vez que ya vimos un set de datos. Es decir \[ p(y^{\text{rep}}|y) = \int p(y^{\text{rep}}|\theta) p(\theta| y) d \theta \]
Para aproximar esa distribución, usamos muestras de la posterior conjunta y el modelo de datos
<- 1000
nsims = length(y)
n
# matriz donde guardamos los datos simulados
<- matrix(NA, nrow = nsims, ncol = n)
y_rep <- length(m3.sim$sims.list$a) # muestras de la posterior disponibles
nsample
# vectores para guardar las sumas de residuos
= numeric(nsims)
sum_res = numeric(nsims)
sum_res_rep
for(i in 1:nsims){
= sample.int(nsample, 1) # número de muestra que vamos a usar
idx = m3.sim$sims.list$a[idx] + m3.sim$sims.list$b[idx] * x
m_rep = m3.sim$sims.list$sigma[idx]
sd_rep
# simulamos los datos
= rnorm(n, m_rep, sd_rep)
y_rep[i, ]
# suma de residuos
= sum(y - m_rep)
sum_res[i] = sum(y_rep[i,] - m_rep)
sum_res_rep[i] }
Podemos hacer comparaciones visuales entre los datos observados y los simulados
= par(mfrow = c(5,5), mar = c(2,1,1,2))
op hist(datos$peso, main = "", xlab = "", ylab = "" , xlim = c(5,65), ylim = c(0,25), col = 2, 20)
for(i in 1: 24){
hist(y_rep[i,],
main = "",
xlab = "",
ylab = "",
xlim = c(5,65),
ylim = c(0,25),
20)
}
par(op)
Podemos definir valores de “p” sobre alguna cantidad o “test”. Por ejemplo, veamos el valor de peso máximo:
= max(datos$peso)
test = numeric(nsims)
idx for(i in 1:nsims){
if(max(y_rep[i,]) > test){
= 1
idx[i]
}
}
= sum(idx)/nsims p_value
Estas comparaciones también las podemos hacer con gráficos. Por ejemplo, veamos el valor de peso promedio
hist(rowMeans(y_rep), 100, main = "", xlab = "mean y_rep")
abline(v = mean(y), lwd = 3)
y el desvío estándar:
# calculamos el desvío de los datos simulados
= numeric(nsims)
sd_sim for(i in 1:nsims) sd_sim[i] <- sd(y_rep[i,])
hist(sd_sim, 100, main = "", xlab = "sd y_rep", xlim = c(4.5, 8))
abline(v = sd(y), lwd = 3)
Vemos que los datos tienen más variabilidad que la predicha por el modelo. Es decir que hay otras fuentes de variabilidad que no están contempladas por la relación lineal entre largo y peso.
También podemos ver qué pasa con las sumas de residuos que calculamos. Para los datos observados y predichos
plot(sum_res, sum_res_rep, asp = 1)
abline(0, 1)
= numeric(nsims)
test for(i in 1:nsims) if(sum_res[i] > sum_res_rep[i]) test[i] = 1
mean(test)
## [1] 0.757
Otra forma de ver si los datos observados son compatibles con los simulados es graficar los intervalos de credibilidad del pp-check en función de \(x\):
library(coda)
<- HPDinterval(as.mcmc(y_rep))
tmp <- sort(datos$largo, index.return = TRUE)
idx
<- par(las = 1, bty = "n")
op plot(datos$largo, datos$peso, pch = 16, col = gray(0.5, 0.5),
cex = 1.5, ylim = c(10,55))
lines(datos$largo[idx$ix], tmp[idx$ix, 1], lwd = 3, col = "darkgray")
lines(datos$largo[idx$ix], tmp[idx$ix, 2], lwd = 3, col = "darkgray")
par(op)
1- Extender el modelo de regresión para incluir además del largo, al sexo y el ancho de cola como predictoras. Los monitos guardan reservas en la base de la cola, por eso es de esperarse que el ancho de cola esté relacionado al peso. Como largo y ancho de cola tienen rangos de valores diferentes conviene estandarizar estas dos variables antes de incluirlas en la regresión.
2- Una vez que estandarizaron largo y ancho de cola, qué previas para los coeficientes de regresión les parecen razonables?
3- Realizar al menos 3 pp-checks para el nuevo modelo de regresión