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 consiste 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:
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 preferencias del mercado.
Inicialmente cargamos la base de datos “vivienda” y visualizamos el contenido de manera resumida.
ruta <- "C:/Users/jmont/OneDrive/Escritorio/3. ESTUDIOS/Metodos Estadisticos para la toma de decisiones/vivienda.csv"
datos_org <- read.csv(ruta, fileEncoding = "UTF-8")
str(datos_org)
## 'data.frame': 8322 obs. of 13 variables:
## $ id : int 8312 8311 8307 8296 8297 8298 8299 8300 8286 8287 ...
## $ zona : chr "Zona Oeste" "Zona Oeste" "Zona Oeste" "Zona Sur" ...
## $ piso : int 4 1 NA 2 NA NA 2 NA NA 2 ...
## $ estrato : int 6 6 5 3 5 5 6 5 5 5 ...
## $ preciom : int 1300 480 1200 220 330 1350 305 480 275 285 ...
## $ areaconst: num 318 300 800 150 112 390 125 280 74 120 ...
## $ parquea : int 2 1 4 1 2 8 2 4 1 2 ...
## $ banios : int 4 4 7 2 4 10 3 4 2 4 ...
## $ habitac : int 2 4 5 4 3 10 3 4 3 3 ...
## $ tipo : chr "Apartamento" "Casa" "Casa" "Casa" ...
## $ barrio : chr "arboleda" "normandía" "miraflores" "el guabal" ...
## $ longitud : num -76576 -76571 -76568 -76565 -76565 ...
## $ latitud : num 3454 3454 3455 3417 3408 ...
Se evidencia que pueden existir valores atipicos, como nulos o campos vacios que pueden generar problemas en el analisis por lo cual se identifican de la siguiente manera:
valores_nulos <- sapply(datos_org, function(x) sum(is.na(x)))
print(valores_nulos)
## id zona piso estrato preciom areaconst parquea banios
## 3 3 2638 3 2 3 1605 3
## habitac tipo barrio longitud latitud
## 3 3 3 3 3
Existe omisión de información en todas las variables, sin embargo, de manera significativa en “piso” y “parqueadero”.
Puesto que existen variables de tipo categórico, las cuales no pueden ser utilizadas en el análisis de componentes principales, por ejemplo, zona, estrato, tipo y barrio, por lo cual se descartaran con el e objetivo de trabajar aquellas de tipo numérico continuo y aquellas que no presenten datos nulos.
Debido a que no es claro que implicaciones tiene incluir las variables latitud, longitud, piso y parqueadero, ya hacen referencia a una ubicación espacial y tienen problemas de consistencia, se incluirán, ya que se trata de un análisis no supervisado, pero se conformaran 4 grupos con el objetivo de comparar las diferencias y como afecta incluir diferentes variables.
En el grupo 1: Se excluye
“id”,“zona”,“estrato”,“tipo”,“barrio”
En el grupo 2: Se excluye
“id”,“zona”,“estrato”,“tipo”,“barrio”,“latitud”,“longitud”
En el grupo 3: Se excluye
“id”,“zona”,“estrato”,“tipo”,“barrio”,“latitud”,“longitud”,“preciom”
En el grupo 4: Se excluye
“id”,“zona”,“estrato”,“tipo”,“barrio”,“latitud”,“longitud”,“preciom”,“piso”
datos_filtrados1 <- datos_org[complete.cases(datos_org), ]
datos_filtrados1 <- datos_filtrados1[, !(names(datos_filtrados1) %in% c("id","zona","estrato","tipo","barrio"))]
datos_filtrados2 <- datos_org[complete.cases(datos_org), ]
datos_filtrados2 <- datos_filtrados2[, !(names(datos_filtrados2) %in% c("id","zona","estrato","tipo","barrio","latitud","longitud"))]
datos_filtrados3 <- datos_org[complete.cases(datos_org), ]
datos_filtrados3 <- datos_filtrados3[, !(names(datos_filtrados3) %in% c("id","zona","estrato","tipo","barrio","latitud","longitud","preciom"))]
datos_filtrados4 <- datos_org[complete.cases(datos_org), ]
datos_filtrados4 <- datos_filtrados4[, !(names(datos_filtrados4) %in% c("id","zona","estrato","tipo","barrio","latitud","longitud","preciom","piso"))]
Es importante tener en cuenta que la media y varianza de cada variable, dista de manera significativa una de otra luego es necesario estandarizar la información con el objetivo de que algunas variables en específico no dominen la mayoría de los componentes por dicha condición.
A continuación, se aplica el Código que nos permite obtener los componentes principales de los cuales extraemos la “Rotación”, la cual hace referencia a el valor de los loadings 𝜙 para cada componente (eigenvector).
cat("\n","CASO 1", "\n", "\n")
##
## CASO 1
##
pca1 <- prcomp(datos_filtrados1, scale = TRUE)
pca1$rotation
## PC1 PC2 PC3 PC4 PC5
## piso -0.12419429 0.140030507 0.83265378 0.498751850 -0.02622018
## preciom 0.45612502 0.023479760 0.31182058 -0.270749207 0.35923399
## areaconst 0.47594221 -0.061642530 -0.03761373 0.009885332 0.48462313
## parquea 0.43038334 -0.002030926 0.23093194 -0.386247494 -0.74604671
## banios 0.47336703 -0.079030917 0.05244521 0.199989342 0.04673895
## habitac 0.35238408 -0.143580250 -0.34778227 0.696851175 -0.27506320
## longitud -0.08419934 -0.690401263 0.12451401 -0.047655559 -0.01128925
## latitud 0.09713146 0.687401741 -0.12451707 0.028076038 -0.02909333
## PC6 PC7 PC8
## piso 0.136988029 -0.05847574 0.005373496
## preciom -0.160129955 0.67005452 -0.131140106
## areaconst 0.605929735 -0.40065364 0.075052848
## parquea 0.208907244 -0.09627282 0.052468864
## banios -0.721333925 -0.44611873 0.073501341
## habitac 0.155221025 0.37993452 -0.067472472
## longitud 0.019514556 -0.12103938 -0.695214341
## latitud 0.001606765 -0.14068771 -0.693620592
Del caso 1 podemos evideciar que:
El primer componente se recoge información principalmente para aquellas variables distintas de latitud, longitud y piso.
El segundo componente recoge información principalmente de las variables latitud y longitud.
El tercer componente recoge información principalmente de la variable piso.
cat("\n","CASO 2", "\n", "\n")
##
## CASO 2
##
pca2 <- prcomp(datos_filtrados2, scale = TRUE)
pca2$rotation
## PC1 PC2 PC3 PC4 PC5
## piso -0.1303842 0.83216185 0.517488231 -0.02596413 0.1358974
## preciom 0.4559601 0.32247810 -0.264482021 0.35698936 -0.1622554
## areaconst 0.4798166 -0.04280053 0.009048408 0.48839255 0.6010120
## parquea 0.4315969 0.23714602 -0.378193665 -0.74696838 0.2128396
## banios 0.4785793 0.03420384 0.205573515 0.04099560 -0.7232466
## habitac 0.3599478 -0.37983485 0.690566723 -0.27150467 0.1600102
## PC6
## piso -0.05975193
## preciom 0.68146114
## areaconst -0.41002386
## parquea -0.10579715
## banios -0.45029456
## habitac 0.38725009
Del caso 2 podemos evideciar que:
El primer componente se recoge información principalmente para las variables de area construida y baños.
El segundo componente recoge información principalmente de las variable piso.
El tercer componente recoge información principalmente de la variable habitaciones.
cat("\n","CASO 3", "\n", "\n")
##
## CASO 3
##
pca3 <- prcomp(datos_filtrados3, scale = TRUE)
pca3$rotation
## PC1 PC2 PC3 PC4 PC5
## piso -0.1904344 0.93126788 0.28712908 -0.0787083 -0.08852557
## areaconst 0.5275034 0.05200875 -0.06855877 -0.8430171 -0.06047379
## parquea 0.4519911 0.27881703 -0.62946144 0.3813402 -0.41990835
## banios 0.5267006 0.17326595 0.10299285 0.2760560 0.77830006
## habitac 0.4514038 -0.14924878 0.71135599 0.2479917 -0.45434803
Del caso 3 podemos evidenciar que:
El primer componente se recoge información principalmente para las variables de área construida y baños.
El segundo componente recoge información principalmente de las variables piso con una diferencia considerable.
El tercer componente recoge información principalmente de la variable habitaciones.
cat("\n","CASO 4", "\n", "\n")
##
## CASO 4
##
pca4 <- prcomp(datos_filtrados4, scale = TRUE)
pca4$rotation
## PC1 PC2 PC3 PC4
## areaconst -0.5343302 -0.05990300 0.8402398 -0.06999985
## parquea -0.4655553 -0.67883567 -0.3045267 0.47927434
## banios -0.5409825 0.03127607 -0.4032278 -0.73740563
## habitac -0.4528658 0.73117411 -0.1966417 0.47077496
Del caso 4 podemos evidenciar que:
El primer componente se recoge información principalmente de las variables área construida y baños.
El segundo componente recoge información principalmente de la variable habitaciones.
El tercer componente recoge información principalmente de la variable área construida.
A continuación, se realiza un análisis gráfico de las primeras dos componentes con el fin de interpretar de mejor manera los resultados.
cat("\n","CASO 1", "\n", "\n")
##
## CASO 1
##
biplot(x = pca1, scale = 0, cex = 0.8, col = c("blue4", "brown3"))
Del
caso 1 podemos evidenciar que:
Los vectores de latitud y longitud tienen una longitud muy alta frente a las demás variables, lo que representa una variabilidad alta.
Los vectores de latitud y longitud no tienen una correlación clara con alguna variable con la que se pueda definir un agrupamiento claro.
Existen tres grupos de puntos que evidencian diferencias entre ellos, estas diferencias se atribuyen al efecto de las variables analizadas en conjunto.
cat("\n","CASO 2", "\n", "\n")
##
## CASO 2
##
biplot(x = pca2, scale = 0, cex = 0.8, col = c("blue4", "brown3"))
Del caso 2 podemos evidenciar que:
El vector de piso tiene una longitud considerable frente a las demás variables, lo que representa una variabilidad alta.
Los vectores de parqueadero y precio de metro tienen una correlación clara, al igual que los vectores baños y área construida
No existen grupos claros entre los datos, lo que quiere decir la latitud y longitud estaban generando que se encontraran diferencias entre los datos, sin embargo, puede deberse a su alta variabilidad y poca correlación con otras variables.
cat("\n","CASO 3", "\n", "\n")
##
## CASO 3
##
biplot(x = pca3, scale = 0, cex = 0.8, col = c("blue4", "brown3"))
Del caso 3 podemos evidenciar que:
El vector de piso tiene mantiene una longitud considerable frente a las demás variables, lo que representa una variabilidad alta.
Los vectores de parqueadero, baño y área construida tienen una correlación clara, aunque también cercana a la variable habitaciones.
Se evidencia la presencia de dos grupos uno de ellos relacionado con las variables parqueadero, baño, área construida y habitaciones, y otro relacionado a otras variables como piso.
cat("\n","CASO 4", "\n", "\n")
##
## CASO 4
##
biplot(x = pca4, scale = 0, cex = 0.8, col = c("blue4", "brown3"))
Del
caso 4 podemos evidenciar que:
Los vectores de las variables parqueadero y habitaciones mantienen una longitud considerable frente a las demás variables, lo que representa una variabilidad alta.
Los vectores de baño y área construida tienen una correlación significativa.
Se evidencia la presencia de tres grupos uno de ellos relacionado con las variables baño y área construida, otro para habitaciones, y otro relacionado con parqueadero.
cat("\n","CASO 1", "\n", "\n")
##
## CASO 1
##
prop_varianza1 <- pca1$sdev^2/sum(pca1$sdev^2)
prop_varianza1
## [1] 0.42350746 0.22983660 0.13049934 0.09109221 0.04324466 0.03807277 0.02336174
## [8] 0.02038521
x_label <- enc2utf8("Componente principal")
y_label <- enc2utf8("Proporcion de varianza explicada")
data <- data.frame(prop_varianza1, pc = 1:8)
ggplot(data, aes(x = pc, y = prop_varianza1)) +
geom_col(width = 0.3) +
scale_y_continuous(limits = c(0, 1)) +
theme_bw() +
labs(x = x_label, y = y_label)
Para el caso 1 la varianza explicada por el componente 1 y 2 llega a representar el 65 %.
cat("\n","CASO 2", "\n", "\n")
##
## CASO 2
##
prop_varianza2 <- pca2$sdev^2/sum(pca2$sdev^2)
prop_varianza2
## [1] 0.56033324 0.17819573 0.12193581 0.05767291 0.05079814 0.03106417
data <- data.frame(prop_varianza2, pc = 1:6)
ggplot(data, aes(x = pc, y = prop_varianza2)) +
geom_col(width = 0.3) +
scale_y_continuous(limits = c(0, 1)) +
theme_bw() +
labs(x = x_label, y = y_label)
Para el caso 2 la varianza explicada por el componente 1 y 2 llega a representar el 74 %.
cat("\n","CASO 3", "\n", "\n")
##
## CASO 3
##
prop_varianza3 <- pca3$sdev^2/sum(pca3$sdev^2)
prop_varianza3
## [1] 0.55049329 0.19391328 0.13356773 0.06396025 0.05806545
data <- data.frame(prop_varianza3, pc = 1:5)
ggplot(data, aes(x = pc, y = prop_varianza3)) +
geom_col(width = 0.3) +
scale_y_continuous(limits = c(0, 1)) +
theme_bw() +
labs(x = x_label, y = y_label)
Para el caso 3 la varianza explicada por el componente 1 y 2 llega a
representar el 74 %.
Lo anterior nos indica que la variable precio m, la cual fue omitida para el caso 3, no representa un aporte significativo a los componentes 1 y 2.
cat("\n","CASO 4", "\n", "\n")
##
## CASO 4
##
prop_varianza4 <- pca4$sdev^2/sum(pca4$sdev^2)
prop_varianza4
## [1] 0.67168539 0.17354286 0.08108199 0.07368976
data <- data.frame(prop_varianza4, pc = 1:4)
ggplot(data, aes(x = pc, y = prop_varianza4)) +
geom_col(width = 0.3) +
scale_y_continuous(limits = c(0, 1)) +
theme_bw() +
labs(x = x_label, y = y_label)
Para el caso 4 la varianza explicada por el componente 1 y 2 llega a
representar el 84 %.
Se recomienda seguir capturando información de latitud, longitud sin embargo esto requiere un análisis especial. Se recomienda realizar depuración y control sobre la variable piso, ya que no siempre se registra de manera completa, tampoco es claro que se esta registrando, si el numero de pisos que tiene una casa, o la planta de ubicación de la unidad inmobiliaria. Como solución se puede incluir en dos variables “número de pisos” y “planta de ubicación” Por otro lado, las variables parqueadero, número de habitaciones, baños y área construida, tienen mucha relevancia pero debe controlarse la consistencia del número de habitaciones ya que presenta muchas inconsistencias.
En cuanto a análisis de componentes principales, existe una
importancia y correlación fuerte entre las variables la cual organizar
en orden de relevancia así:
1. Área construida y baños 2. Número de habitaciones 3. Parqueadero 4.
Pisos 5. Latitud y Longitud
Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas y demandas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.
Debido a que contamos con variables numéricas y categóricas en el conjunto de datos inicial, iniciaremos realizando un análisis de conglomerados para las variables categóricas.
Iniciamos filtrando los datos vacíos y quitando las variables categóricas.
datos_filtrados5 <- datos_org[complete.cases(datos_org), ]
datos_filtrados5 <- datos_filtrados5[, !(names(datos_filtrados5) %in% c("id","areaconst","parquea","banios","habitac","longitud","latitud","preciom","piso"))]
A continuación, se realizan el análisis de conglomerados, seleccionando como número de clúster “4” ya que se obtiene le mejor resultado luego de aplicar las validaciones de Coeficiente de Silhouette.
# distancia euclidiana
dist_df5z <- dist(datos_filtrados5, method = 'euclidean')
## Warning in dist(datos_filtrados5, method = "euclidean"): NAs introducidos por
## coerción
# Cluster jerarquico con el método complete
hc_df5z <- hclust(dist_df5z, method = 'complete')
# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_df5z, k = 4)
# asignamos los clusters
assigned_cluster <- datos_filtrados5 %>% mutate(cluster = as.factor(cluster_assigments))
plot(hc_df5z, cex = 0.6, main = "Dendograma de Empresas", las = 1,
ylab = "Distancia euclidiana", xlab = "Grupos", hang = -1)
# Agregar las etiquetas del eje x
etiquetas_eje_x <- datos_filtrados5$zona
axis(side = 1, at = 1:length(etiquetas_eje_x), labels = etiquetas_eje_x, las = 2,cex.axis = 0.7)
# Dibujar los rectángulos de agrupación
rect.hclust(hc_df5z, k = 4, border = 2:5)
# Calcular el coeficiente de Silhouette
sil <- silhouette(cluster_assigments, dist(datos_filtrados5))
## Warning in dist(datos_filtrados5): NAs introducidos por coerción
sil_avg <- mean(sil[,3])
# Imprimir el coeficiente de Silhouette promedio
cat("Coeficiente de Silhouette: ", sil_avg)
## Coeficiente de Silhouette: 1
Con el objetivo de guardar la información de los grupos se procede a crear un resumen que permita ver el cluster asociado a cada registro que comprende información de “Zona, estrato, tipo y barrio”.
# Crear un data frame con la información de clusters, zonas y estratos
cluster_summary <- assigned_cluster %>%
group_by(cluster, zona, estrato, tipo, barrio) %>%
summarize(num_registros = n()) %>%
arrange(cluster)
## `summarise()` has grouped output by 'cluster', 'zona', 'estrato', 'tipo'. You
## can override using the `.groups` argument.
# Imprimir el resumen
print(cluster_summary)
## # A tibble: 688 × 6
## # Groups: cluster, zona, estrato, tipo [50]
## cluster zona estrato tipo barrio num_registros
## <fct> <chr> <int> <chr> <chr> <int>
## 1 1 Zona Norte 6 APARTAMENTO menga 1
## 2 1 Zona Norte 6 Apartamento acopi 4
## 3 1 Zona Norte 6 Apartamento altos de menga 1
## 4 1 Zona Norte 6 Apartamento chipichape 5
## 5 1 Zona Norte 6 Apartamento ciudad jardín 1
## 6 1 Zona Norte 6 Apartamento el bosque 1
## 7 1 Zona Norte 6 Apartamento juanamb√∫ 14
## 8 1 Zona Norte 6 Apartamento la flora 3
## 9 1 Zona Norte 6 Apartamento menga 4
## 10 1 Zona Norte 6 Apartamento santa monica 18
## # ℹ 678 more rows
Una vez se conoce el cluster al que pertenece cada registro, se lleva esta información a la tabla de datos original que contiene todas las variables, esto se realiza por correspondencia de los atributos categóricos en la tabla de resumen con la tabla de datos origen.
# Agregar la columna de clusters al conjunto de datos originales
datos_clasif <- datos_org[complete.cases(datos_org), ]
#datos_clasif <- subset(datos_clasif, select = -cluster_AC)
datos_clasif$cluster_AC <- NA # Crear una columna vacía
# Recorrer cada fila en los datos originales
for (i in 1:nrow(datos_clasif)) {
# ... (otro código)
# Obtener los valores de zona, estrato, tipo y barrio para la fila actual
zona_actual <- datos_clasif$zona[i]
estrato_actual <- datos_clasif$estrato[i]
tipo_actual <- datos_clasif$tipo[i]
barrio_actual <- datos_clasif$barrio[i]
# Filtrar el resumen de clústeres para encontrar el clúster correspondiente
cluster_actual <- cluster_summary %>%
filter(zona == zona_actual, estrato == estrato_actual, tipo == tipo_actual, barrio == barrio_actual) %>%
pull(cluster)
# Verificar si cluster_actual no está vacío antes de asignar
if (length(cluster_actual) > 0) {
# Asignar el valor del clúster a la columna correspondiente en los datos originales
datos_clasif$cluster_AC[i] <- cluster_actual
}
}
summary(datos_clasif)
## id zona piso estrato
## Min. : 1 Length:4808 Min. : 1.000 Min. :3.000
## 1st Qu.:2479 Class :character 1st Qu.: 2.000 1st Qu.:4.000
## Median :4474 Mode :character Median : 3.000 Median :5.000
## Mean :4427 Mean : 3.886 Mean :4.838
## 3rd Qu.:6413 3rd Qu.: 5.000 3rd Qu.:6.000
## Max. :8316 Max. :12.000 Max. :6.000
## preciom areaconst parquea banios
## Min. : 58.0 Min. : 40.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 244.5 1st Qu.: 85.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 350.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 457.2 Mean : 174.8 Mean : 1.815 Mean : 3.219
## 3rd Qu.: 560.0 3rd Qu.: 225.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1500.0 Max. :10.000 Max. :10.000
## habitac tipo barrio longitud
## Min. : 0.000 Length:4808 Length:4808 Min. :-76576.00
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.: -76.55
## Median : 3.000 Mode :character Mode :character Median : -76.54
## Mean : 3.564 Mean :-16216.63
## 3rd Qu.: 4.000 3rd Qu.: -76.52
## Max. :10.000 Max. : -76.46
## latitud cluster_AC
## Min. : 3.333 Min. :1.00
## 1st Qu.: 3.383 1st Qu.:1.00
## Median : 3.433 Median :3.00
## Mean : 725.008 Mean :2.62
## 3rd Qu.: 3.487 3rd Qu.:4.00
## Max. :3493.000 Max. :4.00
Una vez clasificada la base de datos original, podemos extraer subconjuntos de datos basados en los cluster a los que pertenecen para analizar las variables de tipo numérico pero garantizando homogeneidad en cuanto a la zona y estrato a los que estos pertenecen.
subset_list <- split(datos_clasif, datos_clasif$cluster_AC)
Se obtiene entonces 4 subconjuntos con características homogéneas, los cuales se analizan a continuación, dejando únicamente aquellas variables de tipo numérico y descartando, latitud, longitud y piso, debido a los comportamientos a la alta variabilidad que presentan.
Subconjunto 1: Se identifican 2 grupos, con un coeficiente de Silhouette medio.
subset_1 <- subset_list$'1' [, !(names(subset_list$'1') %in% c("id","zona","estrato","tipo","barrio","latitud","longitud","piso"))]
subset_1 =scale(subset_1)
subset_1z = as.data.frame(subset_1)
summary(subset_1z)
## preciom areaconst parquea banios
## Min. :-1.6143 Min. :-1.1718 Min. :-1.2933 Min. :-3.16156
## 1st Qu.:-0.7614 1st Qu.:-0.6514 1st Qu.:-0.4837 1st Qu.:-0.86021
## Median :-0.2550 Median :-0.3073 Median :-0.4837 Median :-0.09309
## Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.00000
## 3rd Qu.: 0.5180 3rd Qu.: 0.3624 3rd Qu.: 0.3258 3rd Qu.: 0.67403
## Max. : 3.1035 Max. : 7.7354 Max. : 5.9927 Max. : 3.74249
##
## habitac cluster_AC
## Min. :-3.5507 Min. : NA
## 1st Qu.:-0.5668 1st Qu.: NA
## Median :-0.5668 Median : NA
## Mean : 0.0000 Mean :NaN
## 3rd Qu.: 0.4278 3rd Qu.: NA
## Max. : 6.3954 Max. : NA
## NA's :1302
# distancia euclidiana
dist_s1z <- dist(subset_1z, method = 'euclidean')
# Cluster jerarquico con el método complete
hc_s1z <- hclust(dist_s1z, method = 'complete')
# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_s1z, k = 2)
# asignamos los clusters
assigned_cluster <- subset_1z %>% mutate(cluster = as.factor(cluster_assigments))
plot(hc_s1z, cex = 0.4, main = "Dendograma", las = 1,
ylab = "Distancia euclidiana", xlab = "Grupos", hang = -1)
rect.hclust(hc_s1z, k = 2, border = 2:5)
# Calcular el coeficiente de Silhouette
sil <- silhouette(cluster_assigments, dist(subset_1z))
sil_avg <- mean(sil[,3])
# Imprimir el coeficiente de Silhouette promedio
cat("Coeficiente de Silhouette: ", sil_avg)
## Coeficiente de Silhouette: 0.4219258
Subconjunto 2: Se identifican 3 grupos, con un coeficiente de Silhouette alto.
subset_2 <- subset_list$'2' [, !(names(subset_list$'2') %in% c("id","zona","estrato","tipo","barrio","latitud","longitud","piso"))]
subset_2 =scale(subset_2)
subset_2z = as.data.frame(subset_2)
summary(subset_2z)
## preciom areaconst parquea banios
## Min. :-1.2715 Min. :-0.8398 Min. :-0.3197 Min. :-1.1542
## 1st Qu.:-0.7015 1st Qu.:-0.6762 1st Qu.:-0.3197 1st Qu.:-0.3666
## Median :-0.3057 Median :-0.4357 Median :-0.3197 Median :-0.3666
## Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
## 3rd Qu.: 0.4859 3rd Qu.: 0.3846 3rd Qu.:-0.3197 3rd Qu.: 0.4210
## Max. : 8.9561 Max. :10.4745 Max. :11.9223 Max. : 5.1468
##
## habitac cluster_AC
## Min. :-2.0275 Min. : NA
## 1st Qu.:-0.5626 1st Qu.: NA
## Median :-0.5626 Median : NA
## Mean : 0.0000 Mean :NaN
## 3rd Qu.: 0.4140 3rd Qu.: NA
## Max. : 2.8556 Max. : NA
## NA's :434
# distancia euclidiana
dist_s2z <- dist(subset_2z, method = 'euclidean')
# Cluster jerarquico con el método complete
hc_s2z <- hclust(dist_s2z, method = 'complete')
# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_s2z, k = 3)
# asignamos los clusters
assigned_cluster <- subset_2z %>% mutate(cluster = as.factor(cluster_assigments))
plot(hc_s2z, cex = 0.4, main = "Dendograma", las = 1,
ylab = "Distancia euclidiana", xlab = "Grupos", hang = -1)
rect.hclust(hc_s2z, k = 3, border = 2:5)
# Calcular el coeficiente de Silhouette
sil <- silhouette(cluster_assigments, dist(subset_2z))
sil_avg <- mean(sil[,3])
# Imprimir el coeficiente de Silhouette promedio
cat("Coeficiente de Silhouette: ", sil_avg)
## Coeficiente de Silhouette: 0.7417379
Subconjunto 3: Se identifican 2 grupos, con un coeficiente de Silhouette bajo.
subset_3 <- subset_list$'3' [, !(names(subset_list$'3') %in% c("id","zona","estrato","tipo","barrio","latitud","longitud","piso"))]
subset_3 =scale(subset_3)
subset_3z = as.data.frame(subset_3)
summary(subset_3z)
## preciom areaconst parquea banios
## Min. :-1.3582 Min. :-0.9514 Min. :-0.7500 Min. :-2.57965
## 1st Qu.:-0.5965 1st Qu.:-0.5980 1st Qu.:-0.7500 1st Qu.:-0.91568
## Median :-0.2733 Median :-0.4252 Median : 0.2936 Median :-0.08369
## Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.00000
## 3rd Qu.: 0.2345 3rd Qu.: 0.2384 3rd Qu.: 0.2936 3rd Qu.: 0.74830
## Max. : 7.3855 Max. : 8.1356 Max. : 7.5991 Max. : 4.90823
##
## habitac cluster_AC
## Min. :-2.7429 Min. : NA
## 1st Qu.:-0.4103 1st Qu.: NA
## Median :-0.4103 Median : NA
## Mean : 0.0000 Mean :NaN
## 3rd Qu.: 0.3672 3rd Qu.: NA
## Max. : 5.0324 Max. : NA
## NA's :1859
# distancia euclidiana
dist_s3z <- dist(subset_3z, method = 'euclidean')
# Cluster jerarquico con el método complete
hc_s3z <- hclust(dist_s3z, method = 'complete')
# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_s3z, k = 2)
# asignamos los clusters
assigned_cluster <- subset_3z %>% mutate(cluster = as.factor(cluster_assigments))
plot(hc_s3z, cex = 0.4, main = "Dendograma", las = 1,
ylab = "Distancia euclidiana", xlab = "Grupos", hang = -1)
rect.hclust(hc_s3z, k = 2, border = 2:5)
# Calcular el coeficiente de Silhouette
sil <- silhouette(cluster_assigments, dist(subset_3z))
sil_avg <- mean(sil[,3])
# Imprimir el coeficiente de Silhouette promedio
cat("Coeficiente de Silhouette: ", sil_avg)
## Coeficiente de Silhouette: 0.3732079
Subconjunto 4: Se identifican 2 grupos, con un coeficiente de Silhouette alto.
subset_4 <- subset_list$'4' [, !(names(subset_list$'4') %in% c("id","zona","estrato","tipo","barrio","latitud","longitud","piso"))]
subset_4 =scale(subset_4)
subset_4z = as.data.frame(subset_4)
summary(subset_4z)
## preciom areaconst parquea banios
## Min. :-1.3141 Min. :-0.8718 Min. :-0.4555 Min. :-2.4071
## 1st Qu.:-0.6895 1st Qu.:-0.6112 1st Qu.:-0.4555 1st Qu.:-0.6256
## Median :-0.2854 Median :-0.4507 Median :-0.4555 Median :-0.6256
## Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
## 3rd Qu.: 0.4494 3rd Qu.: 0.2912 3rd Qu.:-0.4555 3rd Qu.: 0.2651
## Max. : 7.8708 Max. : 6.7082 Max. :11.9449 Max. : 6.5003
##
## habitac cluster_AC
## Min. :-2.5706 Min. : NA
## 1st Qu.:-0.3056 1st Qu.: NA
## Median :-0.3056 Median : NA
## Mean : 0.0000 Mean :NaN
## 3rd Qu.: 0.4494 3rd Qu.: NA
## Max. : 4.9794 Max. : NA
## NA's :1213
# distancia euclidiana
dist_s4z <- dist(subset_4z, method = 'euclidean')
# Cluster jerarquico con el método complete
hc_s4z <- hclust(dist_s4z, method = 'complete')
# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_s4z, k = 2)
# asignamos los clusters
assigned_cluster <- subset_4z %>% mutate(cluster = as.factor(cluster_assigments))
plot(hc_s4z, cex = 0.4, main = "Dendograma", las = 1,
ylab = "Distancia euclidiana", xlab = "Grupos", hang = -1)
rect.hclust(hc_s4z, k = 2, border = 2:5)
# Calcular el coeficiente de Silhouette
sil <- silhouette(cluster_assigments, dist(subset_4z))
sil_avg <- mean(sil[,3])
# Imprimir el coeficiente de Silhouette promedio
cat("Coeficiente de Silhouette: ", sil_avg)
## Coeficiente de Silhouette: 0.7351665
De estos dos grupos se realizó una división del conjunto de datos original para analizar las variables numéricas y que están más relacionadas con características internas del inmueble, como es el área, numero de dependencias y el precio. De dicho análisis se obtuvieron 9 grupos en total por lo cual estaríamos hablando de 4 grupos generales y 9 subgrupos.
Examinar la relación entre las variables categóricas (tipo de vivienda,zona y barrio) para identificar patrones de comportamiento del mercado inmobiliario.
Para iniciar, se realiza limpieza de la información con el objetivo de evitar valores duplicados.
datos_clasif2<- datos_clasif %>%
mutate(barrio = toupper(trimws(datos_clasif$barrio)))
datos_clasif2 <- datos_clasif2 %>%
mutate(tipo = toupper(trimws(datos_clasif$tipo)))
datos_clasif2 <- datos_clasif2 %>%
mutate(zona = toupper(trimws(datos_clasif$zona)))
# Reemplazar valores en la columna "tipo"
datos_clasif2$tipo <- gsub("APTO", "APARTAMENTO", datos_clasif2$tipo)
Luego se procede a construir una tabla de contingencia para los datos categoricos de barrio y zona, estas dos variables como ya se explico hacen referencia a información externa al inmueble que afecta el precio del metro cuadrado, en el analisis de conglomerados se identificaron 4 grupos para las variables categoricas, lo cual coincide lo que se reporta en el analisis de correspondencia que se expone a continuación.
# Generar tabla de contingencia
tabla_contingencia1 <- table(datos_clasif2$barrio, datos_clasif2$zona)
# Crear un data frame para ggplot2
df_contingencia <- as.data.frame.matrix(tabla_contingencia1)
df_contingencia$Barrio <- rownames(df_contingencia) # Agregar el nombre de barrio como columna
# Reorganizar el data frame para el gráfico de heatmap
df_melted <- melt(df_contingencia, id.vars = "Barrio", variable.name = "Zona", value.name = "Frecuencia")
# Crear un heatmap con ggplot2
heatmap_plot <- ggplot(df_melted, aes(x = Zona, y = Barrio, fill = Frecuencia)) +
geom_tile() +
scale_fill_gradient(low = "white", high = "blue") +
labs(x = "Zona", y = "Barrio") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.1))
# Mostrar el heatmap
print(heatmap_plot)
# Realizar el análisis de correspondencia
resultados_ac <- CA(tabla_contingencia1)
valores_prop <-resultados_ac$eig ; valores_prop
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.9824823 26.20249 26.20249
## dim 2 0.9527724 25.41014 51.61263
## dim 3 0.9498060 25.33102 76.94365
## dim 4 0.8645155 23.05635 100.00000
fviz_screeplot(resultados_ac, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
Si bien la varianza explicada por cada componente es muy similar se
podria decir que se trata de grupos homogeneos.
Ahora realizaremos el analisis con la zona y el tipo de inmueble, lo que nos arroja como resultado que en la base de datos existe un número superior de información para apartamentos en la zona sur y casi nulo en la zona norte. En el analisis se identifica que existe un solo grupo, por lo cual se podria considerar que es homogeneo el analisis entre zonas y tipo de inmueble.
# Generar tabla de contingencia
tabla_contingencia2 <- table(datos_clasif2$tipo, datos_clasif2$zona)
# Crear un data frame para ggplot2
df_contingencia <- as.data.frame.matrix(tabla_contingencia2)
df_contingencia$Tipo <- rownames(df_contingencia) # Agregar el nombre de tipo como columna
# Reorganizar el data frame para el gráfico de heatmap
df_melted <- melt(df_contingencia, id.vars = "Tipo", variable.name = "Zona", value.name = "Frecuencia")
# Crear un heatmap con ggplot2
heatmap_plot <- ggplot(df_melted, aes(x = Zona, y = Tipo, fill = Frecuencia)) +
geom_tile() +
scale_fill_gradient(low = "white", high = "blue") +
labs(x = "Zona", y = "Tipo") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.1))
# Mostrar el heatmap
print(heatmap_plot)
# Realizar el análisis de correspondencia
resultados_ac <- CA(tabla_contingencia2)
valores_prop <-resultados_ac$eig ; valores_prop
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.07642923 100 100
fviz_screeplot(resultados_ac, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
A manera de resumen, al realizar el agrupamiento por las variables categóricas zona, estrato, tipo y barrio, se obtuvieron cuatro grupos estos se pueden interpretar como agrupaciones relacionadas con variables externas al inmueble como lo es la zona, el barrio y el estrato. Se recomienda incluir más variables categóricas relacionadas con el entorno del inmueble como por ejemplo la disponibilidad de transporte, entre otras, de esta manera se lograría generar grupos de análisis más detallados.
Luego se puede concluir que para analizar el comportamiento de los precios de un inmueble debemos primero considerar las variables relacionadas con características de entorno, como ubicación (Latitud, Longitud), zonificación económica (estrato, zona, barrio), a partir de allí se generan grupos homogéneos en cuanto a la influencia externa de dichas variables y se puede analizar posteriormente el comportamiento de las variables intrínsecas del inmuebles como el área construida, planta de ubicación o numero de pisos, dependencias (baños, parqueadero, habitaciones) y se podrían considerar algunas adicionales como cocina, estado de conservación, acabados, con el fin de evaluar de mejor manera las características de determinan el precio por m2.