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.
Para dar solución al requerimiento planteado, se iniciará realizando un análisis exploratorio de datos, en aras de conocer las variables y su contenido, que en este caso corresponde a una muestra de viviendas de la ciudad de Cali. Posteriormente, se aplicará análisis de componentes principales para reducir la dimensionalidad del conjunto de datos, mientras se preserva la mayor cantidad posible de la varianza; análisis de conglomerados para agrupar datos en clústeres basados en su similitud; y análisis de correspondencias para estudiar las relaciones entre variables categóricas.
vf <- vivienda
vf
## # A tibble: 8,322 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona … <NA> 3 250 70 1 3 6
## 2 1169 Zona … <NA> 3 320 120 1 2 3
## 3 1350 Zona … <NA> 3 350 220 2 2 4
## 4 5992 Zona … 02 4 400 280 3 5 3
## 5 1212 Zona … 01 5 260 90 1 2 3
## 6 1724 Zona … 01 5 240 87 1 3 3
## 7 2326 Zona … 01 4 220 52 2 2 3
## 8 4386 Zona … 01 5 310 137 2 3 4
## 9 1209 Zona … 02 5 320 150 2 4 6
## 10 1592 Zona … 02 5 780 380 2 3 3
## # ℹ 8,312 more rows
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Como preámbulo, se describe la base de datos original, la cual consta de 13 atributos y 8.322 registros, que describen características de viviendas en cuanto a su ubicación, distribución y precio. Sin embargo, como es una data extensa, se recomienda renombrar las variables y verificar la calidad e integridad de los registros, por lo que se procede con:
Renombrar variables
Las variables se renombran para identificarlas con mayor facilidad.
renombrar_variables <- function(df, nuevas_variables) {
df %>%
rename_with(~nuevas_variables, everything())
}
vf <- renombrar_variables(vf, c("Id", "Zona", "Piso", "Estrato", "Precio", "Área", "Parqueaderos", "Baños", "Habitaciones", "Tipo", "Barrio", "Longitud", "Latitud"))
print(names(vf))
## [1] "Id" "Zona" "Piso" "Estrato" "Precio"
## [6] "Área" "Parqueaderos" "Baños" "Habitaciones" "Tipo"
## [11] "Barrio" "Longitud" "Latitud"
Verificar tipo de dato
Tipo <- sapply(vf, class)
Tipo
## Id Zona Piso Estrato Precio Área
## "numeric" "character" "character" "numeric" "numeric" "numeric"
## Parqueaderos Baños Habitaciones Tipo Barrio Longitud
## "numeric" "numeric" "numeric" "character" "character" "numeric"
## Latitud
## "numeric"
Se observa que el atributo Piso está como texto o string, por lo que se debe convertir a numérico.
vf$Piso <- as.numeric(as.character(vf$Piso))
str(vf$Piso)
## num [1:8322] NA NA NA 2 1 1 1 1 2 2 ...
Definir operacionalmente las variables
Se observa que la muestra contiene información de 8 variables cuantitativas y 4 cualitativas (descartando el id), las cuales se describen de la siguiente manera, según su definición operacional:
Descripción.v <- data.frame(
Variable = c("Zona", "Piso", "Estrato", "Precio", "Área", "Parqueaderos", "Baños", "Habitaciones", "Tipo", "Barrio", "Longitud", "Latitud"),
Tipo.variabe = c("Cualitativa-Nominal", "Cualitativa-Ordinal" ,"Cualitativa-Ordinal", "Cuantitativa-Continua", "Cuantitativa-Continua", "Cuantitativa-Discreta", "Cuantitativa-Discreta", "Cuantitativa-Discreta", "Cualitativa-Categórica", "Cualitativa-Nominal", "Cuantitativa-Continua", "Cuantitativa-Continua"),
Unidad.medida = c("Ninguna", "Ninguna", "Nivel", "Millones de COP", "Metros2", "Cantidad", "Cantidad", "Cantidad", "Ninguna", "Ninguna", "Grados" , "Grados"),
Definición.variable = c("Sector de ubicación de la vivienda", "Nivel de altura en el que se encuentra la vivienda", "Nivel socioeconómico de la vivienda", "Valor de venta de la vivienda", "Extensión de la vivienda", "Número de parqueaderos con que cuenta la vivienda", "Número de baños con que cuenta la vivienda", "Número de habitaciones con que cuenta la vivienda", "Clasificación de la vivienda", "Lugar de ubicación de la vivienda", "Georeferenciación de la vivienda", "Georeferenciación de la vivienda")
)
print(Descripción.v)
## Variable Tipo.variabe Unidad.medida
## 1 Zona Cualitativa-Nominal Ninguna
## 2 Piso Cualitativa-Ordinal Ninguna
## 3 Estrato Cualitativa-Ordinal Nivel
## 4 Precio Cuantitativa-Continua Millones de COP
## 5 Área Cuantitativa-Continua Metros2
## 6 Parqueaderos Cuantitativa-Discreta Cantidad
## 7 Baños Cuantitativa-Discreta Cantidad
## 8 Habitaciones Cuantitativa-Discreta Cantidad
## 9 Tipo Cualitativa-Categórica Ninguna
## 10 Barrio Cualitativa-Nominal Ninguna
## 11 Longitud Cuantitativa-Continua Grados
## 12 Latitud Cuantitativa-Continua Grados
## Definición.variable
## 1 Sector de ubicación de la vivienda
## 2 Nivel de altura en el que se encuentra la vivienda
## 3 Nivel socioeconómico de la vivienda
## 4 Valor de venta de la vivienda
## 5 Extensión de la vivienda
## 6 Número de parqueaderos con que cuenta la vivienda
## 7 Número de baños con que cuenta la vivienda
## 8 Número de habitaciones con que cuenta la vivienda
## 9 Clasificación de la vivienda
## 10 Lugar de ubicación de la vivienda
## 11 Georeferenciación de la vivienda
## 12 Georeferenciación de la vivienda
Verificar datos faltantes
Registros <- nrow(vf)
Faltantes <- colSums(is.na(vf))
Participación <- round(Faltantes/Registros,4)*100
datosparciales <- data.frame(Faltantes = Faltantes,
Participación = Participación)
datosparciales <- datosparciales[order(-datosparciales$Participación), ]
datosparciales
## Faltantes Participación
## Piso 2638 31.70
## Parqueaderos 1605 19.29
## Id 3 0.04
## Zona 3 0.04
## Estrato 3 0.04
## Área 3 0.04
## Baños 3 0.04
## Habitaciones 3 0.04
## Tipo 3 0.04
## Barrio 3 0.04
## Longitud 3 0.04
## Latitud 3 0.04
## Precio 2 0.02
Los atributos piso y parqueaderos son los que más datos faltantes presentan, 31.70% y 19.29% respectivamente. Eliminarlos representaría una pérdida de información, por lo que se procederá a tratarlos vía imputación. Por otra parte, el resto de variables contienen entre 2 y 3 datos faltantes, por lo que en este caso si se pueden suprimir los registros.
Tratamiento de datos faltantes
vf$Parqueaderos[is.na(vf$Parqueaderos)] <- 0
median_piso <- median(vf$Piso, na.rm = TRUE)
vf$Piso[is.na(vf$Piso)] <- median_piso
vf<- vf[!is.na(vf$Id),]
faltantes2<- colSums(is.na(vf))
Los datos faltantes del atributo Parqueaderos pueden generarse por un problema de representación de datos, es decir, que la ausencia del atributo puede corresponder a que la vivienda no tiene parqueadero, por lo que se imputaron con cero.
Por su lado, los datos faltantes de la variable piso, se reemplazaron por la mediana de la misma, para que no se distorsione en caso de la existencia de datos atípicos, no obstante, resulta prudente aclarar que, no se tiene información precisa de que esta variable represente el mismo atributo para casas como para apartamentos, dado que en el primero puede corresponder al número de pisos de la vivienda, mientras que en el segundo, al nivel en donde se encuentra ubicada la vivienda, por lo que termina siendo una variable generadora de ruido, más aún porque al filtrar el dataset por casas y pisos, se encuentran casas de 10 pisos, lo que no tiene sentido, caso contrario si se hiciera referencia al piso de un apartamento.
Finalmente, se eliminaron los registros de las demás variables que tenían sólo 2 y 3 datos faltantes.
A continuación, se verifica que no hayan quedado datos faltantes:
library(naniar)
gg_miss_var(vf) + labs(x="Variables", y = "Datos Faltantes") +
theme_grey()
El gráfico anterior proporciona información acerca de las variables y la cantidad de datos no observados, por lo que se concluye que la base de datos ahora se encuentra completa, sin embargo, aún se debe continuar con la limpieza de la misma.
Verificar registros duplicados
duplicados = vf[duplicated(vf), ]
if (nrow(duplicados) == 0) {
message("No se detectaron registros duplicados")
} else {
print(duplicados)
}
## No se detectaron registros duplicados
Verificar datos atípicos
par(mfrow = c(1, 4))
boxplot(x = vf$Piso, col="#00B2EE", main="Variable Piso")
boxplot(x = vf$Estrato, col="#CDAD00", main="Variable Estrato")
boxplot(x = vf$Parqueaderos, col="#B23AEE", main="Variable parqueaderos")
boxplot(x = vf$Baños, col="#CD1076", main="Variable Baños")
par(mfrow = c(1, 3))
boxplot(x = vf$Precio, col="#7FFF00", main="Variable Precio")
boxplot(x = vf$Área, col="aquamarine2", main="Variable Área")
boxplot(x = vf$Habitaciones, col="#C1CDC1", main="Variable Habitaciones")
Mediante el diagrama de cajas y bigotes, se encuentra que, los atributos Piso, Parqueaderos, Baños, Precio, Área y Habitaciones son los que presentan datos extremos. Ahora, se procederá a cuantificar y corroborar esta información mediante el método matemático de la desviación estándar, en donde se consideran datos atípicos a todos aquellos que se encuentran por encima o por debajo de 3 desviaciones respecto a la media de la variable:
# Calcular la media
mean_Piso <- mean(vf$Piso, na.rm = TRUE)
mean_Parqueaderos <- mean(vf$Parqueaderos, na.rm = TRUE)
mean_Baños <- mean(vf$Baños, na.rm = TRUE)
mean_Habitaciones <- mean(vf$Habitaciones, na.rm = TRUE)
mean_Precio <- mean(vf$Precio, na.rm = TRUE)
mean_Área <- mean(vf$Área, na.rm = TRUE)
# Calcular la desviación estándar
std_Piso <- sd(vf$Piso, na.rm = TRUE)
std_Parqueaderos <- sd(vf$Parqueaderos, na.rm = TRUE)
std_Baños <- sd(vf$Baños, na.rm = TRUE)
std_Habitaciones <- sd(vf$Habitaciones, na.rm = TRUE)
std_Precio <- sd(vf$Precio, na.rm = TRUE)
std_Área <- sd(vf$Área, na.rm = TRUE)
# Calcular el número de outliers
outliers_Piso <- sum(abs(vf$Piso - mean_Piso) > 3 * std_Piso)
outliers_Parqueaderos <- sum(abs(vf$Parqueaderos - mean_Parqueaderos) > 3 * std_Parqueaderos)
outliers_Baños <- sum(abs(vf$Baños - mean_Baños) > 3 * std_Baños)
outliers_Habitaciones <- sum(abs(vf$Habitaciones - mean_Habitaciones) > 3 * std_Habitaciones)
outliers_Precio <- sum(abs(vf$Precio - mean_Precio) > 3 * std_Precio)
outliers_Área <- sum(abs(vf$Área - mean_Área) > 3 * std_Área)
# Mostrar los resultados
resultados_outliers <- list( outliers_Piso = outliers_Piso,
outliers_Parqueaderos = outliers_Parqueaderos,
outliers_Baños = outliers_Baños,
outliers_Habitaciones = outliers_Habitaciones,
outliers_Precio = outliers_Precio,
outliers_Área = outliers_Área)
print(resultados_outliers)
## $outliers_Piso
## [1] 167
##
## $outliers_Parqueaderos
## [1] 115
##
## $outliers_Baños
## [1] 72
##
## $outliers_Habitaciones
## [1] 272
##
## $outliers_Precio
## [1] 176
##
## $outliers_Área
## [1] 130
Se eliminan los datos atípicos
outlier_indices <- unique(c(
which(vf$Piso %in% outliers_Piso),
which(vf$Parqueaderos %in% outliers_Parqueaderos),
which(vf$Baños %in% outliers_Baños),
which(vf$Habitaciones %in% outliers_Habitaciones),
which(vf$Precio %in% outliers_Precio),
which(vf$Área %in% outliers_Área)
))
vf <- vf[-outlier_indices, ]
shape <- dim(vf)
print(shape)
## [1] 8243 13
Una vez eliminados los registros con datos atípicos, queda una dataset final de 8.243 registros y 13 atrributos.
Se visualizan los datos categóricos
tipovivienda = ggplot(vf, aes(Zona, fill=Tipo) ) +
labs(title = "Tipo de vivienda x Zona")+ylab("Cantidad") +
theme(plot.title = element_text(size = rel(1.5), colour = "black"))
tipovivienda+geom_bar(position="stack") + scale_fill_manual(values = c("green", "yellow")) +
theme(axis.title.x = element_text(face="bold", size=10))
Se observa que el atributo Zona se divide en cinco zonas, mientras que el atributo Tipo en dos tipos, resaltando que la mayor cantidad de viviendas se encuentra en la Zona Sur y que mayoritariamente son apartamentos, seguido de la Zona Norte que concentran la segunda mayor cantidad de viviendas, en donde también sobresalen que la mayoría son apartamentos.
Se calcula la correlación entre atributos
# Seleccionar las columnas específicas
selected_columns <- c("Piso", "Parqueaderos", "Estrato", "Baños", "Habitaciones", "Precio", "Área", "Longitud", "Latitud")
numerical_data <- vf[, selected_columns]
# Calcular la matriz de correlación
correlation_matrix <- cor(numerical_data, use = "complete.obs")
# Mostrar la matriz de correlación
corrplot(correlation_matrix, method = "color", col = colorRampPalette(c("red", "white", "blue"))(200),
tl.cex = 0.8, tl.col = "black", number.cex = 0.7, addCoef.col = "black",
title = "Matriz de Correlación", mar = c(0,0,1,0))
# Identificar atributos redundantes (correlación mayor a 0.85 o menor a -0.85)
redundant_pairs <- data.frame(A = character(), B = character(), Correlation = double())
threshold <- 0.85
for (i in 1:(ncol(correlation_matrix)-1)) {
for (j in (i+1):ncol(correlation_matrix)) {
if (abs(correlation_matrix[i, j]) > threshold) {
redundant_pairs <- rbind(redundant_pairs, data.frame(A = colnames(correlation_matrix)[i],
B = colnames(correlation_matrix)[j],
Correlation = correlation_matrix[i, j]))
}
}
}
# Mostrar atributos redundantes
if (nrow(redundant_pairs) > 0) {
for (i in 1:nrow(redundant_pairs)) {
cat(sprintf("Par de atributos redundantes: %s y %s con correlación de %.2f\n",
redundant_pairs$A[i], redundant_pairs$B[i], redundant_pairs$Correlation[i]))
}
} else {
cat("No se evidencian atributos redundantes a través del método de correlación.\n")
}
## No se evidencian atributos redundantes a través del método de correlación.
A pesar de que no son redundantes debido a que la correlación no es fuerte, si se logra observar que los atributos con mayor correlación positiva moderada son Área y Precio, lo cual es consistente con la realidad, dado que indica que a mayor área de la vivienda, mayor será el precio de venta. Esta relación se puede reflejar mejor en el siguiente gráfico:
ggplot(vf, aes(x = Área, y = Precio, color= factor(Tipo))) +
geom_point() +
facet_wrap(~ Tipo) +
stat_smooth(method = "loess", formula = y ~ x) +
scale_x_continuous(labels = scales::number_format(scale = 2, accuracy = 2, big.mark = ",")) +
scale_y_continuous(labels = scales::number_format(scale = 2, accuracy = 2, big.mark = ","))+
labs(title = "Precio x Área y Tipo")
Hasta este punto se puede inferir que, la relación entre precio y área es mayor en las viviendas tipo casas que en apartamentos, esto se debe a que se valoran más las extensiones de las casas, dado que se le pueden crear más atributos en la construcción, tales como habitaciones, baños, parqueaderos, entre otros, mientras que para los apartamentos hay una limitación por el espacio. En consecuencia, el área no es una variable que explique en alto grado el precio de los apartamentos, como si podría serlo el estrato y la zona, mientras que si podría explicar mejor la variabilidad del precio en las casas.
Finalmente, en esta primera parte del informe, no se utilizarán las variables Longitud y Latitud, debido a que las Zonas de la ciudad también pueden representar la ubicación de las viviendas dentro del sistema de coordenadas.
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.
El método de PCA, además de lo anterior, busca conservar la mayor parte de la variabilidad presente en el conjunto de datos original, por lo que transforma las variables originales en un nuevo conjunto de datos de variables no correlacionadas, que se denominan componentes principales. Para llegar a esto, se ejecutan los siguientes pasos:
Normalización de variables
Debido a que para calcular las componentes principales, primero se deben hallar las varianzas y covarianzas de los datos, las cuales son sensibles a grandes valores, por lo tanto, se procede con la estandarización de los atributos que se utilizaran para hallar las PCA.
vfz=vf[,c(4,5,6,7,8,9)]
vfz= scale(vfz)
head(vfz)
## Estrato Precio Área Parqueaderos Baños Habitaciones
## [1,] -1.5872053 -0.5592655 -0.7340478 -0.3874333 -0.07760334 1.6355960
## [2,] -1.5872053 -0.3470407 -0.3857262 -0.3874333 -0.77594871 -0.4150354
## [3,] -1.5872053 -0.2560873 0.3109171 0.4151836 -0.77594871 0.2685084
## [4,] -0.6149005 -0.1044981 0.7289031 1.2178004 1.31908739 -0.4150354
## [5,] 0.3574043 -0.5289477 -0.5947192 -0.3874333 -0.77594871 -0.4150354
## [6,] 0.3574043 -0.5895834 -0.6156184 -0.3874333 -0.07760334 -0.4150354
Cálculo de componentes principales
vfz.pca=prcomp(vfz)
vfz.pca
## Standard deviations (1, .., p=6):
## [1] 1.8491646 1.1219551 0.6768456 0.6581724 0.4906751 0.4355848
##
## Rotation (n x k) = (6 x 6):
## PC1 PC2 PC3 PC4 PC5
## Estrato 0.3307231 -0.5817181 0.55349643 0.09489379 -0.46079107
## Precio 0.4779451 -0.1887478 -0.02561413 -0.36089498 0.22290663
## Área 0.4418804 0.2381815 -0.26779429 -0.62706453 -0.28070102
## Parqueaderos 0.4096592 -0.2604913 -0.67379212 0.55038693 -0.04911713
## Baños 0.4667978 0.1925411 0.35632780 0.19597534 0.67804660
## Habitaciones 0.2849764 0.6814104 0.20076484 0.35525757 -0.44388228
## PC6
## Estrato 0.15663557
## Precio -0.74522086
## Área 0.45198947
## Parqueaderos 0.07063669
## Baños 0.34627259
## Habitaciones -0.30153348
res.pca <- PCA(vfz, ncp=6,graph = F)
fviz_eig(res.pca, addlabels = TRUE, barcolor = "#4682B4", barfill = "#4F94CD")
Se observa que la primer componente explica el 57% de la varianza de los datos, mientras que la segunda el 21% y la tercera el 7.6%, lo que indica que las tres primeras componentes explican el 85.6% de la variabilidad, recogiendo así buena parte del total de varianza con menos variables.
Cálculo de la contribución
var <- get_pca_var(res.pca)
var$contrib
## Dim.1 Dim.2 Dim.3 Dim.4 Dim.5 Dim.6
## Estrato 10.937779 33.839598 30.63582940 0.9004831 21.2328409 2.4534701
## Precio 22.843149 3.562573 0.06560836 13.0245190 4.9687367 55.5354137
## Área 19.525831 5.673044 7.17137815 39.3209926 7.8793061 20.4294477
## Parqueaderos 16.782067 6.785571 45.39958160 30.2925777 0.2412493 0.4989541
## Baños 21.790020 3.707206 12.69695043 3.8406335 45.9747192 11.9904705
## Habitaciones 8.121154 46.432008 4.03065206 12.6207941 19.7031478 9.0922439
fviz_pca_var(res.pca, col.var = "contrib", gradient.cols =c("yellow", "green", "red"), repel = TRUE)
fviz_contrib(res.pca, choice = "var", axes = 1, top = 10, fill ="#C0FF3E")
fviz_contrib(res.pca, choice = "var", axes = 2, top = 10, fill ="#00CDCD")
Se observa que en la primer componente principal, los atributos Precio, Baños y Área son los que mayor contribución realizan, mientras que para la segunda componente los atributos Habitaciones y Estrato son los de mayor contribución. Así, en este punto se puede indicar que la demanda de viviendas está en función del Precio, el área construida, la distribución interna y el estrato socioeconómico.
Revisión de casos individuales
Para explicar mejor el sentido de las dimensiones, se proceden a seleccionar cuatro viviendas, que se describen a continuación:
vfindividual <- vf[,c(1,4,5,6,7,8,9)]
vfindividual=textshape::column_to_rownames(vfindividual, loc = 1)
datos <- rbind(vfindividual["6100",],
vfindividual["7510",],
vfindividual["78",],
vfindividual["855",])
datos = as.data.frame(datos)
rownames(datos) = c("Id 6100","Id 7510","Id 0078","Id 0855")
datos
## Estrato Precio Área Parqueaderos Baños Habitaciones
## Id 6100 5 1999 800 5 7 5
## Id 7510 6 1950 400 4 5 3
## Id 0078 3 58 50 1 1 2
## Id 0855 3 62 61 0 1 2
casos1 <- rbind(vfz.pca$x[6100,1:2],vfz.pca$x[7510,1:2]) # CP1
rownames(casos1) = c("6100","7510")
casos1 <- as.data.frame(casos1)
casos2 <- rbind(vfz.pca$x[78,1:2], vfz.pca$x[855,1:2]) # CP2
rownames(casos2) = c("0078","0855")
casos2 <- as.data.frame(casos2)
fviz_pca_ind(vfz.pca, col.ind = "#E0EEEE", gradient.cols = c("#FFD700",
"#97FFFF",
"#E066FF"))+
geom_point(data = casos1, aes(x = PC1, y = PC2), color = "red", size = 3) +
geom_point(data = casos2, aes(x = PC1, y = PC2), color = "green", size = 3)
En conjunto, estas dos dimensiones capturan el 78% de la variabilidad en los datos, se observa que las viviendas seleccionadas (puntos rojos y verdes) están situadas cerca del origen, lo que sugiere que no están muy alejadas de la mayoría de las observaciones en las dos dimensiones, en otras palabras, tienen perfiles similares en las dimensiones principales en comparación con el resto de los datos, por lo que no hay una diferencia significativa en las características capturadas por las dos primeras dimensiones del PCA.
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.
El método de análisis de conglomerados se utiliza para descubrir estructuras subyacentes en los datos, permitiendo agrupar objetos de manera lógica y útil, teniendo en cuenta la similitud y distancia de los individuos de los grupos. Para llegar a esto, se ejecutan los siguientes pasos:
Selección del número de clúster método codo
vfc=vf[,c(4,5,6,7,8,9)]
vfc= scale(vfc)
fviz_nbclust(vfc, kmeans, method = "wss") +geom_vline(xintercept = 4, lty = 2, col="red")
Bajo el método del codo de distribución, que es el punto en la gráfica donde la reducción en la suma de las distancias se vuelve menos pronunciada, se podría afirmar que el número de clúster a utilizar es 4.
Selección del número de clúster método Calinski-Harabasz
ch_scores <- vector()
for (k in 2:5) {
kmeans_result <- kmeans(vfc, centers = k, nstart = 25)
ch_scores[k] <- cluster.stats(dist(vfc), kmeans_result$cluster)$ch
}
plot(2:5, ch_scores[2:5], type = "b", pch = 19, frame = FALSE,
xlab = "Número de clústeres", ylab = "Índice de Calinski-Harabasz",
main = "Método de Calinski-Harabasz")
En este método, se considera como óptimo, el número de clústers en donde el índice sea más alto. Para este caso se sitúa en 2.
Selección del número de clúster método silueta
fviz_nbclust(vfc, kmeans, method = "silhouette")
El anterior método, también sugiere tomar solo dos clúster, no obstante, se prefiere tomar un rango intermedio, para también esperar contar con una mayor cantidad de varianza. En consecuencia, una vez evaluado el número de clúster por los tres métodos anteriores, se decide seleccionar 3 clúesters, cuyos grupos se muestran a continuación:
set.seed(123)
kmeans3 =kmeans(vfc, centers = 3, nstart = 25)
fviz_cluster(kmeans3, data = vfc)
observaciones <- table(kmeans_result$cluster)
print(observaciones)
##
## 1 2 3 4 5
## 1652 2577 2604 573 837
vfo=vf[,c(4,5,6,7,8,9)]
kmeans_result <- kmeans(vfo, centers = 3, nstart = 25)
cluster_means <- aggregate(vfo, by = list(Cluster = kmeans_result$cluster), FUN = mean)
print(cluster_means)
## Cluster Estrato Precio Área Parqueaderos Baños Habitaciones
## 1 1 5.788756 1347.2913 435.8043 3.427598 5.119250 4.287905
## 2 2 4.250543 256.5007 111.2060 1.033490 2.469406 3.288740
## 3 3 5.303471 644.2523 269.9108 2.111163 4.220919 4.244841
De los clústeres anteriores, se puede aseverar que:
Los clústeres 1 y 2 se encuentran claramente separados, lo que sugiere que los puntos en estos clústeres son diferentes entre sí en las dimensiones capturadas, especialmente en la dimensión 1, que hace referencia a los valores de Precio, Baños y Área. Por su parte, el clúster 3, cuenta con características intermedias de ambas dimensiones.
Ahora, bajo un análisis más detallado se tiene que:
El clúster 1 se caracteriza en promedio por tener las viviendas con el estrato socioeconómico más alto (6), los Precios de las viviendas ($1.347 millones) y el Área (435 mts2) son los más altos de toda la muestra, también cuentan con más de 3 parqueaderos, más de 5 baños y más de 4 habitaciones. Estas características se asocian más con el tipo de vivienda Casas.
El clúster 2, que se encuentra al otro extremo de las dimensiones, se caracteriza en promedio por tener las viviendas con el estrato socioeconómico más bajo (4), los Precios de las viviendas ($256 millones) y el Área (111 mts2) son los más bajos de toda la muestra, además cuentan con 1 parqueadero, más de 2 baños y más de 3 habitaciones. Estas características se asocian más con el tipo de vivienda Apartamentos.
El clúster 3, que se encuentra en el centro, se caracteriza en promedio por tener viviendas con estrato socioeconómico (5), los Precios de las viviendas ($644 millones) y el (Área 269 mts2) son los segundos más altos de toda la muestra, además cuentan con más de 2 parqueaderos, más de 4 baños y más de 4 habitaciones. Estas características se asocian tanto para casas como para apartamentos.
Examinar la relación entre las variables categóricas (tipo de vivienda, zona y Estrato), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.
La técnica de análisis de correspondencia se utiliza en esencia para las variables categóricas, permitiendo explorar y visualizar las relaciones entre atributos en una tabla de contingencia. Para realizarlo se siguen los siguientes pasos:
Construcción de la tabla cruzada
ftable(vf$Tipo, vf$Zona, vf$Estrato)
## 3 4 5 6
##
## Apartamento Zona Centro 13 7 3 0
## Zona Norte 337 244 494 115
## Zona Oeste 29 58 227 707
## Zona Oriente 58 2 1 1
## Zona Sur 201 1090 1020 446
## Casa Zona Centro 90 7 1 1
## Zona Norte 228 160 270 55
## Zona Oeste 25 26 59 59
## Zona Oriente 280 6 1 0
## Zona Sur 177 517 649 579
No se utilizará la varible barrio, debido a que los mismos se pueden ubicar mejor en cada zona de la ciudad, es decir, los barrios son fijos en cuanto ubicación, como también lo son las zonas.
Ahora, la tabla de frecuencia anterior también se puede representar con el siguiente gráfico:
ggplot(vf,mapping=aes(x=Tipo, y=Estrato, fill=Zona )) +
geom_boxplot()+
labs(title = "Estrato x Tipo y Zona",
x = "Tipo de vivienda",
y = "Estrato de vivienda")+
theme(plot.title = element_text(hjust = 0.5))
Se observa que la zona Centro tiene una menor variabilidad en el estrato, concentrándose en el estrato 3, mientras que las zonas Norte y Oriente, presentan mayor variabilidad con viviendas desde el estrato 3 hasta el 6 tanto para casas como para apartamentos. Por su parte, la zona Sur concentra más las viviendas en los estratos 4 y 5.
Verificación de independencia
tabla1 <- table(vf$Zona, vf$Estrato)
colnames(tabla1) <- c("Estrato 3", "Estrato 4", "Estrato 5", "Estrato 6" )
chisq.test(tabla1)
##
## Pearson's Chi-squared test
##
## data: tabla1
## X-squared = 3809.9, df = 12, p-value < 2.2e-16
Se encuentra que el valor p es muy inferior al nivel de significancia alpha del 5%, por lo que existe evidencia estadística significativa para rechazar la hipótesis nula, por tanto, si existe asociación entre las variables Zona - Estrato.
tabla2 <- table(vf$Tipo, vf$Zona)
chisq.test(tabla2)
##
## Pearson's Chi-squared test
##
## data: tabla2
## X-squared = 684.91, df = 4, p-value < 2.2e-16
Se encuentra que el valor p también es muy inferior al nivel de significancia alpha del 5%, por lo que existe evidencia estadística significativa para rechazar la hipótesis nula, por tanto, si existe asociación entre las variables Tipo - Zona.
tabla3 <- table(vf$Tipo, vf$Estrato)
chisq.test(tabla3)
##
## Pearson's Chi-squared test
##
## data: tabla3
## X-squared = 212.91, df = 3, p-value < 2.2e-16
Se encuentra que de nuevo el valor p es muy inferior al nivel de significancia alpha del 5%, por lo que existe evidencia estadística significativa para rechazar la hipótesis nula, por tanto, si existe asociación entre las variables Tipo - Estrato.
Gráfico de correspondencias
A continuación, se presentan las correspondencias dobles para cada par de atributos y al final, se muestra el resultado al combinar las 3 variables categóricas en dos dimensiones.
ASOCIACIÓN ZONA - ESTRATO
resultados_ac1 <- CA(tabla1)
Se pueden observar relaciones entre los estratos y las zonas; las viviendas del estrato 6 se encuentran en la Zona Oeste; las del estrato 3 se encuentran en las Zonas Oriente y Centro; las de los estratos 4 y 5 se ubican en la Zona Sur; y finalmente no es posible establecer una asociación clara entre la Zona norte y los estratos, aunque si se encuentran más próximos a los estratos 5 y 4.
ASOCIACIÓN TIPO - ZONA
datoscor <- data.frame(
Var1 = as.factor(c(vf$Tipo)),
Var2 = as.factor(c(vf$Zona))
)
mca <- MCA(datoscor, graph = FALSE)
fviz_mca_var(mca, repel = TRUE)
categorias_coords <- mca$var$coord
coord = as.data.frame(categorias_coords[, 1:2])
coord = rownames_to_column(coord, var = "Categoria")
En el gráfico anterior si bien no se establecen correspondencias claras, debido a la distancia entre los atributos, se puede apreciar que, tanto apartamentos como casas se encuentran más cerca de las zonas sur y norte. La zona oeste concentra más apartamentos que casas, mientras que las zonas centro y oriente más casas que apartamentos.
Ahora, se integran los tres atributos en un mismo gráfico para ver las correspondencias múltiples:
ASOCIACIÓN ZONA - ESTRATO - TIPO
datoscorcom <- data.frame(
Var1 = as.factor(c(vf$Tipo)),
Var2 = as.factor(c(vf$Zona)),
Var3 = as.factor(c(vf$Estrato))
)
mca2 <- MCA(datoscorcom, graph = FALSE)
fviz_mca_var(mca2, repel = TRUE, col.var = "purple")
categorias_coordscom <- mca$var$coord
coordenadas = as.data.frame(categorias_coordscom[, 1:2])
coord = rownames_to_column(coordenadas, var = "Categoria")
Se observa que al realizar el análisis con tres variables, la varianza explicada disminuye, esto puede ocurrir porque una de las variable no contribuye significativamente a la explicación de la variabilidad en las primeras dimensiones, lo que provoca que cada dimensión capture una menor proporción de la varianza total.
calcular grado de representatividad
valores_prop <-resultados_ac1$eig ; valores_prop
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.32199794 69.667174 69.66717
## dim 2 0.12922418 27.958822 97.62600
## dim 3 0.01097252 2.374005 100.00000
fviz_screeplot(resultados_ac1, addlabels = TRUE, barcolor = "#4682B4", barfill = "#4F94CD") +
ggtitle("Importancia de las Dimensiones en el MCA ZONA - ESTRATO")
Se observa que la primer componente explica el 69.6% de la varianza de los datos, mientras que la segunda el 28%, lo que indica que, las dos primeras componentes explican el 97.6% de la variabilidad.
valores_prop <-mca$eig ; valores_prop
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.6441265 25.76506 25.76506
## dim 2 0.5000000 20.00000 45.76506
## dim 3 0.5000000 20.00000 65.76506
## dim 4 0.5000000 20.00000 85.76506
## dim 5 0.3558735 14.23494 100.00000
fviz_screeplot(mca , addlabels = TRUE, barcolor = "#FF8C00", barfill = "orange") +
ggtitle("Importancia de las Dimensiones en el MCA TIPO - ZONA")
Se observa que la primer componente explica el 25.7% de la varianza de los datos, mientras que la segunda el 20%, lo que indica que, las dos primeras componentes solo explican el 45.7% de la variabilidad. Lo anterior indica que, ninguna dimensión explica más varianza que una variable original promedio en los datos, por esta razón no era posible apreciar asociaciones directas en el gráfico de dos dimensiones, pues las componentes no alcanzan a recoger una adecuada representación de la variabilidad. Esto también se puede soportar en la lógica de que en una Zona de la ciudad pueden haber tanto casas como apartamentos o también porque la base de datos no se encuentra balanceada entre la cantidad de registros de viviendas para cada una de las Zonas representadas.
valores_prop <-mca2$eig ; valores_prop
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.5612732 21.047744 21.04774
## dim 2 0.4538625 17.019844 38.06759
## dim 3 0.3798355 14.243831 52.31142
## dim 4 0.3334083 12.502810 64.81423
## dim 5 0.3245827 12.171849 76.98608
## dim 6 0.2715077 10.181539 87.16762
## dim 7 0.2004957 7.518588 94.68620
## dim 8 0.1417012 5.313796 100.00000
fviz_screeplot(mca2 , addlabels = TRUE, barcolor = "#CD4F39", barfill = "#FF6347") +
ggtitle("Importancia de las Dimensiones en el MCA TIPO - ZONA - ESTRATO")
En este caso, al haber más variables en el modelo, se requiere una mayor cantidad de dimensiones para explicar la varianza total. Esta situación se presenta principalmente en datos categóricos, debido a que una misma característica puede estar presente en varios atributos; por ejemplo, tanto casas como apartamentos se encuentran en todos los estratos y en todas las zonas. Al observar las componentes, se obtiene que la primer explica el 21% de la varianza de los datos, mientras que la segunda explica el 17%, lo que indica que las dos primeras componentes explican tan sólo el 38% de la variabilidad.
Teniendo en cuenta la relevancia que tiene el sector inmobiliario en la economía nacional, que es generadora de empleos y de ingresos para el país, se puede afirmar que para el caso de la empresa que está interesada en expandir sus operaciones a la ciudad de Cali, cuenta con un panorama alentador para generar beneficios socioeconómicos. Lo anterior es producto de las características que tienen las viviendas observadas, de las cuales se puede inferir que:
Desde el análisis de datos, hay diversificación de las viviendas en cada una de las zonas de la ciudad, sobresaliendo el precio de las viviendas tipo casa, que tiende a incrementar en la medida en que aumenta el área construida, lo que conlleva a pensar en estrategias para ofrecer las casas a grupos de personas o clientes de mayores ingresos, que prefieran más espacio que cualquier otro atributo. Caso contrario ocurre con los apartamentos, en donde prácticamente el precio no se determina tanto por el área construida, sino por la zona y estrato, por lo que este tipo de viviendas se puede ofrecer mejor a un público que prefiera la ubicación como atributo de decisión.
Desde el análisis de componentes principales se encontró que con sólo realizar una combinación lineal de menos variables, se puede establecer que en una de las dimensiones los atributos precio, cantidad de baños y área Construida son los que recogen la mayor variabilidad de la muestra de viviendas, mientras que en la otra dimensión los atributos cantidad de habitaciones y estrato socioeconómico son las más relevante agrupando la mayor parte de la variación. En consecuencia, se puede afirmar que estas dos dimensiones capturan la mayor parte de la información esencial de las viviendas, que, además, van a mejorar la eficiencia de modelos predictivos cuando se requieran ejecutar, dado que disminuye la complejidad y el sobreajuste al reducir las dimensiones.
Desde el análisis de conglomerados la muestra de viviendas se puede dividir en 3 grupos o segmentos representativos, destacando que el clúster 1 abarca mayoritariamente a las viviendas tipo casas, y que tienen precios promedio de venta más altos, con más área construida y mayores atributos en cuanto a la cantidad de habitaciones, baños y parqueaderos. El clúster 2 abarca mayoritariamente a las viviendas tipo apartamento, y que tienen precios promedio de venta más bajos, con menor área construida y menores atributos en cuanto a la distribución interna de las viviendas. Por su parte, el clúster 3 es intermedio en cuanto al precio promedio de venta y la demás cantidad de atributos de las viviendas, está compuesto por casas y apartamentos. Por lo anterior, las viviendas del clúster 1 pueden ser ofrecidas a clientes que tienen mayores ingresos y que prefieren casas con mayor área construida; las del clúster 2 a clientes con ingresos menores y que prefieran apartamentos; y las del clúster 3 a personas con ingresos promedios y que prefieran mejor ubicación que área construida.
Desde el análisis de correspondencia múltiples se identificó que hay una asociación entre los atributos zona y estrato, destacando que, las viviendas con el estrato más alto se encuentran en la zona oeste y las de estrato más bajo en las zonas centro y oriente. Por su parte, las zonas sur y norte concentran viviendas de estrato medio – alto. También se pudo establecer que, las zonas sur y norte son las que más cantidad de viviendas concentra, mientras que la Zona centro y oriente son las que menor cantidad de viviendas tienen. Adicional, la zona oeste está más relacionada con los apartamentos. Así, resulta ventajoso para la inmobiliaria establecer una segmentación de clientes, de acuerdo con las preferencias de ubicación y estrato de las viviendas, que a su vez se relacionan con el área construida, puesto que las casas que son las que mayor área tienen están en el norte y sur de la Ciudad, mientras que los apartamentos con mayor área se encuentran en el oeste.
Con el objetivo de posicionarse en el mercado, obteniendo mayores beneficios, se plantea la necesedidad de recomendar a la empresa:
Realizar campañas de mercadeo y publicidad orientadas a la segmentación del público, según las preferencias de las viviendas; casas - apartamentos y estrato – ubicación. De esta manera podrán clasificar a los potenciales clientes de acuerdo con su nivel de ingresos promedio.
Promover la venta de viviendas en las zonas sur y norte, dado que son las de mayores precios de venta promedio, especialmente las casas, que resultan ofreciéndose a un precio mayor debido al área construida. Asimismo, en la zona oeste promover la venta de apartamentos, que mayoritariamente se encuentran ubicadas en el estrato 6, teniendo un precio promedio de venta alto en función del estrato socioeconómico.
Las zonas centro y oriente son las de menores cantidad de viviendas y a la vez las de menores precios promedios de venta, por lo que se podría pensar en realizar inversiones de largo plazo, adquiriendo viviendas económicas para que se valoricen o también, adquirir lotes para desarrollar nuevos proyectos, buscando maximizar beneficios a través de una mayor cantidad de viviendas que se puedan vender, caso contrario a lo que sucede en la zona oeste, en donde hay menor cantidad de viviendas, en este caso apartamentos, pero que por su ubicación se venden a un precio promedio mayor.
En general, la compañía se debe centrar en ofrecer viviendas a la medida de las necesidades de los clientes en cuanto a preferencias de ubicación y tamaño, partiendo de las características identificadas para cada segmento.
Finalmente, resulta relevante realizar un estudio de mercado en la zona sur, para descubrir otros factores asociados al nivel de valorización que han tenido las viviendas, dado que es la Zona que más cantidad de viviendas concentra, por lo que es probable que el precio promedio de las mismas se incremente en el mediano plazo.