Este documento es un material de estudio guiado. Cada prueba sigue siempre el mismo recorrido, para que aprendas a razonar y no solo a ejecutar una función:
Instrucciones prácticas
Ctrl + Shift + Enter, o generar el
informe completo con Knit.rmarkdown para el botón Knit.encuesta_salud.csv en tu carpeta.| Sesión | Tema | Funciones de R |
|---|---|---|
| 9.1 | Prueba de los signos y prueba de Wilcoxon | binom.test(),
wilcox.test(paired = TRUE) |
| 9.2 | Prueba U de Mann-Whitney | wilcox.test() |
| 10.1 | Chi-cuadrado de independencia y prueba exacta de Fisher | chisq.test(),
fisher.test() |
| 10.2 | Taller con una encuesta de salud pública | todas las anteriores |
| 11.1 | Limitaciones en muestras pequeñas | simulación de potencia |
| 12.1–12.2 | Evaluación parcial y solucionario | todas las anteriores |
Una prueba paramétrica (por ejemplo, la prueba \(t\)) supone que los datos provienen de una familia de distribuciones conocida, normalmente la normal, y contrasta sus parámetros (\(\mu\), \(\sigma\)). Una prueba no paramétrica hace supuestos mucho más débiles: por lo general solo que las observaciones son independientes y provienen de una distribución continua.
¿Cómo lo logra? Trabajando con los signos o con los rangos (el orden) de los datos en lugar de sus valores. La distribución de los rangos bajo \(H_0\) no depende de la forma de la distribución original. Por eso estas pruebas se llaman también de distribución libre.
Cuándo conviene usarlas:
El rango de una observación es su posición cuando los datos se ordenan de menor a mayor. Si dos o más valores son iguales (un empate), cada uno recibe el promedio de las posiciones que ocupan. Veámoslo en R:
| valor | rango |
|---|---|
| 40 | 2 |
| 60 | 4 |
| 20 | 1 |
| 60 | 4 |
| 90 | 6 |
| 60 | 4 |
Los tres valores 60 ocupan las posiciones 3, 4 y 5. Por eso cada uno recibe el rango \((3+4+5)/3 = 4\). Observa una propiedad útil: la suma de los rangos de \(n\) datos siempre es
\[1 + 2 + \cdots + n = \frac{n(n+1)}{2}.\]
Suma de los rangos : 21
Fórmula n(n + 1) / 2 : 21
Para que las salidas sean ordenadas y fáciles de leer, usaremos tres funciones de presentación:
ver(): muestra varios resultados con nombre, uno por
línea y alineados.mostrar(): imprime una tabla redondeada y sin la
numeración de filas.informe(): resume en español cualquier prueba de R
(nombre de la prueba, hipótesis alternativa, estadístico, p-valor y
decisión).R no tiene una función directa para la prueba de los signos ni para el estimador de Hodges-Lehmann. Las definimos una sola vez aquí y las usaremos en todo el documento.
# ---------------------------------------------------------------
# 1) Funciones para mostrar resultados de forma ordenada
# ---------------------------------------------------------------
# fmt(): da formato a un número. Los p-valores muy pequeños se muestran en
# notación científica; los enteros, sin decimales.
fmt <- function(v, dec = 4) {
if (is.null(v) || length(v) == 0 || is.na(v)) return("--")
if (!is.numeric(v)) return(as.character(v))
if (v != 0 && abs(v) < 0.001) return(formatC(v, format = "e", digits = 2))
if (abs(v - round(v)) < 1e-9) return(format(round(v), big.mark = ""))
as.character(round(v, dec))
}
# ver(): muestra varios valores con nombre, uno por línea y alineados.
# Ejemplo: ver("Media" = 10.5, "Mediana" = 9)
ver <- function(..., dec = 4) {
x <- list(...)
etiquetas <- format(names(x))
valores <- vapply(x, fmt, character(1), dec = dec)
cat(paste0(" ", etiquetas, " : ", valores), sep = "\n")
}
# mostrar(): imprime un data.frame redondeado y sin numeración de filas.
mostrar <- function(df, dec = 3) {
df <- as.data.frame(df)
num <- vapply(df, is.numeric, logical(1))
df[num] <- lapply(df[num], round, dec)
print(df, row.names = FALSE)
}
# informe(): resume en español el resultado de cualquier prueba de R
# (binom.test, wilcox.test, chisq.test, fisher.test, shapiro.test).
informe <- function(prueba, alpha = 0.05, extra = list()) {
m <- prueba$method
nombre <- if (grepl("binomial", m)) "Prueba de los signos (binomial exacta)"
else if (grepl("signed rank", m)) paste("Wilcoxon de rangos con signo",
ifelse(grepl("exact", m), "(exacta)", "(aprox. normal)"))
else if (grepl("rank sum", m)) paste("U de Mann-Whitney",
ifelse(grepl("exact", m), "(exacta)", "(aprox. normal)"))
else if (grepl("Yates", m)) "Chi-cuadrado (corrección de Yates)"
else if (grepl("Chi", m)) "Chi-cuadrado de independencia"
else if (grepl("Fisher", m)) "Prueba exacta de Fisher"
else if (grepl("Shapiro", m)) "Shapiro-Wilk (normalidad)"
else m
alternativa <- switch(ifelse(is.null(prueba$alternative), "ninguna", prueba$alternative),
greater = "unilateral, mayor (cola derecha)",
less = "unilateral, menor (cola izquierda)",
two.sided = "bilateral", "no aplica (cola derecha)")
est <- prueba$statistic
nom_est <- if (is.null(est)) "" else switch(names(est),
"number of successes" = "B (signos +)", "X-squared" = "Chi2", names(est))
filas <- list("Prueba" = nombre, "Hipótesis alternativa" = alternativa)
if (!is.null(est)) filas[["Estadístico"]] <- paste(nom_est, "=", fmt(unname(est)))
if (!is.null(prueba$parameter) && !grepl("binomial", m))
filas[["Grados de libertad"]] <- fmt(unname(prueba$parameter))
filas[["p-valor"]] <- fmt(prueba$p.value)
filas[["Decisión (alfa = 0.05)"]] <- ifelse(prueba$p.value < alpha,
"Se rechaza H0", "No se rechaza H0")
for (k in names(extra)) filas[[k]] <- fmt(extra[[k]])
linea <- strrep("-", 62)
cat(linea, "\n")
cat(paste0(" ", format(names(filas)), " : ", unlist(filas)), sep = "\n")
cat(linea, "\n")
}
# ---------------------------------------------------------------
# 2) Funciones estadísticas que R no trae directamente
# ---------------------------------------------------------------
# Estimador de Hodges-Lehmann para una muestra (o para diferencias pareadas):
# mediana de todos los promedios de Walsh (d_i + d_j)/2 con i <= j.
hl_1m <- function(d) {
w <- outer(d, d, "+") / 2
median(w[upper.tri(w, diag = TRUE)])
}
# Estimador de Hodges-Lehmann del desplazamiento entre dos muestras:
# mediana de todas las diferencias x_i - y_j.
hl_2m <- function(x, y) median(outer(x, y, "-"))
# Prueba de los signos para la mediana: cuenta los signos + y -, descarta los
# empates con m0 y aplica una prueba binomial con p = 1/2.
prueba_signos <- function(x, m0 = 0, alternative = "two.sided") {
d <- x - m0
pos <- sum(d > 0); neg <- sum(d < 0)
binom.test(pos, pos + neg, p = 0.5, alternative = alternative)
}Un programa municipal de capacitación productiva se aplicó a 12 hogares. Se registró el ingreso mensual (S/) de cada hogar antes y después de la intervención. La pregunta es: ¿aumentó el ingreso típico de los hogares?
Los datos son pareados: cada hogar se mide dos veces. Por eso no comparamos dos grupos, sino que analizamos la diferencia de cada hogar:
\[D_i = \text{post}_i - \text{pre}_i, \qquad i = 1, \dots, 12.\]
pre <- c(820, 950, 1100, 780, 1250, 900, 1010, 870, 1500, 990, 1120, 760)
post <- c(900, 1010, 1080, 850, 1400, 960, 1150, 870, 1620, 1050, 1180, 800)
d <- post - pre # diferencia de cada hogar
mostrar(data.frame(hogar = 1:12, pre, post, d, signo = sign(d))) hogar pre post d signo
1 820 900 80 1
2 950 1010 60 1
3 1100 1080 -20 -1
4 780 850 70 1
5 1250 1400 150 1
6 900 960 60 1
7 1010 1150 140 1
8 870 870 0 0
9 1500 1620 120 1
10 990 1050 60 1
11 1120 1180 60 1
12 760 800 40 1
ver("Mediana antes" = median(pre),
"Mediana después" = median(post),
"Mediana de las dif." = median(d)) Mediana antes : 970
Mediana después : 1030
Mediana de las dif. : 60
Primera lectura: casi todas las diferencias son positivas, una es negativa (hogar 3) y una es cero (hogar 8). Además hay empates en las diferencias: cuatro hogares aumentaron exactamente S/ 60. Ambas situaciones son frecuentes en datos reales y veremos cómo se tratan.
Si el programa no tuviera efecto, cada hogar tendría la misma probabilidad de aumentar o de disminuir su ingreso, como al lanzar una moneda. Si observamos “demasiados” aumentos para que sean obra del azar, concluimos que el ingreso mediano aumentó.
\[H_0: M = 0 \qquad \text{frente a} \qquad H_1: M > 0 \quad (\text{el ingreso aumentó}).\]
Sea \(B\) el número de diferencias positivas entre las \(n^*\) diferencias no nulas.
Resultado. Bajo \(H_0\), \(B \sim \text{Binomial}\left(n^*, \tfrac12\right)\).
Demostración. Si la mediana de \(D\) es 0, entonces por definición de mediana (y porque \(D\) es continua): \[P(D_i > 0) = P(D_i < 0) = \tfrac12 .\] Definamos \(I_i = 1\) si \(D_i > 0\) e \(I_i = 0\) si \(D_i < 0\). Cada \(I_i\) es una variable de Bernoulli con \(p = \tfrac12\), y las \(I_i\) son independientes porque las \(D_i\) lo son. Entonces \(B = \sum_{i=1}^{n^*} I_i\) es una suma de \(n^*\) Bernoulli independientes con la misma \(p\), es decir, una binomial: \(B \sim \text{Binomial}(n^*, \tfrac12)\). \(\blacksquare\)
¿Y los ceros? Una diferencia igual a cero no aporta información sobre la dirección del cambio. Por eso se descartan y se trabaja con las \(n^*\) diferencias restantes.
P-valor (alternativa “mayor”). Es la probabilidad de observar \(b_{\text{obs}}\) o más signos positivos: \[p = P(B \ge b_{\text{obs}}) = \sum_{k = b_{\text{obs}}}^{n^*} \binom{n^*}{k}\left(\frac12\right)^{n^*}.\]
Muestras grandes (\(n^* > 25\)). Como \(E(B) = n^*/2\) y \(\mathrm{Var}(B) = n^*/4\): \[Z = \frac{B - n^*/2}{\sqrt{n^*}/2} \approx N(0,1).\]
# Paso 1: contar signos (los ceros se descartan)
b <- sum(d > 0) # signos +
neg <- sum(d < 0) # signos -
n_est <- b + neg # n* = diferencias no nulas
ver("Signos positivos (B)" = b,
"Signos negativos" = neg,
"Ceros (se descartan)" = sum(d == 0),
"n* (diferencias útiles)" = n_est) Signos positivos (B) : 10
Signos negativos : 1
Ceros (se descartan) : 1
n* (diferencias útiles) : 11
# Paso 2: p-valor exacto con la fórmula binomial: P(B >= 10) con n* = 11
p_manual <- sum(choose(n_est, b:n_est) * 0.5^n_est)
p_dbinom <- sum(dbinom(b:n_est, size = n_est, prob = 0.5)) # lo mismo con dbinom()
ver("p-valor (fórmula)" = p_manual,
"p-valor (dbinom)" = p_dbinom) p-valor (fórmula) : 0.0059
p-valor (dbinom) : 0.0059
Es decir: \[p = \frac{\binom{11}{10} + \binom{11}{11}}{2^{11}} = \frac{11 + 1}{2048} = \frac{12}{2048} \approx 0.00586.\]
# Distribución de B bajo H0 y región del p-valor
k <- 0:n_est
barplot(dbinom(k, n_est, 0.5), names.arg = k,
col = ifelse(k >= b, "#80162a", "grey75"), border = NA,
main = "Distribución de B bajo H0: Binomial(11; 0.5)",
xlab = "Número de signos positivos (B)", ylab = "Probabilidad")
legend("topleft", fill = "#80162a", legend = "Región del p-valor: B >= 10", bty = "n")res_signos <- prueba_signos(d, alternative = "greater")
informe(res_signos, extra = list("Proporción de signos +" = unname(res_signos$estimate)))--------------------------------------------------------------
Prueba : Prueba de los signos (binomial exacta)
Hipótesis alternativa : unilateral, mayor (cola derecha)
Estadístico : B (signos +) = 10
p-valor : 0.0059
Decisión (alfa = 0.05) : Se rechaza H0
Proporción de signos + : 0.9091
--------------------------------------------------------------
Decisión: \(p = 0.0059 < 0.05\), así que rechazamos \(H_0\). Hay evidencia de que la mediana del ingreso aumentó.
Limitación: la prueba de los signos solo usa la dirección del cambio. Un hogar que ganó S/ 150 más cuenta igual que uno que ganó S/ 40 más. La prueba de Wilcoxon aprovecha también la magnitud.
Ordenamos las diferencias por su tamaño (valor absoluto). Si el programa no tuviera efecto, las diferencias grandes serían positivas o negativas con igual probabilidad. Si las diferencias grandes son casi todas positivas, eso es evidencia de que el ingreso aumentó.
\[H_0: M = 0 \qquad H_1: M > 0.\]
Sin empates, los rangos son \(1, 2, \dots, n\). Definamos \(I_i = 1\) si la diferencia con rango \(i\) es positiva y \(I_i = 0\) si es negativa. Entonces \[W^+ = \sum_{i=1}^{n} i \cdot I_i .\] Bajo \(H_0\) y por la simetría, cada signo es positivo con probabilidad \(\tfrac12\), de forma independiente de su rango. Por tanto \(E(I_i) = \tfrac12\) y \(\mathrm{Var}(I_i) = \tfrac14\), y:
\[E(W^+) = \sum_{i=1}^{n} i \cdot \frac12 = \frac12 \cdot \frac{n(n+1)}{2} = \frac{n(n+1)}{4},\]
\[\mathrm{Var}(W^+) = \sum_{i=1}^{n} i^2 \cdot \frac14 = \frac14 \cdot \frac{n(n+1)(2n+1)}{6} = \frac{n(n+1)(2n+1)}{24}. \qquad \blacksquare\]
Aquí usamos las sumas conocidas \(\sum i = \frac{n(n+1)}{2}\) y \(\sum i^2 = \frac{n(n+1)(2n+1)}{6}\).
Corrección por empates. Si hay grupos de valores empatados de tamaños \(t_1, t_2, \dots\), la varianza disminuye: \[\mathrm{Var}(W^+) = \frac{n(n+1)(2n+1)}{24} - \frac{\sum_j (t_j^3 - t_j)}{48}.\]
Estadístico estandarizado (con corrección por continuidad de \(0.5\)): \[Z = \frac{W^+ - E(W^+) - 0.5}{\sqrt{\mathrm{Var}(W^+)}} \approx N(0,1).\]
# Paso 1: descartar las diferencias nulas
dz <- d[d != 0]
n <- length(dz)
# Paso 2: rangos de los valores absolutos (rango promedio en los empates)
rangos <- rank(abs(dz))
tabla_w <- data.frame(d = dz, abs_d = abs(dz), rango = rangos,
signo = ifelse(dz > 0, "+", "-"))
mostrar(tabla_w[order(tabla_w$abs_d), ]) d abs_d rango signo
-20 20 1.0 -
40 40 2.0 +
60 60 4.5 +
60 60 4.5 +
60 60 4.5 +
60 60 4.5 +
70 70 7.0 +
80 80 8.0 +
120 120 9.0 +
140 140 10.0 +
150 150 11.0 +
# Paso 3: sumas de rangos
W_mas <- sum(rangos[dz > 0])
W_menos <- sum(rangos[dz < 0])
ver("n (sin ceros)" = n,
"W+ (rangos positivos)" = W_mas,
"W- (rangos negativos)" = W_menos,
"Comprobación n(n+1)/2" = n * (n + 1) / 2) n (sin ceros) : 11
W+ (rangos positivos) : 65
W- (rangos negativos) : 1
Comprobación n(n+1)/2 : 66
Los cuatro valores \(|D| = 60\) ocupan las posiciones 3 a 6, así que cada uno recibe el rango \((3+4+5+6)/4 = 4.5\). La única diferencia negativa (\(-20\)) tiene el rango más pequeño: el único hogar que perdió ingreso fue el que menos cambió.
# Paso 4: media y varianza bajo H0, con corrección por empates
E_W <- n * (n + 1) / 4
t_j <- table(abs(dz)) # tamaños de los grupos de empates
V_W <- n * (n + 1) * (2 * n + 1) / 24 - sum(t_j^3 - t_j) / 48
# Paso 5: estadístico Z y p-valor unilateral (cola derecha)
Z <- (W_mas - E_W - 0.5) / sqrt(V_W)
ver("E(W+)" = E_W,
"Var(W+) sin empates" = n * (n + 1) * (2 * n + 1) / 24,
"Var(W+) con empates" = V_W,
"Z" = Z,
"p-valor = P(Z >= z)" = pnorm(Z, lower.tail = FALSE)) E(W+) : 33
Var(W+) sin empates : 126.5
Var(W+) con empates : 125.25
Z : 2.8146
p-valor = P(Z >= z) : 0.0024
Sustituyendo los valores: \[E(W^+) = \frac{11 \cdot 12}{4} = 33, \qquad \mathrm{Var}(W^+) = \frac{11 \cdot 12 \cdot 23}{24} - \frac{4^3 - 4}{48} = 126.5 - 1.25 = 125.25,\] \[Z = \frac{65 - 33 - 0.5}{\sqrt{125.25}} = 2.815, \qquad p = P(Z \ge 2.815) = 0.00244.\]
Como hay ceros y empates, R no puede usar la distribución exacta y aplica la aproximación normal que acabamos de calcular:
res_w <- wilcox.test(post, pre, paired = TRUE, alternative = "greater",
exact = FALSE, correct = TRUE)
informe(res_w)--------------------------------------------------------------
Prueba : Wilcoxon de rangos con signo (aprox. normal)
Hipótesis alternativa : unilateral, mayor (cola derecha)
Estadístico : V = 65
p-valor : 0.0024
Decisión (alfa = 0.05) : Se rechaza H0
--------------------------------------------------------------
R llama \(V\) al estadístico \(W^+\). Obtuvimos el mismo valor: \(V = 65\) y \(p =
0.00244\). La función informe() resume en español lo
que imprime wilcox.test(); si quieres ver la salida
original de R, escribe print(res_w).
Nota sobre la distribución exacta. Sin empates, para \(n\) pequeño se usa la distribución exacta de \(W^+\), que se obtiene contando los \(2^n\) patrones de signos igualmente probables. Por ejemplo, con \(n = 11\) y sin empates, solo 2 de los 2048 patrones dan \(W^+ \geq 65\):
P(W+ >= 65), n = 11 : 9.77e-04
2 / 2048 : 9.77e-04
Estimador de Hodges-Lehmann. Se calcula la mediana de los promedios de Walsh \(\frac{D_i + D_j}{2}\) para todos los pares \(i \le j\). Estima el cambio típico y es robusto frente a valores extremos:
HL <- hl_1m(dz)
r <- Z / sqrt(n) # tamaño del efecto
ver("Hodges-Lehmann (S/)" = HL,
"Tamaño del efecto r" = r) Hodges-Lehmann (S/) : 70
Tamaño del efecto r : 0.8486
Tamaño del efecto \(r = Z/\sqrt{n}\). Como referencia (Cohen): \(0.1\) es pequeño, \(0.3\) mediano y \(0.5\) grande. Aquí \(r = 0.85\): el efecto es grande.
par(mfrow = c(1, 2), mar = c(4, 4, 2.5, 1))
matplot(rbind(1, 2), rbind(pre, post), type = "b", pch = 19, lty = 1,
col = ifelse(post > pre, "darkgreen", ifelse(post < pre, "firebrick", "grey50")),
xaxt = "n", xlab = "", ylab = "Ingreso (S/)", main = "Ingreso por hogar")
axis(1, at = 1:2, labels = c("Antes", "Después"))
plot(sort(d), type = "h", lwd = 3, col = "#1856a0", ylab = "S/",
xlab = "Hogares (ordenados)", main = "Diferencias post - pre"); abline(h = 0)| Prueba de los signos | Prueba de Wilcoxon | |
|---|---|---|
| Usa | solo la dirección | dirección y magnitud |
| Supuesto adicional | ninguno | simetría de \(D\) |
| Estadístico | \(B = 10\) de \(n^* = 11\) | \(W^+ = 65\) |
| p-valor (unilateral) | 0.0059 | 0.0024 |
Redacción sugerida: “El ingreso mensual de los hogares aumentó tras la intervención (mediana de las diferencias = S/ 60). La prueba de Wilcoxon de rangos con signo fue significativa (\(W^+ = 65\), \(p = 0.002\), \(r = 0.85\)), con un aumento típico estimado de S/ 70 (Hodges-Lehmann). La prueba de los signos llevó a la misma conclusión (\(p = 0.006\)).”
Cuidado con la causalidad. Sin un grupo de control no podemos atribuir el aumento al programa: el ingreso pudo subir por otras razones, como la temporada o la economía local.
Con los datos de la evaluación (caso 1, sección 7), calcula a
mano \(W^+\), \(E(W^+)\) y \(\mathrm{Var}(W^+)\) con la corrección por
empates, y verifica tu resultado con wilcox.test().
Se registró el salario mensual (S/) de 10 hombres y 9 mujeres que ocupan el mismo puesto en una empresa. ¿Existen diferencias salariales por género?
Ahora las muestras son independientes: son personas distintas y no hay emparejamiento.
hombres <- c(1850, 2100, 1720, 2600, 1980, 2300, 1650, 2450, 2050, 3100)
mujeres <- c(1600, 1750, 1500, 1900, 2020, 1680, 1450, 2200, 1580)
n1 <- length(hombres); n2 <- length(mujeres); N <- n1 + n2
ver("n1 (hombres)" = n1,
"n2 (mujeres)" = n2,
"Mediana hombres" = median(hombres),
"Mediana mujeres" = median(mujeres)) n1 (hombres) : 10
n2 (mujeres) : 9
Mediana hombres : 2075
Mediana mujeres : 1680
Juntamos los 19 salarios, los ordenamos y anotamos de qué grupo es cada uno. Si no hubiera diferencia entre grupos, los rangos de hombres y mujeres estarían bien mezclados. Si los hombres ocupan sistemáticamente los rangos altos, hay evidencia de que tienden a ganar más.
\[H_0: \text{las dos distribuciones son iguales} \;\; \big(P(X > Y) = \tfrac12\big)\] \[H_1: \text{una tiende a dar valores mayores que la otra} \;\; \big(P(X > Y) \neq \tfrac12\big)\]
Importante: Mann-Whitney contrasta si \(P(X > Y) = 1/2\). Solo equivale a comparar medianas si ambas distribuciones tienen la misma forma y difieren solo en su ubicación.
Interpretación de \(U_1\) (demostración). \(U_1\) es el número de pares \((X_i, Y_j)\) en los que el valor del grupo 1 supera al del grupo 2: \[U_1 = \#\{(i,j): X_i > Y_j\}.\] Demostración. Ordenemos el grupo 1: \(X_{(1)} < X_{(2)} < \cdots < X_{(n_1)}\). El rango de \(X_{(i)}\) en la muestra combinada es su posición: hay \(i - 1\) valores del grupo 1 por debajo de él, más los valores del grupo 2 menores que él. Por tanto \[\text{rango}(X_{(i)}) = i + \#\{j: Y_j < X_{(i)}\}.\] Sumando sobre \(i = 1, \dots, n_1\): \[R_1 = \sum_{i=1}^{n_1} i + \sum_{i=1}^{n_1} \#\{j: Y_j < X_{(i)}\} = \frac{n_1(n_1+1)}{2} + U_1. \qquad \blacksquare\]
Por eso \(\frac{U_1}{n_1 n_2}\) estima \(P(X > Y)\), la probabilidad de que un hombre elegido al azar gane más que una mujer elegida al azar.
Distribución bajo \(H_0\). Todas las asignaciones de
rangos a los grupos son igualmente probables. Se demuestra que \[E(U) = \frac{n_1 n_2}{2}, \qquad \mathrm{Var}(U)
= \frac{n_1 n_2 (N+1)}{12} \quad (\text{sin empates}),\] y para
muestras grandes \(Z = \dfrac{U -
E(U)}{\sqrt{\mathrm{Var}(U)}} \approx N(0,1)\). Para muestras
pequeñas y sin empates se usa la distribución exacta
(función pwilcox() en R).
# Paso 1: rangos de la muestra combinada
todos <- c(hombres, mujeres)
grupo <- rep(c("Hombre", "Mujer"), c(n1, n2))
rg <- rank(todos)
tabla_mw <- data.frame(salario = todos, grupo, rango = rg)
mostrar(tabla_mw[order(tabla_mw$rango), ]) salario grupo rango
1450 Mujer 1
1500 Mujer 2
1580 Mujer 3
1600 Mujer 4
1650 Hombre 5
1680 Mujer 6
1720 Hombre 7
1750 Mujer 8
1850 Hombre 9
1900 Mujer 10
1980 Hombre 11
2020 Mujer 12
2050 Hombre 13
2100 Hombre 14
2200 Mujer 15
2300 Hombre 16
2450 Hombre 17
2600 Hombre 18
3100 Hombre 19
# Paso 2: sumas de rangos (comprobación: R1 + R2 = N(N+1)/2)
R1 <- sum(rg[grupo == "Hombre"]); R2 <- sum(rg[grupo == "Mujer"])
# Paso 3: estadísticos U (comprobación: U1 + U2 = n1 * n2)
U1 <- R1 - n1 * (n1 + 1) / 2
U2 <- n1 * n2 - U1
# Paso 4: verificar la interpretación de U1 contando los pares directamente
pares <- sum(outer(hombres, mujeres, ">"))
ver("R1 (hombres)" = R1,
"R2 (mujeres)" = R2,
"R1 + R2" = R1 + R2,
"N(N + 1) / 2" = N * (N + 1) / 2,
"U1" = U1,
"U2" = U2,
"n1 * n2" = n1 * n2,
"Pares con hombre > mujer" = pares) R1 (hombres) : 129
R2 (mujeres) : 61
R1 + R2 : 190
N(N + 1) / 2 : 190
U1 : 74
U2 : 16
n1 * n2 : 90
Pares con hombre > mujer : 74
El conteo directo confirma la demostración: en 74 de los 90 pares hombre–mujer, el hombre gana más.
# Paso 5: p-valor exacto bilateral = 2 * P(U <= min(U1, U2))
p_exacto <- 2 * pwilcox(min(U1, U2), n1, n2)
# Aproximación normal, solo para comparar
EU <- n1 * n2 / 2; VU <- n1 * n2 * (N + 1) / 12
Zu <- (U1 - EU) / sqrt(VU)
ver("E(U)" = EU,
"Var(U)" = VU,
"Z" = Zu,
"p-valor exacto" = p_exacto,
"p-valor aprox. normal" = 2 * pnorm(-abs(Zu))) E(U) : 45
Var(U) : 150
Z : 2.3678
p-valor exacto : 0.0172
p-valor aprox. normal : 0.0179
Con estos valores: \[U_1 = 129 - \frac{10 \cdot 11}{2} = 74, \qquad E(U) = \frac{90}{2} = 45,\] \[\mathrm{Var}(U) = \frac{90 \cdot 20}{12} = 150, \qquad Z = \frac{74 - 45}{\sqrt{150}} = 2.37 .\]
--------------------------------------------------------------
Prueba : U de Mann-Whitney (exacta)
Hipótesis alternativa : bilateral
Estadístico : W = 74
p-valor : 0.0172
Decisión (alfa = 0.05) : Se rechaza H0
--------------------------------------------------------------
En R, el estadístico \(W\) de
wilcox.test(x, y) es \(U_1\), la \(U\) del primer grupo.
ver("P(hombre > mujer)" = U1 / (n1 * n2),
"Correlación biserial r_rb" = 2 * U1 / (n1 * n2) - 1,
"Hodges-Lehmann (S/)" = hl_2m(hombres, mujeres)) P(hombre > mujer) : 0.8222
Correlación biserial r_rb : 0.6444
Hodges-Lehmann (S/) : 400
boxplot(list(Hombres = hombres, Mujeres = mujeres), col = c("#cfdcf0", "#f3d9de"),
ylab = "Salario (S/)", main = "Salario mensual por género")
stripchart(list(hombres, mujeres), vertical = TRUE, method = "jitter", add = TRUE, pch = 19)Redacción sugerida: “Los salarios de los hombres (Md = 2075) fueron mayores que los de las mujeres (Md = 1680) en el mismo puesto, \(U = 74\), \(p = 0.017\) (prueba exacta). En el 82 % de los pares hombre–mujer el hombre percibe más (\(r_{rb} = 0.64\), efecto grande), con una diferencia típica estimada de S/ 400 mensuales (Hodges-Lehmann).”
Para discutir: comparar a personas del mismo puesto controla parte de las diferencias, pero no otros factores como la antigüedad, las horas trabajadas o la formación.
Agrega una mujer con salario de S/ 3200 y repite el análisis. ¿Cambia mucho \(U\)? ¿Y cambiaría mucho la diferencia de medias? Explica por qué las pruebas de rangos son robustas frente a valores extremos.
En una encuesta a 360 personas se registró el nivel educativo y si tuvieron acceso a servicios de salud cuando lo necesitaron. ¿El acceso depende del nivel educativo?
tabla <- matrix(c(45, 55, 90, 60, 85, 25), nrow = 3, byrow = TRUE,
dimnames = list(Educacion = c("Primaria", "Secundaria", "Superior"),
Acceso = c("Si", "No")))
addmargins(tabla) Acceso
Educacion Si No Sum
Primaria 45 55 100
Secundaria 90 60 150
Superior 85 25 110
Sum 220 140 360
Si el acceso no dependiera de la educación, la proporción con acceso sería la misma en los tres niveles, igual a la proporción global \(220/360 = 61.1\%\). La prueba compara las frecuencias observadas con las que esperaríamos si hubiera independencia.
\[H_0: \text{el acceso es independiente del nivel educativo} \qquad H_1: \text{existe asociación}.\]
Dos eventos \(A\) y \(B\) son independientes si \(P(A \cap B) = P(A)\,P(B)\). Estimando cada probabilidad marginal con su proporción muestral, \(\hat P(\text{fila } i) = f_i/n\) y \(\hat P(\text{columna } j) = c_j/n\), la frecuencia esperada en la celda \((i,j)\) bajo \(H_0\) es
\[E_{ij} = n \cdot \frac{f_i}{n} \cdot \frac{c_j}{n} = \frac{f_i \, c_j}{n} = \frac{(\text{total de la fila } i)(\text{total de la columna } j)}{n}.\]
\[\chi^2 = \sum_{i=1}^{r} \sum_{j=1}^{c} \frac{(O_{ij} - E_{ij})^2}{E_{ij}} \;\sim\; \chi^2_{(r-1)(c-1)} \quad \text{(aproximadamente, bajo } H_0).\]
Supuestos: observaciones independientes y frecuencias esperadas suficientemente grandes (\(E_{ij} \ge 5\) en todas las celdas, o al menos en el 80 % y ninguna menor que 1). Si no se cumplen, usamos la prueba exacta de Fisher.
# Paso 1: totales marginales
f_i <- rowSums(tabla); c_j <- colSums(tabla); n <- sum(tabla)
# Paso 2: frecuencias esperadas E_ij = f_i * c_j / n
E <- outer(f_i, c_j) / n
round(E, 2) Si No
Primaria 61.11 38.89
Secundaria 91.67 58.33
Superior 67.22 42.78
¿Todas las esperadas >= 5? : Sí
Por ejemplo, \(E_{11} = \dfrac{100 \times 220}{360} = 61.11\): si hubiera independencia, esperaríamos 61 personas con primaria y con acceso, y observamos 45.
Acceso
Educacion Si No
Primaria 4.247 6.675
Secundaria 0.030 0.048
Superior 4.702 7.388
chi2 <- sum(contrib)
# Paso 4: grados de libertad, valor crítico y p-valor
gl <- (nrow(tabla) - 1) * (ncol(tabla) - 1)
ver("Chi2 calculado" = chi2,
"Grados de libertad" = gl,
"Valor crítico (5 %)" = qchisq(0.95, gl),
"p-valor" = pchisq(chi2, gl, lower.tail = FALSE)) Chi2 calculado : 23.0897
Grados de libertad : 2
Valor crítico (5 %) : 5.9915
p-valor : 9.69e-06
Decisión: \(\chi^2 = 23.09 > 5.99\) (valor crítico con 2 gl), o equivalentemente \(p < 0.001\). Rechazamos la independencia.
curve(dchisq(x, df = 2), from = 0, to = 26, lwd = 2, col = "#1856a0",
ylab = "Densidad", xlab = expression(chi^2),
main = "Distribución chi-cuadrado con 2 gl")
abline(v = qchisq(0.95, 2), lty = 2, col = "grey40")
abline(v = chi2, lwd = 2, col = "#80162a")
legend("topright", bty = "n", lty = c(2, 1), col = c("grey40", "#80162a"),
legend = c("Valor crítico 5.99", "Valor observado 23.09"))--------------------------------------------------------------
Prueba : Chi-cuadrado de independencia
Hipótesis alternativa : no aplica (cola derecha)
Estadístico : Chi2 = 23.0897
Grados de libertad : 2
p-valor : 9.69e-06
Decisión (alfa = 0.05) : Se rechaza H0
--------------------------------------------------------------
Residuos estandarizados ajustados. Indican qué celdas se alejan más de lo esperado: \[r_{ij} = \frac{O_{ij} - E_{ij}}{\sqrt{E_{ij}\left(1 - \frac{f_i}{n}\right)\left(1 - \frac{c_j}{n}\right)}} \approx N(0,1).\] Valores con \(|r_{ij}| > 2\) señalan celdas con más casos (si es positivo) o menos casos (si es negativo) de los esperados.
Acceso
Educacion Si No
Primaria -3.889 3.889
Secundaria -0.365 0.365
Superior 4.172 -4.172
Acceso
Educacion Si No
Primaria -3.889 3.889
Secundaria -0.365 0.365
Superior 4.172 -4.172
V de Cramér. Mide la fuerza de la asociación entre 0 (ninguna) y 1 (perfecta): \[V = \sqrt{\frac{\chi^2}{n\,(k-1)}}, \qquad k = \min(r, c).\]
V de Cramér : 0.2533
Acceso
Educacion Si No
Primaria 0.450 0.550
Secundaria 0.600 0.400
Superior 0.773 0.227
barplot(prop.table(tabla, 1)[, "Si"], col = c("#80162a", "#1856a0", "#2c7a4b"),
ylim = c(0, 1), ylab = "Proporción con acceso",
main = "Acceso a servicios de salud por nivel educativo")
abline(h = sum(tabla[, "Si"]) / n, lty = 2)Redacción sugerida: “El acceso a servicios de salud se asoció con el nivel educativo, \(\chi^2(2, N = 360) = 23.09\), \(p < 0.001\), \(V = 0.25\). El acceso aumentó de 45 % en primaria a 77 % en educación superior. Los residuos ajustados muestran que la asociación proviene de los extremos: primaria por debajo de lo esperado (\(r = -3.89\)) y superior por encima (\(r = 4.17\)).”
En un programa piloto con 20 personas se registró si participaron y si mejoraron:
t2 <- matrix(c(7, 2, 3, 8), 2, byrow = TRUE,
dimnames = list(Programa = c("Si", "No"), Mejora = c("Si", "No")))
addmargins(t2) Mejora
Programa Si No Sum
Si 7 2 9
No 3 8 11
Sum 10 10 20
Si No
Si 4.5 4.5
No 5.5 5.5
Las frecuencias esperadas son menores que 5, así que la aproximación chi-cuadrado no es confiable.
Fundamento de Fisher. Con los totales de filas y columnas fijos, la frecuencia \(a\) de la celda superior izquierda sigue una distribución hipergeométrica bajo \(H_0\): \[P(a) = \frac{\binom{a+b}{a}\binom{c+d}{c}}{\binom{n}{a+c}}.\] El p-valor bilateral suma las probabilidades de todas las tablas tan o menos probables que la observada.
# Valores posibles de a: de 0 a 9 (total de la fila 1).
# m = 10 mejoran, n = 10 no mejoran, k = 9 participan en el programa.
a_pos <- 0:9
prob <- dhyper(a_pos, m = 10, n = 10, k = 9)
mostrar(data.frame(a = a_pos, probabilidad = prob), dec = 5) a probabilidad
0 0.00006
1 0.00268
2 0.03215
3 0.15004
4 0.31508
5 0.31508
6 0.15004
7 0.03215
8 0.00268
9 0.00006
p_fisher <- sum(prob[prob <= prob[a_pos == 7] * (1 + 1e-7)])
ver("p-valor de Fisher (manual)" = p_fisher,
"p-valor de fisher.test()" = fisher.test(t2)$p.value,
"Chi-cuadrado (no confiable)" = chisq.test(t2, correct = FALSE)$p.value) p-valor de Fisher (manual) : 0.0698
p-valor de fisher.test() : 0.0698
Chi-cuadrado (no confiable) : 0.0246
Moraleja: el chi-cuadrado daría \(p = 0.025\) (significativo) y Fisher da \(p = 0.070\) (no significativo). Con muestras pequeñas, la elección de la prueba cambia la conclusión. Por eso siempre se revisan las frecuencias esperadas antes de interpretar.
Duplica todas las frecuencias de la tabla t2 y repite
ambas pruebas. ¿Qué ocurre con los p-valores? ¿Y con las frecuencias
esperadas?
Trabajaremos con una encuesta de 240 personas (datos simulados con fines didácticos, con una estructura similar a la de las encuestas de salud). La base viene incluida en este archivo.
# La base viene incluida en este mismo archivo (bloque oculto "base-incluida", al inicio),
# por lo que el notebook funciona sin archivos adicionales.
# Si prefiere leer un CSV desde su computadora, use en su lugar:
# df <- read.csv(file.choose())
df <- read.csv(text = encuesta_csv)
write.csv(df, "encuesta_salud.csv", row.names = FALSE) # deja una copia del CSV en la carpeta
ver("Personas (filas)" = nrow(df), "Variables (columnas)" = ncol(df)) Personas (filas) : 240
Variables (columnas) : 10
id sexo area edad nivel_educ fuma actividad_min imc pas_pre pas_post
1 Mujer Rural 22 Secundaria No 74 26.0 NA NA
2 Hombre Rural 29 Superior No 289 23.3 NA NA
3 Mujer Rural 28 Secundaria Si 92 23.8 NA NA
4 Mujer Urbano 38 Secundaria No 29 26.3 NA NA
5 Mujer Urbano 23 Superior No 175 25.5 141 136
6 Hombre Urbano 27 Secundaria No 250 28.3 152 149
| Variable | Tipo | Descripción |
|---|---|---|
sexo, area |
categórica | Mujer / Hombre; Urbano / Rural |
edad |
numérica | edad en años (18 a 69) |
nivel_educ |
ordinal | Primaria / Secundaria / Superior |
fuma |
categórica | Si / No |
actividad_min |
numérica | minutos de actividad física por semana |
imc |
numérica | índice de masa corporal (kg/m²) |
pas_pre, pas_post |
numérica (pareada) | presión arterial sistólica antes y después de un programa (solo 25 participantes) |
# Resumen de las variables numéricas
num <- df[, c("edad", "actividad_min", "imc")]
mostrar(data.frame(variable = names(num),
minimo = sapply(num, min),
mediana = sapply(num, median),
media = sapply(num, mean),
maximo = sapply(num, max),
desv_est = sapply(num, sd)), dec = 2) variable minimo mediana media maximo desv_est
edad 18 43.00 42.92 69.0 15.41
actividad_min 2 130.00 161.43 1392.0 147.93
imc 21 25.35 25.89 39.4 2.89
Area
Rural Urbano Sum
92 148 240
Fuma
Sexo No Si Sum
Hombre 93 23 116
Mujer 108 16 124
Sum 201 39 240
Metodología del taller. Para cada pregunta seguimos los mismos pasos: (1) identificar el tipo de variable y de diseño; (2) plantear las hipótesis; (3) elegir y justificar la prueba; (4) ejecutarla; (5) interpretar.
La prueba de Shapiro-Wilk contrasta \(H_0\): los datos provienen de una distribución normal. La complementamos con gráficos, porque con muestras grandes la prueba detecta desviaciones mínimas.
normalidad <- do.call(rbind, lapply(split(df$actividad_min, df$area), function(x) {
sw <- shapiro.test(x)
data.frame(n = length(x), mediana = median(x), media = mean(x),
W = unname(sw$statistic), p_valor = sw$p.value)
}))
normalidad$p_valor <- format(normalidad$p_valor, digits = 3, scientific = TRUE)
mostrar(cbind(area = rownames(normalidad), normalidad)) area n mediana media W p_valor
Rural 92 151.5 199.043 0.733 1.24e-11
Urbano 148 124.0 138.047 0.884 2.13e-09
par(mfrow = c(1, 2), mar = c(4, 4, 2.5, 1))
hist(df$actividad_min, breaks = 25, col = "#cfdcf0", border = "white",
main = "Actividad física (min/sem)", xlab = "minutos")
abline(v = c(mean(df$actividad_min), median(df$actividad_min)), col = c("#80162a", "#1856a0"), lwd = 2)
legend("topright", bty = "n", col = c("#80162a", "#1856a0"), lwd = 2, legend = c("media", "mediana"))
qqnorm(df$actividad_min, pch = 19, cex = .6, main = "Gráfico Q-Q normal"); qqline(df$actividad_min, col = "#80162a", lwd = 2)Interpretación: la distribución es muy asimétrica a la derecha (la media supera a la mediana y los puntos se alejan de la recta en el Q-Q). Shapiro-Wilk rechaza la normalidad en ambas áreas (\(p < 0.001\)). Usaremos pruebas basadas en rangos.
rur <- df$actividad_min[df$area == "Rural"]
urb <- df$actividad_min[df$area == "Urbano"]
ver("n rural" = length(rur),
"n urbano" = length(urb),
"Mediana rural" = median(rur),
"Mediana urbano" = median(urb)) n rural : 92
n urbano : 148
Mediana rural : 151.5
Mediana urbano : 124
mw <- wilcox.test(rur, urb, exact = FALSE, correct = TRUE)
U_r <- unname(mw$statistic)
informe(mw, extra = list(
"P(rural > urbano)" = U_r / (length(rur) * length(urb)),
"Correlación biserial r_rb" = 2 * U_r / (length(rur) * length(urb)) - 1,
"Hodges-Lehmann (min)" = hl_2m(rur, urb)))--------------------------------------------------------------
Prueba : U de Mann-Whitney (aprox. normal)
Hipótesis alternativa : bilateral
Estadístico : W = 7977.5
p-valor : 0.0254
Decisión (alfa = 0.05) : Se rechaza H0
P(rural > urbano) : 0.5859
Correlación biserial r_rb : 0.1718
Hodges-Lehmann (min) : 29
--------------------------------------------------------------
Hay empates y las muestras son grandes, así que R usa la
aproximación normal (exact = FALSE).
Interpretación: la población rural realiza más actividad física (\(p = 0.025\)), con una diferencia típica de unos 29 minutos semanales. Sin embargo, el efecto es pequeño (\(r_{rb} = 0.17\)): en el 59 % de los pares rural–urbano, la persona rural se ejercita más.
Fuma
Sexo No Si Sum
Hombre 93 23 116
Mujer 108 16 124
Sum 201 39 240
Fuma
Sexo No Si
Hombre 97.15 18.85
Mujer 103.85 20.15
--------------------------------------------------------------
Prueba : Chi-cuadrado (corrección de Yates)
Hipótesis alternativa : no aplica (cola derecha)
Estadístico : Chi2 = 1.6333
Grados de libertad : 1
p-valor : 0.2012
Decisión (alfa = 0.05) : No se rechaza H0
--------------------------------------------------------------
ver("p-valor sin corrección" = chisq.test(t, correct = FALSE)$p.value,
"p-valor de Fisher" = fisher.test(t)$p.value) p-valor sin corrección : 0.1462
p-valor de Fisher : 0.1638
Fuma
Sexo No Si
Hombre 0.802 0.198
Mujer 0.871 0.129
En tablas \(2 \times 2\), R aplica por defecto la corrección de Yates: \(\chi^2 = \sum \frac{(|O - E| - 0.5)^2}{E}\), que hace la prueba más conservadora.
Interpretación: la prevalencia de tabaquismo es 19.8 % en hombres y 12.9 % en mujeres, pero la diferencia no es significativa (\(p = 0.20\)). Atención: no significativo no significa “no hay diferencia”. Con solo 39 fumadores, la prueba tiene poca potencia para detectar diferencias de esta magnitud.
pa <- na.omit(df[, c("pas_pre", "pas_post")])
dif <- pa$pas_post - pa$pas_pre
ver("Participantes" = nrow(pa),
"Mediana del cambio" = median(dif),
"Cambios negativos" = sum(dif < 0),
"Cambios positivos" = sum(dif > 0),
"Cambios nulos" = sum(dif == 0)) Participantes : 25
Mediana del cambio : -7
Cambios negativos : 23
Cambios positivos : 2
Cambios nulos : 0
res_pa <- wilcox.test(pa$pas_post, pa$pas_pre, paired = TRUE, alternative = "less",
exact = FALSE, correct = TRUE)
informe(res_pa, extra = list(
"Hodges-Lehmann (mmHg)" = hl_1m(dif[dif != 0]),
"p-valor prueba de signos" = prueba_signos(dif, alternative = "less")$p.value))--------------------------------------------------------------
Prueba : Wilcoxon de rangos con signo (aprox. normal)
Hipótesis alternativa : unilateral, menor (cola izquierda)
Estadístico : V = 20.5
p-valor : 6.92e-05
Decisión (alfa = 0.05) : Se rechaza H0
Hodges-Lehmann (mmHg) : -7
p-valor prueba de signos : 9.72e-06
--------------------------------------------------------------
Interpretación: la presión sistólica disminuyó de forma significativa (\(V = 20.5\), \(p < 0.001\)), con una reducción típica de 7 mmHg. La prueba de los signos coincide.
ver("Mediana del IMC" = median(df$imc),
"Por encima de 25" = sum(df$imc > 25),
"Por debajo de 25" = sum(df$imc < 25),
"Iguales a 25" = sum(df$imc == 25)) Mediana del IMC : 25.35
Por encima de 25 : 129
Por debajo de 25 : 105
Iguales a 25 : 6
--------------------------------------------------------------
Prueba : Prueba de los signos (binomial exacta)
Hipótesis alternativa : unilateral, mayor (cola derecha)
Estadístico : B (signos +) = 129
p-valor : 0.0663
Decisión (alfa = 0.05) : No se rechaza H0
--------------------------------------------------------------
Interpretación: aunque la mediana muestral (25.35) supera ligeramente a 25, la evidencia no es suficiente al 5 % (\(p = 0.066\)).
p_val <- c(shapiro.test(rur)$p.value, mw$p.value, chisq.test(t)$p.value,
res_pa$p.value, prueba_signos(df$imc, 25, "greater")$p.value)
resumen <- data.frame(
Pregunta = c("Normalidad", "Actividad por área",
"Tabaquismo y sexo", "Presión pre/post", "Mediana IMC > 25"),
Prueba = c("Shapiro-Wilk", "Mann-Whitney", "Chi2 (Yates)",
"Wilcoxon pareado", "Signos"),
p_valor = vapply(p_val, fmt, character(1)),
Decision = c("No normal", "Diferencia pequeña", "No rechaza H0",
"La presión bajó", "No rechaza H0"))
mostrar(resumen) Pregunta Prueba p_valor Decision
Normalidad Shapiro-Wilk 1.24e-11 No normal
Actividad por área Mann-Whitney 0.0254 Diferencia pequeña
Tabaquismo y sexo Chi2 (Yates) 0.2012 No rechaza H0
Presión pre/post Wilcoxon pareado 6.92e-05 La presión bajó
Mediana IMC > 25 Signos 0.0663 No rechaza H0
par(mfrow = c(1, 3), mar = c(4, 4, 2.5, 1))
boxplot(actividad_min ~ area, data = df, col = c("#f3d9de", "#cfdcf0"),
main = "Actividad física (min/sem)", xlab = "")
barplot(prop.table(t, 1)[, "Si"], col = c("#1856a0", "#80162a"), main = "Prevalencia de tabaquismo")
hist(dif, breaks = 10, col = "#2c7a4b", border = "white", main = "Cambio en PAS (post - pre)", xlab = "mmHg")
abline(v = 0, lwd = 2)Usando la base, contrasta si el IMC difiere entre fumadores y no fumadores, y si el nivel educativo se asocia con el área. Para cada caso, justifica la prueba elegida.
Una prueba exacta solo puede tomar un número finito de valores. En la prueba de los signos (y en la de Wilcoxon), el resultado más extremo posible es que todos los signos sean iguales, y su probabilidad bilateral es \[p_{\min} = 2 \cdot \left(\tfrac12\right)^n .\] Con \(n \le 5\) se tiene \(p_{\min} \ge 0.0625\): ningún resultado puede ser significativo al 5 %, por grande que sea el efecto.
# Signos o Wilcoxon: p mínimo bilateral = 2 * (1/2)^n
mostrar(data.frame(n = 3:8, p_min_bilateral = 2 * 0.5^(3:8)), dec = 4) n p_min_bilateral
3 0.2500
4 0.1250
5 0.0625
6 0.0312
7 0.0156
8 0.0078
# Mann-Whitney con n1 = n2 = m: p mínimo bilateral = 2 / C(2m, m)
mostrar(data.frame(n_por_grupo = 2:4, p_min_bilateral = 2 / choose(2 * (2:4), 2:4)), dec = 4) n_por_grupo p_min_bilateral
2 0.3333
3 0.1000
4 0.0286
Consecuencia ética: diseñar un estudio con 5 pares y concluir “el programa no tiene efecto” es un error metodológico, no un hallazgo.
La potencia es la probabilidad de rechazar \(H_0\) cuando es falsa. La estimamos simulando muchas muestras con un efecto real (desplazamiento de 0.5) y contando cuántas veces se rechaza.
set.seed(123)
potencia <- function(n, rdist, delta = 0.5, B = 4000) {
rech <- replicate(B, {
x <- rdist(n) + delta # muestra con efecto real
c(t = t.test(x)$p.value < 0.05,
w = suppressWarnings(wilcox.test(x)$p.value) < 0.05)
})
rowMeans(rech) # proporción de rechazos
}
res <- expand.grid(n = c(8, 15, 30), dist = c("Normal", "t con 3 gl"), stringsAsFactors = FALSE)
pot <- t(mapply(function(n, dist)
potencia(n, if (dist == "Normal") rnorm else function(k) rt(k, df = 3)), res$n, res$dist))
mostrar(data.frame(n = res$n, distribucion = res$dist,
potencia_t = pot[, "t"], potencia_wilcoxon = pot[, "w"])) n distribucion potencia_t potencia_wilcoxon
8 Normal 0.230 0.185
15 Normal 0.446 0.422
30 Normal 0.756 0.743
8 t con 3 gl 0.156 0.152
15 t con 3 gl 0.252 0.286
30 t con 3 gl 0.415 0.536
Lectura:
Para cada caso: (a) plantea \(H_0\) y \(H_1\); (b) justifica la prueba; (c) ejecútala; (d) reporta el estadístico, el p-valor y el tamaño del efecto; (e) interpreta en el contexto.
Tiempo de espera promedio (minutos) en 10 centros, antes y después de implementar citas digitales.
antes <- c(35, 42, 28, 50, 38, 45, 33, 40, 55, 30)
despues <- c(30, 35, 29, 41, 30, 38, 30, 36, 44, 27)
dd <- despues - antes
mostrar(data.frame(centro = 1:10, antes, despues, dif = dd, rango_abs = rank(abs(dd)))) centro antes despues dif rango_abs
1 35 30 -5 5.0
2 42 35 -7 6.5
3 28 29 1 1.0
4 50 41 -9 9.0
5 38 30 -8 8.0
6 45 38 -7 6.5
7 33 30 -3 2.5
8 40 36 -4 4.0
9 55 44 -11 10.0
10 30 27 -3 2.5
Solución. Datos pareados; \(H_0\): la mediana del cambio es 0; \(H_1\): el tiempo disminuyó.
# Prueba de los signos: 9 negativos y 1 positivo
ver("Diferencias negativas" = sum(dd < 0), "Diferencias positivas" = sum(dd > 0)) Diferencias negativas : 9
Diferencias positivas : 1
--------------------------------------------------------------
Prueba : Prueba de los signos (binomial exacta)
Hipótesis alternativa : unilateral, menor (cola izquierda)
Estadístico : B (signos +) = 1
p-valor : 0.0107
Decisión (alfa = 0.05) : Se rechaza H0
--------------------------------------------------------------
# Wilcoxon: el único positivo (+1) tiene el rango 1, así que W+ = 1
informe(wilcox.test(despues, antes, paired = TRUE, alternative = "less",
exact = FALSE, correct = TRUE),
extra = list("Hodges-Lehmann (min)" = hl_1m(dd)))--------------------------------------------------------------
Prueba : Wilcoxon de rangos con signo (aprox. normal)
Hipótesis alternativa : unilateral, menor (cola izquierda)
Estadístico : V = 1
p-valor : 0.004
Decisión (alfa = 0.05) : Se rechaza H0
Hodges-Lehmann (min) : -5.5
--------------------------------------------------------------
Conclusión: el tiempo de espera se redujo de forma significativa (signos \(p = 0.011\); Wilcoxon \(W^+ = 1\), \(p = 0.004\)). La reducción típica es de unos 5.5 minutos por centro.
con_seguro <- c(120, 85, 60, 150, 95, 70, 110)
sin_seguro <- c(180, 140, 210, 160, 125, 240, 175, 130)
w2 <- wilcox.test(sin_seguro, con_seguro, alternative = "greater", exact = TRUE)
informe(w2, extra = list(
"Correlación biserial r_rb" = 2 * unname(w2$statistic) / (7 * 8) - 1,
"Hodges-Lehmann (S/)" = hl_2m(sin_seguro, con_seguro)))--------------------------------------------------------------
Prueba : U de Mann-Whitney (exacta)
Hipótesis alternativa : unilateral, mayor (cola derecha)
Estadístico : W = 53
p-valor : 0.0011
Decisión (alfa = 0.05) : Se rechaza H0
Correlación biserial r_rb : 0.8929
Hodges-Lehmann (S/) : 67.5
--------------------------------------------------------------
Solución. Dos grupos independientes y muestras pequeñas con gasto asimétrico, así que no podemos verificar la normalidad: usamos Mann-Whitney. Los hogares sin seguro gastan significativamente más (\(U = 53\) de 56 pares posibles, \(p = 0.001\)), con un efecto muy grande (\(r_{rb} = 0.89\)) y una diferencia típica de S/ 67.5 mensuales.
vac <- matrix(c(120, 30, 95, 55, 60, 40), 3, byrow = TRUE,
dimnames = list(Region = c("Costa", "Sierra", "Selva"),
Vacunacion = c("Completa", "Incompleta")))
c3 <- chisq.test(vac, correct = FALSE)
informe(c3, extra = list("V de Cramér" = unname(sqrt(c3$statistic / sum(vac)))))--------------------------------------------------------------
Prueba : Chi-cuadrado de independencia
Hipótesis alternativa : no aplica (cola derecha)
Estadístico : Chi2 = 14.4485
Grados de libertad : 2
p-valor : 7.29e-04
Decisión (alfa = 0.05) : Se rechaza H0
V de Cramér : 0.1901
--------------------------------------------------------------
Vacunacion
Region Completa Incompleta
Costa 103.12 46.88
Sierra 103.12 46.88
Selva 68.75 31.25
Vacunacion
Region Completa Incompleta
Costa 3.76 -3.76
Sierra -1.81 1.81
Selva -2.18 2.18
Vacunacion
Region Completa Incompleta
Costa 0.800 0.200
Sierra 0.633 0.367
Selva 0.600 0.400
Solución. La vacunación completa depende de la región (\(\chi^2 = 14.45\), gl = 2, \(p < 0.001\)), con una asociación débil a moderada (\(V = 0.19\)). La Costa (80 %) supera a la Sierra (63 %) y a la Selva (60 %). Recomendación: priorizar las campañas de vacunación en la Sierra y la Selva.
| Prueba | Estadístico | Distribución bajo \(H_0\) | Tamaño del efecto |
|---|---|---|---|
| Signos | \(B = \#\{D_i > 0\}\) | \(\text{Bin}(n^*, 1/2)\) | proporción de signos + |
| Wilcoxon (signo) | \(W^+ = \sum\) rangos de \(D_i > 0\) | \(E = \frac{n(n+1)}{4}\), \(V = \frac{n(n+1)(2n+1)}{24}\) | \(r = Z/\sqrt{n}\) |
| Mann-Whitney | \(U_1 = R_1 - \frac{n_1(n_1+1)}{2}\) | \(E = \frac{n_1 n_2}{2}\), \(V = \frac{n_1 n_2 (N+1)}{12}\) | \(r_{rb} = \frac{2U_1}{n_1 n_2} - 1\) |
| Chi-cuadrado | \(\sum \frac{(O - E)^2}{E}\), \(E_{ij} = \frac{f_i c_j}{n}\) | \(\chi^2_{(r-1)(c-1)}\) | \(V = \sqrt{\frac{\chi^2}{n(k-1)}}\) |
| Fisher | celda \(a\) de la tabla \(2\times2\) | hipergeométrica | odds ratio |
| Situación | Prueba | Función de R |
|---|---|---|
| Mediana de una muestra, o datos pareados, sin suponer simetría | Signos | binom.test() |
| Una muestra o pares, distribución simétrica | Wilcoxon de rangos con signo | wilcox.test(x, y, paired = TRUE) |
| Dos muestras independientes | Mann-Whitney | wilcox.test(x, y) |
| Dos variables categóricas, esperadas \(\ge 5\) | Chi-cuadrado | chisq.test(tabla) |
| Dos variables categóricas, esperadas \(< 5\) | Fisher exacta | fisher.test(tabla) |
“Se aplicó la prueba [nombre] porque [tipo de datos y diseño; supuesto que no se cumple]. Se obtuvo [estadístico] = [valor], $p = $ [valor], con un tamaño del efecto de [valor] ([pequeño / mediano / grande]). Esto indica que [conclusión en el contexto]. Limitaciones: [tamaño de muestra, diseño, generalización].”