Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.
El reto principal consisten en realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere aplicar diversas técnicas de análisis de datos, incluyendo:
1.Análisis de Componentes Principales: Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y oferta del mercado.
2.Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas de las ofertas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.
Análisis de Correspondencia : Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.
Visualización de resultados: Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.
Para realizar un análisis holístico de los datos con el objetivo de identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades, es necesario realizar inicialmente un análisis exploratorio de los datos:
knitr::opts_chunk$set(
fig.path = "figs/",
fig.keep = "all"
)
dir.create("figs", showWarnings = FALSE)
library(dplyr)
library(ggplot2)
library(knitr)
library(corrplot)
library(car)
library(ggfortify)
library(kableExtra)
library(patchwork)
library(rlang)
library(tidyverse)
library(FactoMineR)
library(factoextra)
library(cluster)
library(paqueteMODELOS)
library(naniar)
library(stringr)Para empezar se carga la base de datos “Vivienda” desde el paquete MODELOS, suministrada en el ejercicio:
# devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
data("vivienda")
#Creo una copia de mi dataset original
vivienda_original <- vivienda
tipos <- data.frame(
Variable = names(vivienda),
Tipo = sapply(vivienda, class)
)
knitr::kable(tipos, caption = "Tabla de variables y sus tipos", align = "lc")| Variable | Tipo | |
|---|---|---|
| id | id | numeric |
| zona | zona | character |
| piso | piso | character |
| estrato | estrato | numeric |
| preciom | preciom | numeric |
| areaconst | areaconst | numeric |
| parqueaderos | parqueaderos | numeric |
| banios | banios | numeric |
| habitaciones | habitaciones | numeric |
| tipo | tipo | character |
| barrio | barrio | character |
| longitud | longitud | numeric |
| latitud | latitud | numeric |
Como se puede observar las variables zona, piso, tipo y barrio, son categóricas; las demás son de tipo numérico.
A continuación se procede a eliminar columna ID, y convertir la variable estrato a una variable categórica ordinal.
#Eliminar variable id
vivienda <- vivienda %>% select(-id)
#Convertir variable estrato a categorica
vivienda$estrato <- as.factor(vivienda$estrato)
tipos <- data.frame(
Variable = names(vivienda),
Tipo = sapply(vivienda, class)
)
knitr::kable(tipos, caption = "Tabla de variables y sus tipos", align = "lc")| Variable | Tipo | |
|---|---|---|
| zona | zona | character |
| piso | piso | character |
| estrato | estrato | factor |
| preciom | preciom | numeric |
| areaconst | areaconst | numeric |
| parqueaderos | parqueaderos | numeric |
| banios | banios | numeric |
| habitaciones | habitaciones | numeric |
| tipo | tipo | character |
| barrio | barrio | character |
| longitud | longitud | numeric |
| latitud | latitud | numeric |
En este paso se detectaron cuáles eran los registros que presentaban datos faltantes. Se generó una lista con los registros que contienen datos faltantes y es la que se muestra a continuación:
# Número de valores faltantes por variable
na_por_variable <- sapply(vivienda, function(x) sum(is.na(x)))
na_por_variable <- sort(na_por_variable, decreasing = TRUE)
# Convertir en tabla
na_tabla <- data.frame(Variable = names(na_por_variable), NA_Conteo = na_por_variable)
knitr::kable(na_tabla, caption = "Número de valores faltantes por variable")| Variable | NA_Conteo | |
|---|---|---|
| piso | piso | 2638 |
| parqueaderos | parqueaderos | 1605 |
| zona | zona | 3 |
| estrato | estrato | 3 |
| areaconst | areaconst | 3 |
| banios | banios | 3 |
| habitaciones | habitaciones | 3 |
| tipo | tipo | 3 |
| barrio | barrio | 3 |
| longitud | longitud | 3 |
| latitud | latitud | 3 |
| preciom | preciom | 2 |
Como se observa en la tabla anterior, las variables que presentan mayor cantidad de datos faltantes son las variables pisos y parqueaderos con 2638 y 1605 datos faltantes respectivamente.
Posteriormente, se realiza la prueba estadística para evaluar si los datos faltantes existentes en nuestra base de datos, existen aleatoriamente y no obedecen a ninguna otra variable, observada o no observada.
Como se puede observar el P valor es 0, es decir muy inferior a 0.05, por lo tanto se indica que se rechaza la hipótesis Nula, es decir que los datos faltantes no están completamente al azar, es decir que NO son datos MCAR, por lo tanto no se van a eliminar valores faltantes.
Teniendo en cuenta que el manejo de datos faltantes no se puede realizar eliminando valores, se reemplaza por el valor de la mediana en las variables numéricas y por la moda en las variables categóricas y posteriormente se verifica que ya no aparezcan datos faltantes, después de realizar este proceso.
# Función para calcular la moda
moda <- function(x) {
ux <- na.omit(unique(x))
ux[which.max(tabulate(match(x, ux)))]
}
# Reemplazo automático de NA por mediana o moda según el tipo de variable
for (col in names(vivienda)) {
if (any(is.na(vivienda[[col]]))) {
# Si es numérica → usar mediana
if (is.numeric(vivienda[[col]])) {
mediana <- median(vivienda[[col]], na.rm = TRUE)
vivienda[[col]][is.na(vivienda[[col]])] <- mediana
# Si es carácter o factor → usar moda
} else if (is.character(vivienda[[col]]) || is.factor(vivienda[[col]])) {
moda_val <- moda(vivienda[[col]])
vivienda[[col]][is.na(vivienda[[col]])] <- moda_val
}
}
}## [1] 0
En este punto, procedemos a identificar cuántos registros se encuentran duplicados en nuestra base de datos.
# Verificar filas duplicadas completas
duplicados <- vivienda[duplicated(vivienda), ]
# Mostrar cuántos hay
cat("Número de registros duplicados:", nrow(duplicados))## Número de registros duplicados: 61
A partir del análisis realizado, se identificaron los registros duplicados, por eso se procede a eliminarlos, y se verifica nuevamente que ya no hayan quedado registros duplicados.
# Eliminar filas duplicadas
vivienda <- vivienda %>% distinct()
#Verifico que ya no aparezcan duplicados
duplicados <- vivienda[duplicated(vivienda), ]
cat("Número de registros duplicados:", nrow(duplicados))## Número de registros duplicados: 0
Una vez eliminados los registros duplicados e imputados los datos faltantes, se procede a realizar la estadística descriptiva: Para las variables numéricas se determinan valores mínimos, máximos, media, mediana, primer y tercer cuartil y para las variables categóricas Tabla de frecuencias y porcentajes:
# Seleccionar variables numéricas
vivienda_num <- vivienda %>%
select(where(is.numeric))
# Resumen general
summary(vivienda_num)## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 434.7 Mean : 175.2 Mean : 1.869 Mean : 3.113
## 3rd Qu.: 548.0 3rd Qu.: 229.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :10.000 Max. :10.000
## habitaciones longitud latitud
## Min. : 0.000 Min. :-76.59 Min. :3.333
## 1st Qu.: 3.000 1st Qu.:-76.54 1st Qu.:3.381
## Median : 3.000 Median :-76.53 Median :3.416
## Mean : 3.607 Mean :-76.53 Mean :3.418
## 3rd Qu.: 4.000 3rd Qu.:-76.52 3rd Qu.:3.452
## Max. :10.000 Max. :-76.46 Max. :3.498
A continuación se realiza la limpieza de la Variable Barrio, y se calculan las tablas de frecuencia para las variables categóricas:
library(stringr)
library(stringi)
#Limpieza variable Barrio
fix_text <- function(x){
# 1) Normalizar codificación
y <- stri_encode(x, from = "windows-1252", to = "UTF-8")
idx_na <- is.na(y)
if (any(idx_na)) y[idx_na] <- stri_encode(x[idx_na], from = "latin1", to = "UTF-8")
# 2) Reemplazos puntuales
repl <- c(
"Alameda Del Ra-O" = "Alameda Del Rio",
"Alfa\\^Sa\\(C\\)Rez Real" = "Alferez Real",
"Alfonso Laaaaaaa³Pez" = "Alfonso Lopez",
"Alfonso Laaaaaaa³Pez I" = "Alfonso Lopez",
"Alfonso Laaaaaa³Pez" = "Alfonso Lopez",
"Alfonso Laaaaaa³Pez I" = "Alfonso Lopez",
"Antonio Naria\\+/-O" = "Antonio Narino",
"Base Aa\\^Sa\\(C\\)Rea" = "Base Aerea",
"Benjama-N Herrera" = "Benjamin Herrera",
"Boyaca!" = "Boyaca",
"Bretaa\\+/-A" = "Bretana",
"Caa\\+/-" = "Canas",
"Ciudad Caaaa³Rdoba" = "Ciudad Cordoba",
"Ciudad Jarda-N" = "Ciudad Jardin",
"Ciudad Los A!Lamos" = "Ciudad Los Alamos",
"Ciudad Mela\\^Sa\\(C\\)Ndez" = "Ciudad Melendez",
"Ciudadela Del Ra-O" = "Ciudadela Del Rio",
"Cristaaaa³Bal Colaaaa³N" = "Cristobal Colon",
"Cristobal Colaaaa³N" = "Cristobal Colon",
"Cristaaaaa³Bal Colaaaaa³N" = "Cristobal Colon",
"Cristobal Colaaaaa³N" = "Cristobal Colon",
"El Tra\\^Sa\\(C\\)Bol" = "El Troncal",
"Evaristo Garca-A" = "Evaristo Garcia",
"JuanambaSa<< ?" = "Juanambu",
"JuanambaSa<<" = "Juanambu",
"Juna-N" = "Junin",
"La Campia\\+/-A" = "La Campina",
"Los Ca!Mbulos" = "Los Cambulos",
"Marroqua-N Iii" = "Marroquin",
"Mela\\^Sa\\(C\\)Ndez" = "Melendez",
"Na!Poles" = "Napoles",
"Pacara!" = "Pacara",
"RepaSa<<Blica De Israel" = "Republica De Israel",
"Rincaaaa³N De Salomia" = "Rincon De Salomia",
"Rincaaaaa³N" = "Rincon",
"San Joaqua-N" = "San Joaquin",
"San Nicola!S" = "San Nicolas",
"Santa Ba!Rbara" = "Santa Barbara",
"Santa Maaaaaaa³Nica" = "Santa Monica",
"Santa Maaaaaaa³Nica Alta" = "Santa Monica Alta",
"Santa Maaaaaaa³Nica Popular" = "Santa Monica Popular",
"Santa Maaaaaaa³Nica Residencial" = "Santa Monica Residencial",
"Simaaaa³N Bolivar" = "Simon Bolivar",
"Terraaaa³N Colorado" = "Terron Colorado",
"Uniaaaa³N De Vivienda" = "Unidades De Vivienda",
"Urbanizaciaaaa³N" = "Urbanizacion",
"Urbanizaciaaaaa³N" = "Urbanizacion",
"Urbanizaciaaaaaaa³N" = "Urbanizacion",
"El Jarda-N" = "El Jordan",
"El Jorda!N" = "El Jordan",
"El Paraa-So" = "El Prado",
"El Pea\\+/-On" = "El Penon",
"Alto Jorda!N" = "Alto Jordan",
"(?i)^Alto Jorda!N$" = "Alto Jordan",
"(?i)^Base Aa\\^Sa\\(C\\)Rea$" = "Base Aerea"
)
y <- str_replace_all(y, repl)
# 3) Reglas genéricas útiles
# Urbanizacia... -> Urbanizacion + resto del nombre
y <- str_replace(y, "^Urbanizacia.*?\\s", "Urbanizacion ")
# Variantes rotas de "Santa Monica"
y <- str_replace_all(y, "Santa M[aA]+[^[:alpha:]]*nica", "Santa Monica")
# Comprimir repeticiones absurdas: "Laaaaaaaa" -> "Laa"
y <- str_replace_all(y, "([A-Za-z])\\1{2,}", "\\1\\1")
# Quitar " Lopez I" final
y <- str_replace_all(y, "L\\s*opez\\s*I\\b", "Lopez")
# 4) Mojibake suelto y caracteres no ASCII
#y <- str_replace_all(y, c("³" = "o", "Â" = "")) # casos comunes
y <- stri_trans_general(y, "Latin-ASCII") # sin tildes
y <- str_replace_all(y, "[^\\x00-\\x7F]", "") # purga cualquier no-ASCII remanente
# 5) Limpieza final
y <- str_squish(y)
y <- str_to_title(y)
y
# Homologar variantes de Alfonso Lopez (Laapez / Laapez I / Lopez I)
y <- str_replace_all(y, c(
"(?i)^Alfonso\\s+La+pez(?:\\s+I)?$" = "Alfonso Lopez",
"(?i)^Alfonso\\s+Lopez(?:\\s+I)?$" = "Alfonso Lopez"
))
# Homologar variantes de Alameda Del Rio
y <- str_replace_all(y, c(
"(?i)^Alameda\\s+Del\\s+Ra-?O$" = "Alameda Del Rio",
"(?i)^Alameda\\s+Del\\s+Rio$" = "Alameda Del Rio"
))
# Homologar variantes de Alferez Real
y <- str_replace_all(y, c(
"(?i)^Alfa\\^Sa\\(C\\)Rez\\s+Real$" = "Alferez Real",
"(?i)^Alferez\\s+Real$" = "Alferez Real"
))
# Homologar Antonio Narino
y <- str_replace_all(y, c(
"(?i)^Antonio\\s+Naria\\+/-O$" = "Antonio Narino",
"(?i)^Antonio\\s+Nari[nñ]o$" = "Antonio Narino"
))
# Homologar Las Americas
y <- str_replace_all(y, c(
"(?i)^Las\\s+Ama\\^Sa\\(C\\)Ricas$" = "Las Americas",
"(?i)^Las\\s+Americas$" = "Las Americas"
))
}
# Usar:
vivienda$barrio <- fix_text(vivienda$barrio)# Seleccionar solo variables categoricas
vivienda_cat <- vivienda %>%
select(where(~is.character(.) || is.factor(.)))
# Función para tabla con frecuencia y porcentaje
tabla_frecuencias <- function(x) {
tab <- as.data.frame(table(x, useNA = "ifany"))
colnames(tab) <- c("Categoria", "Frecuencia")
tab$Porcentaje <- round(100 * tab$Frecuencia / sum(tab$Frecuencia), 2)
return(tab)
}
# Aplicar a todas las variables categoricas
resumen_categoricas <- lapply(names(vivienda_cat), function(var) {
cat("### ", var, "\n\n")
print(knitr::kable(tabla_frecuencias(vivienda_cat[[var]]),
caption = paste("Distribución de", var)))
cat("\n\n")
})## ### zona
##
##
##
## Table: Distribución de zona
##
## |Categoria | Frecuencia| Porcentaje|
## |:------------|----------:|----------:|
## |Zona Centro | 124| 1.50|
## |Zona Norte | 1908| 23.10|
## |Zona Oeste | 1195| 14.47|
## |Zona Oriente | 350| 4.24|
## |Zona Sur | 4684| 56.70|
##
##
## ### piso
##
##
##
## Table: Distribución de piso
##
## |Categoria | Frecuencia| Porcentaje|
## |:---------|----------:|----------:|
## |01 | 852| 10.31|
## |02 | 4063| 49.18|
## |03 | 1089| 13.18|
## |04 | 603| 7.30|
## |05 | 561| 6.79|
## |06 | 245| 2.97|
## |07 | 203| 2.46|
## |08 | 207| 2.51|
## |09 | 144| 1.74|
## |10 | 129| 1.56|
## |11 | 82| 0.99|
## |12 | 83| 1.00|
##
##
## ### estrato
##
##
##
## Table: Distribución de estrato
##
## |Categoria | Frecuencia| Porcentaje|
## |:---------|----------:|----------:|
## |3 | 1445| 17.49|
## |4 | 2105| 25.48|
## |5 | 2728| 33.02|
## |6 | 1983| 24.00|
##
##
## ### tipo
##
##
##
## Table: Distribución de tipo
##
## |Categoria | Frecuencia| Porcentaje|
## |:-----------|----------:|----------:|
## |Apartamento | 5061| 61.26|
## |Casa | 3200| 38.74|
##
##
## ### barrio
##
##
##
## Table: Distribución de barrio
##
## |Categoria | Frecuencia| Porcentaje|
## |:--------------------------------|----------:|----------:|
## |20 De Julio | 3| 0.04|
## |3 De Julio | 1| 0.01|
## |Acopi | 157| 1.90|
## |Agua Blanca | 1| 0.01|
## |Aguablanca | 2| 0.02|
## |Aguacatal | 109| 1.32|
## |Alameda | 16| 0.19|
## |Alameda Del Rio | 3| 0.04|
## |Alamos | 14| 0.17|
## |Alborada | 1| 0.01|
## |Alcazares | 2| 0.02|
## |Alferez Real | 7| 0.08|
## |Alfonso Lopez | 23| 0.28|
## |Alto Jorda!N | 1| 0.01|
## |Altos De Guadalupe | 4| 0.05|
## |Altos De Menga | 3| 0.04|
## |Altos De Santa | 1| 0.01|
## |Antonio Narino | 2| 0.02|
## |Aranjuez | 15| 0.18|
## |Arboleda | 5| 0.06|
## |Arboleda Campestre Candelaria | 1| 0.01|
## |Arboledas | 38| 0.46|
## |Atanasio Girardot | 9| 0.11|
## |Autopista Sur | 1| 0.01|
## |Bajo Aguacatal | 1| 0.01|
## |Barranquilla | 6| 0.07|
## |Barrio 7de Agosto | 1| 0.01|
## |Barrio El Recuerdo | 1| 0.01|
## |Barrio Eucara-Stico | 1| 0.01|
## |Barrio Obrero | 1| 0.01|
## |Barrio Tranquilo Y | 1| 0.01|
## |Base Aa^Sa(C)Rea | 2| 0.02|
## |Belalcazar | 4| 0.05|
## |Belisario Caicedo | 2| 0.02|
## |Bella Suiza | 18| 0.22|
## |Bella Suiza Alta | 4| 0.05|
## |Bellavista | 43| 0.52|
## |Benjama-N Herrera | 8| 0.10|
## |Berlin | 1| 0.01|
## |Bloques Del Limonar | 1| 0.01|
## |Bochalema | 33| 0.40|
## |Bolivariano | 1| 0.01|
## |Bosques De Alboleda | 1| 0.01|
## |Bosques Del Limonar | 20| 0.24|
## |Boyaca! | 1| 0.01|
## |Bretaa+/-A | 16| 0.19|
## |Brisas De Guadalupe | 1| 0.01|
## |Brisas De Los | 81| 0.98|
## |Brisas Del Guabito | 1| 0.01|
## |Brisas Del Limonar | 1| 0.01|
## |Bueno Madrid | 1| 0.01|
## |Buenos Aires | 7| 0.08|
## |Caa+/-Asgordas | 7| 0.08|
## |Caa+/-Averalejo | 12| 0.15|
## |Caa+/-Averales | 21| 0.25|
## |Caa+/-Averales Los Samanes | 1| 0.01|
## |Caldas | 1| 0.01|
## |Cali | 37| 0.45|
## |Cali Bella | 1| 0.01|
## |Cali Canto | 1| 0.01|
## |Calibella | 1| 0.01|
## |Calicanto | 8| 0.10|
## |Calicanto Vii | 1| 0.01|
## |Calima | 6| 0.07|
## |Calimio Norte | 5| 0.06|
## |Calipso | 11| 0.13|
## |Cambulos | 3| 0.04|
## |Camino Real | 36| 0.44|
## |Campestre | 1| 0.01|
## |Caney | 88| 1.07|
## |Caney Especial | 5| 0.06|
## |Capri | 56| 0.68|
## |Cascajal | 1| 0.01|
## |Cataya Real | 1| 0.01|
## |Ceibas | 1| 0.01|
## |Centelsa | 1| 0.01|
## |Centenario | 16| 0.19|
## |Centro | 4| 0.05|
## |Cerro Cristales | 22| 0.27|
## |Cerros De Guadalupe | 1| 0.01|
## |Champagnat | 14| 0.17|
## |Chapinero | 7| 0.08|
## |Chiminangos | 17| 0.21|
## |Chiminangos 1 Etapa | 1| 0.01|
## |Chiminangos 2 Etapa | 2| 0.02|
## |Chipichape | 30| 0.36|
## |Ciudad 2000 | 94| 1.14|
## |Ciudad Antejardin | 1| 0.01|
## |Ciudad Bochalema | 47| 0.57|
## |Ciudad Capri | 13| 0.16|
## |Ciudad Cardoba | 15| 0.18|
## |Ciudad Cardoba Reservado | 1| 0.01|
## |Ciudad Cordoba | 20| 0.24|
## |Ciudad Country | 1| 0.01|
## |Ciudad Del Campo | 1| 0.01|
## |Ciudad Jarda-N | 514| 6.22|
## |Ciudad Jardin | 22| 0.27|
## |Ciudad Jardin Pance | 1| 0.01|
## |Ciudad Los A!Lamos | 25| 0.30|
## |Ciudad Los Alamos | 1| 0.01|
## |Ciudad Mela^Sa(C)Ndez | 1| 0.01|
## |Ciudad Melendez | 1| 0.01|
## |Ciudad Modelo | 7| 0.08|
## |Ciudad Pacifica | 3| 0.04|
## |Ciudad Real | 3| 0.04|
## |Ciudad Talanga | 1| 0.01|
## |Ciudad Universitaria | 1| 0.01|
## |Ciudadela Comfandi | 17| 0.21|
## |Ciudadela Del Ra-O | 1| 0.01|
## |Ciudadela Melendez | 1| 0.01|
## |Ciudadela Paso Ancho | 1| 0.01|
## |Ciudadela Pasoancho | 21| 0.25|
## |Colinas De Menga | 3| 0.04|
## |Colinas Del Bosque | 1| 0.01|
## |Colinas Del Sur | 7| 0.08|
## |Colon | 1| 0.01|
## |Colseguros | 44| 0.53|
## |Colseguros Andes | 5| 0.06|
## |Comfenalco | 1| 0.01|
## |Compartir | 1| 0.01|
## |Conjunto Gibraltar | 1| 0.01|
## |Cristabal Colan | 2| 0.02|
## |Cristales | 83| 1.00|
## |Cristobal Colan | 14| 0.17|
## |Cuarto De Legua | 43| 0.52|
## |Departamental | 29| 0.35|
## |Ed Benjamin Herrera | 1| 0.01|
## |El Bosque | 50| 0.61|
## |El Caney | 205| 2.48|
## |El Castillo | 5| 0.06|
## |El Cedro | 8| 0.10|
## |El Diamante | 2| 0.02|
## |El Dorado | 6| 0.07|
## |El Gran Limonar | 8| 0.10|
## |El Guabal | 19| 0.23|
## |El Guabito | 1| 0.01|
## |El Ingenio | 203| 2.46|
## |El Ingenio 3 | 1| 0.01|
## |El Ingenio I | 19| 0.23|
## |El Ingenio Ii | 40| 0.48|
## |El Jarda-N | 15| 0.18|
## |El Jorda!N | 1| 0.01|
## |El Lido | 58| 0.70|
## |El Limonar | 133| 1.61|
## |El Nacional | 1| 0.01|
## |El Paraa-So | 3| 0.04|
## |El Pea+/-On | 59| 0.71|
## |El Prado | 2| 0.02|
## |El Refugio | 119| 1.44|
## |El Rodeo | 1| 0.01|
## |El Sena | 1| 0.01|
## |El Tra^Sa(C)Bol | 5| 0.06|
## |El Troncal | 19| 0.23|
## |El Vallado | 1| 0.01|
## |Eucara-Stico | 2| 0.02|
## |Evaristo Garca-A | 2| 0.02|
## |Farrallones De Pance | 1| 0.01|
## |Fenalco Kennedy | 1| 0.01|
## |Fepicol | 1| 0.01|
## |Flora | 1| 0.01|
## |Flora Industrial | 16| 0.19|
## |Floralia | 6| 0.07|
## |Fonaviemcali | 1| 0.01|
## |Francisco Eladio Ramirez | 1| 0.01|
## |Fuentes De La | 1| 0.01|
## |Gaitan | 1| 0.01|
## |Gran Limonar | 23| 0.28|
## |Granada | 15| 0.18|
## |Guadalupe | 21| 0.25|
## |Guadalupe Alto | 1| 0.01|
## |Guaduales | 2| 0.02|
## |Guayaquil | 16| 0.19|
## |Hacienda Alferez Real | 1| 0.01|
## |Ingenio | 1| 0.01|
## |Ingenio I | 1| 0.01|
## |Ingenio Ii | 1| 0.01|
## |Jamundi | 4| 0.05|
## |Jamundi Alfaguara | 1| 0.01|
## |Jorge Eliecer Gaita!N | 1| 0.01|
## |Jorge Isaacs | 1| 0.01|
## |Jose Manuel Marroqua-N | 1| 0.01|
## |Juanamba^Sa^<< | 53| 0.64|
## |Juanambu | 2| 0.02|
## |Juna-N | 6| 0.07|
## |Junin | 18| 0.22|
## |La Alborada | 5| 0.06|
## |La Alianza | 4| 0.05|
## |La Arboleda | 18| 0.22|
## |La Base | 15| 0.18|
## |La Buitrera | 3| 0.04|
## |La Campia+/-A | 13| 0.16|
## |La Cascada | 7| 0.08|
## |La Ceibas | 1| 0.01|
## |La Esmeralda | 1| 0.01|
## |La Flora | 363| 4.39|
## |La Floresta | 17| 0.21|
## |La Fortaleza | 4| 0.05|
## |La Gran Colombia | 1| 0.01|
## |La Hacienda | 165| 2.00|
## |La Independencia | 12| 0.15|
## |La Libertad | 2| 0.02|
## |La Luisa | 1| 0.01|
## |La Merced | 26| 0.31|
## |La Morada | 1| 0.01|
## |La Nueva Base | 8| 0.10|
## |La Playa | 1| 0.01|
## |La Portada Al | 1| 0.01|
## |La Primavera | 1| 0.01|
## |La Reforma | 1| 0.01|
## |La Rivera | 11| 0.13|
## |La Rivera I | 2| 0.02|
## |La Rivera Ii | 2| 0.02|
## |La Riverita | 1| 0.01|
## |La Riviera | 1| 0.01|
## |La Selva | 11| 0.13|
## |La Villa Del | 1| 0.01|
## |Laflora | 1| 0.01|
## |Lares De Comfenalco | 1| 0.01|
## |Las Acacias | 12| 0.15|
## |Las Americas | 3| 0.04|
## |Las Camelias | 1| 0.01|
## |Las Ceibas | 23| 0.28|
## |Las Delicias | 5| 0.06|
## |Las Granjas | 10| 0.12|
## |Las Quintas De | 1| 0.01|
## |Las Vegas | 1| 0.01|
## |Las Vegas De | 1| 0.01|
## |Libertadores | 3| 0.04|
## |Los Alamos | 1| 0.01|
## |Los Alca!Zares | 5| 0.06|
## |Los Alcazares | 17| 0.21|
## |Los Andes | 21| 0.25|
## |Los Ca!Mbulos | 6| 0.07|
## |Los Cambulos | 25| 0.30|
## |Los Cristales | 154| 1.86|
## |Los Cristales Club | 1| 0.01|
## |Los Farallones | 4| 0.05|
## |Los Guaduales | 26| 0.31|
## |Los Guayacanes | 3| 0.04|
## |Los Jockeys | 1| 0.01|
## |Los Libertadores | 4| 0.05|
## |Los Parques Barranquilla | 6| 0.07|
## |Los Robles | 1| 0.01|
## |Lourdes | 2| 0.02|
## |Mamellan | 1| 0.01|
## |Manzanares | 5| 0.06|
## |Mariano Ramos | 1| 0.01|
## |Marroqua-N Ii | 1| 0.01|
## |Mayapan Las Vegas | 46| 0.56|
## |Mela^Sa(C)Ndez | 22| 0.27|
## |Melendez | 52| 0.63|
## |Menga | 23| 0.28|
## |Metropolitano Del Norte | 21| 0.25|
## |Miradol Del Aguacatal | 1| 0.01|
## |Miraflores | 26| 0.31|
## |Morichal De Comfandi | 3| 0.04|
## |Multicentro | 27| 0.33|
## |Municipal | 3| 0.04|
## |Na!Poles | 29| 0.35|
## |Napoles | 2| 0.02|
## |Normanda-A | 153| 1.85|
## |Normanda-A West Point | 1| 0.01|
## |Normandia | 5| 0.06|
## |Norte | 9| 0.11|
## |Norte La Flora | 1| 0.01|
## |Nueva Base | 1| 0.01|
## |Nueva Floresta | 15| 0.18|
## |Nueva Tequendama | 72| 0.87|
## |Oasis De Comfandi | 6| 0.07|
## |Oasis De Pasoancho | 1| 0.01|
## |Occidente | 11| 0.13|
## |Pacara | 19| 0.23|
## |Pacara! | 4| 0.05|
## |Palmas Del Ingenio | 1| 0.01|
## |Pampa Linda | 26| 0.31|
## |Pampalinda | 12| 0.15|
## |Panamericano | 9| 0.11|
## |Pance | 410| 4.96|
## |Parcelaciones Pance | 61| 0.74|
## |Parque Residencial El | 1| 0.01|
## |Paseo De Los | 2| 0.02|
## |Paso Del Comercio | 6| 0.07|
## |Pasoancho | 6| 0.07|
## |Poblado Campestre | 2| 0.02|
## |Ponce | 1| 0.01|
## |Popular | 6| 0.07|
## |Portada De Comfandi | 2| 0.02|
## |Portales De Comfandi | 1| 0.01|
## |Porvenir | 3| 0.04|
## |Prados De Oriente | 6| 0.07|
## |Prados Del Limonar | 21| 0.25|
## |Prados Del Norte | 126| 1.53|
## |Prados Del Sur | 2| 0.02|
## |Primavera | 2| 0.02|
## |Primero De Mayo | 37| 0.45|
## |Primitivo Crespo | 3| 0.04|
## |Puente Del Comercio | 6| 0.07|
## |Puente Palma | 1| 0.01|
## |Quintas De Don | 72| 0.87|
## |Quintas De Salomia | 4| 0.05|
## |Rafael Uribe Uribe | 1| 0.01|
## |Refugio | 2| 0.02|
## |Repa^Sa^<<Blica De Israel | 1| 0.01|
## |Rincan De Salomia | 1| 0.01|
## |Rincon De La | 1| 0.01|
## |Riveras Del Valle | 1| 0.01|
## |Rozo La Torre | 1| 0.01|
## |Saavedra Galindo | 4| 0.05|
## |Salomia | 39| 0.47|
## |Samanes | 1| 0.01|
## |Samanes De Guadalupe | 1| 0.01|
## |Sameco | 1| 0.01|
## |San Antonio | 24| 0.29|
## |San Bosco | 8| 0.10|
## |San Carlos | 4| 0.05|
## |San Cayetano | 9| 0.11|
## |San Fernando | 55| 0.67|
## |San Fernando Nuevo | 10| 0.12|
## |San Fernando Viejo | 18| 0.22|
## |San Joaqua-N | 16| 0.19|
## |San Joaquin | 4| 0.05|
## |San Juan Bosco | 7| 0.08|
## |San Judas | 1| 0.01|
## |San Judas Tadeo | 2| 0.02|
## |San Lua-S | 1| 0.01|
## |San Luis | 2| 0.02|
## |San Nicola!S | 1| 0.01|
## |San Nicolas | 1| 0.01|
## |San Pedro | 3| 0.04|
## |San Vicente | 47| 0.57|
## |Santa | 1| 0.01|
## |Santa Anita | 50| 0.61|
## |Santa Anita Sur | 1| 0.01|
## |Santa Ba!Rbara | 3| 0.04|
## |Santa Elena | 10| 0.12|
## |Santa Fe | 8| 0.10|
## |Santa Helena De | 1| 0.01|
## |Santa Isabel | 63| 0.76|
## |Santa Manica | 3| 0.04|
## |Santa Manica Alta | 1| 0.01|
## |Santa Manica Popular | 7| 0.08|
## |Santa Manica Residencial | 39| 0.47|
## |Santa Monica | 52| 0.63|
## |Santa Monica Norte | 2| 0.02|
## |Santa Monica Popular | 2| 0.02|
## |Santa Monica Residencial | 5| 0.06|
## |Santa Rita | 45| 0.54|
## |Santa Rosa | 1| 0.01|
## |Santa Teresita | 263| 3.18|
## |Santafe | 1| 0.01|
## |Santander | 1| 0.01|
## |Santo Domingo | 6| 0.07|
## |Sector Aguacatal | 1| 0.01|
## |Sector Caa+/-Averalejo Guadalupe | 2| 0.02|
## |Seminario | 32| 0.39|
## |Sierras De Normanda-A | 1| 0.01|
## |Siete De Agosto | 8| 0.10|
## |Siman Bolivar | 1| 0.01|
## |Tejares Cristales | 4| 0.05|
## |Tejares De San | 14| 0.17|
## |Templete | 4| 0.05|
## |Tequendama | 43| 0.52|
## |Tequendema | 1| 0.01|
## |Terran Colorado | 1| 0.01|
## |Torres De Comfandi | 57| 0.69|
## |Unian De Vivienda | 3| 0.04|
## |Unicentro Cali | 1| 0.01|
## |Urbanizacian Barranquilla | 4| 0.05|
## |Urbanizacian Boyaca! | 1| 0.01|
## |Urbanizacian Colseguros | 3| 0.04|
## |Urbanizacian La Flora | 83| 1.00|
## |Urbanizacian La Merced | 4| 0.05|
## |Urbanizacian La Nueva | 4| 0.05|
## |Urbanizacian Las Cascadas | 1| 0.01|
## |Urbanizacian Nueva Granada | 3| 0.04|
## |Urbanizacian Pacara | 1| 0.01|
## |Urbanizacian Ra-O Lili | 5| 0.06|
## |Urbanizacian San Joaquin | 4| 0.05|
## |Urbanizacian Tequendama | 7| 0.08|
## |Urbanizacion El Saman | 1| 0.01|
## |Urbanizacion Gratamira | 1| 0.01|
## |Urbanizacion Lili | 2| 0.02|
## |Valle De Lili | 1| 0.01|
## |Valle Del Lili | 995| 12.04|
## |Valle Grande | 1| 0.01|
## |Versalles | 71| 0.86|
## |Villa Colombia | 6| 0.07|
## |Villa De Veracruz | 6| 0.07|
## |Villa Del Lago | 10| 0.12|
## |Villa Del Parque | 1| 0.01|
## |Villa Del Prado | 52| 0.63|
## |Villa Del Sol | 25| 0.30|
## |Villa Del Sur | 5| 0.06|
## |Villas De Veracruz | 9| 0.11|
## |Vipasa | 32| 0.39|
## |Zona Centro | 1| 0.01|
## |Zona Norte | 32| 0.39|
## |Zona Norte Los | 1| 0.01|
## |Zona Oeste | 26| 0.31|
## |Zona Oriente | 18| 0.22|
## |Zona Residencial | 1| 0.01|
## |Zona Sur | 74| 0.90|
En esta sección se identifican valores atípicos e inconsistencias para cada variable.
# Seleccionar variables numéricas
vivienda_num <- vivienda %>% select(where(is.numeric))
# Función para contar outliers por IQR
contar_outliers <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
sum(x < (Q1 - 1.5 * IQR) | x > (Q3 + 1.5 * IQR), na.rm = TRUE)
}
# Aplicar a todas las variables
outliers_por_variable <- sapply(vivienda_num, contar_outliers)
# Convertir en tabla
tabla_outliers <- data.frame(
Variable = names(outliers_por_variable),
N_Outliers = outliers_por_variable
)
knitr::kable(tabla_outliers, caption = "Número de valores atípicos por variable numérica")| Variable | N_Outliers | |
|---|---|---|
| preciom | preciom | 552 |
| areaconst | areaconst | 381 |
| parqueaderos | parqueaderos | 565 |
| banios | banios | 72 |
| habitaciones | habitaciones | 884 |
| longitud | longitud | 130 |
| latitud | latitud | 0 |
for (var in names(vivienda_num)) {
print(
ggplot(vivienda, aes_string(y = var)) +
geom_boxplot(fill = "#fdbb84", color = "#e34a33", outlier.color = "black") +
theme_minimal() +
labs(title = paste("Boxplot -", var), y = var)
)
}
Como se puede observar, en este análisis solo la variable latitud no
presenta valores faltantes.
Este análisis es muy importante para determinar que debemos hacer con estos valores antes de aplicar el análisis de Componentes Principales:
Variable preciom: Hay una gran cantidad de valores atípicos altos (outliers en la parte superior del boxplot), es decir, precios de vivienda significativamente superiores al resto, como son datos importantes provenientes de valores recuperados con el webscrapping y presentes en la base de datos, se toma la desición de aplicar transformación logaritmica para normalizar.
Variable areaconst: El comportamiento es similar al de la variable preciom, por eso también se toma la desición de aplicar transformación logaritmica para normalizar.
Variable parqueaderos: Se toma la desición de aplicar transformación logaritmica para normalizar, sin embargo la distribución no es muy sesgada..
Variable banios: Se toma la desición de no aplicar transformación logaritmica para normalizar, ya que la distribución es aceptable.
Variablehabitaciones: Al igual que la variable banios, se toma la desición de no aplicar transformación logaritmica para normalizar, ya que la distribución es aceptable.
library(dplyr)
# Variables transformadas y selección para ACP
variables_acp <- vivienda %>%
mutate(
log_preciom = log1p(preciom),
log_areaconst = log1p(areaconst),
log_parqueaderos = log1p(parqueaderos)
) %>%
select(log_preciom, log_areaconst, log_parqueaderos, banios, habitaciones) %>%
mutate(across(everything(), as.numeric))
head(variables_acp)library(FactoMineR)
library(factoextra)
library(mice)
#Revisar datos faltantes
colSums(is.na(variables_acp))## log_preciom log_areaconst log_parqueaderos banios
## 0 0 0 0
## habitaciones
## 0
El análisis indica que no se presentan datos faltantes
Ahora procedemos a estandarizar las variables numéricas para el ACP. Con el fin de evitar que las variables que tiene una escala con valores más grandes afecten las estimaciones realizadas (sesgos) se realiza la estandarización de las variables antes de proceder a realizar el proceso de estimación de los componentes principales.
## log_preciom log_areaconst log_parqueaderos banios
## "numeric" "numeric" "numeric" "numeric"
## habitaciones
## "numeric"
# Estandarización
variables_acp_scaled <- scale(variables_acp)
# Visualizar primeras filas
head(variables_acp_scaled)## log_preciom log_areaconst log_parqueaderos banios habitaciones
## [1,] -0.47819099 -1.0132540 -1.0565972 -0.0791619 1.6391412
## [2,] -0.11036655 -0.2071974 -1.0565972 -0.7785806 -0.4155266
## [3,] 0.02323056 0.7035816 0.3133364 -0.7785806 0.2693627
## [4,] 0.22236653 1.0667488 1.2853198 1.3196755 -0.4155266
## [5,] -0.41977368 -0.6380095 -1.0565972 -0.7785806 -0.4155266
## [6,] -0.53898364 -0.6886954 -1.0565972 -0.0791619 -0.4155266
Luego se elige el número de componentes principales:
En este caso el primer componente principal explica el 65.4% de la variabilidad contenida en la base de datos, entre los dos primeros el 82%, y junto con el tercero casi el 92% de la variavilidad contenida en la base de datos.
Teniendo en cuenta el criterio del codo, el gráfico muestra un quiebre pronunciado después de la primera dimensión y un segundo quiebre después de la segunda.
En este ejercicio se tienen en cuenta solo los dos primeros componentes ya que explican más del 80% de la varianza del modelo.
fviz_pca_var(res.pca,
col.var = "contrib",# Mostramos Dim1 vs Dim2
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE)Este gráfico Sree Plot muestra la relación entre las variables originales y las dos primeras dimensiones principales (Dim1 y Dim2).
Las variables que tienen flechas en direcciones similares, lo que significa que están positivamente correlacionadas son log_preciom, log_areaconst, banios y log_parqueaderos, lo cual indica que a mayor área construida y parqueaderos, tiende a ser mayor el precio.
Las variables que tienen flechas en direcciones opuestas, lo que significa que están negativamente correlacionadas son habitaciones, lo cual indica que que tener más habitaciones no necesariamente está asociado a mayor precio, sin embargo la variable baño puede que influya en el valor, pero no tan significativamente.
A continuación realizo el calculo de correlaciones para verificar el resultado anterior:
library(factoextra)
# Extraer información de las variables
var <- get_pca_var(res.pca)
# Correlaciones (cargas) - solo Dim1 y Dim2
coord_tbl <- round(var$coord[, 1:2], 2)
# Contribuciones (%) - solo Dim1 y Dim2
contrib_tbl <- round(var$contrib[, 1:2], 2)
# Calidad de representación (cos2) - solo Dim1 y Dim2
cos2_tbl <- round(var$cos2[, 1:2], 2)
# Mostrar tablas
coord_tbl## Dim.1 Dim.2
## log_preciom 0.86 0.27
## log_areaconst 0.92 -0.03
## log_parqueaderos 0.68 0.52
## banios 0.89 -0.13
## habitaciones 0.65 -0.69
## Dim.1 Dim.2
## log_preciom 22.86 8.69
## log_areaconst 25.65 0.12
## log_parqueaderos 14.24 32.82
## banios 24.15 1.90
## habitaciones 13.10 56.47
## Dim.1 Dim.2
## log_preciom 0.75 0.07
## log_areaconst 0.84 0.00
## log_parqueaderos 0.47 0.27
## banios 0.79 0.02
## habitaciones 0.43 0.47
Se puede observar que en Dim1,las variables que más aportan son log_areaconst (25.65%) y banios (24.15%) y en Dim2, la más importante es habitaciones (56.47%), y a su vez esta última no está muy bien representada en Dim1.
library(factoextra)
# Calcular distancia desde el centro (0,0) usando las dos primeras componentes
distancias <- apply(res.pca$x[, 1:2], 1, function(x) sqrt(sum(x^2)))
# Seleccionar los 5 casos más extremos
casos_extremos <- order(distancias, decreasing = TRUE)[1:5]
# Extraer coordenadas de los casos extremos
casos_df <- as.data.frame(res.pca$x[casos_extremos, 1:2])
colnames(casos_df) <- c("Dim1", "Dim2")
rownames(res.pca$x) <- 1:nrow(res.pca$x)
# Crear la columna 'nombre'
casos_df$nombre <- rownames(res.pca$x)[casos_extremos]
head(casos_df)# Graficar con nombres
fviz_pca_ind(res.pca,
geom.ind = "point",
col.ind = "grey80",
title = "Casos extremos en el plano PCA") +
geom_point(data = casos_df, aes(x = Dim1, y = Dim2),
color = "red", size = 3) +
geom_text(data = casos_df, aes(x = Dim1, y = Dim2, label = nombre),
vjust = -1, color = "red", size = 4)Este gráfico muestra los casos extremos (outliers) en el plano definido por las dos primeras dimensiones del ACP.
sugiere que los registros IDs 6185, 460, 6031, 5914, 2777 tienen combinaciones de variables muy diferentes respecto al resto, principalmente en lo que define Dim1, lo cual puede deberse a Valores muy altos en variables como log_areaconst, banios o log_preciom, que tienen fuerte peso en Dim1, o inmubles con caracteristicas muy diferentes a la mayoría del dataset.
fviz_pca_biplot(res.pca,
repel = TRUE,
col.ind = alpha("skyblue", 0.5), # Transparencia
col.var = "red",
pointshape = 16,
pointsize = 1
)La interpretacipon de este gráfico Biplot comprueba lo observado anetriormente, donde log_precioM, log_areaconst y log_parqueaderos, están correlacionadas positivamente y baños y habitaciones también se proyectan bien, pero en direcciones distintas.
Dentro de mi análisis se habían estadarizado las variables para realizar el Análisis de Componentes Principales, por eso a continuación solo voy a verificar que estén correctas:
## Importance of components:
## PC1 PC2 PC3 PC4 PC5
## Standard deviation 1.8082 0.9118 0.70308 0.51888 0.3681
## Proportion of Variance 0.6539 0.1663 0.09886 0.05385 0.0271
## Cumulative Proportion 0.6539 0.8202 0.91905 0.97290 1.0000
## log_preciom log_areaconst log_parqueaderos banios habitaciones
## [1,] -0.47819099 -1.0132540 -1.0565972 -0.0791619 1.6391412
## [2,] -0.11036655 -0.2071974 -1.0565972 -0.7785806 -0.4155266
## [3,] 0.02323056 0.7035816 0.3133364 -0.7785806 0.2693627
## [4,] 0.22236653 1.0667488 1.2853198 1.3196755 -0.4155266
## [5,] -0.41977368 -0.6380095 -1.0565972 -0.7785806 -0.4155266
## [6,] -0.53898364 -0.6886954 -1.0565972 -0.0791619 -0.4155266
El análisis de componentes principales permitió reducir las 5 variables originales (precio, área construida, parqueaderos, baños, habitaciones) a 2 componentes que explican el 82% de la variabilidad de la oferta.
La primera componente está asociada principalmente a tamaño y valor de la propiedad, mientras que la segunda recoge diferencias en la distribución interna y dotaciones adicionales.
Esto permite simplificar el análisis y facilitar la agrupación de propiedades en segmentos homogéneos.
#Utilizar solo las dos primeras componentes principales
coord_pca <- res.pca$x[, 1:2] # Solo Dim1 y Dim2
colnames(coord_pca) <- c("Dim1", "Dim2")
head(coord_pca)## Dim1 Dim2
## 1 -0.5862375 -1.93227966
## 2 -1.0894133 -0.21119752
## 3 0.2005947 0.06697136
## 4 1.6297549 0.89563241
## 5 -1.4555524 -0.28755378
## 6 -1.1945276 -0.41730074
Una vez seleccionadas las dos componentes principales realizo el cálculo de las distancias Euclidiana, de Manhattan y Minkowski:
# Distancia euclidiana
dist_eucl <- dist(coord_pca, method = "euclidean")
# Distancia Manhattan
dist_manh <- dist(coord_pca, method = "manhattan")
# Distancia Minkowski (p=2 es igual a Euclidiana)
dist_mink <- dist(coord_pca, method = "minkowski", p = 2)
# Mostrar primeras filas de cada matriz de distancias
"Matriz de distancias euclidianas"## [1] "Matriz de distancias euclidianas"
## 1 2 3 4 5 6 7 8
## 1 0.000000 1.7931284 2.1485134 3.592730 1.8603310 1.6325373 2.5434342 1.9948040
## 2 1.793128 0.0000000 1.3196585 2.935805 0.3740162 0.2313602 0.7654938 1.1963996
## 3 2.148513 1.3196585 0.0000000 1.652022 1.6936680 1.4767822 1.7158746 0.1623698
## 4 3.592730 2.9358045 1.6520224 0.000000 3.3043987 3.1145409 3.1328024 1.8051648
## 5 1.860331 0.3740162 1.6936680 3.304399 0.0000000 0.2914931 0.7398487 1.5695634
## 6 1.632537 0.2313602 1.4767822 3.114541 0.2914931 0.0000000 0.9124740 1.3406796
## 7 2.543434 0.7654938 1.7158746 3.132802 0.7398487 0.9124740 0.0000000 1.6495773
## 8 1.994804 1.1963996 0.1623698 1.805165 1.5695634 1.3406796 1.6495773 0.0000000
## [1] "Matriz de distancias Manhattan"
## 1 2 3 4 5 6 7 8
## 1 0.000000 2.2242579 2.7860833 5.043904 2.5140408 2.1232690 3.2696602 2.5574882
## 2 2.224258 0.0000000 1.5681769 3.825998 0.4424953 0.3112175 1.0454023 1.3395818
## 3 2.786083 1.5681769 0.0000000 2.257821 2.0106722 1.8793944 2.0572414 0.2285951
## 4 5.043904 3.8259981 2.2578212 0.000000 4.2684935 4.1372156 3.5447591 2.4864163
## 5 2.514041 0.4424953 2.0106722 4.268493 0.0000000 0.3907718 0.7556195 1.7820772
## 6 2.123269 0.3112175 1.8793944 4.137216 0.3907718 0.0000000 1.1463913 1.6507993
## 7 3.269660 1.0454023 2.0572414 3.544759 0.7556195 1.1463913 0.0000000 2.0789711
## 8 2.557488 1.3395818 0.2285951 2.486416 1.7820772 1.6507993 2.0789711 0.0000000
## [1] "Matriz de distancias Minkowski"
## 1 2 3 4 5 6 7 8
## 1 0.000000 1.7931284 2.1485134 3.592730 1.8603310 1.6325373 2.5434342 1.9948040
## 2 1.793128 0.0000000 1.3196585 2.935805 0.3740162 0.2313602 0.7654938 1.1963996
## 3 2.148513 1.3196585 0.0000000 1.652022 1.6936680 1.4767822 1.7158746 0.1623698
## 4 3.592730 2.9358045 1.6520224 0.000000 3.3043987 3.1145409 3.1328024 1.8051648
## 5 1.860331 0.3740162 1.6936680 3.304399 0.0000000 0.2914931 0.7398487 1.5695634
## 6 1.632537 0.2313602 1.4767822 3.114541 0.2914931 0.0000000 0.9124740 1.3406796
## 7 2.543434 0.7654938 1.7158746 3.132802 0.7398487 0.9124740 0.0000000 1.6495773
## 8 1.994804 1.1963996 0.1623698 1.805165 1.5695634 1.3406796 1.6495773 0.0000000
library(cluster)
# Agrupamiento jerárquico con método de enlace (por ejemplo, completo)
hc <- hclust(dist_eucl, method = "complete")
# Calculo el K a utilizar:
sil_width <- c()
for (k in 2:10) {
grupos <- cutree(hc, k = k)
sil <- silhouette(grupos, dist_eucl)
sil_width[k] <- mean(sil[, 3])
}
# k óptimo
which.max(sil_width)## [1] 2
# Gráfico silhouette vs k
plot(1:10, sil_width, type = "b", pch = 19,
xlab = "Numero de clusters (k)", ylab = "Silhouette promedio")Realicé la prueba de silhouette para determinar cual es el mejor K, para hacer el dendograma, y determiné utilizar un k=2, porque el índice Silhouette promedio alcanzó su valor máximo en K=2.
Ahora Procedo a graficar el Dendograma, a partir de la Distancia Euclidiana, ya que funciona bien porque las variables están estandarizadas, y cada dimensión contribuye proporcionalmente:
# Clúster jerárquico con método complete
hc <- hclust(dist_eucl, method = "complete")
# Graficar dendrograma con rectángulo de clusters
plot(hc, labels = FALSE, hang = -1,
main = "Dendrograma - 2 primeras componentes", xlab = "", sub = "")El dendrograma identifica dos grupos de propiedades con diferencias claras en precio, área, construida, número de parqueaderos, baños y habitaciones, lo que refleja las diferentes dinámicas de oferta en la ciudad, que se pueden asociar a la ubicación y el estrato socioeconómico.
alturas <- sort(hc$height, decreasing = TRUE)
barplot(head(alturas, 10), horiz = TRUE, col = "lightblue",
main = "Agregaciones (distancias euclidianas)",
ylab = "Nodo", xlab = "Peso")El salto más grande en las barras ocurre cerca del valor 10 (última barra), lo que indica que en ese punto se unen dos grandes grupos muy diferentes entre sí, aquí se observa que el corte óptimo del dendrograma es k = 2, lo que coindice con el cálculo realizado del índice Silhouette, por lo tanto el gráfico de barras y el dendrograma sugieren que dos grupos es la solución más coherente para nuestro set de datos.
Para realizar el análisis de Correspondencias (AC) se seleccionarion las variables Zona y Estratos:
Primero seleccionamos solo las variables necesarias
Luego tomo una muestra aleatoria de mi base de datos
#Muestra aleatoria de tamaño 4000
set.seed(1234)
vivienda_ac <- sample_n(vivienda_ac, min(4000, nrow(vivienda_ac)))Verifico que haya realizado correctamente la limpieza de datos faltantes:
## /\ /\
## { `---' }
## { O O }
## ==> V <== No need for mice. This data set is completely observed.
## \ \|/ /
## `-----'
## zona estrato
## 4000 1 1 0
## 0 0 0
Posteriormente, creo mi tabla de contingencia con las variables seleccionadas:
##
## 3 4 5 6
## Zona Centro 59 5 2 1
## Zona Norte 272 203 372 81
## Zona Oeste 25 33 143 371
## Zona Oriente 173 0 2 0
## Zona Sur 169 773 809 507
Realizo la prueba Chi Cuadrado para verificar la relación entre las variables:
##
## Pearson's Chi-squared test
##
## data: tabla_ac
## X-squared = 1978.1, df = 12, p-value < 2.2e-16
La prueba Chi cuadrado, indica que El p-value es menor a 0.05, por lo tanto rechazamos la hipótesis nula, es decir que sí existe asociación entre las dos variables categóricas seleccionadas que son Zona y Estrato.
Posteriormente realizo análisis de correspondencia:
Las Dim 1 (70.89%) y Dim 2 (27.11%) suman 98% de la variabilidad de la relación, lo que significa que casi toda la información de asociación se representa en este plano.
El gráfico muestra asociaciones claras: Zona Oeste se vincula principalmente con Estrato 6, mientras que Zona Norte se asocia con Estratos 4 y 5. Por su parte, Zona Oriente y Zona Centro están más relacionadas con Estrato 3, lo que indica que la distribución de estratos no es aleatoria entre las zonas, sino que hay una relación marcada entre ubicación geográfica y nivel socioeconómico.
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.350568040 70.890152 70.89015
## dim 2 0.134041322 27.105180 97.99533
## dim 3 0.009913545 2.004669 100.00000
En el análisis de la Varianza explicada, se encuentra que los dos primeros ejes explican el 97,99 % de la varianza, lo que significa que el plano factorial del gráfico captura casi toda la información relevante de la relación entre las categorías, lo que respalda que interpretar solo Dim 1 y Dim 2 es suficiente para entender las asociaciones observadas en el análisis de correspondencia, ya que la pérdida de información es mínima (solo un 2 %).
# Gráfico Screeplot (porcentaje de varianza explicada por eje)
fviz_screeplot(resultados_ac, addlabels = TRUE, ylim = c(0, 80)) +
ggtitle("") +
ylab("Porcentaje de varianza explicado") +
xlab("Ejes")Este gráfico es el scree plot del análisis de correspondencia, y muestra cuánta varianza explica cada dimensión, lo que confrma el gráfico anterior y los cálculos de varianza explicada, encontrando que En conjunto, Dim 1 + Dim 2 explican 97,99 % de la información.
• El Análisis Exploratorio de Datos es indispensable para cualquier otro tipo de análisis que se realice, una correcta detección e imputación de datos faltantes, en gran medida asegura los resultados óptimos de la elaboración de Modelos descriptivos de análisis multivariado.
• En el análisis exploratorio de datos es importante saber a que tipo de variables nos estamos enfrentando, para poder darles el tratamiento de datos adecuados, por ejemplo, en un Análisis de Componente Principales se deben utilizar solo las variables numéricas porque osino podemos estar introduciendo información a la modelo innecesaria y que puede modificar nuestros resultados esperados, brindando resultados erróneos.
• Para la selección del número de componentes principales es importante tener claridad que el porcentaje de varianza acumulado sea alto, por encima del 80%, sin embargo, si las variables no fueron correctamente estandarizadas o no se tomaron el tipo de variables correctas llegar a ese 80% puede ser muy dispendioso, lo que puede llevar a pensar en una revisión del procedimiento y detectar posibles errores de limpieza o selección de datos.
• Para el análisis de Correspondencia es importante utilizar variables categóricas que permitan realizar un análisis más claro, por ejemplo, si se utilizaba la variable Barrios el gráfico biplot puede ser demasiado denso, y en este caso se puede filtrar solo los barrios con más frecuencia o mostrar solo un grupo de interés. Sin embargo, como estadísticamente es posible, sería utilizar otro tipo de gráficos para realizar el análisis.
• Es importante profundizar más en el tema de datos extremos, para tener claridad cómo manejar los datos atípicos y saber hasta que punto un dato puede considerarse extremo para saber que manejo debe darse o si sólo con la estandarización es suficiente para escalar los valores.
• Cada conglomerado agrupa observaciones con características similares en las variables originales, mientras que entre grupos se mantienen diferencias sustanciales, lo que confirma la utilidad de esta segmentación para posteriores interpretaciones y análisis descriptivos.
• El análisis multivariante, permitió reducir la dimensionalidad del conjunto de datos mediante ACP, mejorando la interpretabilidad de las variables más relevantes.
• El análisis de conglomerados, identificó segmentos bien definidos de propiedades con características internas homogéneas.
• El análisis de correspondencia evidenció relaciones estadísticamente significativas entre variables categóricas, aportando una representación gráfica que facilita la detección de patrones estructurales en la oferta inmobiliaria.
• El análisis integral realizado, a partir de Modelos descriptivos de análisis multivariado, incluyó técnicas de Análisis de Componentes Principales, Análisis de Conglomerados y Análisis de Correspondencia, lo que permitió reducir la complejidad de los datos y descubrir patrones, relaciones y segmentaciones relevantes en la oferta inmobiliaria urbana.
• El análisis de componentes principales permitió reducir las 5 variables originales (precio, área construida, parqueaderos, baños, habitaciones) a 2 componentes que explican el 82% de la variabilidad de la oferta. La primera componente está asociada principalmente a tamaño y valor de la propiedad, mientras que la segunda recoge diferencias en la distribución interna y dotaciones adicionales. Se identificaron agrupaciones de propiedades con características similares en variables como precio, área construida, número de habitaciones, parqueaderos y baños.
• Las variables que están positivamente correlacionadas son el precio, el área construida, los baños y los parqueaderos indican que a mayor área construida y parqueaderos, tiende a ser mayor el precio.
• La variable habitaciones que está negativamente correlacionadas indica que tener más habitaciones no necesariamente está asociado a mayor precio, sin embargo, la variable baño puede que influya en el valor, pero no tan significativamente.
• Se determinaron asociaciones claras entre determinadas zonas y niveles de estrato. Estos hallazgos evidencian que la ubicación y el estrato están fuertemente vinculados a la configuración de la oferta. La Zona Oeste de la ciudad se vincula principalmente con Estrato 6, mientras que Zona Norte se asocia con Estratos 4 y 5. Por su parte, Zona Oriente y Zona Centro están más relacionadas con Estrato 3, lo que indica que la distribución de estratos no es aleatoria entre las zonas, sino que hay una relación marcada entre ubicación geográfica y nivel socioeconómico.
• En conjunto, los resultados aportan información para optimizar la compra, venta y valoración de inmuebles, facilitando la detección de nichos de mercado, la focalización de estrategias comerciales y la toma de decisiones fundamentadas en un entorno competitivo y dinámico del mercado inmobiliario urbano.