Markdown Estadística descriptiva
Variable cualitativa nominal: Región
Autor: Samantha Pacheco
Fecha: 11/11/2026
library(dplyr)
library(knitr)
library(kableExtra)
load("pollution_2000_2023.RData")
datos <- as.data.frame(pollution_2000_2023)
n <- nrow(datos) # tamaño muestral
n
## [1] 665414
El tamaño muestral es n = 665,414.
Cada registro trae su dirección, ciudad, condado y estado. Para resumir un conjunto de datos tan extenso, las direcciones se agrupan en condados, los condados en estados y los estados en regiones (clasificación del Censo de EE.UU.). La variable que se analiza es la Región.
estado <- trimws(as.character(datos$State))
idx <- match(tolower(estado), tolower(state.name))
Region <- as.character(state.region[idx])
Region <- recode(Region,
"Northeast" = "Noreste",
"South" = "Sur",
"North Central" = "Midwest",
"Midwest" = "Midwest",
"West" = "Oeste")
Region[is.na(Region)] <- "Otra / Exterior" # p. ej. Country Of Mexico
# Cuántas categorías hay en cada nivel de la jerarquía
jerarquia <- data.frame(
Nivel = c("Dirección", "Ciudad", "Condado", "Estado", "Región"),
`Categorías distintas` = c(n_distinct(datos$Address),
n_distinct(paste(datos$City, datos$County, estado)),
n_distinct(paste(datos$County, estado)),
n_distinct(estado),
n_distinct(Region)),
check.names = FALSE
)
kable(jerarquia, align = "lr", format.args = list(big.mark = ",")) %>%
kable_styling(full_width = FALSE)
| Nivel | Categorías distintas |
|---|---|
| Dirección | 221 |
| Ciudad | 181 |
| Condado | 143 |
| Estado | 48 |
| Región | 5 |
t <- table(Region)
conteo <- data.frame(Region = names(t), ni = as.numeric(t),
stringsAsFactors = FALSE)
conteo <- conteo[order(-conteo$ni), ] # de mayor a menor
rownames(conteo) <- NULL
conteo
## Region ni
## 1 Oeste 296702
## 2 Sur 150716
## 3 Noreste 127933
## 4 Midwest 79546
## 5 Otra / Exterior 10517
Los cálculos se hacen con todos los decimales y solo se redondea al mostrar (hi con 4 decimales y %hi con 2 decimales). Por eso los totales dan exactamente n, 1.0000 y 100.00%.
# Tabla con todos los decimales (con esta se construyen los gráficos)
tdf_final_regiones <- conteo
tdf_final_regiones$hi <- tdf_final_regiones$ni / n
tdf_final_regiones$pct_hi <- 100 * tdf_final_regiones$hi
# Verificación de totales
stopifnot(sum(tdf_final_regiones$ni) == n,
isTRUE(all.equal(sum(tdf_final_regiones$hi), 1)),
isTRUE(all.equal(sum(tdf_final_regiones$pct_hi), 100)))
# Presentación (redondeo solo aquí)
fmt <- function(v) format(v, big.mark = ",", scientific = FALSE, trim = TRUE)
salida <- data.frame(
i = c(seq_len(nrow(tdf_final_regiones)), ""),
v = c(tdf_final_regiones$Region, "TOTAL"),
ni = c(fmt(tdf_final_regiones$ni), fmt(sum(tdf_final_regiones$ni))),
hi = sprintf("%.4f", c(tdf_final_regiones$hi, sum(tdf_final_regiones$hi))),
pct = paste0(sprintf("%.2f", c(tdf_final_regiones$pct_hi,
sum(tdf_final_regiones$pct_hi))), "%"),
stringsAsFactors = FALSE
)
names(salida) <- c("i", "Región", "ni", "hi", "%hi")
kable(salida, align = "clrrr", row.names = FALSE,
caption = "Tabla 1. Distribución de frecuencias de la variable Región") %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE) %>%
row_spec(0, background = "#404040", color = "white") %>%
row_spec(nrow(salida), bold = TRUE, background = "#D9D9D9")
| i | Región | ni | hi | %hi |
|---|---|---|---|---|
| 1 | Oeste | 296,702 | 0.4459 | 44.59% |
| 2 | Sur | 150,716 | 0.2265 | 22.65% |
| 3 | Noreste | 127,933 | 0.1923 | 19.23% |
| 4 | Midwest | 79,546 | 0.1195 | 11.95% |
| 5 | Otra / Exterior | 10,517 | 0.0158 | 1.58% |
| TOTAL | 665,414 | 1.0000 | 100.00% |
Fuente: Índice de Calidad del Aire (ICA), EE. UU.,
2000–2023.
Conjunto de datos: https://www.kaggle.com/datasets/guslovesmath/us-pollution-data-200-to-2022/data
Autor: Samantha Pacheco
Nota: “Otra / Exterior” agrupa los registros que no
pertenecen a ninguno de los 50 estados (Distrito de Columbia).
grises <- gray.colors(nrow(tdf_final_regiones), start = 0.25, end = 0.85)
armar_titulo <- function(num, linea2) {
paste0("Gráfica Nro. ", num, "\n", linea2,
"\npara el análisis estadístico de la contaminación de EE. UU.")
}
graf_barras <- function(num, tipo, alcance) {
y <- tdf_final_regiones[[tipo]]
nombres <- tdf_final_regiones$Region
if (tipo == "ni") {
nom <- "absoluta (ni)"
ylab <- "Frecuencia absoluta (ni)"
etq <- format(y, big.mark = ",", scientific = FALSE, trim = TRUE)
tope_g <- n
l_tope <- paste0("n = ", format(n, big.mark = ",", scientific = FALSE))
f_eje <- function(v) format(v, big.mark = ",", scientific = FALSE, trim = TRUE)
} else {
nom <- "relativa (hi)"
ylab <- "Frecuencia relativa (hi)"
etq <- sprintf("%.4f", y)
tope_g <- 1
l_tope <- "Total = 1"
f_eje <- function(v) sprintf("%.1f", v)
}
tope <- if (alcance == "global") tope_g else max(y) * 1.15
par(mar = c(5, 7, 7, 2))
b <- barplot(y, names.arg = nombres, ylim = c(0, tope),
col = grises, border = "black", axes = FALSE, las = 1,
main = armar_titulo(num, paste0("Distribución de la frecuencia ", nom,
" ", alcance, " de la variable Región")),
xlab = "Región", ylab = "", cex.main = 1.05)
ticks <- pretty(c(0, tope))
axis(2, at = ticks, labels = f_eje(ticks), las = 1)
mtext(ylab, side = 2, line = 5)
text(b, y, labels = etq, pos = 3, cex = 0.9, xpd = TRUE)
if (alcance == "global") {
abline(h = tope, lty = 2, col = "gray30")
text(par("usr")[2], tope, labels = l_tope, adj = c(1.05, 1.6), cex = 0.9)
}
}
graf_circular <- function(num) {
par(mar = c(2, 2, 7, 2))
etq <- paste0(sprintf("%.2f", tdf_final_regiones$pct_hi), " %")
pie(tdf_final_regiones$ni, labels = etq, col = grises, border = "black",
clockwise = TRUE, init.angle = 90, radius = 0.85, cex = 1.05,
main = armar_titulo(num, "Distribución circular de la variable Región"),
cex.main = 1.05)
legend("right", legend = tdf_final_regiones$Region, fill = grises,
title = "Leyenda", bty = "o", cex = 1)
}
# ---- Versión con hi en porcentaje (reemplaza a la anterior) ----
graf_barras <- function(num, tipo, alcance) {
y <- tdf_final_regiones[[tipo]]
nombres <- tdf_final_regiones$Region
if (tipo == "ni") {
nom <- "absoluta (ni)"
ylab <- "Frecuencia absoluta (ni)"
etq <- format(y, big.mark = ",", scientific = FALSE, trim = TRUE)
tope_g <- n
l_tope <- paste0("n = ", format(n, big.mark = ",", scientific = FALSE))
f_eje <- function(v) format(v, big.mark = ",", scientific = FALSE, trim = TRUE)
} else {
y <- tdf_final_regiones$pct_hi # hi expresado en porcentaje
nom <- "relativa porcentual (%hi)"
ylab <- "Frecuencia relativa porcentual (%hi)"
etq <- paste0(sprintf("%.2f", y), "%")
tope_g <- 100
l_tope <- "Total = 100%"
f_eje <- function(v) paste0(v, "%")
}
tope <- if (alcance == "global") tope_g else max(y) * 1.15
par(mar = c(5, 7, 7, 2))
b <- barplot(y, names.arg = nombres, ylim = c(0, tope),
col = grises, border = "black", axes = FALSE, las = 1,
main = armar_titulo(num, paste0("Distribución de la frecuencia ", nom,
" ", alcance, " de la variable Región")),
xlab = "Región", ylab = "", cex.main = 1.05)
ticks <- pretty(c(0, tope))
axis(2, at = ticks, labels = f_eje(ticks), las = 1)
mtext(ylab, side = 2, line = 5)
text(b, y, labels = etq, pos = 3, cex = 0.9, xpd = TRUE)
if (alcance == "global") {
abline(h = tope, lty = 2, col = "gray30")
text(par("usr")[2], tope, labels = l_tope, adj = c(1.05, 1.6), cex = 0.9)
}
}
graf_barras(1, "ni", "local")
graf_barras(2, "ni", "global")
graf_barras(3, "hi", "local")
graf_barras(4, "hi", "global")
graf_circular(5)
Al ser una variable cualitativa nominal, la única medida de tendencia central aplicable es la moda. La media, la mediana y las medidas de posición (cuantiles) requieren valores numéricos u ordenados, por lo que no se pueden calcular.
max_ni <- max(tdf_final_regiones$ni)
modas <- tdf_final_regiones$Region[tdf_final_regiones$ni == max_ni]
moda <- modas[1]
kable(data.frame(Medida = "Moda",
Región = paste(modas, collapse = " ; "),
ni = fmt(max_ni),
`%hi` = paste0(sprintf("%.2f", 100 * max_ni / n), "%"),
check.names = FALSE),
align = "llrr") %>%
kable_styling(full_width = FALSE)
| Medida | Región | ni | %hi |
|---|---|---|---|
| Moda | Oeste | 296,702 | 44.59% |