El mercado de viviendas urbanas es un sector complejo y dinámico que se encuentra en constante evolución. Para tener éxito en este mercado, las empresas inmobiliarias necesitan comprender en profundidad las tendencias del mercado, las necesidades de los clientes y la competencia.
En este informe, se presenta un análisis del mercado de viviendas urbanas en Cali. El análisis se basa en información detallada sobre diversas propiedades residenciales disponibles en el mercado de la ciudad de Cali. Se aplicarán diversas técnicas estadísticas para poder extraer conclusiones relevantes para el cliente.
Se realiza un primer análisis exploratorio de la base de datos. Este análisis se estructurará en la identificación de las columnas y dimensiones de la tabla, lo segundo será identificar los tipos de datos de las columnas y seleccionar cuáles serán relevantes para el análisis. Lo tercero será realizar la identificación de valores perdidos dentro de las columnas seleccionadas para el análisis y brindar posibles propuestas para poder solucionar los datos faltantes. Con la realización de estos pasos se puede determinar la relación de algunas variables con respecto a los objetivos del presente informe.
head(vivienda)
cat("Cantidad columnas: ", ncol(vivienda), "Cantidad filas: ", nrow(vivienda))
## Cantidad columnas: 13 Cantidad filas: 8322
Según los objetivos del estudio, se identifican como variables relevantes zona, barrio, tipo como variables categóricas. Para análisis de variables numéricas se selecciona principalmente la variable preciom, como variables para análisis de caraterísticas se seleccionan piso, estrato, areaconst, banios, habitac. A continuación se realiza una verificación de datos categóricos relevantes:
# Vector con los nombres de las columnas a analizar
columnas_interes <- c("zona", "piso", "tipo", "estrato")
# Bucle for para iterar sobre cada columna
for (columna in columnas_interes) {
valores_unicos <- unique(vivienda[[columna]])
print(paste0("Valores únicos para ", columna, ": ", paste0(valores_unicos, collapse = ", ")))
}
## [1] "Valores únicos para zona: Zona Oriente, Zona Sur, Zona Norte, Zona Oeste, Zona Centro, NA"
## [1] "Valores únicos para piso: NA, 02, 01, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12"
## [1] "Valores únicos para tipo: Casa, Apartamento, NA"
## [1] "Valores únicos para estrato: 3, 4, 5, 6, NA"
# Generando vector para resumir las columnas seleccionadas
specific_columns <- c("preciom", "parqueaderos", "areaconst", "banios", "habitaciones")
summary(vivienda[, specific_columns])
## preciom parqueaderos areaconst banios
## Min. : 58.0 Min. : 1.000 Min. : 30.0 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 1.000 1st Qu.: 80.0 1st Qu.: 2.000
## Median : 330.0 Median : 2.000 Median : 123.0 Median : 3.000
## Mean : 433.9 Mean : 1.835 Mean : 174.9 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 2.000 3rd Qu.: 229.0 3rd Qu.: 4.000
## Max. :1999.0 Max. :10.000 Max. :1745.0 Max. :10.000
## NA's :2 NA's :1605 NA's :3 NA's :3
## habitaciones
## Min. : 0.000
## 1st Qu.: 3.000
## Median : 3.000
## Mean : 3.605
## 3rd Qu.: 4.000
## Max. :10.000
## NA's :3
Se evidencia que vienen valores de id con vacíos, para lo cual es necesario quitarlos de la base de datos:
faltantes_id = sum(is.na(vivienda$id))
cat("Cantidad de id faltantes:", faltantes_id)
## Cantidad de id faltantes: 3
## Se remueven los id vacios de la tabla:
vivienda = subset(vivienda, !is.na(id))
cat("Nueva cantidad filas: ", nrow(vivienda))
## Nueva cantidad filas: 8319
También se realiza la verificación de datos duplicados dentro de la tabla provista:
duplicates <- duplicated(vivienda)
n_duplicates <- sum(duplicates)
cat("Cantidad de filas duplicadas: ", n_duplicates)
## Cantidad de filas duplicadas: 0
## Se remueven los duplicados encontrados:
vivienda <- unique(vivienda)
cat("Nueva cantidad filas: ", nrow(vivienda))
## Nueva cantidad filas: 8319
Con lo identificado en la selección de variables se realiza la limpieza de las columnas de tipo, zona y barrio. Adicionalmente, al haber identificado un valor de “APTO” en la columna zona, esta se unifica con “APARTAMENTO” para normalizar los datos.
upper_cols <- c("zona", "barrio", "tipo")
for (col in upper_cols) {
vivienda[[col]] <- toupper(vivienda[[col]])
}
head(vivienda[, upper_cols])
## uses visdat
vis_miss(vivienda)
Haciendo uso de la librería visdat se logra identificar
que dentro de la tabla dispuesta, después de la limpieza se tiene 3.9 %
de datos faltantes. Todos distribuidos en las columnas
parqueaderos y piso.
## uses visdat
md.pattern(vivienda, rotate.names = TRUE)
## id zona estrato preciom areaconst banios habitaciones tipo barrio longitud
## 4808 1 1 1 1 1 1 1 1 1 1
## 1909 1 1 1 1 1 1 1 1 1 1
## 876 1 1 1 1 1 1 1 1 1 1
## 726 1 1 1 1 1 1 1 1 1 1
## 0 0 0 0 0 0 0 0 0 0
## latitud parqueaderos piso
## 4808 1 1 1 0
## 1909 1 1 0 1
## 876 1 0 1 1
## 726 1 0 0 2
## 0 1602 2635 4237
La primera posible solución para poder trabajar con los datos de la
forma más completa posible puede ser precindir de usar las variables
parqueaderos y piso. Esta estrategia tiene
como ventaja que puede ser de las más rápidas de implementar, así como
reducir la dimensionalidad de los datos, con lo que aplicar algunos
algoritmos puede llegar a ser más eficiente. Así mismo, la reducción de
información dentro de la tabla puede generar eliminación de información
importante, lo que puede conllevar a presentar sesgo en el
resultado.
vivienda %>% count(parqueaderos)
Entendiendo el contexto, y que no existen valores de 0 dentro de la información que tenemos, es prudente asumir que existen viviendas que no tienen parqueaderos, por lo cual se procede a inputar el valor de 0 en los missing values
vivienda <- vivienda %>% mutate(parqueaderos = ifelse(is.na(parqueaderos), 0, parqueaderos))
Se supone una correlación entre la variable piso con la variable tipo de vivienda. En las variables casa, no tendría sentido tener el piso en el que se encuentra ubicada la casa (como sí lo tendría en un apartamento). Así que se propone realizar la revisión. A menos de que se trate de la cantidad de pisos de una vivienda. Se procede a reemplazar los valores de la mediana por tipo
# Convertir a numérico
vivienda$piso <- as.numeric(vivienda$piso)
# reemplazar valores por mediana por tipo: CASA
only_casas <- subset(vivienda, vivienda$tipo == "CASA")
median_casa_piso = median(only_casas$piso)
vivienda <- vivienda %>% mutate(piso = ifelse(is.na(piso) & tipo == "CASA", median_casa_piso, piso))
# reemplazar valores por mediana por tipo: CASA
only_apto <- subset(vivienda, vivienda$tipo == "APARTAMENTO")
median_apt_piso = median(only_apto$piso)
vivienda <- vivienda %>% mutate(piso = ifelse(is.na(piso) & tipo == "APARTAMENTO", median_apt_piso, piso))
verificación de dataset completo:
## uses visdat
vis_miss(vivienda)
Encoding de variables para hallar matrices de correlación:
vivienda$zona_num <- as.integer(factor(vivienda$zona))
vivienda$tipo_num <- as.integer(factor(vivienda$tipo))
vivienda$barrio_num <- as.integer(factor(vivienda$barrio))
Con el dataframe codificado se procede a realizar una matriz de correlación para hallar la relación entre los valores
library(psych)
enc_vivienda <- vivienda[, !names(vivienda) %in% c("barrio", "zona", "tipo")]
correlacion <- round(cor(enc_vivienda), 1)
corPlot(correlacion, number.cex = 0.5)
## Warning in axis(2, at = at2, labels = lab2, las = ylas, ...): "number.cex" is
## not a graphical parameter
## Warning in axis(xaxis, at = at1, labels = lab1, las = xlas, line = line, :
## "number.cex" is not a graphical parameter
## Warning in text.default(rx, ry, rv, cex = 1.5 * cex, ...): "number.cex" is not
## a graphical parameter
## Warning in axis(4, at = at2, labels = labels, las = 2, ...): "number.cex" is
## not a graphical parameter
El Análisis de Componentes Principales (ACP) es una técnica estadística utilizada en el análisis de datos. Su principal objetivo es reducir la dimensionalidad de un conjunto de datos mientras se conserva la mayor cantidad posible de varianza.
## Se seleccionan las variables que se van a utilizar dentro del ACP
selected_vars <- c("piso", "estrato", "preciom", "areaconst", "parqueaderos", "banios","habitaciones", "zona_num", "tipo_num")
selected_vivienda <- vivienda[, selected_vars]
## Reescalar las variables seleccionadas
scale_vivienda <- scale(selected_vivienda)
Generación de los componentes principales haciendo uso de la librería
PCA
library(FactoMineR)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
res.ACP <- PCA(scale_vivienda, graph = FALSE)
## Verficación de valores propios
res.ACP$eig
## eigenvalue percentage of variance cumulative percentage of variance
## comp 1 3.7134103 41.260114 41.26011
## comp 2 1.8575851 20.639835 61.89995
## comp 3 1.0235814 11.373126 73.27308
## comp 4 0.7766444 8.629382 81.90246
## comp 5 0.4603713 5.115237 87.01770
## comp 6 0.4264659 4.738510 91.75621
## comp 7 0.3180017 3.533352 95.28956
## comp 8 0.2396958 2.663287 97.95284
## comp 9 0.1842441 2.047156 100.00000
Visualización de información de acumulación de la varianza dentro de los componentes generados
fviz_eig(res.ACP, addlabels = TRUE)
Se procede a realizar el círculo de correlación
Permite realizar la identificación de cómo se correlaciónan las variables estudiadas y cuál es su relación con respectoa los datos.
##Circulo de correlación (nube de variables)
fviz_pca_var(res.ACP,
col.var = "contrib", # Color by contributions to the PC
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE # Avoid text overlapping
)
Así mismo, se tiene lo siguiente:
El análisis de conglomerados busca agrupar las propiedades residenciales en clusters. Esto significa que las propiedades dentro de cada grupo compartirán características similares, como ubicación, tamaño, precio y otras. Al identificar estos grupos, se puede entender mejor las dinámicas de la data y el comportamiento que tienen las agrupaciones.
fviz_nbclust(scale_vivienda, kmeans, method = "wss")
Usando el elbow method, no se tiene una curva con una relación acentuada
en algún cluster específico, así que se decide usar el más central,
eligiendo realizar el análisis con cuatro clusters y se procede a
realizar la visualización para poder hacer la verificación de
resultados
cluster_generation <- kmeans(scale_vivienda, centers = 4, nstart = 25)
fviz_cluster(cluster_generation, data = scale_vivienda,
geom = "point", stand = FALSE,
ellipse = TRUE, show.clust.cent = TRUE)
Se encuentra que:
Los cluster encontrados tienen una distribución en cuanto a frecuencia muy similar. El primer cluster tiene 2219 observaciones, el segundo tiene 1785, mientras que el tercero tiene 1026 observaciones y el último tiene 3289.
## Se asigna el número del cluster a cada uno de los registros del dataframe
selected_vivienda$cluster <- as.factor(cluster_generation$cluster)
## Se halla la cantidad y distribución de los cluster
cat("Distribución de la cantidad de filas por cluster:")
## Distribución de la cantidad de filas por cluster:
print(table(selected_vivienda$cluster))
##
## 1 2 3 4
## 1785 3289 2219 1026
cat("\n")
## Se genera información relevante para cada uno de los cluster
interest_vars <- c("preciom", "banios", "estrato", "piso", "areaconst")
for (clus in 1:4) {
cat("Resulados para cluster: ", clus)
cat("\n")
cluster_info <- subset(selected_vivienda, selected_vivienda$cluster == clus)
print(summary(cluster_info[, interest_vars]))
cat("\n\n")
}
## Resulados para cluster: 1
## preciom banios estrato piso areaconst
## Min. : 190 Min. :0.000 Min. :4.00 Min. : 1.000 Min. : 50.0
## 1st Qu.: 397 1st Qu.:3.000 1st Qu.:5.00 1st Qu.: 4.000 1st Qu.:115.0
## Median : 530 Median :3.000 Median :6.00 Median : 4.000 Median :143.2
## Mean : 604 Mean :3.529 Mean :5.65 Mean : 5.222 Mean :163.1
## 3rd Qu.: 720 3rd Qu.:4.000 3rd Qu.:6.00 3rd Qu.: 7.000 3rd Qu.:196.0
## Max. :1801 Max. :6.000 Max. :6.00 Max. :12.000 Max. :932.0
##
##
## Resulados para cluster: 2
## preciom banios estrato piso
## Min. : 58.0 Min. :0.000 Min. :3.000 Min. : 1.000
## 1st Qu.:149.0 1st Qu.:2.000 1st Qu.:4.000 1st Qu.: 3.000
## Median :210.0 Median :2.000 Median :4.000 Median : 4.000
## Mean :215.4 Mean :2.036 Mean :4.191 Mean : 4.016
## 3rd Qu.:270.0 3rd Qu.:2.000 3rd Qu.:5.000 3rd Qu.: 5.000
## Max. :900.0 Max. :4.000 Max. :6.000 Max. :12.000
## areaconst
## Min. : 35.00
## 1st Qu.: 60.00
## Median : 74.00
## Mean : 80.02
## 3rd Qu.: 90.00
## Max. :605.00
##
##
## Resulados para cluster: 3
## preciom banios estrato piso
## Min. : 80.0 Min. : 0.000 Min. :3.000 Min. : 1.000
## 1st Qu.: 260.0 1st Qu.: 3.000 1st Qu.:3.000 1st Qu.: 2.000
## Median : 350.0 Median : 3.000 Median :4.000 Median : 2.000
## Mean : 367.9 Mean : 3.349 Mean :4.076 Mean : 2.074
## 3rd Qu.: 450.0 3rd Qu.: 4.000 3rd Qu.:5.000 3rd Qu.: 2.000
## Max. :1500.0 Max. :10.000 Max. :6.000 Max. :10.000
## areaconst
## Min. : 30.0
## 1st Qu.: 135.0
## Median : 190.0
## Mean : 207.7
## 3rd Qu.: 260.0
## Max. :1365.0
##
##
## Resulados para cluster: 4
## preciom banios estrato piso
## Min. : 255.0 Min. : 0.000 Min. :3.000 Min. : 1.000
## 1st Qu.: 680.0 1st Qu.: 4.000 1st Qu.:5.000 1st Qu.: 2.000
## Median : 880.0 Median : 5.000 Median :6.000 Median : 2.000
## Mean : 981.2 Mean : 5.317 Mean :5.489 Mean : 2.274
## 3rd Qu.:1250.0 3rd Qu.: 6.000 3rd Qu.:6.000 3rd Qu.: 2.000
## Max. :1999.0 Max. :10.000 Max. :6.000 Max. :12.000
## areaconst
## Min. : 128.0
## 1st Qu.: 300.0
## Median : 395.5
## Mean : 428.8
## 3rd Qu.: 500.0
## Max. :1745.0
El análisis de correspondencia busca identificar y visualizar las relaciones entre las variables categóricas de un conjunto de datos. En el contexto de los datos, se espera que este análisis permita descubrir cómo se distribuyen los diferentes tipos de vivienda en las distintas zonas y estratos
cat_vivienda <- table(vivienda$zona, vivienda$estrato)
cat_vivienda
##
## 3 4 5 6
## ZONA CENTRO 105 14 4 1
## ZONA NORTE 572 407 769 172
## ZONA OESTE 54 84 290 770
## ZONA ORIENTE 340 8 2 1
## ZONA SUR 382 1616 1685 1043
chisq.test(cat_vivienda)
##
## Pearson's Chi-squared test
##
## data: cat_vivienda
## X-squared = 3830.4, df = 12, p-value < 2.2e-16
Un p-valor menor a 0.05 en un análisis de correspondencia indica que la relación entre las categorías de tipo de vivienda y zona. Esto significa que existe una fuerte asociación entre estas variables. En otras palabras, la distribución de los tipos de vivienda no es uniforme en todas las zonas y barrios, lo que te permite identificar patrones específicos en el mercado inmobiliario.
cat_vivienda_AC <- CA(cat_vivienda, graph = TRUE)
Se encuentra lo siguiente:
El análisis nos muestra una relación con respect a los estratos 4 y 5 con la Zona Sur, así como una relación entre el estrato 6 y la Zona Oeste.
fviz_screeplot(cat_vivienda_AC, addlabels = TRUE)
Cada una de las técnicas empleadas ha aportado valiosa información para comprender la estructura y las dinámicas del mercado inmobiliario. El ACP ha permitido reducir la dimensionalidad de los datos y destacar las variables más influyentes, mientras que el análisis de conglomerados ha facilitado la identificación de segmentos homogéneos de propiedades. Por su parte, el análisis de correspondencias ha revelado relaciones significativas entre las variables categóricas, enriqueciendo la comprensión de la distribución espacial de la oferta inmobiliaria.
Haciendo uso de la reducción de la dimensionalidad se logra la visualización de datos multidimensionales, identificación de patrones, mejora de la eficiencia en modelos.Mientras tranto el análisis de Correspondencias sirve para encontrar las relaciones entre variables categóricas, visualización de tablas de contingencia, identificación de asociaciones y patrones, segmentación de datos. El Análisis de Conglomerados identifica grupos homogéneos, segmentación de datos para estrategias personalizadas, descubrimiento de patrones ocultos. En conjunto, estos resultados proporcionan una base sólida para la toma de decisiones estratégicas en el sector inmobiliario, permitiendo una mejor comprensión de las preferencias de los consumidores, la identificación de oportunidades de mercado y la optimización de las estrategias de marketing y ventas.
Se llevaron a cabo diversos análisis exploratorios con el objetivo de identificar patrones y relaciones subyacentes en los datos del mercado inmobiliario. A continuación, se presentan las principales conclusiones obtenidas:
Análisis de Componentes Principales (ACP): Se identificaron diversas relaciones entre las variables analizadas. Observamos una correlación positiva entre variables como parqueaderos y precio, baños y área construida, así como entre habitaciones y tipo de vivienda, lo que sugiere una asociación directa entre ellas. Por otro lado, no se encontró una relación lineal significativa entre estrato y número de habitaciones, estrato y tipo de vivienda, ni entre piso y precio. En cuanto a la contribución a los componentes principales, las variables baños y área construida mostraron una mayor influencia en la definición del segundo componente. Finalmente, se detectó una correlación negativa significativa entre piso y número de habitaciones, así como entre piso y tipo de vivienda, indicando una relación inversa entre estas variables.
Análisis de Conglomerados: A través del análisis de conglomerados, se identificaron cuatro grupos en la base de datos. Estos grupos se caracterizan por presentar diferencias significativas en términos de estrato socioeconómico, área construida y número de habitaciones, lo que permite una segmentación más precisa para lograr ofrecer productos específicos desde la inmobiliaria.
Análisis de Correspondencias: El análisis de correspondencias evidenció una fuerte asociación entre las variables “zona” y “estrato socioeconómico”. Esta técnica resultó especialmente útil para explorar las relaciones entre las variables categóricas presentes en la base de datos, permitiendo identificar patrones de distribución y co-ocurrencia entre las diferentes categorías.