Análisis Exploratorio

El análisis exploratorio inició con la carga de la base de datos de viviendas, la cual contenía 8,322 registros con información detallada sobre las propiedades en Cali. Se realizó una evaluación inicial del número de valores faltantes por variable, evidenciando que piso presentaba 2,638 valores ausentes (≈ 31 % de los datos), mientras que parqueaderos tenía 1,605 valores faltantes (≈19 %). Para abordar este problema, se implementó una estrategia de imputación diferenciada: dado que parqueaderos es una variable discreta y la ausencia de valores podría interpretarse como la no existencia de parqueaderos en algunas propiedades, se decidió imputar los valores faltantes con cero (0). Por otro lado, la variable piso, al ser de tipo numérico y contar con una relación significativa con otras variables estructurales de la vivienda (areaconst, preciom, banios y habitaciones), fue imputada utilizando el método de Predictive Mean Matching (PMM) a través del paquete mice, permitiendo estimaciones más precisas basadas en valores reales presentes en el conjunto de datos.

Adicionalmente, se llevó a cabo una georreferenciación de las viviendas mediante la intersección de sus coordenadas con los polígonos de barrios y comunas disponibles en el sistema geoespacial de la Alcaldía de Santiago de Cali. Para ello, se utilizó la función st_join() del paquete sf, que permitió asignar a cada observación su correspondiente comuna y barrio a partir de los límites espaciales definidos en los conjuntos de datos geográficos oficiales. esto ultimo permitio generar un mapa interactivo que resume, a nivel de comuna, indicadores clave del mercado inmobiliario. el cualfue generado agrupando las viviendas por comuna para calcular promedios y medianas de variables relevantes (preciom, areaconst, estrato, banios, habitaciones, parqueaderos y piso). La unión de estos resúmenes con la capa geográfica de las comunas permitió visualizar la variabilidad espacial del precio promedio y otros indicadores en cada comuna. Los pop-ups del mapa presentan de forma concisa la información de cada indicador, facilitando la identificación de diferencias territoriales y conectando directamente la ubicación con los aspectos económicos y estructurales de las viviendas.

# Cargar datos
data(vivienda)
comunas <- st_read("~/Workshop/Rproj/Nexus_databases/mc_comunas", layer = "mc_comunas") %>%
st_transform(crs = 4326) %>% 
mutate(comuna = sprintf("%02d", as.integer(comuna)))

barrios <- st_read("~/Workshop/Rproj/Nexus_databases/mc_barrios", layer = "mc_barrios", options = "ENCODING=LATIN1") %>% st_transform(crs = 4326)

# EDA #####
# Crear data para EDA y posteriores analisis

dat <- vivienda %>% 
  filter(!is.na(id)) %>%
  mutate(
    piso = as.numeric(piso),
    estrato = factor(estrato, ordered = TRUE),
    parqueaderos = if_else(is.na(parqueaderos), 0, parqueaderos)) %>%
  { # imputar datos
    impute_data <- select(., piso, areaconst, preciom, banios, habitaciones)
    imp <- mice(impute_data, m = 1, method = "pmm", seed = 123, printFlag = FALSE)
    imputed <- complete(imp, 1)
    # actualizar 'piso'
    mutate(., piso = imputed$piso)
  } %>%
  st_as_sf(coords = c("longitud", "latitud"), crs = 4326, remove = FALSE) %>% 
  st_join(barrios, join = st_within) %>% 
  rename(barrio = barrio.y) %>%
  mutate(comuna = factor(comuna, ordered = TRUE)) %>% 
  select(id, zona, comuna, barrio, tipo, estrato, preciom, areaconst, piso, 
         parqueaderos, banios, habitaciones, longitud, latitud)
