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.
# install.packages("learnr") # solo una vez
# install.packages("devtools") # solo una vez
# install.packages("ade4")
# devtools::install_github("dgonxalex80/paqueteMODELOS") #descarga paquete
library('paqueteMODELOS')
## Loading required package: boot
## Loading required package: broom
## Loading required package: GGally
## Loading required package: ggplot2
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
## Loading required package: gridExtra
## Loading required package: knitr
## Loading required package: summarytools
library('tidyverse')
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.3 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ lubridate 1.9.3 ✔ tibble 3.2.1
## ✔ purrr 1.0.2 ✔ tidyr 1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::combine() masks gridExtra::combine()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ tibble::view() masks summarytools::view()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library('corrplot')
## corrplot 0.92 loaded
library("cluster")
library("dendextend")
##
## ---------------------
## Welcome to dendextend version 1.17.1
## Type citation('dendextend') for how to cite the package.
##
## Type browseVignettes(package = 'dendextend') for the package vignette.
## The github page is: https://github.com/talgalili/dendextend/
##
## Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
## You may ask questions at stackoverflow, use the r and dendextend tags:
## https://stackoverflow.com/questions/tagged/dendextend
##
## To suppress this message use: suppressPackageStartupMessages(library(dendextend))
## ---------------------
##
##
## Attaching package: 'dendextend'
##
## The following object is masked from 'package:stats':
##
## cutree
library("factoextra")
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library("ggplot2")
library(psych)
##
## Attaching package: 'psych'
##
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
##
## The following object is masked from 'package:boot':
##
## logit
library(factoextra)
library(cluster)
library(FactoMineR)
library(factoextra)
library(gridExtra)
data(vivienda)
head(vivienda)
## # A tibble: 6 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona O… <NA> 3 250 70 1 3 6
## 2 1169 Zona O… <NA> 3 320 120 1 2 3
## 3 1350 Zona O… <NA> 3 350 220 2 2 4
## 4 5992 Zona S… 02 4 400 280 3 5 3
## 5 1212 Zona N… 01 5 260 90 1 2 3
## 6 1724 Zona N… 01 5 240 87 1 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Inicialmente, debemos hacer una limpieza de la base de datos, en la que eliminaremos las columnas innecesarias para el objetivo del estudio, revisaremos el tipo de datos que contiene cada columna y de ser necesario, se acomodará.
Primero, se eliminan las variables id, barrio, latitud y longitud ya que no se consideran relevantes en el análisis propuesto en esta actividad:
vivienda <- vivienda[,c(-1, -11, -12, -13)]
head(vivienda)
## # A tibble: 6 × 9
## zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 Zona O… <NA> 3 250 70 1 3 6 Casa
## 2 Zona O… <NA> 3 320 120 1 2 3 Casa
## 3 Zona O… <NA> 3 350 220 2 2 4 Casa
## 4 Zona S… 02 4 400 280 3 5 3 Casa
## 5 Zona N… 01 5 260 90 1 2 3 Apar…
## 6 Zona N… 01 5 240 87 1 3 3 Apar…
str(vivienda)
## tibble [8,322 × 9] (S3: tbl_df/tbl/data.frame)
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ piso : chr [1:8322] NA NA NA "02" ...
## $ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
se observa que el tipo de variables es el adecuado en cada columna, por lo que no se considera necesario hacer algún ajuste.
A continuación, evaluaremos la cantidad de datos faltantes presentes en la base y se tomará la decisión de cómo darle manejo a este tipo de datos:
colSums(is.na(vivienda)) # Número de datos faltantes por variable
## zona piso estrato preciom areaconst parqueaderos
## 3 2638 3 2 3 1605
## banios habitaciones tipo
## 3 3 3
La variable piso y parqueaderos tienen un gran número de datos faltantes, por lo que se propone eliminar “piso” del dataset (para efectos prácticos) pero en el caso de parqueaderos, notamos que el valor minimo que toma es 1, por lo tanto se puede asumir que los datos faltantes en esta variable corresponden a las viviendas que no tienen parqueaderos y por tanto se puede imputar por cero.
vivienda <- vivienda[,c(-2)]
vivienda$parqueaderos[is.na(vivienda$parqueaderos)] <-0 #se imputa el dato ausente por cero en la variable parqueaderos
Dado que la cantidad de datos ausente por variable no supera 3 datos, de 8322, podemos eliminar las filas que presenten estos datos y así tratar de no introducir mucho ruido en la base.
vivienda <- na.omit(vivienda)
colSums(is.na(vivienda)) # verificamos el número de datos faltantes
## zona estrato preciom areaconst parqueaderos banios
## 0 0 0 0 0 0
## habitaciones tipo
## 0 0
nrow(vivienda) # Número de datos resultantes al quitar los faltantes
## [1] 8319
summarytools::descr(vivienda)
## Non-numerical variable(s) ignored: zona, tipo
## Descriptive Statistics
## vivienda
## N: 8319
##
## areaconst banios estrato habitaciones parqueaderos preciom
## ----------------- ----------- --------- --------- -------------- -------------- ---------
## Mean 174.93 3.11 4.63 3.61 1.48 433.90
## Std.Dev 142.96 1.43 1.03 1.46 1.24 328.67
## Min 30.00 0.00 3.00 0.00 0.00 58.00
## Q1 80.00 2.00 4.00 3.00 1.00 220.00
## Median 123.00 3.00 5.00 3.00 1.00 330.00
## Q3 229.00 4.00 5.00 4.00 2.00 540.00
## Max 1745.00 10.00 6.00 10.00 10.00 1999.00
## MAD 84.51 1.48 1.48 1.48 1.48 207.56
## IQR 149.00 2.00 1.00 1.00 1.00 320.00
## CV 0.82 0.46 0.22 0.40 0.84 0.76
## Skewness 2.69 0.93 -0.18 1.63 1.65 1.85
## SE.Skewness 0.03 0.03 0.03 0.03 0.03 0.03
## Kurtosis 12.91 1.13 -1.11 3.98 5.42 3.67
## N.Valid 8319.00 8319.00 8319.00 8319.00 8319.00 8319.00
## Pct.Valid 100.00 100.00 100.00 100.00 100.00 100.00
table(vivienda$zona)
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## 124 1920 1198 351 4726
table(vivienda$tipo)
##
## Apartamento Casa
## 5100 3219
El estrato oscila entre 3 y 6, siendo estratos medios y altos los que contempla esta fuente de información, el precio va entre 58 y casi 2 mil millones de pesos, siendo en promedio 434 millones, el 75% de las viviendas tienen 229 metros cuadrados o menos de construidas, cuentan con 2 parqueaderos, 4 baños y 4 habitaciones. Encontramos un mayor número de viviendas ubicadas al sur y norte de la ciudad de la ciudad y la mayoría son de tipo apartamento.
table(vivienda$zona, vivienda$estrato) %>%
prop.table(margin = 1)*100
##
## 3 4 5 6
## Zona Centro 84.6774194 11.2903226 3.2258065 0.8064516
## Zona Norte 29.7916667 21.1979167 40.0520833 8.9583333
## Zona Oeste 4.5075125 7.0116861 24.2070117 64.2737896
## Zona Oriente 96.8660969 2.2792023 0.5698006 0.2849003
## Zona Sur 8.0829454 34.1938214 35.6538299 22.0694033
El \(85\%\) de las viviendas en la zona centro son de estrato 3, mientras que en la zona norte tenemos mayoría de viviendas de estrato 5 pero tambien una participación importante en el estraro 3 y 4. En el oeste prima el estrato 6 mientras que casi la totalidad de viviendas de la zona oriente son de estrato 6. Finalmente el sur tiene casi que igual participación de viviendas de estrato 4 y 5, seguidos del estrato 6.
x <- vivienda[c("tipo","zona")]
conteo <- table(x$tipo,x$zona)
barplot(conteo, main="Número de viviendas por zona y tipo",
ylab="Número de viviendas",
col=c("#0d3b66","#f4d35e"),
legend = rownames(conteo),
las=2,
names.arg=c("Centro","Norte","Oeste","Oriente","Sur"),
args.legend = list(x = "top"))
Frente al tipo de vivienda, se aprecia que el centro tiene la minoría de viviendas, pero casi en su totalidad son de tipo casa. En la zona sur y norte es equitativo la proporción de casas y apartamentos, mientras que en el oeste en su gran mayoría son de apartamentos, contrario al oriente que priman las casas.
par(mfrow=c(3,2))
boxplot(vivienda$habitaciones~vivienda$zona,
main = "Distribución habitaciones por zona",
ylab="habitaciones",
xlab = "Zona", las=2,
col=c("burlywood1","#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("Centro","Norte","Oeste", "Oriente", "Sur"))
boxplot(vivienda$banios~vivienda$zona,
main = "Distribución baños por zona",
ylab="baños",
xlab = "Zona", las=2,
col=c("burlywood1","#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("Centro","Norte","Oeste", "Oriente", "Sur"))
boxplot(vivienda$parqueaderos ~ vivienda$zona,
main = "Distribución parqueaderos por zona",
ylab="parqueaderos",
xlab = "Zona", las=2,
col=c("burlywood1","#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("Centro","Norte","Oeste", "Oriente", "Sur"))
###
boxplot(vivienda$habitaciones~vivienda$tipo,
main = "Distribución habitaciones por tipo",
ylab="habitaciones",
xlab = "tipo", las=2,
col=c("burlywood1", "darkolivegreen1"),
names = c("Apartamento","Casa"))
boxplot(vivienda$banios~vivienda$tipo,
main = "Distribución baños por tipo",
ylab="baños",
xlab = "tipo", las=2,
col=c("burlywood1", "darkolivegreen1"),
names = c("Apartamento","Casa"))
boxplot(vivienda$parqueaderos ~ vivienda$tipo,
main = "Distribución parqueaderos por tipo",
ylab="parqueaderos",
xlab = "tipo", las=2,
col=c("burlywood1", "darkolivegreen1"),
names = c("Apartamento","Sur"))
Finalmente, podemos concluir que el número de habitaciones de las viviendas en la zona norte, oeste y sur tienen una distribución similar y esto se debe a que en su mayoría son de tipo apartamento, donde priman entre 2 y 4 habitaciones, mientras que las zonas cuyo nivel de participación de casas es mayor, tienen mas habitaciones, oscilando entre 3 y 5 para el centro y 4 y 7 para el oriente. Al evaluar el número de baños, observamos que las viviendas en la zona norte ti tienen menor número de barrios que las otras zonas, mientras que el oeste tiene un comportamiento similar al observado en las zonas con más casas.
En el centro y oriente es menor la disponibilidad de parqueaderos, teniendo que el \(25\%\) de las viviendas no tienen este beneficio, mientras que en el oeste es común encontrar apartamentos con al menos un parqueadero disponible.
Al comparar las variables anteriores por tipo de vivienda, es evidente que las casas tienen mayor número de habitaciones, baños y paqueadero disponible y esto se puede intuir que es porque tienen mayor espacio construído que los apartamentos.
# Correlaciones
library(corrplot)
str(vivienda)
## tibble [8,319 × 8] (S3: tbl_df/tbl/data.frame)
## $ zona : chr [1:8319] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ estrato : num [1:8319] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8319] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8319] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8319] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8319] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8319] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8319] "Casa" "Casa" "Casa" "Casa" ...
## - attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
## ..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"
num <- vivienda[,c(-1, -8)]
str(num)
## tibble [8,319 × 6] (S3: tbl_df/tbl/data.frame)
## $ estrato : num [1:8319] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8319] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8319] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8319] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8319] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8319] 6 3 4 3 3 3 3 4 6 3 ...
## - attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
## ..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"
corrplot(cor(num, use = "complete.obs", method="spearman"), method = "color", addCoef.col="grey", order = "AOE")
Dado que las variables númericas son discretas, se realiza el análisis de correlaciones con el metodo de Spearman, donde es evidente el alto nivel de correlación entre el precio de la vivienda vs el estrato, el número de parqueaderos con el estrato y el precio. el número de baños con el precio y claramente el precio con el area construida. Por otra parte, el estraro y el número de habitaciones no tienen relacion, al igual que el número de parqueaderos con el número de habitaciones.
El objetivo del análisis, es 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 Análisis de Componentes Principales, en adelante ACP, se realiza sobre las variables númericas mencionadas a continuación:
head(num)
## # A tibble: 6 × 6
## estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 3 250 70 1 3 6
## 2 3 320 120 1 2 3
## 3 3 350 220 2 2 4
## 4 4 400 280 3 5 3
## 5 5 260 90 1 2 3
## 6 5 240 87 1 3 3
Sin embargo, antes de realizar el ACP, evaluamos la viabilidad de aplicarlo; para ello usamos el test Kaiser-Meyer-Olkin (KMO) que evalúa la proporción de varianza en las variables que puede ser explicada por factores subyacentes. Valores altos cercanos a 1.0 sugieren que un análisis factorial podría ser beneficioso con los datos disponibles. Por otro lado, valores por debajo de 0.50 indican que los resultados del análisis factorial probablemente no serán significativos o útiles.
KMO(num)
## Kaiser-Meyer-Olkin factor adequacy
## Call: KMO(r = num)
## Overall MSA = 0.77
## MSA for each item =
## estrato preciom areaconst parqueaderos banios habitaciones
## 0.71 0.76 0.80 0.91 0.79 0.60
De acuerdo al KMO, es util realizar el ACP, además todas las variables tienen un indice alto, por lo que todas se pueden considerar en el análisis.
Usamos tambien el test de Bartlett cuya hipótesis es que la matriz de correlaciones es una matriz de identidad, lo que indicaría que las variables no están relacionadas y, por lo tanto, no son adecuadas para la detección de componentes.
cortest.bartlett(cor(num),n=1819)
## $chisq
## [1] 5873.168
##
## $p.value
## [1] 0
##
## $df
## [1] 15
Como resultado, se rechaza la hipotesis nula, por lo tanto es util hacer el ACP
Dado lo anterior, procedemos con la construcción del ACP, pero antes, debemos escalar las variables para llevarlas todas a un mismo rango y permitir que sean comparables entre ellas:
### Escalar las variables
numZ= num[1:6] %>%
scale()
head(numZ)
## estrato preciom areaconst parqueaderos banios habitaciones
## [1,] -1.5872276 -0.5595498 -0.7339949 -0.3875522 -0.07793773 1.6406840
## [2,] -1.5872276 -0.3465670 -0.3842568 -0.3875522 -0.77811479 -0.4147626
## [3,] -1.5872276 -0.2552886 0.3152194 0.4168506 -0.77811479 0.2703863
## [4,] -0.6156201 -0.1031580 0.7349051 1.2212534 1.32241640 -0.4147626
## [5,] 0.3559875 -0.5291236 -0.5940997 -0.3875522 -0.77811479 -0.4147626
## [6,] 0.3559875 -0.5899759 -0.6150839 -0.3875522 -0.07793773 -0.4147626
summary(numZ)
## estrato preciom areaconst parqueaderos
## Min. :-1.5872 Min. :-1.1437 Min. :-1.0138 Min. :-1.1920
## 1st Qu.:-0.6156 1st Qu.:-0.6508 1st Qu.:-0.6640 1st Qu.:-0.3876
## Median : 0.3560 Median :-0.3161 Median :-0.3633 Median :-0.3876
## Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
## 3rd Qu.: 0.3560 3rd Qu.: 0.3228 3rd Qu.: 0.3782 3rd Qu.: 0.4169
## Max. : 1.3276 Max. : 4.7620 Max. :10.9822 Max. : 6.8521
## banios habitaciones
## Min. :-2.17847 Min. :-2.4702
## 1st Qu.:-0.77812 1st Qu.:-0.4148
## Median :-0.07794 Median :-0.4148
## Mean : 0.00000 Mean : 0.0000
## 3rd Qu.: 0.62224 3rd Qu.: 0.2704
## Max. : 4.82330 Max. : 4.3813
acp <- prcomp(numZ)
A continuación, se escoge el número de componentes adecuado:
## Elección del numero de componentes
fviz_eig(acp, addlabels = TRUE)
summary(acp)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6
## Standard deviation 1.8481 1.1222 0.6780 0.65924 0.49098 0.43579
## Proportion of Variance 0.5692 0.2099 0.0766 0.07243 0.04018 0.03165
## Cumulative Proportion 0.5692 0.7791 0.8557 0.92817 0.96835 1.00000
Aquí, se logra apreciar que con las dos primeras componentes se logra explicar casi el \(78\%\) de la varianza, por lo tanto se escoge este número de componentes. Ahora, se pretende explicar la contribución que tienen las variables en la construcción de los ejes:
fviz_pca_var(acp,
col.var = "contrib", # Color by contributions to the PC
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE # Avoid text overlapping
)
# Contribución a los ejes
PC1<-acp[[2]][,1]
PC2<-acp[[2]][,2]
Componentes<-cbind(PC1,PC2)
head(Componentes)
## PC1 PC2
## estrato 0.3306904 -0.5816950
## preciom 0.4781754 -0.1883533
## areaconst 0.4417250 0.2388377
## parqueaderos 0.4096354 -0.2607395
## banios 0.4668745 0.1921461
## habitaciones 0.2847775 0.6813261
contrib<-Componentes*Componentes
contrib <- as.matrix(contrib)
corrplot(contrib,is.corr=FALSE)
Gráfica y numéricamente se puede observar que la primera componente tiene mayor contibución de las variables precio, area construída, parqueaderos y baños, mientras que la segunda componente la componen el estrato (de manera inversa) y el número de habitaciones. Además, se confirma la relación existente entre el area y el número de baños y el precio con el número de parqueaderos. Tambien se confirma la poca relación entre el número de habitaciones y el estrato al que pertenece la vivienda.
Podemos concluir que en la primera componente encontramos viviendas de areas grandes, con varios baños y disponibilidad de parqueaderos, pero con precios elevados, mientras que en la segunda componente la caracteristica principal es que tienen varias habitaciones pero pertenecen a estratos 3, por lo que se deduce que está representado por viviendas ubicadas en la zona centro, cuyas caracteristicas cumplen con la descripción mencionada.
El objetivo de este análisis, es 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.
Nuevamente iniciamos escalando la matriz de modo que tengan media cero y desviación estándar 1:
## Estandarizamos los datos de tal modo que quedan con media cero y sd 1
numS <- num %>% mutate_all(~(scale(.) %>% as.vector))
summarytools::descr(numS)
## Descriptive Statistics
## numS
## N: 8319
##
## areaconst banios estrato
## ----------------- ---------------------- ---------------------- ----------------------
## Mean 0.00 0.00 0.00
## Std.Dev 1.00 1.00 1.00
## Min -1.01 -2.18 -1.59
## Q1 -0.66 -0.78 -0.62
## Median -0.36 -0.08 0.36
## Q3 0.38 0.62 0.36
## Max 10.98 4.82 1.33
## MAD 0.59 1.04 1.44
## IQR 1.04 1.40 0.97
## CV -9930211125493004.00 -4761446946698595.00 -1868927009707192.50
## Skewness 2.69 0.93 -0.18
## SE.Skewness 0.03 0.03 0.03
## Kurtosis 12.91 1.13 -1.11
## N.Valid 8319.00 8319.00 8319.00
## Pct.Valid 100.00 100.00 100.00
##
## Table: Table continues below
##
##
##
## habitaciones parqueaderos preciom
## ----------------- --------------------- -------------------- ---------------------
## Mean 0.00 0.00 0.00
## Std.Dev 1.00 1.00 1.00
## Min -2.47 -1.19 -1.14
## Q1 -0.41 -0.39 -0.65
## Median -0.41 -0.39 -0.32
## Q3 0.27 0.42 0.32
## Max 4.38 6.85 4.76
## MAD 1.02 1.19 0.63
## IQR 0.69 0.80 0.97
## CV -702626407488378.88 717701339031022.62 7975613688152231.00
## Skewness 1.63 1.65 1.85
## SE.Skewness 0.03 0.03 0.03
## Kurtosis 3.98 5.42 3.67
## N.Valid 8319.00 8319.00 8319.00
## Pct.Valid 100.00 100.00 100.00
Inicialmente, evaluaremos un método jerárquico. Para ello, calculamos la matriz de distancias d:
# distancia euclidiana
d <- dist(numS, method = 'euclidean')
A continuación, llevamos a cabo el agrupamiento y se evalúa el número de cluster apropiados:
# Cluster jerarquico con el método complete
hc_num <- hclust(d, method = 'complete')
# Elección de numero de cluster
barplot(sort(hc_num$height, decreasing = TRUE), horiz = TRUE,
main = "Agregaciones (distancias euclidianas)",
col = "lightblue", ylab = "Nodo", xlab = "Peso", xlim = c(0, 2.5))
Asumiendo 4 cluster, se evalúa la distribución de los clúster de manera gráfica:
# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_num, k = 4)
# asignamos los clusters
cl <- numS %>% mutate(cluster = as.factor(cluster_assigments))
# Observamos los individuos por cluster
fviz_cluster(list(data= num, cluster=cluster_assigments))
Se puede evidenciar que, dado el volumen de datos, el método jerarquico puede presentar algunas restricciones computacionales, como no poder gráficar el dendograma, además los cluster no presentan una separación clara, al menos gráficamente. Por lo anterior, evaluamos otro método de agrupamiento, en este caso el K-Means:
Primero evaluamos el número optimo de cluster:
### K means
fviz_nbclust(numS, kmeans, method = "wss") +
geom_vline(xintercept = 4, linetype = 2)
De la gráfica, podemos concluir que se puede ajustar el k-means con 4 clúster:
set.seed(1995)
km.res <- kmeans(numS, 4)
## Vemos el cluster
fviz_cluster(km.res, data = num,
palette = c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07"),
ggtheme = theme_minimal()
)
Al comparar el gráfico de los cluster obtenidos en el k-means vs el jerárquico, se observa una mejor partición y agrupamiento de los datos, sin embargo, el coeficiente de silueta es de solo 0.3.
## Coeficiente de silueta
library(cluster)
sil <- silhouette(km.res$cluster, dist(numS))
sil_avg <- mean(sil[,3])
sil_avg
## [1] 0.3033401
No obstante se procede a describir estos grupos:
# Tamaño de los cluster
km.res$size
## [1] 3537 881 2895 1006
# Puntos medios de agrupación
km.res$centers
## estrato preciom areaconst parqueaderos banios habitaciones
## 1 -0.6906127 -0.67653762 -0.5956852 -0.5983753 -0.7725720 -0.4974763
## 2 -0.7170819 0.05693307 0.8877774 -0.2350717 0.9830570 2.0287536
## 3 0.6899251 0.10023766 -0.0939632 0.2101233 0.1687568 -0.1757296
## 4 1.0706888 2.04032552 1.5873063 1.7050146 1.3697445 0.4781104
Para tener una descripción mas clara, se evaluan las demás variables que contiene la base total:
df <- cbind(vivienda, cluster = km.res$cluster)
head(df)
## zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 Zona Oriente 3 250 70 1 3 6
## 2 Zona Oriente 3 320 120 1 2 3
## 3 Zona Oriente 3 350 220 2 2 4
## 4 Zona Sur 4 400 280 3 5 3
## 5 Zona Norte 5 260 90 1 2 3
## 6 Zona Norte 5 240 87 1 3 3
## tipo cluster
## 1 Casa 2
## 2 Casa 1
## 3 Casa 1
## 4 Casa 3
## 5 Apartamento 1
## 6 Apartamento 3
table(df$cluster)
##
## 1 2 3 4
## 3537 881 2895 1006
x <- df[c("tipo","cluster")]
conteo <- table(x$tipo,x$cluster)
barplot(conteo, main="Número de viviendas por tipo y cluster",
ylab="Número de viviendas",
col=c("#0d3b66","#f4d35e"),
legend = rownames(conteo),
las=2,
names.arg=c("CL1","CL2","CL3","CL4"),
args.legend = list(x = "top"))
Se evidencia que el clusteer 2 y 4 están representados mayormente por
viviendas tipo casa, mientras que los cluster 1 y 3 son viviendas de
tipo apartamento.
x <- df[c("zona","cluster")]
conteo <- table(x$zona,x$cluster)
#View(conteo)
barplot(conteo, main="Número de viviendas por zona y cluster",
ylab="Número de viviendas",
col=c("#2E9FDF", "#00AFBB", "#E7B800", "#FC4E07", "#0d3b66"),
legend = rownames(conteo),
las=2,
names.arg=c("CL1","CL2","CL3","CL4"),
args.legend = list(x = "top"))
Los cluster 3 y 4 contienen viviendas de las zonas de mayor estrato: sur, oeste y norte, mientras que los cluster 1 y 2 contienen viviendas de todos los estratos.
par(mfrow=c(3,2))
boxplot(df$habitaciones~df$cluster,
main = "Distribución habitaciones por cluster",
ylab="habitaciones",
xlab = "Cluster", las=2,
col=c("#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("CL1","CL2","CL3","CL4"))
boxplot(df$banios~df$cluster,
main = "Distribución baños por cluster",
ylab="baños",
xlab = "Cluster", las=2,
col=c("#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("CL1","CL2","CL3","CL4"))
boxplot(df$parqueaderos ~ df$cluster,
main = "Distribución parqueaderos por cluster",
ylab="parqueaderos",
xlab = "Cluster", las=2,
col=c("#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("CL1","CL2","CL3","CL4"))
boxplot(df$areaconst~df$cluster,
main = "Distribución área por cluster",
ylab="Area",
xlab = "Cluster", las=2,
col=c("#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("CL1","CL2","CL3","CL4"))
boxplot(df$preciom~df$cluster,
main = "Distribución precio por cluster",
ylab="Precio",
xlab = "Cluster", las=2,
col=c("#ee964b", "cadetblue1", "pink", "darkolivegreen1"),
names = c("CL1","CL2","CL3","CL4"))
## Promedio de cada grupo respecto a cada variable
prom <- round(aggregate(num, by=list(cluster=km.res$cluster), mean),0)
prom
## cluster estrato preciom areaconst parqueaderos banios habitaciones
## 1 1 4 212 90 1 2 3
## 2 2 4 453 302 1 5 7
## 3 3 5 467 162 2 3 3
## 4 4 6 1104 402 4 5 4
El cluster 1 está conformado por viviendas de estrato 4, con un precio promedio de 212 millones de pesos, un area construida de 90 metros cuadrados, 1 parqueadero, dos baños y 3 habitaciones. El cluster 2 contiene viviendas de estrato 4, con un precio de 453 millones, areas más grandes que el cluster 1 y 1 parqueadero, 5 baños y 7 habitaciones.
El cluster 3 tiene viviendas más costosas pero con menos metros cuadrados que el cluster 2, con mayor número de parqueaderos y 3 habitaciones, 3 baños. Finalmente el cluster 4 tiene las viviendas más costosas, con más metros cuadrados construidos, más parqueaderos y un número importante de baños y habitaciones, por lo que se puede concluir que este cluster está agrupando esas viviendas con un comportamiento superior.
El objetivo de este análisis es examinar la relación entre las variables categóricas para identificar patrones de comportamiento de la oferta en mercado inmobiliario. Cabe aclarar que se debe incluír la variable estrato como catégorica para poder hacer el ejercicio, ya que tipo solo tiene dos opciones y esto limita el análisis si solo se tiene en cuenta tipo y zona.
str(vivienda)
## tibble [8,319 × 8] (S3: tbl_df/tbl/data.frame)
## $ zona : chr [1:8319] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ estrato : num [1:8319] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8319] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8319] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8319] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8319] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8319] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8319] "Casa" "Casa" "Casa" "Casa" ...
## - attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
## ..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"
cat <- vivienda[,c(1, 2, 8)]
cat$estrato <- as.character(cat$estrato)
str(cat)
## tibble [8,319 × 3] (S3: tbl_df/tbl/data.frame)
## $ zona : chr [1:8319] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ estrato: chr [1:8319] "3" "3" "3" "4" ...
## $ tipo : chr [1:8319] "Casa" "Casa" "Casa" "Casa" ...
## - attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
## ..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"
## Tablas cruzadas
tabla1 <- table(cat$estrato, cat$zona)
tabla1
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## 3 105 572 54 340 382
## 4 14 407 84 8 1616
## 5 4 769 290 2 1685
## 6 1 172 770 1 1043
tabla2 <- table(cat$estrato, cat$tipo )
tabla2
##
## Apartamento Casa
## 3 639 814
## 4 1404 725
## 5 1766 984
## 6 1291 696
tabla3 <- table(cat$zona, cat$tipo )
tabla3
##
## Apartamento Casa
## Zona Centro 24 100
## Zona Norte 1198 722
## Zona Oeste 1029 169
## Zona Oriente 62 289
## Zona Sur 2787 1939
## Analisis de correspondencia
res.mca <- MCA(cat, graph = TRUE)
res.mca$eig
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.5620882 21.078306 21.07831
## dim 2 0.4531232 16.992119 38.07043
## dim 3 0.3796450 14.236687 52.30711
## dim 4 0.3334444 12.504164 64.81128
## dim 5 0.3233137 12.124263 76.93554
## dim 6 0.2717762 10.191608 87.12715
## dim 7 0.2013563 7.550862 94.67801
## dim 8 0.1419197 5.321990 100.00000
Gráficamente se puede evidenciar que la zona oeste está caracterizada por ser de estrato 6, la zona oriente y centro comparte similitudes comportamentales entre si, la zona norte y sur se caracterizan por tener viviendas de estratos 4 y 5, tanto casas como apartamentos.
Respecto a la contribución de los ejes, observamos que los dos primeros ejes solo explican el \(38.07\%\) de la varianza, que el eje x está representado por el tipo de vivienda y el eje y por estrato y zona.
Cabe resaltar que para alcanzar el \(77\%\) de la varianza explicada, se deben tomar 5 componentes.
fviz_screeplot(res.mca, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
A nivel técnico, en este informe se evaluaron tres tipos de modelos multivariados, cuyos resultados permiten concluir que para el conjunto de datos de viviendas, el ACP y ACM tienen una gran potencia para describir el comportamiento y caracteristicas de los predios en estudio, mientras que los métodos de agrupamiento tienen métricas poco aceptables pero permiten agrupar las viviendas de acuerdo al número de parqueaderos, baños, habitaciones y precio.
Por otra parte, se pudo evidenciar que las viviendas de estrato 3 están ubicadas mayormente en las zonas centro y oriente, además, el precio por metro cuadrado en estas zonas es inferior que en las demás y son las zonas que contienen más casas, por lo tanto cuentan con un gran tamaño, ideales par familias numerosas o que necesitan tener espacio suficiente para sus actividades, pero deben sacrificar comodidades como tener parqueadero propio. La zona sur y norte tienen caracteristicas similares, siendo más frecuente la aparición de viviendas tipo apartamento, de estratos 4 y 5, con precios mas elevados que la zona centro y oriente pero inferiores al oeste, además estas zonas cuentan con disponibilidad de parqueadero, 2 baños en promedio y entre 3 y 4 habitaciones, siendo ideales para familiar de entre 3 y 4 miembros. Por último, la zona oeste tiene las viviendas con mayor número de parqueaderos, son apartamentos de estrato 6 y con precios superiores al resto de la ciudad; se caracterizan por tener amplios espacios y son ideales para familias con ingresos altos, que prefieren invertir más en sus viviendas para tener mayores comodidades.