Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.
El reto principal consisten en realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere aplicar diversas técnicas de análisis de datos, incluyendo:
El informe final debe incluir análisis detallados de los resultados obtenidos, las conclusiones clave y las recomendaciones específicas para guiar las decisiones estratégicas de la empresa inmobiliaria. Se espera que este análisis de datos proporcione ventajas competitivas en el mercado, optimizando la inversión y maximizando los beneficios en un entorno altamente competitivo y en constante cambio.
Se importa el data set y se imprimen los primeros registros.
data("vivienda") #Import data viviendas
vivienda <- as.data.frame(vivienda) #Convert to dataframe
# Set factor attributes
vivienda$zona <- as.factor(vivienda$zona)
vivienda$piso <- as.numeric(vivienda$piso) #Convert to num
vivienda$estrato <- as.factor(vivienda$estrato)
vivienda$tipo <- as.factor(vivienda$tipo)
vivienda$barrio <- tolower(vivienda$barrio) #Convert to lower
vivienda$barrio <- chartr("áéíóú", "aeiou", vivienda$barrio) #Remove special characters
vivienda$barrio <- gsub(" ", "", vivienda$barrio) #Remove spaces
vivienda$barrio <- as.factor(vivienda$barrio) #Convert to a factor
# Print sample
formattable(head(vivienda))
id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1147 | Zona Oriente | NA | 3 | 250 | 70 | 1 | 3 | 6 | Casa | 20dejulio | -76.51168 | 3.43382 |
1169 | Zona Oriente | NA | 3 | 320 | 120 | 1 | 2 | 3 | Casa | 20dejulio | -76.51237 | 3.43369 |
1350 | Zona Oriente | NA | 3 | 350 | 220 | 2 | 2 | 4 | Casa | 20dejulio | -76.51537 | 3.43566 |
5992 | Zona Sur | 2 | 4 | 400 | 280 | 3 | 5 | 3 | Casa | 3dejulio | -76.54000 | 3.43500 |
1212 | Zona Norte | 1 | 5 | 260 | 90 | 1 | 2 | 3 | Apartamento | acopi | -76.51350 | 3.45891 |
1724 | Zona Norte | 1 | 5 | 240 | 87 | 1 | 3 | 3 | Apartamento | acopi | -76.51700 | 3.36971 |
Se resume el comportamiento de las variables numéricas.
options(digits = 2)
descr(vivienda[, -c(1)])[c(1:7, 10, 14:15), ]
areaconst banios habitaciones latitud longitud parqueaderos piso
Mean 174.93 3.11 3.6 3.4e+00 -7.7e+01 1.84 3.77
Std.Dev 142.96 1.43 1.5 4.3e-02 1.7e-02 1.12 2.61
Min 30.00 0.00 0.0 3.3e+00 -7.7e+01 1.00 1.00
Q1 80.00 2.00 3.0 3.4e+00 -7.7e+01 1.00 2.00
Median 123.00 3.00 3.0 3.4e+00 -7.7e+01 2.00 3.00
Q3 229.00 4.00 4.0 3.5e+00 -7.7e+01 2.00 5.00
Max 1745.00 10.00 10.0 3.5e+00 -7.6e+01 10.00 12.00
CV 0.82 0.46 0.4 1.2e-02 -2.3e-04 0.61 0.69
N.Valid 8319.00 8319.00 8319.0 8.3e+03 8.3e+03 6717.00 5684.00
Pct.Valid 99.96 99.96 100.0 1.0e+02 1.0e+02 80.71 68.30
preciom
Mean 433.89
Std.Dev 328.65
Min 58.00
Q1 220.00
Median 330.00
Q3 540.00
Max 1999.00
CV 0.76
N.Valid 8320.00
Pct.Valid 99.98
Se resumen los valores de las variables cualitativas
summary(vivienda[, c("zona", "estrato", "tipo")])
zona estrato tipo
Zona Centro : 124 3 :1453 Apartamento:5100
Zona Norte :1920 4 :2129 Casa :3219
Zona Oeste :1198 5 :2750 NA's : 3
Zona Oriente: 351 6 :1987
Zona Sur :4726 NA's: 3
NA's : 3
Se observa que los valores únicos de las variables tienen buen comportamiento y no presentan errores de escritura.
Se analizan los valores vacíos por atributo.
empty_vivienda <- gg_miss_var(vivienda, show_pct = TRUE) +
geom_text(data = miss_var_summary(vivienda),
aes(y = pct_miss, label = round(as.numeric(pct_miss), 2),
vjust = 1.5), size = 3) +
labs(y = "% Prop datos faltantes", x = "")
empty_vivienda
Se observa que el atributo “piso” tiene el 31.7% de sus registros vacíos, al ser una proporción superior al 10% no se recomendaría eliminarlos, sino más bien imputarlos, de igual manera sucede con el atributo “parqueaderos”. El resto de atributos, contienen registros vacíos que se podrían omitir.
Se realizan boxplots para analizar el comportamiento de valores atípicos por atributo.
# Piso as number
vivienda$piso <- as.numeric(vivienda$piso)
# Creat boxplot
box_pisos <- boxplot(as.numeric(vivienda$piso) ~ vivienda$tipo,
xlab = NA,
ylab = "Pisos")
# Add stats
stats <- box_pisos$stats
n <- ncol(stats)
for (i in 1:n) {
text(i, stats[1, i], labels = round(stats[1, i], 2), pos = 4, col = "blue")
text(i, stats[2, i], labels = round(stats[2, i], 2), pos = 4, col = "blue")
text(i, stats[3, i], labels = round(stats[3, i], 2), pos = 4, col = "blue")
text(i, stats[4, i], labels = round(stats[4, i], 2), pos = 4, col = "blue")
text(i, stats[5, i], labels = round(stats[5, i], 2), pos = 4, col = "blue")
}
A pesar de que el atributo “piso” se tratará como una variable discreta, según el método del criterio del rango intercuartílico multiplicado por 1.5, una casa con más de 4 pisos se considerará como un dato atípico, lo cual es lógico y por tal razón, se mantendrá ese criterio para la limpieza de datos del atributo piso.
# Creat boxplot
box_preciom <- boxplot(vivienda$preciom,
xlab = "Precio m^2",
ylab = "Decenas de miles $COP")
# Add stats
text(1, box_preciom$stats,
labels = round(box_preciom$stats, 2),
pos = 2,
col = "blue")
Como no se cuenta con información acerca de la unidad del atributo “preciom”, se supone que hace referencia al precio del metro cuadrado y, para obtener valores cercanos a la realidad, se establece la unidad en decenas de miles de pesos colombianos, así, por ejemplo, un registro cuyo preciom sea igual a 250 y cuyo atributo areaconst (cuya unidad se supone en m^2) sea igual a 70, es razonable que cueste $175.000.000 COP obtenidos al multiplicar 250 x 10x1000 x70 (es decir, preciom por decenas de miles por areaconst).
En este atributo, se observan datos atípicos por encima de 1015 y por debajo de 58, como el precio del m^2 puede estar influenciado por los demás atributos, no se elegirá el criterio del rango intercuartíclico para el tratamiento de datos atípicos, más bien se utilizará la técnica de Isulation Forest la cual separa aquellas observaciones con características distintas al resto por medio de la combinación de múltiples árboles llamados isolation trees.
# Creat boxplot
box_areaconst <- boxplot(vivienda$areaconst ~ vivienda$tipo,
xlab = NA,
ylab = "Area construida m^2",
ylim = c(-10, 1750))
# Add stats
stats <- box_areaconst$stats
n <- ncol(stats)
for (i in 1:n) {
text(i, stats[1, i], labels = round(stats[1, i], 2), pos = 1, col = "blue", cex = 0.7)
text(i, stats[2, i], labels = round(stats[2, i], 2), pos = 2, col = "blue", cex = 0.7)
text(i, stats[3, i], labels = round(stats[3, i], 2), pos = 4, col = "blue", cex = 0.7)
text(i, stats[4, i], labels = round(stats[4, i], 2), pos = 2, col = "blue", cex = 0.7)
text(i, stats[5, i], labels = round(stats[5, i], 2), pos = 2, col = "blue", cex = 0.7)
}
En el atributo de areaconst también se observan datos atípicos. Como dicho atributo puede estar influenciado por los demás atributos, no se elegirá el criterio del rango intercuartíclico para el tratamiento de datos atípicos, sino la técnica de Isulation Forest.
# Define frame
par(mfrow = c(1, 3))
# Parqueaderos
box_parqueaderos <- boxplot(vivienda$parqueaderos,
xlab = NA,
ylab = "Parqueaderos")
# Add stats
text(1, box_parqueaderos$stats,
labels = round(box_parqueaderos$stats, 2),
pos = 3,
col = "blue")
# Banios
box_banios <- boxplot(vivienda$banios,
xlab = NA,
ylab = "banios")
# Add stats
text(1, box_banios$stats,
labels = round(box_banios$stats, 2),
pos = 3,
col = "blue")
# Habitaciones
box_habitaciones <- boxplot(vivienda$habitaciones,
xlab = NA,
ylab = "habitaciones")
# Add stats
text(1, box_habitaciones$stats,
labels = round(box_habitaciones$stats, 2),
pos = 3,
col = "blue")
par(mfrow = c(1, 1))
En el resto de atributos tratados como variables discretas, se observa que hay registros que no cuentan con baño o habitaciones, como el estudio va dirigido a viviendas (y toda vivienda deberá tener por lo menos 1 baño y 1 habitación), los registros que tengan un valor cero en estos atributos, no serán tenidos en cuenta. A parte de lo mencionado, no se realizará una limpieza de datos adicional en estos atributos.
Como se determinó en la Exploración de los Datos, aquellos registros que tienen datos faltantes en una proporción inferior al 10% no serán tenidos en cuenta y se eliminarán del dataset, por lo que sólo se imputarán datos en los atributos “piso” y “parqueaderos”.
Para el atributo “piso” se analiza el comportamiento de los datos faltantes respecto al resto de atributos.
source("Script Faltantes Piso.r")
Se observa que los datos faltantes del atributo “piso” presentan una distribucion casi uniforme respecto a los atributos “estrato” y “tipo”, así entonces, se interpreta que la relación de datos faltantes es aleatoria (MAR) y podrían reemplazarse por la media o eliminarse sin afectar la distribución de los datos. Por otra parte, la relación de los datos faltantes del atributo “piso” con los atributos “zona” y “habitaciones” no fue aleatoria (MNAR), pues no todas las zonas tuvieron la misma probabilidad de obtener datos faltantes del atributo “piso”, igualmente sucedió con “habitaciones”, por lo tanto, se tiene en cuenta esta relación para imputar los datos faltantes del atributo “piso” utilizando el método de la mediana.
El valor con el que se imputarán los datos faltantes del atributo “piso” se calculará obteniendo la mediana de los registros que se encuentren en la misma zona y que tengán un mismo número de habitaciones.
Para el atributo “parqueadero” se analiza el comportamiento de los datos faltantes respecto al resto de atributos.
source("Script Faltantes Parqueo.r")
Para los datos faltantes del atributo “parqueaderos” se observa una relación MAR con el atributo “tipo” y una MNAR con el resto de atributos, por lo que se procederá de manera similar a la imputación planteada para el atributo “piso”.
Una vez se realizó la imputacion, se verifica que no haya datos faltantes nuevamente.
colSums(is.na(vivienda_clean))
id zona piso estrato preciom areaconst
0 0 0 0 0 0
parqueaderos banios habitaciones tipo barrio longitud
0 0 0 0 0 0
latitud
0
Como se mencionó en la Exploración de Datos, no se considerarán viviendas que no cuenten con baños ni habitaciones, de igual manera, se determinó que las casas con más de 4 pisos quedarán excluidas del análisis por considerarse atípicas.
#Clean DataSet
vivienda_clean <- subset(vivienda_clean,
habitaciones != 0
& banios != 0
& !(tipo == "casa" & piso > 4))
Se utilizará la técnico Isolation Tree para la determinación de datos atípicos. Los atributos Latitud y Longitud no son de interés, por lo que se excluirán del análisis.
set.seed(123)
# Import library
library(isotree)
vivienda_clean <- vivienda_clean[, -c(11:13)] #Select all columns without Latitud y longitud
#Create the model
modelo_iforest <- isolation.forest(vivienda_clean, ntrees = 100, sample_size = 256)
# Calculate puntuaciones of anomalies for each observation
puntuaciones <- predict(modelo_iforest, vivienda_clean, type = "score")
# Add puntuaciones to the dataframe
vivienda_clean$anomaly_score <- puntuaciones
# Define the limit to declare outlier
umbral <- 0.6
#Get dataset with no outliers
vivienda_clean <- subset(vivienda_clean, vivienda_clean$anomaly_score < umbral)
#Calculate prop removed data
vivienda_clean_nrow <- nrow(vivienda_clean)
vivienda_nrow <- nrow(vivienda)
prop_clean <- round(vivienda_clean_nrow/vivienda_nrow, 4)*100
Luego de la imputación de datos faltantes y tratamiento de datos atípicos, la proporción de datos remanentes respecto al dataset original fue de 97.44% con lo que se corrobora la poca pérdida de información durante el preprocesamiento de los datos.
Inicialmente, se analisa el precio de la vivienda por zona a través de un análisis bivariado por medio del siguiente diagrama de cajas.
precio_zona_box <- ggplot(vivienda_clean, aes(x = zona, y = preciom)) +
geom_boxplot(outlier.shape = NA, notch = TRUE) +
stat_boxplot(geom = "errorbar", width = 0.15) +
labs(x = "", y = "Decenas de miles $COP")
print(precio_zona_box)
Para ejecutar el análsis multivariado, se estandarizan las variables numéricas (excluyendo el atributo “estrato” ya que se tratará como categórico) y la variable “preciom” ya que se tratará como variable de respuesta.
vivienda_num_z <- scale(vivienda_clean[, c(3, 6:9)]) #Scale num variables
vivienda_z <- cbind(vivienda_clean[, c(1:2, 4:5, 10)], vivienda_num_z) #Join categoric columns
Posteriormente, los atributos categóricos “zona” y “tipo” se tratan con la técnica one-hot enconding, aunque aumentará la dimensionalidad del dataset, se cuenta con un número de registros muy superior al número de atributos con los que se contará.
vivienda_z["Zona_Centro"] <- ifelse(vivienda_z$zona == "Zona Centro", 1, 0)
vivienda_z["Zona_Norte"] <- ifelse(vivienda_z$zona == "Zona Norte", 1, 0)
vivienda_z["Zona_Oeste"] <- ifelse(vivienda_z$zona == "Zona Oeste", 1, 0)
vivienda_z["Zona_Oriente"] <- ifelse(vivienda_z$zona == "Zona Oriente", 1, 0)
vivienda_z["Zona_Sur"] <- ifelse(vivienda_z$zona == "Zona Sur", 1, 0)
vivienda_z["Casa"] <- ifelse(vivienda_z$tipo == "Casa", 1, 0)
vivienda_z["Apartamento"] <- ifelse(vivienda_z$tipo == "Apartamento", 1, 0)
vivienda_z <- vivienda_z[, c(1, 3:4, 6:17)] #Select columns with numbers
Para este análisis, sólo se incluyen los atributos numéricos contínuos, no tiene sentido incluir aquellos categóricos ya que la metodología se basa en las covarianzas y un atributo categórico no cuenta con esta propiedad.
El análisis arrojó 5 componentes principales cuyas varianzas explicadas se presentan en la siguiente gráfica.
pca <- prcomp(vivienda_z[, c(4:8)]) #Analisis made just in continues attributes
pca$rotation
PC1 PC2 PC3 PC4 PC5
piso 0.22 -0.840 -0.480 0.053 -0.12
areaconst -0.53 -0.061 0.011 0.813 -0.22
parqueaderos -0.43 -0.417 0.578 -0.421 -0.36
banios -0.53 -0.184 -0.133 -0.142 0.80
habitaciones -0.45 0.288 -0.646 -0.373 -0.40
prop_var <- (pca$sdev^2)/sum(pca$sdev^2)
pca_bar <- barplot(prop_var,
names.arg = c("PC1", "PC2", "PC3",
"PC4", "PC5"),
ylim = c(0, 0.7),
ylab = "Explained Variance %",
axes = TRUE)
points(pca_bar, prop_var)
lines(pca_bar, prop_var)
text(pca_bar, prop_var + 0.05,
labels = round(prop_var, 3))
Se observa que las primeras 4 componentes principales explican casi el 94% de la varianza total, por lo que se reduciría la dimension de 5 a 4. Al agregar las variables categóricas, se obtendría un dataset en función de componentes principales cuyas dimensiones son 8109x14.
#Add Principal Componentes to the DataSet
vivienda_pca <- vivienda_z
vivienda_pca["PC1"] <- pca$x[, 1]
vivienda_pca["PC2"] <- pca$x[, 2]
vivienda_pca["PC3"] <- pca$x[, 3]
vivienda_pca["PC4"] <- pca$x[, 4]
#Select just Principal Componentes and Categoric Variables
vivienda_pca <- vivienda_pca[, c(1:3, 9:19)]
#print head
formattable(head(vivienda_pca))
id | estrato | preciom | Zona_Centro | Zona_Norte | Zona_Oeste | Zona_Oriente | Zona_Sur | Casa | Apartamento | PC1 | PC2 | PC3 | PC4 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1147 | 3 | 250 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | -0.23 | 1.43 | -1.25 | -1.031 |
1169 | 3 | 320 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0.97 | 0.90 | 0.31 | 0.232 |
1350 | 3 | 350 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | -0.22 | 0.64 | 0.42 | 0.156 |
5992 | 4 | 400 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | -1.78 | -0.44 | 1.20 | 0.064 |
1212 | 5 | 260 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1.00 | 1.29 | 0.52 | 0.017 |
1724 | 5 | 240 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0.61 | 1.15 | 0.42 | -0.109 |
Para el análisis de correspondencia, se tomarán sólo las variables categóricas sin transformarlas mediante one-hot enconding. El dataset preparado previamente, cuenta con 3 atributos categóricos “zona”, “estrato” y “tipo” lo que nos obliga a realizar un análisis de correspondencia múltiple. Para ello, se utilizó la función MCA() de la libreria factoextra.
vivienda_mca <- vivienda_clean[, c(2, 4, 10)] #Select just categoric attributes
mca_result <- MCA(vivienda_mca, graph = FALSE) #Multiple correspondance analysis
formattable(mca_result$eig) #Print Eigenvalues
eigenvalue percentage of variance cumulative percentage of variance
dim 1 0.563 21.11 21.11
dim 2 0.4548 17.05 38.17
dim 3 0.3806 14.27 52.44
dim 4 0.3335 12.51 64.95
dim 5 0.3235 12.13 77.08
dim 6 0.2695 10.11 87.18
dim 7 0.1998 7.492 94.68
dim 8 0.142 5.324 100
El resultado del análisis arroja 8 dimensiones con porcentajes de explicación bajos, lo que implica que la varianza explicada acumulada sólo alcance un valor superior al 95% considerando todas las dimensiones.
# Get Coordinates from variables
var_coords <- as.data.frame(mca_result$var$coord)
var_coords["Variable"] <- ifelse(startsWith(rownames(var_coords), "Zona"),
"Zona",
ifelse((startsWith(rownames(var_coords), "Apartamento")
| startsWith(rownames(var_coords), "Casa")),
"tipo",
"estrato"))
names(var_coords) <- c("Dim1", "Dim2", "Dim3", "Dim4", "Dim5", "Variable")
# Create Graph
ggplot(var_coords, aes(x = Dim1, y = Dim2,
color = Variable,
shape = Variable,
label = rownames(var_coords))) +
geom_point() +
geom_text(vjust = -0.5) +
labs(title = "Análisis de Correspondencia Múltiple (MCA)",
x = paste("Dimensión1(", round(mca_result$eig[1, 2], 2), "%)", sep = ""),
y = paste("Dimensión2(", round(mca_result$eig[2, 2], 2), "%)", sep = "")) +
theme_minimal()
Al graficar la relación de atributos categóricos, se observa que la Zona Oriente y Centro tienen más relación con el estrato 3 y posteriormente con el 6 con similitud en el número de casas y apartamentos, además por las distancias que tiene respecto a los demás atributos, se observa que el número de viviendas disponibles en estas zonas es inferior a las demás.
Respecto a la Zona Oeste, se observa que en su mayoría tendrá viviendas de estrato 6 y se encontrarán más apartamentos que casas.
Finalmente, se observa que la mayor oferta de viviendas se encuentran en las Zonas Norte y Sur, donde los estratos más comunes son el 5 y el 4. En la zona Norte, se encontrarán más casas de estrato 5 que de cualquier otro estrato, mientras que en la zona sur se pueden encontrar casas y apartamentos en proporciones similares y en su mayoría de estrato 5.
Debido a la gran cantidad de Datos y Atributos, se decide realizar un análisis no jerárquico utilizando el método K-means. Se utilizará el DataSet con variables numéricas continuas estandarizadas y varaibles categóricas representadas con variables binarias mediante la técnica one-hot enconding. El análisis se realizará por Zona buscando caracterizar las viviendas bajo este atributo y sólo se presentarán las zonas sur y norte, las cuales contienen aproximadamente el 80% de los datos.
Se selecciona el número de clústers bajo el criterio del “codo” graficando el Total Within Sum of Square, se observa que cuando k=8 aparece el cambio de pendiente.
#Dataframe
TWSS_k <- data.frame(seq(1, kmax), TWSS)
names(TWSS_k) <- c("k", "TWSS")
#Plot TWSS vs k
plot(seq(1, kmax), TWSS, type = "o",
xlim = c(0, 20),
xlab = "k",
main = "TWSS vs k")
#Point k = 15
lines(c(8, 8), c(0, 11000), col = "red")
text(8, 15000, labels = "TWSS = 8758 \n k = 8", col = "red")
Se realiza el análisis MCA con k=8
mca_table
Value
Número Clusters 8
TOTSS 31143
TWSS 8758
BSS 22386
BSS/TSS 72
En la tabla anterior se observa el rendimiento del agrupamiento donde se destaca el valor del cociente BSS/TSS lo que implica que la distribución de los clústers explica en un 72% la variación total de los datos.
En la siguiente gráfica se observa que los clusters están sobrepuestos, sin embargo, podría ser un resultado de graficarlos sólo en 2 dimensiones, pues al agregar más dimensiones puede que no ocupen el mismo espacio ya que las componentes principales de los ejes sólo explican un 75% de la varianza total.
# Plot Clusters
fviz_cluster(mca_result,
data = vivienda_z_sur,
ellipse.type = "euclid", # Elipse de concentración
star.plot = FALSE, # No añadir segmentos desde los centroides a los ítems
ggtheme = theme_minimal(),
geom = "none", # No mostrar puntos de observaciones
show.clust.cent = TRUE) # Mostrar solo los centroides
Se selecciona el número de clústers bajo el criterio del “codo” graficando el Total Within Sum of Square, se observa que cuando k=7 aparece el cambio de pendiente.
#Dataframe
TWSS_k <- data.frame(seq(1, kmax), TWSS)
names(TWSS_k) <- c("k", "TWSS")
#Plot TWSS vs k
plot(seq(1, kmax), TWSS, type = "o",
xlim = c(0, 20),
xlab = "k",
main = "TWSS vs k")
#Point k = 15
lines(c(7, 7), c(0, 4000), col = "red")
text(7, 4500, labels = "TWSS = 3391 \n k = 7", col = "red")
Se realiza el análisis MCA con k=7
mca_table
Value
Número Clusters 7
TOTSS 11297
TWSS 3473
BSS 7823
BSS/TSS 69
En la tabla anterior se observa el rendimiento del agrupamiento donde se destaca el valor del cociente BSS/TSS lo que implica que la distribución de los clústers explica en un 69% la variación total de los datos.
En la siguiente gráfica se observa que los clusters están sobrepuestos, sin embargo, podría ser un resultado de graficarlos sólo en 2 dimensiones, pues al agregar más dimensiones puede que no ocupen el mismo espacio ya que las componentes principales de los ejes sólo explican un 73.5% de la varianza total.
# Plot Clusters
fviz_cluster(mca_result,
data = vivienda_z_norte,
ellipse.type = "euclid", # Elipse de concentración
star.plot = FALSE, # No añadir segmentos desde los centroides a los ítems
ggtheme = theme_minimal(),
geom = "none", # No mostrar puntos de observaciones
show.clust.cent = TRUE) # Mostrar solo los centroides
Las conclusiones que deja el presente análisis son varias.
La primera de ellas, es que la mayor oferta de la ciudad en materia inmobiliaria es la zona sur, donde los estratos 4 y 5 tienen mayor participación y donde las casas y apartamentos tienen una proporción similar según los resultados del análisis de correspondencia. En esta zona el preciom promedio es de $414 x 10.000 millones COP.
La zona norte presenta un comportamiento similar a la zona sur, sin embargo, en esta encontraremos más oferta de casas que de apartamentos.
La zona Oeste ofrece los bienes inmobiliarios más costosos según el análisis bivariado, esto podría ser consecuencia de que en esa zona hay mayor oferta de viviendas en el estrato 6 según lo mostrado en el análisis de correspondencia.
Por último, las zonas centro y oriente son las menos atractivas en términos de oferta. En estas zonas se encontrarán viviendas en su mayoría de estrato 3 y más casas que apartamentos.
Respecto al comportamiento del preciom de las viviendas, el análisis de componentes principales, a través del peso de cada atributo sobre las componentes, nos muestra que el atributo numérico que más peso tiene sobre las componentes es el número de parqueos, seguida del número de habitaciones y el área construida.