Resumen datos originales
Variable Stats / Values Freqs (% of Valid) Graph Missing
id [numeric]
Mean (sd) : 4160 (2401.6)
min ≤ med ≤ max:
1 ≤ 4160 ≤ 8319
IQR (CV) : 4159 (0.6)
8319 distinct values 3 (0.0%)
zona [character]
1. Zona Centro
2. Zona Norte
3. Zona Oeste
4. Zona Oriente
5. Zona Sur
124(1.5%)
1920(23.1%)
1198(14.4%)
351(4.2%)
4726(56.8%)
3 (0.0%)
piso [character]
1. 02
2. 03
3. 01
4. 04
5. 05
6. 06
7. 08
8. 07
9. 09
10. 10
[ 2 others ]
1450(25.5%)
1097(19.3%)
860(15.1%)
607(10.7%)
567(10.0%)
245(4.3%)
211(3.7%)
204(3.6%)
146(2.6%)
130(2.3%)
167(2.9%)
2638 (31.7%)
estrato [numeric]
Mean (sd) : 4.6 (1)
min ≤ med ≤ max:
3 ≤ 5 ≤ 6
IQR (CV) : 1 (0.2)
3:1453(17.5%)
4:2129(25.6%)
5:2750(33.1%)
6:1987(23.9%)
3 (0.0%)
preciom [numeric]
Mean (sd) : 433.9 (328.6)
min ≤ med ≤ max:
58 ≤ 330 ≤ 1999
IQR (CV) : 320 (0.8)
539 distinct values 2 (0.0%)
areaconst [numeric]
Mean (sd) : 174.9 (143)
min ≤ med ≤ max:
30 ≤ 123 ≤ 1745
IQR (CV) : 149 (0.8)
652 distinct values 3 (0.0%)
parqueaderos [numeric]
Mean (sd) : 1.8 (1.1)
min ≤ med ≤ max:
1 ≤ 2 ≤ 10
IQR (CV) : 1 (0.6)
1:3155(47.0%)
2:2475(36.8%)
3:520(7.7%)
4:384(5.7%)
5:68(1.0%)
6:68(1.0%)
7:18(0.3%)
8:17(0.3%)
9:4(0.1%)
10:8(0.1%)
1605 (19.3%)
banios [numeric]
Mean (sd) : 3.1 (1.4)
min ≤ med ≤ max:
0 ≤ 3 ≤ 10
IQR (CV) : 2 (0.5)
11 distinct values 3 (0.0%)
habitaciones [numeric]
Mean (sd) : 3.6 (1.5)
min ≤ med ≤ max:
0 ≤ 3 ≤ 10
IQR (CV) : 1 (0.4)
11 distinct values 3 (0.0%)
tipo [character]
1. Apartamento
2. Casa
5100(61.3%)
3219(38.7%)
3 (0.0%)
barrio [character]
1. valle del lili
2. ciudad jardín
3. pance
4. la flora
5. santa teresita
6. el caney
7. el ingenio
8. la hacienda
9. acopi
10. los cristales
[ 426 others ]
1008(12.1%)
516(6.2%)
409(4.9%)
366(4.4%)
262(3.1%)
208(2.5%)
202(2.4%)
164(2.0%)
158(1.9%)
154(1.9%)
4872(58.6%)
3 (0.0%)
longitud [numeric]
Mean (sd) : -76.5 (0)
min ≤ med ≤ max:
-76.6 ≤ -76.5 ≤ -76.5
IQR (CV) : 0 (0)
2928 distinct values 3 (0.0%)
latitud [numeric]
Mean (sd) : 3.4 (0)
min ≤ med ≤ max:
3.3 ≤ 3.4 ≤ 3.5
IQR (CV) : 0.1 (0)
3679 distinct values 3 (0.0%)
Resumen datos análisis
Variable Stats / Values Freqs (% of Valid) Graph Missing
id [numeric]
Mean (sd) : 4160 (2401.6)
min ≤ med ≤ max:
1 ≤ 4160 ≤ 8319
IQR (CV) : 4159 (0.6)
8319 distinct values 0 (0.0%)
zona [character]
1. Zona Centro
2. Zona Norte
3. Zona Oeste
4. Zona Oriente
5. Zona Sur
124(1.5%)
1920(23.1%)
1198(14.4%)
351(4.2%)
4726(56.8%)
0 (0.0%)
comuna [ordered, factor]
1. 01
2. 02
3. 03
4. 04
5. 05
6. 06
7. 07
8. 08
9. 09
10. 10
[ 12 others ]
135(1.6%)
1455(17.5%)
449(5.4%)
165(2.0%)
331(4.0%)
90(1.1%)
78(0.9%)
173(2.1%)
196(2.4%)
316(3.8%)
4931(59.3%)
0 (0.0%)
barrio [character]
1. Lili
2. Parcelaciones Pance
3. Urbanización Ciudad Jardí
4. Caney
5. El Ingenio
6. San Pedro
7. La Hacienda
8. Santa Teresita
9. La Flora
10. Senderos de La Flora
[ 290 others ]
819(9.8%)
536(6.4%)
330(4.0%)
286(3.4%)
250(3.0%)
151(1.8%)
150(1.8%)
134(1.6%)
129(1.6%)
127(1.5%)
5407(65.0%)
0 (0.0%)
tipo [character]
1. Apartamento
2. Casa
5100(61.3%)
3219(38.7%)
0 (0.0%)
estrato [ordered, factor]
1. 3
2. 4
3. 5
4. 6
1453(17.5%)
2129(25.6%)
2750(33.1%)
1987(23.9%)
0 (0.0%)
preciom [numeric]
Mean (sd) : 433.9 (328.7)
min ≤ med ≤ max:
58 ≤ 330 ≤ 1999
IQR (CV) : 320 (0.8)
539 distinct values 0 (0.0%)
areaconst [numeric]
Mean (sd) : 174.9 (143)
min ≤ med ≤ max:
30 ≤ 123 ≤ 1745
IQR (CV) : 149 (0.8)
652 distinct values 0 (0.0%)
piso [numeric]
Mean (sd) : 3.7 (2.6)
min ≤ med ≤ max:
1 ≤ 3 ≤ 12
IQR (CV) : 3 (0.7)
12 distinct values 0 (0.0%)
parqueaderos [numeric]
Mean (sd) : 1.5 (1.2)
min ≤ med ≤ max:
0 ≤ 1 ≤ 10
IQR (CV) : 1 (0.8)
11 distinct values 0 (0.0%)
banios [numeric]
Mean (sd) : 3.1 (1.4)
min ≤ med ≤ max:
0 ≤ 3 ≤ 10
IQR (CV) : 2 (0.5)
11 distinct values 0 (0.0%)
habitaciones [numeric]
Mean (sd) : 3.6 (1.5)
min ≤ med ≤ max:
0 ≤ 3 ≤ 10
IQR (CV) : 1 (0.4)
11 distinct values 0 (0.0%)
longitud [numeric]
Mean (sd) : -76.5 (0)
min ≤ med ≤ max:
-76.6 ≤ -76.5 ≤ -76.5
IQR (CV) : 0 (0)
2928 distinct values 0 (0.0%)
latitud [numeric]
Mean (sd) : 3.4 (0)
min ≤ med ≤ max:
3.3 ≤ 3.4 ≤ 3.5
IQR (CV) : 0.1 (0)
3679 distinct values 0 (0.0%)
# Calcular el resumen por comuna

