Objetivo. Documento listo para RPubs con: (1) Bondad de ajuste para un dado (uniforme), (2) Bondad de ajuste a Normal con clases por intervalos, (3) Independencia en 2×3 (Opinión×Ingreso). Incluye tablas, gráficos e interpretaciones tipo APA.
Nota: Se evitaron referencias inline a objetos para prevenir errores de knit; todas las conclusiones se imprimen dentro de chunks ejecutables.
# Paquetes
instalar_si_falta <- function(pkgs){
falta <- pkgs[!pkgs %in% rownames(installed.packages())]
if(length(falta)) install.packages(falta, dependencies = TRUE)
}
instalar_si_falta(c("ggplot2","dplyr","tidyr","scales","knitr"))
suppressPackageStartupMessages({
library(ggplot2); library(dplyr); library(tidyr); library(scales); library(knitr)
})
theme_set(theme_minimal(base_size = 12))
set.seed(123)
Planteo del problema. Se tira un dado legal 120 veces. Bajo \(H_0\) las probabilidades son uniformes \(p_i=1/6\). Se observan las frecuencias: 20, 22, 17, 18, 19, 24.
Hipótesis.
- \(H_0\): \(P(1)=\cdots=P(6)=1/6\) (dado justo).
- \(H_1\): al menos una probabilidad
difiere.
Estadístico. \(\chi^2=\sum (o_i-e_i)^2/e_i\); \(gl=k-1=5\).
# Datos y test
obs <- c(20, 22, 17, 18, 19, 24)
esp <- rep(20, 6)
chisq_test <- chisq.test(x = obs, p = rep(1/6, 6))
# Cálculo manual y p-valor
chi2_calc <- sum((obs - esp)^2 / esp)
gl <- length(obs) - 1
p_value <- pchisq(chi2_calc, df = gl, lower.tail = FALSE)
# Tabla obs–esp
data.frame(Cara = 1:6, Observada = obs, Esperada = esp) |>
kable(digits = 1, caption = "Dado: frecuencias observadas y esperadas")
| Cara | Observada | Esperada |
|---|---|---|
| 1 | 20 | 20 |
| 2 | 22 | 20 |
| 3 | 17 | 20 |
| 4 | 18 | 20 |
| 5 | 19 | 20 |
| 6 | 24 | 20 |
# Conclusión en formato APA
cat(sprintf("\n**Conclusión (APA):** χ²(%d) = %.2f, p = %.3f.\n", gl, chi2_calc, p_value))
##
## **Conclusión (APA):** χ²(5) = 1.70, p = 0.889.
if(p_value >= 0.05){
cat("No se rechaza H0: el dado se comporta como uniforme.\n")
} else {
cat("Se rechaza H0: evidencia de dado no uniforme.\n")
}
## No se rechaza H0: el dado se comporta como uniforme.
Gráfico.
df <- data.frame(cara=factor(1:6), Observada=obs, Esperada=esp)
df_long <- tidyr::pivot_longer(df, -cara, names_to="Tipo", values_to="Frecuencia")
ggplot(df_long, aes(cara, Frecuencia, fill=Tipo)) +
geom_col(position="dodge") +
labs(title="Bondad de ajuste: dado justo", x="Cara", y="Frecuencia") +
theme_minimal()
Planteo del problema. Se miden 40 duraciones de baterías. Se propone \(X\sim N(\mu=3.5,\sigma=0.7)\) y se agrupan observaciones en clases de 0.5 unidades entre 1.45 y 4.95.
Hipótesis.
- \(H_0\): la población sigue \(N(3.5,0.7^2)\).
- \(H_1\): no sigue esa Normal.
set.seed(123)
x <- rnorm(40, mean = 3.5, sd = 0.7)
breaks <- seq(1.45, 4.95, by = 0.5)
oi <- as.numeric(table(cut(x, breaks, include.lowest = TRUE)))
ei <- diff(pnorm(breaks, mean = 3.5, sd = 0.7)) * 40
tabla_norm <- data.frame(Intervalo = paste(head(breaks,-1), tail(breaks,-1), sep="-"),
Observadas = oi, Esperadas = round(ei,1))
kable(tabla_norm, caption = "Frecuencias por clase: observadas y esperadas (Normal teórica)")
| Intervalo | Observadas | Esperadas |
|---|---|---|
| 1.45-1.95 | 0 | 0.5 |
| 1.95-2.45 | 2 | 2.1 |
| 2.45-2.95 | 4 | 6.0 |
| 2.95-3.45 | 12 | 10.2 |
| 3.45-3.95 | 11 | 10.7 |
| 3.95-4.45 | 8 | 6.9 |
| 4.45-4.95 | 3 | 2.7 |
# Advertencia sobre esperadas
if(any(ei < 5)){
cat("\n**Aviso:** hay esperadas < 5; combinar clases contiguas antes de aplicar χ² para garantizar validez.\n")
}
##
## **Aviso:** hay esperadas < 5; combinar clases contiguas antes de aplicar χ² para garantizar validez.
# Test (equivalente a usar ei/sum(ei) como probabilidades teóricas)
gof_norm <- chisq.test(oi, p = ei/sum(ei))
gof_norm
##
## Chi-squared test for given probabilities
##
## data: oi
## X-squared = 1.5893, df = 6, p-value = 0.9533
# Conclusión APA
cat(sprintf("\n**Conclusión (APA):** χ²(%d) = %.2f, p = %.3f.\n",
gof_norm$parameter, gof_norm$statistic, gof_norm$p.value))
##
## **Conclusión (APA):** χ²(6) = 1.59, p = 0.953.
if(gof_norm$p.value >= 0.05){
cat("No se rechaza H0: la Normal propuesta ajusta razonablemente los datos.\n")
} else {
cat("Se rechaza H0: la Normal propuesta no explica adecuadamente las frecuencias.\n")
}
## No se rechaza H0: la Normal propuesta ajusta razonablemente los datos.
Planteo. ¿Son independientes la Opinión sobre una reforma fiscal (A favor / En contra) y el Nivel de ingreso (Bajo / Medio / Alto) en una muestra de 1000 votantes?
Hipótesis.
- \(H_0\): Opinión e Ingreso son
independientes.
- \(H_1\): Existe asociación entre
ambas.
tabla <- matrix(c(182,213,203,154,138,110), nrow=2, byrow=TRUE)
colnames(tabla) <- c("Bajo","Medio","Alto")
rownames(tabla) <- c("A favor","En contra")
kable(as.data.frame.matrix(tabla), caption = "Tabla 2×3: Opinión × Ingreso", align = "r")
| Bajo | Medio | Alto | |
|---|---|---|---|
| A favor | 182 | 213 | 203 |
| En contra | 154 | 138 | 110 |
chisq_indep <- chisq.test(tabla)
chisq_indep
##
## Pearson's Chi-squared test
##
## data: tabla
## X-squared = 7.8782, df = 2, p-value = 0.01947
chisq_indep$expected
## Bajo Medio Alto
## A favor 200.928 209.898 187.174
## En contra 135.072 141.102 125.826
# Tamaño de efecto (V de Cramér)
cramers_v <- function(tab){
chi2 <- suppressWarnings(chisq.test(tab)$statistic)
n <- sum(tab); r <- nrow(tab); c <- ncol(tab)
as.numeric(sqrt(chi2 / (n * (min(r,c)-1))))
}
V <- cramers_v(tabla)
# Conclusión APA
cat(sprintf("\n**Conclusión (APA):** χ²(%d) = %.2f, p = %.3f; V de Cramér = %.3f; n = %d.\n",
chisq_indep$parameter, chisq_indep$statistic, chisq_indep$p.value, V, sum(tabla)))
##
## **Conclusión (APA):** χ²(2) = 7.88, p = 0.019; V de Cramér = 0.089; n = 1000.
Gráfico de proporciones por fila.
prop.table(tabla, margin=1) |>
as.data.frame.table() |>
ggplot(aes(x=Var2, y=Freq, fill=Var1)) +
geom_col(position="dodge") +
labs(
title="Opinión sobre reforma fiscal por nivel de ingreso",
x="Nivel de ingreso",
y="Proporción dentro de cada grupo",
fill="Opinión"
) +
coord_cartesian(ylim = c(0, 1)) +
theme_minimal()
Diagnóstico por celdas (post‑hoc con residuales).
res_std <- chisq_indep$stdres
p_res <- 2 * pnorm(-abs(res_std))
p_adj <- p.adjust(p_res, method = "bonferroni")
list(
ResidualesEstandarizados = round(res_std, 2),
P_sin_ajuste = round(p_res, 4),
P_ajustada_Bonferroni = round(matrix(p_adj, nrow = nrow(tabla), dimnames = dimnames(tabla)), 4)
)
## $ResidualesEstandarizados
## Bajo Medio Alto
## A favor -2.58 0.42 2.2
## En contra 2.58 -0.42 -2.2
##
## $P_sin_ajuste
## Bajo Medio Alto
## A favor 0.0098 0.6751 0.0277
## En contra 0.0098 0.6751 0.0277
##
## $P_ajustada_Bonferroni
## Bajo Medio Alto
## A favor 0.0585 1 0.1663
## En contra 0.0585 1 0.1663
# Bondad de ajuste (uniforme)
obs <- c(20,22,17,18,19,24); esp <- rep(20,6)
chisq.test(obs, p = rep(1/6,6))
chi2_calc <- sum((obs - esp)^2 / esp); gl <- length(obs)-1
p_value <- pchisq(chi2_calc, df=gl, lower.tail=FALSE)
cat(sprintf("χ²(%d)=%.2f, p=%.3f\n", gl, chi2_calc, p_value))
# GoF a Normal con clases
set.seed(123); x <- rnorm(40, 3.5, 0.7)
breaks <- seq(1.45, 4.95, 0.5)
oi <- as.numeric(table(cut(x, breaks, include.lowest=TRUE)))
ei <- diff(pnorm(breaks, 3.5, 0.7)) * 40
chisq.test(oi, p = ei/sum(ei))
# Independencia 2×3
tabla <- matrix(c(182,213,203,154,138,110), 2, byrow=TRUE)
colnames(tabla) <- c("Bajo","Medio","Alto"); rownames(tabla) <- c("A favor","En contra")
chisq_indep <- chisq.test(tabla)
cramers_v <- function(tab){ chi2 <- suppressWarnings(chisq.test(tab)$statistic)
n <- sum(tab); r <- nrow(tab); c <- ncol(tab); sqrt(chi2 / (n * (min(r,c)-1))) }
V <- cramers_v(tabla)
cat(sprintf("χ²(%d)=%.2f, p=%.3f; V=%.3f\n", chisq_indep$parameter, chisq_indep$statistic, chisq_indep$p.value, V))