6.3. Pruebas T, parte 2#
Nota
Esta libreta es la parte 2 del análisis bayesiano de comparación de medias, pero también es la introducción para los modelos lineales
En esta libreta veremos otro método para realizar una comparación de dos medias.
Te sugiero que revises esta libreta de la documentación de bambi
para mayor explicación del modelo y un ejemplo diferente.
Cuando utilizamos una prueba t, lo que hacemos es comparar dos medias en dos grupo con la hipótesis \(\mu_0 \ne \mu_1\), al hacerlo, utilizamos de forma implícita un modelo lineal, donde comparamos:
En este caso \(i\) es valor dicotómico de la población en cuestión. Retomando el dataset palmer penguins
, si comparamos el peso entre la población Adelie y la Chinstrap, dentro del modelo, \(\text{adelie} = 0\) y \(\text{chinstrap} = 1\); de tal forma que \(i \in {0, 1}\). Al sustituir los índices:
Por lo tanto, para el caso de la hipótesis nula \(\mu_0 = \mu_1\), la ecuación de interés es:
Resolviendo para \(\beta_1\):
Repasando, en el modelo que contruiremos, la categoría de referencia es «Adelie», \(\beta_0\) es la media de peso de los Adelie y \(\beta_1\) es la diferencia de peso entre los Adelie y los Chinstrap.
6.3.1. Librerías y datos#
import bambi as bmb
import arviz as az
import pingouin as pg
data = pg.read_dataset('penguins')
data.head()
species | island | bill_length_mm | bill_depth_mm | flipper_length_mm | body_mass_g | sex | |
---|---|---|---|---|---|---|---|
0 | Adelie | Biscoe | 37.8 | 18.3 | 174.0 | 3400.0 | female |
1 | Adelie | Biscoe | 37.7 | 18.7 | 180.0 | 3600.0 | male |
2 | Adelie | Biscoe | 35.9 | 19.2 | 189.0 | 3800.0 | female |
3 | Adelie | Biscoe | 38.2 | 18.1 | 185.0 | 3950.0 | male |
4 | Adelie | Biscoe | 38.8 | 17.2 | 180.0 | 3800.0 | male |
6.3.2. Definición del modelo#
Necesitaremos la fórmula, los datos y especificaremos la familia de la distrubicón, en este caso utilizaremos la distribución T, ya que es más robusta para el análisis por sus colas pesadas, también especificamos que queremos eliminar los valores faltantes, bambi
no los tira por default como statsmodels.
clean_data = data[data['species'] != 'Gentoo'] # solo queremos adelie y chinstrap
model = bmb.Model(
'body_mass_g ~ species', # también podemos usar la sintaxis de fórmula
clean_data, # pasamos los datos limpios
family='t', # podemos usar también Gaussian
dropna=True # especificamos que queremos retirar los NaN
)
model
Automatically removing 1/220 rows from the dataset.
Formula: body_mass_g ~ species
Family: t
Link: mu = identity
Observations: 219
Priors:
target = mu
Common-level effects
Intercept ~ Normal(mu: 3710.7306, sigma: 1310.4353)
species ~ Normal(mu: 0.0, sigma: 2351.7068)
Auxiliary parameters
sigma ~ HalfStudentT(nu: 4.0, sigma: 435.2532)
nu ~ Gamma(alpha: 2.0, beta: 0.1)
Ahora ajustaremos los datos al modelo con el método fit
. Este proceso es costoso, computacionalmente hablando, ya que genera el muestreo de la posterior por el método Montecarlo por cadenas Markov (MCMC). Dependiendo de tu instalación pude que veas algunas advertencias, ignóralas por el momento, se deben a que no instalamos una las dependencias de PyMC.
result = model.fit(chains=4)
Initializing NUTS using jitter+adapt_diag...
C:\Users\User\Documents\coding\curso_python\.venv\Lib\site-packages\pytensor\link\c\cmodule.py:2968: UserWarning: PyTensor could not link to a BLAS installation. Operations that might benefit from BLAS will be severely degraded.
This usually happens when PyTensor is installed via pip. We recommend it be installed via conda/mamba/pixi instead.
Alternatively, you can use an experimental backend such as Numba or JAX that perform their own BLAS optimizations, by setting `pytensor.config.mode == 'NUMBA'` or passing `mode='NUMBA'` when compiling a PyTensor function.
For more options and details see https://pytensor.readthedocs.io/en/latest/troubleshooting.html#how-do-i-configure-test-my-blas-library
warnings.warn(
Multiprocess sampling (4 chains in 4 jobs)
NUTS: [sigma, nu, Intercept, species]
Sampling 4 chains for 1_000 tune and 1_000 draw iterations (4_000 + 4_000 draws total) took 18 seconds.
Cuando ejecutes el código, observa en el output la columna de «Divergences», deberías observar que las no encontraron divergencias, lo cual es excelente y nos indica que podemos continuar.
Hagamos diagnóstico de las cadenas, debemos analizar si convergieron adecuadamente. El análisis puede realizarse con arviz
.
az.plot_trace(result,);

Podemos ver que las cadenas parecen haber convergido ya que las trazas tienen forma de «ruido». Ahora podemos analizar los resultados.
az.plot_posterior(result);

Esto grafica las posteriores, pero en realidad solo nos interesa el grupo de especie.
az.plot_posterior(
result, # el objeto resultado de fit
var_names="species", # el nombre de la variable que nos interesa
ref_val=0 # el valor de referencia con que queremos comparar.
);

Podemos ver que efectivamente no parece haber diferencia, ya que el intervalo de credibilidad cruza el cero. Veamoslo en forma de tabla.
az.summary(result)
mean | sd | hdi_3% | hdi_97% | mcse_mean | mcse_sd | ess_bulk | ess_tail | r_hat | |
---|---|---|---|---|---|---|---|---|---|
sigma | 423.677 | 22.407 | 381.918 | 466.119 | 0.341 | 0.326 | 4342.0 | 2969.0 | 1.0 |
nu | 30.179 | 15.369 | 7.039 | 57.730 | 0.227 | 0.273 | 4302.0 | 2709.0 | 1.0 |
Intercept | 3694.867 | 37.099 | 3624.077 | 3762.506 | 0.494 | 0.575 | 5610.0 | 3009.0 | 1.0 |
species[Chinstrap] | 36.213 | 64.414 | -91.113 | 152.362 | 0.776 | 1.018 | 6893.0 | 3302.0 | 1.0 |
Sigma es la diferencia entre las especies, el intercepto es la media de peso para la especie de referencia (Adelie). Bambi tomó el primer valor como el de referencia, es posible especificar el grupo de referencia pero lo veremos en otra lección, en este caso ambos grupos son adecuados. species[Chinstrap]
es la diferencia en el valor del peso para esta especie.
En la tabla también podemos ver el valor \(\hat{R}=1\) en todos los casos, lo que es importante para la confiabilidad de los resultados.
Podemos ver que el intervalo creible cruza igualmente por el cero, por lo que podemos rechazar la significancia de la diferencia. Pero calculemos el factor bayes, para ello necesitamos muestear el prior y unirlo a nuestro resultado.
prior = model.prior_predictive() # muestreamos el prior
result.extend(prior) # extendemos el resultado para que contenga el prior
az.bayes_factor(result, 'species') # especificamos la variable de interés
Sampling: [Intercept, body_mass_g, nu, sigma, species]
arviz - WARNING - Posterior distribution has {posterior.ndim} dimensions
{'BF10': np.float64(0.003911852822721657),
'BF01': np.float64(255.63333931981975)}
Podemos ver que tenemos evidencia muy fuerte a favor de la hipótesis nula.
6.3.3. Ejercicio#
Ejecuta este método para comparación de medias con tus propios datos.
Compara varias cadenas. Ajusta el modelo con diferente número de chains (por ejemplo, 2 o 8) y analiza si afecta el resultado o la convergencia.
Realiza la prueba T comparando el peso de acuerdo al sexo.