comuna_profile <- dat %>% 
  st_drop_geometry() %>% 
  group_by(comuna) %>% 
  summarise(
    avg_precio       = mean(preciom, na.rm = TRUE),
    avg_areaconst    = mean(areaconst, na.rm = TRUE),
    avg_estrato      = median(as.numeric(estrato), na.rm = TRUE),
    avg_banios       = median(banios, na.rm = TRUE),
    avg_habitaciones = median(habitaciones, na.rm = TRUE),
    avg_parqueaderos = median(parqueaderos, na.rm = TRUE),
    avg_piso         = median(piso, na.rm = TRUE)
  )

# Mapa interactivo

comuna_map <- comunas %>% 
  left_join(comuna_profile, by = "comuna")

map <- leaflet(comuna_map) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~colorNumeric("YlOrRd", domain = avg_precio)(avg_precio),
    weight = 1, opacity = 1, color = "white", dashArray = "3", fillOpacity = 0.7,
    highlight = highlightOptions(
      weight = 3, color = "#666", dashArray = "", fillOpacity = 0.7, bringToFront = TRUE
    ),
    label = 
    ~paste0("<div style='max-width:250px; font-size:12px;'>",
           "<strong>Comuna:</strong> ", nombre, "<br>",
           "<strong>Precio Promedio:</strong> ", round(avg_precio,2), "<br>",
           "<strong>Área Construida Promedio:</strong> ", round(avg_areaconst,2), "<br>",
           "<strong>Estrato Promedio:</strong> ", round(avg_estrato,2), "<br>",
           "<strong>Baños Promedio:</strong> ", round(avg_banios,2), "<br>",
           "<strong>Habitaciones Promedio:</strong> ", round(avg_habitaciones,2), "<br>",
           "<strong>Parqueaderos Promedio:</strong> ", round(avg_parqueaderos,2), "<br>",
           "<strong>Piso Promedio:</strong> ", round(avg_piso,2),
            "</div>") %>% lapply(htmltools::HTML)
  ) %>%
  addLegend(
    pal = colorNumeric("YlOrRd", domain = comuna_map$avg_precio),
    values = ~avg_precio, opacity = 0.7,
    title = "Precio Promedio", position = "bottomright"
  )

