El presente informe detalla el protocolo de limpieza y preprocesamiento aplicado al dataset Boston Housing. El objetivo es garantizar la integridad de los datos mediante la validación de reglas de negocio, imputación multivariada y detección de observaciones influyentes, asegurando un dataset robusto para modelos predictivos.
Al revisar el dataset, la única variable identificada como “chr” o de tipo caracter fue “chas”. esta variable presentaba múltiples representaciones para una misma categoría lógica, por lo que para garantizar la coherencia del análisis, se implementó un protocolo de estandarización basado en los siguientes criterios:
Conversión de todos los registros a minúsculas para eliminar posibles duplicidades.
Recodificación de múltiples etiquetas (“yes”, “y”, “true”, “river”, “no”, “n”, “false”) hacia una estructura binaria de 0 y 1.
Identificación de valores indeterminados como “??” y su posterior conversión a valores faltantes (NA), permitiendo su tratamiento posterior mediante técnicas de imputación.
Conversión de la variable a tipo factor para su correcta interpretación.
A continuación, se presenta la implementación técnica de este procedimiento:
datos_original <- datos #Guardar una copia original antes de la limpieza
datos$chas <- tolower(datos$chas) #Convertir a minúsculas para evitar inconsistencias
datos$chas <- dplyr::recode(datos$chas, "yes" = "1", "no" = "0", "n"= "0","y"= "1", "true" = "1", "false" = "0", "river" = "1") #Reemplazar valores inconsistentes
datos$chas[datos$chas == "??"] <- NA #Convertir "??" a NA
datos$chas <- as.factor(datos$chas) #Convertir a factor
Tras la normalización de variables cualitativas, el proceso luego se centró en la validación de las variables cuantitativas. Para ello, se implementó un sistema de reglas lógicas utilizando la librería validate, permitiendo identificar registros que contradicen el comportamiento natural del mercado inmobiliario o presentan fallos de captura.
Se establecieron diecisiete reglas de validación que abarcan rangos numéricos, pertenencia a conjuntos y relaciones de integridad específicas. Un punto crítico del análisis es la regla de condicionalidad: en el contexto de Boston, se determinó que si la tasa de impuestos (tax) es superior a 700, es estadísticamente improbable que el valor de la vivienda (medv) supere los 40 mil dólares.
Rules_Boston
## Object of class 'validator' with 17 elements:
## V01: crim >= 0
## V02: zn >= 0
## V03: indus >= 0
## V04: nox > 0
## V05: rm > 0
## V06: rm < 15
## V07: age > 0
## V08: dis > 0
## V09: rad >= 1
## V10: tax > 0
## V11: ptratio > 0
## V12: black >= 0
## V13: lstat >= 0
## V14: medv > 0
## V15: medv <= 100
## V16: chas %in% c(0, 1)
## V17: tax <= 700 | (medv < 40)
Utilizando el algoritmo de localización de errores de la librería errorlocate, se identificaron las celdas específicas que violaban las reglas predefinidas. En lugar de eliminar filas completas, ya que esto reduciría drásticamente el tamaño de la muestra, se optó por una estrategia de depuración selectiva: las celdas erróneas se convirtieron en valores faltantes (NA).
loc <- locate_errors(datos, Rules_Boston)
errores <- validate::values(loc)
datos$crim[errores[, "crim"]] <- NA
datos$zn[errores[, "zn"]] <- NA
datos$indus[errores[, "indus"]] <- NA
datos$chas[errores[, "chas"]] <- NA
datos$nox[errores[, "nox"]] <- NA
datos$rm[errores[, "rm"]] <- NA
datos$age[errores[, "age"]] <- NA
datos$dis[errores[, "dis"]] <- NA
datos$rad[errores[, "rad"]] <- NA
datos$tax[errores[, "tax"]] <- NA
datos$ptratio[errores[, "ptratio"]] <- NA
datos$black[errores[, "black"]] <- NA
datos$lstat[errores[, "lstat"]] <- NA
datos$medv[errores[, "medv"]] <- NA
Tras la depuración de inconsistencias lógicas, el dataset presenta una estructura de datos faltantes inducida por el proceso de limpieza. Es fundamental cuantificar esta ausencia para determinar la viabilidad de la muestra.
Se empleó la librería naniar para identificar la magnitud del vacío de información. El diagnóstico permite distinguir entre registros completos (datos íntegros) y registros con presencia de NA, los cuales requieren obligatoriamente una fase de imputación para evitar el sesgo por eliminación de casos. Se observa que del total de datos, 1.3% son datos faltantes (84 registros), presentándose en 6 variables esta ausencia de datos.
naniar::vis_miss(datos) +
theme(axis.text.x = element_text(size = 10, angle = 60, hjust = 1))
Mediante una inspección secuencial, se identificó que los datos faltantes se distribuyen de manera aleatoria a lo largo de los registros, lo que sugiere que no hay un sesgo de captura sistemático. Sin embargo, los histogramas de variables como rm (habitaciones) y medv (valor de la vivienda) muestran que la eliminación de registros inconsistentes alteró levemente la forma de las distribuciones originales, creando “huecos” que deben ser reconstruidos para recuperar la continuidad de la información.
Para evaluar el impacto de los datos faltantes antes de la imputación, se desarrolló la función integral Vis_Boston.El primer gráfico identifica la ubicación de los NA por índice de registro, confirmando una distribución aleatoria que valida la viabilidad de la imputación. Los histogramas permiten monitorear cómo la eliminación de inconsistencias afectó la forma de las variables, asegurando que no se pierdan las tendencias centrales de los datos de Boston. Los diagramas de dispersión y la matriz de correlación verifican que las asociaciones críticas (como la relación inversa entre lstat y medv) sigan presentes a pesar de la ausencia de datos.
Vis_Boston(datos)
El objetivo de esta fase es recuperar la integridad del dataset mediante la estimación de los valores faltantes generados en la etapa anterior. Se evaluaron cuatro metodologías para determinar cuál preserva mejor la variabilidad y las relaciones entre variables.
Se implementaron y compararon los siguientes algoritmos:
La variable crítica rm (Número de habitaciones) se utilizó como indicador de desempeño. El gráfico de densidad permite visualizar el impacto de cada método:
datos_plot$Metodo <- factor(
datos_plot$Metodo,
levels = c("Original (con NA)", "Media", "Regresión", "KNN", "PMM")
)
ggplot(datos_plot, aes(x = rm)) +
geom_density(fill = "steelblue", alpha = 0.4, color = "black") +
facet_wrap(~Metodo, ncol = 2) +
theme_bw() +
labs(
title = "Comparación de distribuciones por método de imputación",
subtitle = "Efecto de la imputación sobre la variable rm (Habitaciones)",
x = "rm",
y = "Densidad"
)
Tras analizar la matriz de correlación y los histogramas de la función
Vis_Boston, se seleccionó el método PMM. A diferencia de la media o la
regresión simple, el PMM preserva la variabilidad y el comportamiento
natural de la distribución original y no genera valores fuera de los
rangos lógicos, garantizando que el dataset imputado sea
estadísticamente coherente con la realidad inmobiliaria observada.
Una vez imputado el dataset mediante PMM, se procedió a identificar observaciones extremas que podrían distorsionar los parámetros de los modelos estadísticos. Este análisis se dividió en dos fases: univariada y multivariada.
Para la variable objetivo (medv), se contrastaron dos criterios: la estandarización (Z-score) y el método robusto de Tukey (Boxplot). El diagnóstico visual revela que, aunque la mayoría de los datos siguen una distribución coherente, existen registros en las colas que superan las 3 desviaciones estándar o los límites de los bigotes del boxplot.
old_par <- par(no.readonly = TRUE)
on.exit(par(old_par), add = TRUE)
par(mfrow = c(3, 1), mar = c(4, 4, 2, 1))
hist(datos_final$medv,
freq = FALSE,
col = "lightblue",
main = "Histograma de medv (Precio)",
xlab = "medv")
boxplot(datos_final$medv,
horizontal = TRUE,
col = "lightblue",
main = "Boxplot de medv")
hist(scale(datos_final$medv),
freq = FALSE,
col = "lightblue",
main = "Histograma estandarizado de medv",
xlab = "z-score")
Para identificar observaciones extremas en la variable objetivo (medv), se aplicaron dos metodologías con distintos niveles de sensibilidad. El Z-Score se utilizó como criterio paramétrico, señalando el índice de los registros que se alejan más de 3 desviaciones estándar de la media. Por otro lado, el Criterio de Tukey se empleó como una medida robusta basada en el rango intercuartílico, permitiendo localizar valores atípicos incluso en distribuciones no normales.
#Identificación por Z-Score
id_z <- which(abs(scale(datos_final$medv)) > 3)
id_z
## [1] 465
# Identificación por Tukey}
out_tukey <- boxplot.stats(datos_final$medv)$out
id_tukey <- which(datos_final$medv %in% out_tukey)
id_tukey
## [1] 98 99 158 162 163 164 167 181 183 187 196 203 204 205 225 226 227 229 233
## [20] 234 254 257 258 262 263 268 269 281 283 284 369 370 371 372 373 465
Con el fin de estandarizar el proceso de limpieza y evitar la redundancia de código, se desarrolló la función id_out_uni. Esta herramienta permite alternar entre los métodos de zscore y tukey para cualquier variable numérica del dataset, facilitando la identificación de índices atípicos de manera ágil y reproducible.
id_out_uni <- function(x, method = c("zscore", "tukey"), z_cut = 3) {
method <- match.arg(method)
if (!is.numeric(x)) stop("La variable debe ser numérica.")
if (method == "zscore") {
id_out <- which(abs(scale(x)) > z_cut)
} else if (method == "tukey") {
id_out <- which(x %in% boxplot.stats(x)$out)
}
return(id_out)
}
El análisis se extendió de forma masiva a las 13 dimensiones numéricas del estudio. Mediante el uso de lapply y una configuración de cuadrícula dinámica, se generaron boxplots para todas las variables, permitiendo una inspección visual rápida de la “contaminación” por atípicos en cada dimensión
par(mfrow = c(n_row, 4), mar = c(2, 2, 2, 1))
invisible(lapply(names(datos_num_final), function(x) {
boxplot(datos_num_final[[x]], main = x, col = "lightblue", horizontal = TRUE)
}))
Para obtener una visión integral de la calidad del dataset, se
implementó un procedimiento de identificación automática en todas las
dimensiones numéricas. Utilizando la familia de funciones lapply, se
aplicaron los dos métodos de detección (Z-score y Tukey) de forma
simultánea a cada columna, permitiendo una comparación sistemática de la
sensibilidad de cada criterio.
# Identificación automática de valores atípicos en todo el dataset
out_zscore <- lapply(datos_num_final, id_out_uni, method = "zscore")
out_tukey <- lapply(datos_num_final, id_out_uni, method = "tukey")
# Resumen: comparación de métodos
resumen_uni <- data.frame(
Variable = names(datos_num_final),
Zscore_3sd = sapply(out_zscore, length),
Tukey_Boxplot = sapply(out_tukey, length)
)
resumen_uni
## Variable Zscore_3sd Tukey_Boxplot
## crim crim 9 65
## zn zn 14 68
## indus indus 0 0
## nox nox 5 5
## rm rm 9 29
## age age 0 0
## dis dis 5 5
## rad rad 0 0
## tax tax 0 0
## ptratio ptratio 0 17
## black black 25 76
## lstat lstat 3 6
## medv medv 1 36
El análisis multivariado comienza evaluando cómo las variables se relacionan entre sí para identificar patrones de colinealidad o agrupamientos inusuales. Se calculó la matriz de correlación para cuantificar la fuerza y dirección de las relaciones lineales entre las variables numéricas. Mientras que la correlación nos da un número, la función pairs permite ver la forma de la nube de puntos.
#------------------------------------------------------------------------#
#### 3.2.1. Correlaciones y matriz de dispersión ####
#------------------------------------------------------------------------#
# Se utiliza datos_num (proveniente del data set imputado)
Bost_cor <- cor(datos_num, method = "pearson")
par(mfrow = c(1, 1))
corrplot::corrplot(
Bost_cor,
method = "number",
type = "upper",
number.cex = 0.7,
tl.col = "black"
)
pairs(datos_num, lower.panel = panel.smooth, pch = 16)
Tras analizar ambos gráficos, se observa que el dataset no solo presenta
valores extremos aislados, sino una dependencia estructural compleja.
Mientras que la matriz de correlación cuantifica asociaciones lineales
fuertes (como la relación directa de 0.68 entre habitaciones y precio),
la matriz de dispersión revela la “letra pequeña”: nubes de puntos con
observaciones que se alejan de las tendencias marcadas por las líneas
rojas. Estos puntos representan registros donde la lógica del grupo se
rompe, confirmando que existen anomalías relacionales que el análisis
univariado no logra capturar. En conjunto, ambos gráficos justifican
técnicamente el paso hacia métodos multivariados, ya que demuestran que
para entender la calidad de este dataset es necesario evaluar cómo
interactúan todas las variables simultáneamente.
Una vez analizadas las correlaciones, se implementó una estrategia de detección robusta mediante la función personalizada out_mult. El objetivo es identificar observaciones que se comportan como atípicas no por sus valores individuales, sino por la relación inconsistente entre todas sus dimensiones simultáneamente.
id_out_mult <- out_mult(datos_num, k = 5, alpha = 0.01)
Los gráficos revelan una presencia significativa de anomalías multivariadas. Mientras que la Distancia de Mahalanobis identifica registros que rompen la estructura global de los datos (puntos sobre la línea roja), el LOF detecta aquellos aislados en su vecindad local. La coincidencia de índices como el 381 y 406 en ambos métodos confirma la existencia de atípicos críticos que deben ser tratados para evitar sesgos en el modelado predictivo.
El análisis de observaciones influyentes busca detectar aquellos datos que, por su peso estadístico, tienen la capacidad de alterar drásticamente la pendiente y los resultados del modelo de regresión. Se utiliza para asegurar que la relación entre el valor de la vivienda (medv) y el estatus social (lstat) sea representativa de la mayoría de los datos y no esté sesgada por unos pocos registros excepcionales.
mod <- lm(medv ~ lstat, data = datos_pmm)
Se aplicó la Distancia de Cook para medir el impacto de cada observación sobre el modelo. Se estableció un umbral de 4 veces el promedio de las distancias para identificar los puntos “influyentes” (aquellos con el poder de cambiar la pendiente de la regresión), cuyos índices quedaron registrados en id_cook para su revisión.
#------------------------------------------------------------------------#
#### Cálculo de la Distancia de Cook ####
#------------------------------------------------------------------------#
# Distancia de Cook
cook_d <- cooks.distance(mod)
# Criterio: 4 veces el promedio de la distancia
cut_cook <- 4 * mean(cook_d)
# Identificación de índices influyentes
id_cook <- which(cook_d > cut_cook)
id_cook
## 9 49 142 149 162 163 164 167 187 196 202 204 205 215 225 226 229 234 258 263
## 9 49 142 149 162 163 164 167 187 196 202 204 205 215 225 226 229 234 258 263
## 268 281 283 284 369 370 371 372 373 374 375 413 415 465 504
## 268 281 283 284 369 370 371 372 373 374 375 413 415 465 504
#------------------------------------------------------------------------#
#### Visualización de observaciones influyentes ####
#------------------------------------------------------------------------#
plot(cook_d,
pch = 20,
main = "Distancia de Cook (medv ~ lstat)",
ylab = "Cook's Distance")
# Línea de umbral crítico
abline(h = cut_cook, col = "red", lwd = 2, lty = 2)
# Etiquetas para los registros que superan el umbral
text(id_cook, cook_d[id_cook], labels = id_cook, col = "red", pos = 3, cex = 0.8)
El gráfico de la Distancia de Cook permite visualizar el peso relativo
de cada observación sobre el modelo. La línea roja horizontal establece
el umbral crítico (\(4 \times
\text{promedio}\)); los puntos que la superan, como los índices
375, 413 y 504, se identifican como observaciones influyentes. Estos
registros son críticos porque poseen la capacidad de sesgar los
coeficientes de la regresión, por lo que su análisis individual es
esencial para garantizar la estabilidad y validez de las conclusiones
del modelo.
5. Verificación final Como paso final y definitivo, se consolidaron todos los registros identificados como problemáticos en las fases univariada, multivariada y de influencia para proceder con su depuración técnica. En lugar de eliminar filas completas, lo cual reduciría la potencia estadística de la muestra, se optó por convertir estos valores extremos en datos faltantes (NA) para luego aplicar una imputación mediante PMM (Predictive Mean Matching). Este procedimiento permite “sanar” el dataset, reemplazando el ruido de los atípicos por valores coherentes con la distribución real del mercado de Boston, garantizando así que el modelo final sea robusto, estable y libre de sesgos por observaciones excepcionales.
summary(mod_final)
##
## Call:
## lm(formula = medv ~ lstat, data = datos_definitivos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -12.0368 -2.1138 -0.4041 1.4206 13.2615
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 28.94717 0.39601 73.1 <2e-16 ***
## lstat -0.63584 0.03057 -20.8 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.439 on 504 degrees of freedom
## Multiple R-squared: 0.4619, Adjusted R-squared: 0.4608
## F-statistic: 432.6 on 1 and 504 DF, p-value: < 2.2e-16