1. Introducción

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.

1.1. Revisión de variables categóricas

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:

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

1.2. Detección de inconsistencias en los datos

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

1.3. Presencia de datos faltantes

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))

2. Tratamiento de datos faltantes

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.

3. Identificación de datos atípicos

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.

Análisis Univariado: Z-Score y Criterio de Tukey

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
Análisis Multivariado: Mahalanobis y LOF.

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.

4. Observaciones influyentes

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