cat('<h5>`Resumen de Variables Inmobiliarias por Comuna`</h5>')
Resumen de Variables Inmobiliarias por Comuna
map

Análisis de Componentes Principales (PCA)

Con la base de datos ya depurada y transformada, se realizó un análisis de componentes principales utilizando las variables cuantitativas: área construida, precio, piso, parqueaderos, baños y habitaciones. La estandarización previa de estas variables permitió que las diferencias en sus escalas no influyeran en la extracción de los componentes. Los resultados muestran que el primer componente Dim.1 tiene un eigenvalue de 3.16, explicando el 52.61% de la varianza total, mientras que el segundo componente Dim.2 aporta un 19.07% adicional, de forma que en conjunto ambos componentes capturan el 71.68% de la variabilidad. La incorporación del tercer componente Dim.3, con un eigenvalue de 0.76, eleva la varianza explicada al 84.36%, lo que evidencia que la mayor parte de la información relevante se concentra en las primeras dimensiones.

El análisis de la calidad de representación, medido mediante el coseno al cuadrado (cos²), reveló que en Dim.1 las variables banios (cos² = 0.76), areaconst (cos² = 0.74) y preciom (cos² = 0.70) se representan de manera sobresaliente, lo que indica que estos atributos son determinantes en la conformación de esta dimensión. En contraste, la variable piso presenta un cos² muy bajo en Dim.1 (0.04), aunque su calidad de representación mejora considerablemente en Dim.2 (0.65) y en Dim.3 (0.30). Esto sugiere que los factores relacionados con el tamaño y el valor de la propiedad dominan la variabilidad principal del mercado, mientras que la información referente al piso se manifiesta de forma más sutil y se despliega en dimensiones secundarias. El biplot del PCA, que visualiza simultáneamente la distribución de las observaciones y la dirección e importancia de cada variable, confirma que la diferenciación en función de zona y estrato es limitada, mientras que la variable tipo revela una separación moderada entrea las viviendas tipo apartamentos y casas. Este resultado indica que, en el contexto del mercado inmobiliario de Cali ,y bajo el acercamiento del analisis de componentes principales, las características económicas y estructurales como el precio, el área y el número de baños, habitaciones o parqueaderos presentan variabilidad segun el tipo de vivienda

