5.5. Tamaños de muestra#
En seguimiento a la libreta previa donde hablamos sobre los tamaños de muestra, esta libreta mostrará cómo realizar el cálculo del tamaño muestral en varios escenarios.
Importante
Esta libreta se divide en dos secciones. En la primera utilizaremos las fórmulas más comunes para el cálculo de tamaños muestrales, y utilizaremos scipy
y numpy
, que ya debes tener instalados. En la segunda parte, utilizaremos la librería statsmodels
que también ya deberías tener disponible.
5.5.1. Librerías#
from math import (
ceil, # redondea hacia arriba
sqrt, # la raíz cuadrada
)
import numpy as np
import pandas as pd
import scipy.stats as stats
from scipy.optimize import root_scalar # para resolver numéricamente
import statsmodels.api as sm
print('Librerías importadas')
Librerías importadas
5.5.2. Estimación para proporciones#
5.5.2.1. Fórmula de Cochran#
La primera fórmula que revisaremos es la fórmula de Cochran, utilizada para estimar el tamaño de muestra necesario al estudiar una proporción en un estudio descriptivo.
Donde:
\(Z_{1-\alpha/2}\) es el valor crítico de la distribución normal estándar para un nivel de confianza dado, en este caso para el error de tipo 1.
\(p\) es la proporción esperada,
\(d\) es el margen de error (precisión deseada).
def cochran_una_p(p, d, alfa=0.05, hipotesis='dos colas'):
"""
Calcula el tamaño de muestra para una proporción usando la fórmula de Cochran.
Parámetros:
- p: proporción esperada (entre 0 y 1)
- d: margen de error (precisión)
- alfa: nivel de significancia (por defecto 0.05)
- hipotesis: 'dos colas' o 'una cola'
Retorna:
- n: tamaño de muestra (entero)
"""
hips = 'dos colas', 'una cola'
if hipotesis not in hips:
raise ValueError("La hipótesis debe ser 'dos colas' o 'una cola'.")
if hipotesis == 'dos colas':
z = stats.norm.ppf(1-alfa/2)
else:
z = stats.norm.ppf(1-alfa)
n = (z**2 * p * (1 - p)) / d**2
return ceil(n)
print('Función definida')
Función definida
Probemos la función con los siguientes parámetros:
Nivel de significancia: \(\alpha = 0.05\)
Proporción esperada: \(p = 0.5\)
Margen de error: \(d = 0.1\)
Hipótesis: una cola
cochran_una_p(0.5, 0.1, hipotesis='una cola')
68
Para este estudio, requeriríamos de al menos 68 pacientes.
5.5.2.2. Estudios de equivalencia en una proporción#
En este caso, queremos conocer si la proporción estimada es diferente de un valor preestablecido, para ello, se modifica ligeramente la fórmula para incluir el poder del estudio.
Donde:
\(Z_\beta\) es el valor crítico de la distribución normal estándar para un nivel de confianza dado, en este caso para el error de tipo 2.
def cochran_eq_una_p(p, d, alfa=0.05, beta=0.2, hipotesis='dos colas'):
"""
Calcula el tamaño de muestra para detectar diferencia en una proporción,
incorporando el poder estadístico (estudios de equivalencia o hipótesis).
Parámetros:
- p: proporción esperada (entre 0 y 1)
- d: diferencia mínima que se desea detectar (margen de error)
- alfa: nivel de significancia (por defecto 0.05)
- beta: error tipo II (por defecto 0.2, que equivale a 80% de poder)
Retorna:
- n: tamaño de muestra (entero)
"""
hips = 'dos colas', 'una cola'
if hipotesis not in hips:
raise ValueError("La hipótesis debe ser 'dos colas' o 'una cola'.")
if hipotesis == 'dos colas':
z_alfa = stats.norm.ppf(1-alfa/2)
else:
z_alfa = stats.norm.ppf(1-alfa)
z_beta = stats.norm.ppf(1 - beta)
n = ((z_alfa + z_beta)**2 * p * (1 - p)) / d**2
return ceil(n)
print('Función definida')
Función definida
Probemos la función con los siguientes parámetros:
Nivel de significancia: \(\alpha = 0.05\)
Probabilidad de error de tipo 2: \(\beta = 0.1\)
Proporción esperada: \(p = 0.3\)
Margen de error: \(d = 0.1\)
Hipótesis: una cola
cochran_eq_una_p(0.3, 0.1, beta=0.2, hipotesis='una cola')
130
Para este estudio requeriríamos entonces 130 sujetos.
5.5.2.3. Una proporción con población finita (Cochran)#
En ocasiones, el tamaño de la población es conocido y limitado. En estos casos, se recomienda aplicar una corrección por población finita a la fórmula de Cochran:
Donde:
\(N\) es el tamaño total de la población,
\(Z\) es el valor crítico para el nivel de confianza deseado,
\(p\) es la proporción esperada,
\(d\) es el margen de error (precisión deseada).
def cochran_poblacion_finita(p, d, N, alfa=0.05, hipotesis='dos colas'):
"""
Calcula el tamaño de muestra para una proporción ajustado a una población finita.
Parámetros:
- p: proporción esperada (entre 0 y 1)
- d: margen de error (precisión)
- N: tamaño total de la población
- alfa: nivel de significancia (por defecto 0.05)
Retorna:
- n: tamaño de muestra ajustado (float, no redondeado)
"""
hips = 'dos colas', 'una cola'
if hipotesis not in hips:
raise ValueError("La hipótesis debe ser 'dos colas' o 'una cola'.")
if hipotesis == 'dos colas':
z = stats.norm.ppf(1-alfa/2)
else:
z = stats.norm.ppf(1-alfa)
q = 1 - p
num = N * (z**2) * p * q
den = (d**2) * (N - 1) + (z**2) * p * q
n = num / den
return ceil(n)
print('Función definida')
Función definida
Utilicemos ahora la fórmula con los siguientes parámetros:
\(p = 0.4\)
\(d = 0.05\)
\(N = 350\)
\(\alpha = 0.05\)
\(hipótesis = \text{una cola}\)
cochran_poblacion_finita(p=0.4, d=0.05, N=350)
180
5.5.3. Estimar una Media#
Utilizaremos la fórmula para una media.
Muy parecida a la fórmula que utilizamos para la proporción. Ten en cuenta que \(\sigma\) es la desviación estándar de la media a estimar y \(d\) es el margen de error de la media.
def estimar_una_media(sigma, d, alfa=0.05, hipotesis='dos colas'):
"""
Calcula el tamaño de muestra para estimar una media en población indefinida.
Parámetros:
- sigma: desviación estándar esperada
- d: margen de error (precisión)
- alfa: nivel de significancia (por defecto 0.05)
- hipotesis: 'dos colas' o 'una cola'
Retorna:
- n: tamaño de muestra (entero)
"""
hips = 'dos colas', 'una cola'
if hipotesis not in hips:
raise ValueError("La hipótesis debe ser 'dos colas' o 'una cola'.")
if hipotesis == 'dos colas':
z = stats.norm.ppf(1 - alfa/2)
else:
z = stats.norm.ppf(1 - alfa)
n = (z * sigma / d)**2
return ceil(n)
print('Función definida')
Función definida
Supongamos que deseamos estimar el promedio de una variable continua (por ejemplo, niveles séricos de una proteína), con:
una desviación estándar estimada de 12 unidades,
un margen de error aceptable de 3 unidades,
un nivel de confianza del 99%,
y una hipótesis a dos colas.
Llamamos a la función así:
estimar_una_media(sigma=12, d=3, alfa=0.01)
107
5.5.4. Ejercicio 1#
Diseña tu propia función para estimar:
La media en un estudio analítico, tanto para muestras pareadas como para muestras independientes.
Para estimar dos proporciones.
5.5.5. Estimar un Odds Ratio#
Cuando deseamos estimar la asociación entre una exposición y un evento binario (por ejemplo, enfermedad presente o ausente), es común utilizar un estudio de casos y controles y calcular el odds ratio (OR).
Para calcular el tamaño de muestra necesario en este tipo de estudios, se requiere conocer:
el OR esperado (efecto mínimo que se desea detectar),
la prevalencia de exposición en los controles (\(p_0\)),
el nivel de significancia (\(\alpha\)),
el poder estadístico deseado (\(1 - \beta\)),
y la razón de controles por cada caso.
La fórmula aproximada para calcular el número de casos es:
Donde:
\(k\) es la razón de controles por caso,
\(p_0\) es la prevalencia de exposición en controles,
\(p_1\) es la prevalencia de exposición en casos, calculada como:
\(\bar{p}\) es la proporción combinada de exposición:
def un_odds_ratio(OR, p0, alpha=0.05, beta=0.2, ratio=1):
"""
Calcula el tamaño de muestra para estimar un odds ratio en un estudio de casos y controles.
Parámetros:
- OR: odds ratio esperado
- p0: prevalencia de exposición en los controles
- alpha: error tipo I (por defecto 0.05)
- beta: error tipo II (por defecto 0.2, equivalente a 80% de poder)
- ratio: razón de controles por caso (por defecto 1:1)
Retorna:
- n_casos: número de casos requeridos (entero)
- n_controles: número de controles requeridos (entero)
"""
poder = 1 - beta
# Prevalencia esperada de exposición en los casos
p1 = (OR * p0) / (1 + p0 * (OR - 1))
# Valores críticos de Z
Za = stats.norm.ppf(1 - alpha / 2)
Zb = stats.norm.ppf(poder)
# Proporciones combinadas
p_bar = (p1 + ratio * p0) / (1 + ratio)
q_bar = 1 - p_bar
# Cálculo del tamaño de muestra
numerador = (Za * sqrt((1 + ratio) * p_bar * q_bar) + Zb * sqrt(p0 * (1 - p0) + ratio * p1 * (1 - p1))) ** 2
denominador = ratio * (p1 - p0) ** 2
n_casos = numerador / denominador
n_controles = ratio * n_casos
return ceil(n_casos), ceil(n_controles)
print('Función definida')
Función definida
# Supongamos que queremos detectar un OR = 2.0
# con p0 = 0.3, alpha = 0.05, beta = 0.2, y ratio 2:1
un_odds_ratio(OR=2.0, p0=0.3, alpha=0.05, beta=0.2, ratio=2)
(105, 209)
Por lo que requeriríamos un total de 105 casos y 209 controles como mínimo.
5.5.6. Prueba de independencia (Chi²)#
Cuando se desea evaluar la asociación entre dos variables categóricas, se puede utilizar la prueba de independencia Chi-cuadrado. En este caso, se parte de una tabla de contingencia con las probabilidades conjuntas esperadas bajo la hipótesis alternativa.
El tamaño de muestra se calcula a partir del parámetro de no centralidad \(\lambda\) que satisface:
Donde:
\(\chi^2_{df}(\lambda)\) es una chi-cuadrada no central con \(df = (r-1)(c-1)\) grados de libertad y parámetro \(\lambda\),
\(\chi^2_{1-\alpha, df}\) es el valor crítico de la chi-cuadrada central al nivel \(\alpha\),
\(\beta\) es el error tipo II (el poder es \(1 - \beta\)).
Una vez que se estima \(\lambda\), el tamaño de muestra se calcula como:
donde \(w^2\) es el tamaño del efecto según la diferencia entre proporciones conjuntas y esperadas:
def chi2_independencia(p_ij, alpha=0.05, beta=0.2):
"""
Calcula el tamaño de muestra para una prueba de chi² de independencia.
Parámetros:
- p_ij: matriz (2D) de probabilidades conjuntas bajo la hipótesis alternativa, debe sumar 1.
- alpha: error tipo I (por defecto 0.05)
- beta: error tipo II (por defecto 0.2, equivalente a 80% de poder)
Retorna:
- n: tamaño de muestra requerido (float)
Basado en: Chow et al. (2008), sección 6.2.1
"""
if not np.isclose(np.sum(p_ij), 1):
raise ValueError("La matriz p_ij debe sumar 1.")
r = len(p_ij)
c = len(p_ij[0] if r else 0)
df = (r - 1) * (c - 1)
# Valor crítico de chi² central
crit = stats.chi2.ppf(1 - alpha, df)
# Resolver lambda para que la chi² no central tenga el poder deseado
def objetivo(delta):
return stats.ncx2.sf(crit, df, delta) - (1 - beta)
sol = root_scalar(objetivo, bracket=[0, 50], method='brentq')
if not sol.converged:
raise RuntimeError("No se pudo encontrar el valor de lambda.")
delta = sol.root
# Calcular márgenes
p_i = np.sum(p_ij, axis=1) # sumas por fila
p_j = np.sum(p_ij, axis=0) # sumas por columna
# Tamaño del efecto (w²)
efecto = 0.0
for i in range(r):
for j in range(c):
esperado = p_i[i] * p_j[j]
if esperado > 0:
efecto += ((p_ij[i, j] - esperado) ** 2) / esperado
# Tamaño de muestra
n = delta / efecto
return ceil(n)
print('Función definida')
Función definida
p = np.array([
[0.25, 0.15],
[0.10, 0.50]
]) # Matriz de probabilidades conjuntas que suma 1
chi2_independencia(p)
36
En esta sección exploramos distintas fórmulas para calcular el tamaño de muestra en estudios cuantitativos:
Usamos la fórmula de Cochran para estimar proporciones y medias, tanto en poblaciones grandes como finitas.
Incorporamos el poder estadístico para estudios comparativos (por ejemplo, al estimar una diferencia de proporciones o un odds ratio).
Aplicamos la prueba chi² de independencia para detectar asociación entre variables categóricas, usando una aproximación basada en la distribución no central.
Estas herramientas son fundamentales para planear estudios con suficiente poder para detectar efectos reales, y evitar tamaños muestrales insuficientes o excesivos.
Esta siguiente sección es demostrativa.
5.5.7. Cálculo de tamaño de muestra con statsmodels
#
La librería statsmodels
incluye utilidades para calcular el tamaño de muestra, el poder estadístico o la magnitud del efecto en distintos tipos de estudios. Usaremos el módulo statsmodels.stats.power
.
5.5.7.1. Proporciones: comparación de dos grupos#
from statsmodels.stats.power import NormalIndPower
# Instanciar objeto para pruebas z de dos proporciones independientes
power = NormalIndPower()
# Calcular tamaño de muestra necesario por grupo
n = power.solve_power(
effect_size=0.5, # tamaño del efecto (p. ej. g de Hedge)
alpha=0.05,
power=0.8,
ratio=1.0, # razón de tamaños de grupo (n2/n1)
alternative='two-sided'
)
print(f"Tamaño por grupo: {ceil(n)}")
Tamaño por grupo: 63
5.5.7.2. Comparación medias en dos grupos#
Como en el caso de las pruebas t.
from statsmodels.stats.power import TTestIndPower
t_power = TTestIndPower()
n = t_power.solve_power(
effect_size=0.6, # tamaño del efecto (Cohen's d)
alpha=0.05,
power=0.8,
ratio=1.0,
alternative='two-sided'
)
print(f"Tamaño por grupo: {ceil(n)}")
Tamaño por grupo: 45
5.5.7.3. ANOVA de una vía (comparación de más de dos medias)#
Para estudios donde se desea comparar más de dos grupos en cuanto a su media, se utiliza un ANOVA de una vía. Podemos calcular el tamaño de muestra necesario por grupo con FTestAnovaPower
de statsmodels
.
from statsmodels.stats.power import FTestAnovaPower
anova_power = FTestAnovaPower()
n = anova_power.solve_power(
effect_size=0.25, # tamaño del efecto (Cohen's f)
alpha=0.05,
power=0.8,
k_groups=3 # número de grupos
)
print(f"Tamaño por grupo: {ceil(n)}")
Tamaño por grupo: 158
Puedes convertir de \(\eta^2\) (eta cuadrada) a f usando:
Por ejemplo:
Queremos detectar una diferencia moderada en la media de colesterol en 3 grupos de dieta con:
un tamaño del efecto \(\eta^2\) = 0.03,
un nivel de significancia de 0.05,
un poder del 80%.
from statsmodels.stats.power import FTestAnovaPower
anova_power = FTestAnovaPower()
eta2 = 0.03
f = sqrt(eta2 / (1 - eta2))
n = anova_power.solve_power(
effect_size=f,
alpha=0.05,
power=0.8,
k_groups=3
)
print(f"Tamaño por grupo: {ceil(n)}")
Tamaño por grupo: 315