Este documento presenta un análisis integral de datos enfocado en técnicas estadísticas y visualización. A lo largo de mi trayectoria como analista, he trabajado con diversos conjuntos de datos donde la combinación de métodos descriptivos e inferenciales permite extraer insights valiosos para la toma de decisiones.
El objetivo aquí es mostrar un flujo de trabajo completo: desde la exploración inicial hasta la aplicación de modelos estadísticos, siempre manteniendo un enfoque práctico y orientado a resultados.
Vamos a trabajar con el conjunto de datos mtcars, un
clásico que nos permite demostrar técnicas fundamentales. Aunque es un
dataset conocido, la forma en que lo abordamos es lo que marca la
diferencia en un análisis profesional.
# Cargamos y preparamos los datos
datos <- mtcars %>%
mutate(
transmission = factor(am, levels = c(0, 1), labels = c("Automática", "Manual")),
cylinders = factor(cyl),
efficiency_class = cut(mpg,
breaks = c(0, 15, 20, 25, Inf),
labels = c("Baja", "Media", "Alta", "Muy Alta"))
)
# Vista preliminar
head(datos) %>%
kable(caption = "Primeras observaciones del dataset") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | transmission | cylinders | efficiency_class | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 | Manual | 6 | Alta |
| Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 | Manual | 6 | Alta |
| Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 | Manual | 4 | Alta |
| Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 | Automática | 6 | Alta |
| Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 | Automática | 8 | Media |
| Valiant | 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 | Automática | 6 | Media |
Una buena práctica es comenzar con un resumen estadístico que nos dé una visión general de la distribución de nuestras variables.
# Resumen de variables numéricas clave
resumen <- datos %>%
select(mpg, hp, wt, qsec) %>%
summarise(across(everything(),
list(Media = mean,
Mediana = median,
DE = sd,
Min = min,
Max = max))) %>%
pivot_longer(everything(),
names_to = c("Variable", "Estadístico"),
names_sep = "_") %>%
pivot_wider(names_from = Estadístico, values_from = value)
resumen %>%
kable(digits = 2, caption = "Estadísticos descriptivos principales") %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| Variable | Media | Mediana | DE | Min | Max |
|---|---|---|---|---|---|
| mpg | 20.09 | 19.20 | 6.03 | 10.40 | 33.90 |
| hp | 146.69 | 123.00 | 68.56 | 52.00 | 335.00 |
| wt | 3.22 | 3.33 | 0.98 | 1.51 | 5.42 |
| qsec | 17.85 | 17.71 | 1.79 | 14.50 | 22.90 |
La visualización es fundamental para entender patrones que las estadísticas por sí solas no revelan. Aquí muestro algunas técnicas que uso frecuentemente en mi trabajo.
# Densidad con anotaciones estadísticas
media_mpg <- mean(datos$mpg)
mediana_mpg <- median(datos$mpg)
ggplot(datos, aes(x = mpg)) +
geom_histogram(aes(y = ..density..), bins = 12,
fill = "#3498db", alpha = 0.7, color = "white") +
geom_density(color = "#2c3e50", size = 1.2) +
geom_vline(xintercept = media_mpg, linetype = "dashed",
color = "#e74c3c", size = 1) +
geom_vline(xintercept = mediana_mpg, linetype = "dotted",
color = "#27ae60", size = 1) +
annotate("text", x = media_mpg + 2, y = 0.06,
label = paste("Media:", round(media_mpg, 2)),
color = "#e74c3c", size = 4) +
annotate("text", x = mediana_mpg - 2, y = 0.065,
label = paste("Mediana:", round(mediana_mpg, 2)),
color = "#27ae60", size = 4) +
labs(title = "Distribución de Eficiencia de Combustible",
subtitle = "Millas por galón (MPG) en la muestra",
x = "MPG", y = "Densidad") +
theme_custom
La distribución muestra una ligera asimetría hacia la derecha, con algunos vehículos de alta eficiencia que funcionan como outliers positivos.
# Boxplot comparativo con puntos individuales
ggplot(datos, aes(x = transmission, y = mpg, fill = transmission)) +
geom_boxplot(alpha = 0.6, outlier.shape = NA) +
geom_jitter(width = 0.2, alpha = 0.5, size = 2.5) +
scale_fill_manual(values = c("#e67e22", "#9b59b6")) +
stat_summary(fun = mean, geom = "point", shape = 23,
size = 4, fill = "yellow", color = "black") +
labs(title = "Eficiencia según Tipo de Transmisión",
subtitle = "El rombo amarillo indica la media del grupo",
x = "Tipo de Transmisión",
y = "Millas por Galón (MPG)") +
theme_custom +
theme(legend.position = "none")
Vemos claramente que los vehículos con transmisión manual tienden a ser más eficientes. Este es un hallazgo que amerita una prueba estadística formal.
Entender las relaciones entre variables es crucial. Voy a explorar esto desde múltiples ángulos.
# Matriz de correlación
variables_num <- datos %>%
select(mpg, cyl, disp, hp, drat, wt, qsec, vs, am, gear, carb)
matriz_cor <- cor(variables_num)
# Visualización elegante
corrplot(matriz_cor, method = "color", type = "upper",
order = "hclust",
addCoef.col = "black",
number.cex = 0.7,
tl.col = "black", tl.srt = 45,
col = colorRampPalette(c("#d73027", "white", "#4575b4"))(200),
title = "Matriz de Correlaciones - Variables del Vehículo",
mar = c(0,0,2,0))
Las correlaciones más fuertes son las esperadas: peso (wt) y cilindros (cyl) tienen correlación negativa fuerte con MPG. El desplazamiento del motor (disp) y la potencia (hp) también muestran asociaciones fuertes entre sí.
# Scatter plot con regresión y bandas de confianza
ggplot(datos, aes(x = wt, y = mpg)) +
geom_point(aes(color = cylinders, size = hp), alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "#34495e",
fill = "#95a5a6", alpha = 0.3, size = 1.2) +
scale_color_brewer(palette = "Set1", name = "Cilindros") +
scale_size_continuous(name = "Potencia (HP)", range = c(3, 10)) +
labs(title = "Relación entre Peso y Eficiencia de Combustible",
subtitle = "El tamaño de los puntos representa la potencia del motor",
x = "Peso (1000 lbs)",
y = "Millas por Galón (MPG)") +
theme_custom
La relación lineal negativa es evidente: a mayor peso, menor eficiencia. Los vehículos de 4 cilindros (en rojo) se agrupan en la zona de bajo peso y alta eficiencia.
Ahora vamos a formalizar algunas de las observaciones visuales con pruebas estadísticas.
# Prueba t de dos muestras
test_resultado <- t.test(mpg ~ transmission, data = datos, var.equal = FALSE)
# Presentación de resultados
cat("Prueba t de Welch\n")
## Prueba t de Welch
cat("=====================================\n")
## =====================================
cat("H0: Las medias de MPG son iguales entre transmisiones\n")
## H0: Las medias de MPG son iguales entre transmisiones
cat("H1: Las medias de MPG son diferentes\n\n")
## H1: Las medias de MPG son diferentes
cat("Estadístico t:", round(test_resultado$statistic, 3), "\n")
## Estadístico t: -3.767
cat("Grados de libertad:", round(test_resultado$parameter, 2), "\n")
## Grados de libertad: 18.33
cat("Valor p:", format.pval(test_resultado$p.value, digits = 3), "\n")
## Valor p: 0.00137
cat("IC 95%: [", round(test_resultado$conf.int[1], 2), ",",
round(test_resultado$conf.int[2], 2), "]\n")
## IC 95%: [ -11.28 , -3.21 ]
Con un valor p < 0.05, rechazamos la hipótesis nula. Hay evidencia estadística suficiente para afirmar que el tipo de transmisión está asociado con diferencias significativas en la eficiencia de combustible.
# ANOVA de un factor
modelo_anova <- aov(hp ~ cylinders, data = datos)
summary(modelo_anova)
## Df Sum Sq Mean Sq F value Pr(>F)
## cylinders 2 104031 52015 36.18 1.32e-08 ***
## Residuals 29 41696 1438
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Comparaciones post-hoc
tukey_result <- TukeyHSD(modelo_anova)
# Visualización de intervalos de confianza Tukey
tukey_df <- as.data.frame(tukey_result$cylinders)
tukey_df$comparison <- rownames(tukey_df)
ggplot(tukey_df, aes(x = comparison, y = diff)) +
geom_point(size = 4, color = "#e74c3c") +
geom_errorbar(aes(ymin = lwr, ymax = upr), width = 0.2, size = 1) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
coord_flip() +
labs(title = "Comparaciones Múltiples de Tukey",
subtitle = "Diferencias en potencia (HP) entre grupos de cilindros",
x = "Comparación entre Grupos",
y = "Diferencia de Medias (IC 95%)") +
theme_custom
Todas las comparaciones son significativas: cada aumento en cilindros está asociado con un incremento sustancial en la potencia del motor.
Finalmente, construyo un modelo de regresión lineal múltiple para predecir la eficiencia.
# Modelo multivariado
modelo <- lm(mpg ~ wt + hp + am + cyl, data = datos)
# Resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = mpg ~ wt + hp + am + cyl, data = datos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4765 -1.8471 -0.5544 1.2758 5.6608
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 36.14654 3.10478 11.642 4.94e-12 ***
## wt -2.60648 0.91984 -2.834 0.0086 **
## hp -0.02495 0.01365 -1.828 0.0786 .
## am 1.47805 1.44115 1.026 0.3142
## cyl -0.74516 0.58279 -1.279 0.2119
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.509 on 27 degrees of freedom
## Multiple R-squared: 0.849, Adjusted R-squared: 0.8267
## F-statistic: 37.96 on 4 and 27 DF, p-value: 1.025e-10
# Gráficos de diagnóstico
par(mfrow = c(2, 2))
plot(modelo, which = c(1, 2, 3, 5), col = "#3498db", pch = 19)
par(mfrow = c(1, 1))
El modelo explica aproximadamente el 84% de la varianza en MPG (R² ajustado). Los residuos muestran un comportamiento razonablemente normal, aunque hay algunos puntos influyentes que podrían investigarse más a fondo.
# Predicciones vs valores reales
datos$prediccion <- predict(modelo)
ggplot(datos, aes(x = mpg, y = prediccion)) +
geom_point(aes(color = transmission), size = 3, alpha = 0.7) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed",
color = "gray30", size = 1) +
scale_color_manual(values = c("#e67e22", "#9b59b6"),
name = "Transmisión") +
labs(title = "Valores Observados vs Predichos",
subtitle = "Línea de referencia indica predicción perfecta",
x = "MPG Observado",
y = "MPG Predicho") +
theme_custom
Este análisis demuestra un flujo de trabajo completo desde la exploración hasta el modelado:
En mi experiencia, este tipo de análisis iterativo—donde alternamos entre visualización, estadística descriptiva e inferencial—es lo que realmente genera valor en proyectos de ciencia de datos. No se trata solo de aplicar técnicas, sino de contar una historia coherente con los datos.
sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] kableExtra_1.4.0 knitr_1.50 RColorBrewer_1.1-3 gridExtra_2.3
## [5] corrplot_0.95 scales_1.4.0 lubridate_1.9.4 forcats_1.0.1
## [9] stringr_1.5.2 dplyr_1.1.4 purrr_1.1.0 readr_2.1.5
## [13] tidyr_1.3.1 tibble_3.3.0 ggplot2_4.0.0 tidyverse_2.0.0
##
## loaded via a namespace (and not attached):
## [1] sass_0.4.10 generics_0.1.4 xml2_1.4.0 lattice_0.22-7
## [5] stringi_1.8.7 hms_1.1.4 digest_0.6.37 magrittr_2.0.4
## [9] evaluate_1.0.5 grid_4.5.1 timechange_0.3.0 fastmap_1.2.0
## [13] Matrix_1.7-3 jsonlite_2.0.0 mgcv_1.9-3 viridisLite_0.4.2
## [17] textshaping_1.0.4 jquerylib_0.1.4 cli_3.6.5 rlang_1.1.6
## [21] splines_4.5.1 withr_3.0.2 cachem_1.1.0 yaml_2.3.10
## [25] tools_4.5.1 tzdb_0.5.0 vctrs_0.6.5 R6_2.6.1
## [29] lifecycle_1.0.4 pkgconfig_2.0.3 pillar_1.11.1 bslib_0.9.0
## [33] gtable_0.3.6 glue_1.8.0 systemfonts_1.3.1 xfun_0.53
## [37] tidyselect_1.2.1 rstudioapi_0.17.1 farver_2.1.2 nlme_3.1-168
## [41] htmltools_0.5.8.1 rmarkdown_2.30 svglite_2.2.1 labeling_0.4.3
## [45] compiler_4.5.1 S7_0.2.0