pca_res <- dat %>% 
  st_drop_geometry() %>% 
  select(areaconst, preciom, piso, parqueaderos, banios, habitaciones) %>% 
  prcomp(center = T, scale. = T)

cat('<h5>`(a) Scree Plot: Varianza explicada por cada componente`</h5>')
(a) Scree Plot: Varianza explicada por cada componente
# Scree Plot
fviz_eig(pca_res, addlabels = TRUE, ylim = c(0, 60),barfill = "#73bac9",barcolor = "white")

cat('<h5>`(b) Calidad de representación (cos2) de las variables`</h5>')
(b) Calidad de representación (cos2) de las variables
# valores de cos2 con corrplot.
var <- get_pca_var(pca_res)
corrplot(var$cos2,
         is.corr = FALSE,
         mar = c(0, 0, 1, 0),
         col = pal_gsea()(12))

cat('<h5>`(c) Biplot PCA: Representación conjunta de individuos y variables`</h5>')
(c) Biplot PCA: Representación conjunta de individuos y variables
# Biplot
combined_plot <- (plot_pca_by("zona","Biplot de PCA - Zona") + plot_pca_by("estrato", "Biplot de PCA - Estrato")) / plot_pca_by("tipo","Biplot de PCA - Tipo de vivienda")
combined_plot

Análisis de Conglomerados

Para el análisis de conglomerados del mercado inmobiliario se generó un resumen por comuna a partir de las propiedades residenciales, utilizando variables cuantitativas como areaconst, preciom, piso, parqueaderos, banios y habitaciones. El objetivo fue comprender las dinámicas de la oferta en distintas áreas de la ciudad y en diferentes estratos socioeconómicos. En lugar de utilizar directamente los clusters resultantes de un algoritmo como k-means, se optó por representar los datos mediante un Heatmap combinado con un dendrograma. Para ello, se construyó una matriz en la que cada columna corresponde a una comuna y cada fila a una variable, utilizando los valores estandarizados (z-scores) de los indicadores.

La similitud entre comunas se midió mediante la distancia Euclidiana, y se aplicó el método de enlace promedio para generar el dendrograma que agrupa las comunas según la similitud de sus características. El heatmap resultante revela dos grandes tendencias en el eje horizontal: por un lado, un grupo de comunas que presentan altos valores en precio, área y parqueaderos, asociadas a estratos relativamente altos; y por otro, un grupo de comunas con valores bajos en la mayoría de los indicadores, correspondientes a estratos más bajos. En el eje vertical, el clustering de variables muestra que las variables relacionadas con el tamaño (área construida, número de habitaciones y baños) tienden a agruparse, lo que sugiere que las comunas con construcciones de mayor envergadura suelen contar también con más habitaciones y baños. Asimismo, las variables vinculadas mayores valores de la variable piso y parqueaderos se agrupan, lo que indica que en comunas con viviendas de mayor valor de piso es más frecuente encontrar un mayor número de parqueaderos.

mat <- comuna_profile %>% 
  select(avg_precio, avg_areaconst, avg_piso, avg_parqueaderos, avg_banios, avg_habitaciones) %>%
  as.matrix() %>% 
  scale() %>% 
  t()
colnames(mat) <- comuna_profile$comuna

# anotaciones para el heatmap
anno_df <- comuna_profile %>% 
  select(comuna, avg_estrato) %>% 
  as.data.frame() %>% 
  column_to_rownames("comuna")

# Escala para 'estrato'
estrato_fun <- colorRamp2(range(as.numeric(anno_df$avg_estrato)), c("lightblue", "darkblue"))

ha <- HeatmapAnnotation(
  df = anno_df %>% rename(estrato = avg_estrato),
  col = list(estrato = estrato_fun),
  annotation_name_side = "left"
)

# Paleta heatmap 
col_fun <- colorRamp2(seq(min(mat, na.rm = TRUE), max(mat, na.rm = TRUE), length.out = 12),
                       pal_gsea()(12))

# Heatmap
cat('<h5>`Heatmap de indicadores inmobiliarios por Comuna`</h5>')
Heatmap de indicadores inmobiliarios por Comuna
Heatmap(mat, name = "z-scores", col = col_fun,
        top_annotation = ha,
        column_title = "Comunas",
        row_title = "Variables",
        show_column_names = TRUE,
        clustering_distance_columns = "euclidean",
        clustering_method_columns = "average")

Análisis de Correspondencia

Se aplicó un análisis de correspondencia múltiple (MCA) a las variables categóricas—transformadas para mejorar su legibilidad (por ejemplo, anteponiendo “Comuna” a los valores de comuna y “Estrato” a los de estrato)—con el fin de explorar las relaciones entre las características de ubicación y clasificación de las viviendas. Se evaluó la contribución de cada variable a la variación total mediante análisis univariados (ANOVA) en cada dimensión. En la primera dimensión, las variables relacionadas con la ubicación comuna y zona y el nivel socioeconómico estrato mostraron altos coeficientes de determinación (R² ≈ 0.71 para comuna, 0.72 para zona y 0.69 para estrato), lo que indica que estos factores explican la mayor parte de la variabilidad de los datos; en contraste, la variable tipo presentó una contribución significativamente menor (R² ≈ 0.12).

El análisis a nivel de categorías reveló que ciertos niveles, como Estrato 3, Zona Oriente y la Comuna 08 y Comuna 11, se asocian de forma positiva y marcada con la estructura global de los datos, mientras que otros niveles, como las Comuna 01 o Comuna 20, muestran estimaciones negativas, indicando una influencia menor. Además, en la segunda dimensión la fuerza de la asociación para las variables de ubicación se mantiene elevada, superando la contribución de estrato y tipo, lo que refuerza la importancia de los factores espaciales en la segmentación de las propiedades.

# Datos para el MCA
dat_mca <- dat %>% 
  st_drop_geometry() %>% 
  mutate(
    comuna  = paste("Comuna", comuna),
    estrato = paste("Estrato", estrato)
  ) %>% 
  select(comuna, tipo, zona, estrato) %>% 
  mutate(across(everything(), as.factor))

#  MCA
res_mca <- MCA(dat_mca, graph = FALSE)
#dimdesc(res_mca)

# Generar el biplot
cat('<h5>`MCA: Comuna, Tipo, Zona y Estrato`</h5>')
MCA: Comuna, Tipo, Zona y Estrato
plotMCA(res_mca)

Conclusión

En conclusión, los análisis multivariados—que incluyeron el estudio exploratorio, el análisis de componentes principales, el análisis de conglomerados y el análisis de correspondencia—demostraron de forma consistente que el nivel socioeconómico (estrato) junto con las variables de ubicación (comuna y zona) son determinantes para diferenciar la oferta inmobiliaria en Cali. El PCA evidenció que atributos como el precio, el área construida y el número de baños concentran la mayor parte de la variabilidad en las primeras dimensiones, lo que resalta su importancia en la estructura de los datos. Además, el análisis de conglomerados permitió identificar dos grandes grupos de comunas: uno caracterizado por valores altos en indicadores clave (como mayor precio, mayor área construida y un mayor número de parqueaderos) y asociado a estratos superiores, y otro con valores bajos en dichos indicadores, típicamente vinculados a estratos más bajos. Por su parte, el análisis de correspondencia confirmó que las diferencias en las categorías de ubicación y estrato son fundamentales para segmentar la oferta, mientras que el tipo de vivienda presenta un impacto relativamente menor. Con base en estos hallazgos, se recomienda que la empresa focalice sus esfuerzos en desarrollar y promocionar propiedades que maximicen atributos como el área construida y la calidad de ubicación, dirigiendo sus estrategias hacia segmentos con mayor capacidad adquisitiva.