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.
Se cargan los datos contenidos en paqueteMOD
## Rows: 8,322
## Columns: 13
## $ id <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
## $ zona <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
## $ piso <chr> NA, NA, NA, "02", "01", "01", "01", "01", "02", "02", "02…
## $ estrato <dbl> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
## $ preciom <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
## $ areaconst <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
## $ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, NA, 3, 2, 2, 1, 4, 2, 2, 2,…
## $ banios <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
## $ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
## $ tipo <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
## $ barrio <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
## $ longitud <dbl> -76.51168, -76.51237, -76.51537, -76.54000, -76.51350, -7…
## $ latitud <dbl> 3.43382, 3.43369, 3.43566, 3.43500, 3.45891, 3.36971, 3.4…
## id zona piso estrato
## Min. : 1 Length:8322 Length:8322 Min. :3.000
## 1st Qu.:2080 Class :character Class :character 1st Qu.:4.000
## Median :4160 Mode :character Mode :character Median :5.000
## Mean :4160 Mean :4.634
## 3rd Qu.:6240 3rd Qu.:5.000
## Max. :8319 Max. :6.000
## NA's :3 NA's :3
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean : 1.835 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :10.000 Max. :10.000
## NA's :2 NA's :3 NA's :1605 NA's :3
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8322 Length:8322 Min. :-76.59
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76.54
## Median : 3.000 Mode :character Mode :character Median :-76.53
## Mean : 3.605 Mean :-76.53
## 3rd Qu.: 4.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.46
## NA's :3 NA's :3
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
## NA's :3
Las variables zona, piso, estado, tipo y barrio son de tipo categóricas en donde se visualiza poca cantidad de datos faltantes en una de las variables, la cual es estrato. Esto quiere decir que la mayoría de los registros tiene datos completos.
| Name | data |
| Number of rows | 8322 |
| Number of columns | 13 |
| _______________________ | |
| Column type frequency: | |
| character | 4 |
| numeric | 9 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| zona | 3 | 1.00 | 8 | 12 | 0 | 5 | 0 |
| piso | 2638 | 0.68 | 2 | 2 | 0 | 12 | 0 |
| tipo | 3 | 1.00 | 4 | 11 | 0 | 2 | 0 |
| barrio | 3 | 1.00 | 4 | 29 | 0 | 436 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| id | 3 | 1.00 | 4160.00 | 2401.63 | 1.00 | 2080.50 | 4160.00 | 6239.50 | 8319.00 | ▇▇▇▇▇ |
| estrato | 3 | 1.00 | 4.63 | 1.03 | 3.00 | 4.00 | 5.00 | 5.00 | 6.00 | ▅▆▁▇▆ |
| preciom | 2 | 1.00 | 433.89 | 328.65 | 58.00 | 220.00 | 330.00 | 540.00 | 1999.00 | ▇▂▁▁▁ |
| areaconst | 3 | 1.00 | 174.93 | 142.96 | 30.00 | 80.00 | 123.00 | 229.00 | 1745.00 | ▇▁▁▁▁ |
| parqueaderos | 1605 | 0.81 | 1.84 | 1.12 | 1.00 | 1.00 | 2.00 | 2.00 | 10.00 | ▇▁▁▁▁ |
| banios | 3 | 1.00 | 3.11 | 1.43 | 0.00 | 2.00 | 3.00 | 4.00 | 10.00 | ▇▇▃▁▁ |
| habitaciones | 3 | 1.00 | 3.61 | 1.46 | 0.00 | 3.00 | 3.00 | 4.00 | 10.00 | ▂▇▂▁▁ |
| longitud | 3 | 1.00 | -76.53 | 0.02 | -76.59 | -76.54 | -76.53 | -76.52 | -76.46 | ▁▅▇▂▁ |
| latitud | 3 | 1.00 | 3.42 | 0.04 | 3.33 | 3.38 | 3.42 | 3.45 | 3.50 | ▃▇▅▇▅ |
De acuerdo con el análisis de correlación, podemos concluir lo siguiente:
El coeficiente de Pearson, que mide el grado de asociación lineal entre variables, muestra lo siguiente en relación con el precio: la correlación con el área construida es de 0.7, la correlación con el número de habitaciones es de 0.3, la correlación con el número de baños es de 0.7.
Estos valores sugieren que, en general, un mayor precio está asociado con una mayor área construida, un mayor número de habitaciones, y un mayor número de baños. Además, el coeficiente de Pearson para la asociación entre el área construida y otras variables muestra: una correlación de 0.5 con el número de habitaciones y una correlación de 0.6 con el número de baños.
Esto indica que una mayor área construida tiende a estar asociada con un mayor número de habitaciones y baños.
Finalmente, la correlación entre el número de habitaciones y el número de baños es de 0.6, lo que sugiere que un mayor número de habitaciones suele estar acompañado por un mayor número de baños.
conteo_valores_zona <- table(data$zona)
conteo_valores_zona_df <- as.data.frame(conteo_valores_zona)
frecuencias <- conteo_valores_zona_df$Freq
etiquetas <- paste0(conteo_valores_zona_df$Var1, ": ", round(frecuencias / sum(frecuencias) * 100, 1), "%")
colores <- brewer.pal(n = length(frecuencias), name = "Set3")
pie(frecuencias, labels = etiquetas, col = colores, clockwise = TRUE, radius = 1, init.angle = 90,
main = "Distribución de Inmuebles por Zona")
graphics::legend("topright", legend = conteo_valores_zona_df$Var1, fill = colores, cex = 0.8,
title = "Zonas")El mayor porcentaje se concentró en la zona sur con un 56.8%, seguida por la zona norte con un 23.1%. Las zonas oeste, oriente y centro registraron el 14.4%, 4.2% y 1.5%, respectivamente.
conteo_tipo <- table(data$tipo)
conteo_tipo_df <- as.data.frame(conteo_tipo)
ggplot(conteo_tipo_df, aes(x = Var1, y = Freq, fill = Var1)) + geom_bar(stat = "identity", color = "black") + geom_text(aes(label = Freq), vjust = -0.5, color = "black", size = 3.5) + labs(title = "Distribución de Inmuebles por Tipo", x = "Tipo de Inmueble", y = "Número de Inmuebles") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.title = element_blank()) + scale_fill_brewer(palette = "Set3")conteo_valores_estrato <- table(data$estrato)
conteo_valores_estrato_df <- as.data.frame(conteo_valores_estrato)
frecuencias <- conteo_valores_estrato_df$Freq
porcentajes <- round(frecuencias / sum(frecuencias) * 100, 2)
etiquetas <- paste0(conteo_valores_estrato_df$Var1, ": ", porcentajes, "%")
ggplot(conteo_valores_estrato_df, aes(x = Var1, y = frecuencias, fill = Var1)) +
geom_bar(stat = "identity", color = "black") +
geom_text(aes(label = frecuencias), vjust = -0.5, color = "black", size = 3.5) +
labs(title = "Distribución por Estrato",
x = "Estrato",
y = "Número de Inmuebles") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.title = element_blank()) +
scale_fill_brewer(palette = "Set3")
La gráfica muestra que el estrato 5 concentra el mayor porcentaje de
inmuebles, con un 33%. Le siguen el estrato 4 con un 26%, el estrato 6
con un 24%, y el estrato 3 con un 17%.
miss_var_summary_df <- as.data.frame(miss_var_summary(data))
miss_var_summary_df$var <- factor(miss_var_summary_df$var, levels = miss_var_summary_df$var[order(miss_var_summary_df$n_miss)])
ggplot(miss_var_summary_df, aes(x = var, y = n_miss, label = n_miss)) +
geom_bar(stat = "identity", fill = "steelblue") +
geom_text(aes(label = n_miss), vjust = -0.3, color = "black", size = 3.5) +
coord_flip() +
labs(title = "Número de Valores Faltantes por Variable",
x = "Variable",
y = "Número de Valores Faltantes") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))##
## Variables sorted by number of missings:
## Variable Count
## piso 0.3169911079
## parqueaderos 0.1928622927
## id 0.0003604903
## zona 0.0003604903
## estrato 0.0003604903
## areaconst 0.0003604903
## banios 0.0003604903
## habitaciones 0.0003604903
## tipo 0.0003604903
## barrio 0.0003604903
## longitud 0.0003604903
## latitud 0.0003604903
## preciom 0.0002403268
El diagrama muestra que aproximadamente el 50% de los datos contienen al menos una característica con valores faltantes (NA), lo que añade una capa adicional de complejidad en la aplicación de los algoritmos.
En este contexto, se ha decidido proceder con la imputación de los valores faltantes en la variable “Piso y Parqueadero” excluyendo estas variables del análisis.
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.
## estrato preciom areaconst banios habitaciones
## [1,] -1.5872276 -0.5595420 -0.7339949 -0.07793773 1.6406840
## [2,] -1.5872276 -0.3465477 -0.3842568 -0.77811479 -0.4147626
## [3,] -1.5872276 -0.2552644 0.3152194 -0.77811479 0.2703863
## [4,] -0.6156201 -0.1031256 0.7349051 1.32241640 -0.4147626
## [5,] 0.3559875 -0.5291143 -0.5940997 -0.77811479 -0.4147626
## [6,] 0.3559875 -0.5899698 -0.6150839 -0.07793773 -0.4147626
## [7,] -0.6156201 -0.6508253 -0.8599006 -0.77811479 -0.4147626
## [8,] 0.3559875 -0.3769755 -0.2653459 -0.07793773 0.2703863
## [9,] 0.3559875 -0.3465477 -0.1744140 0.62223934 1.6406840
## [10,] 0.3559875 1.0531293 1.4343813 -0.07793773 -0.4147626
## Standard deviations (1, .., p=5):
## [1] 1.7126702 1.0901061 0.6673491 0.4916092 0.4376110
##
## Rotation (n x k) = (5 x 5):
## PC1 PC2 PC3 PC4 PC5
## estrato 0.3300034 -0.6744210 0.4209148 -0.4795679 0.1705853
## preciom 0.5068979 -0.2807716 -0.3015624 0.2214323 -0.7240509
## areaconst 0.4940391 0.1638245 -0.6525161 -0.2985094 0.4628195
## banios 0.5189521 0.1092985 0.3767724 0.6647387 0.3672975
## habitaciones 0.3475145 0.6538646 0.4051619 -0.4358863 -0.3123167
## **Results for the Principal Component Analysis (PCA)**
## The analysis was performed on 8319 individuals, described by 5 variables
## *The results are available in the following objects:
##
## name description
## 1 "$eig" "eigenvalues"
## 2 "$var" "results for the variables"
## 3 "$var$coord" "coord. for the variables"
## 4 "$var$cor" "correlations variables - dimensions"
## 5 "$var$cos2" "cos2 for the variables"
## 6 "$var$contrib" "contributions of the variables"
## 7 "$ind" "results for the individuals"
## 8 "$ind$coord" "coord. for the individuals"
## 9 "$ind$cos2" "cos2 for the individuals"
## 10 "$ind$contrib" "contributions of the individuals"
## 11 "$call" "summary statistics"
## 12 "$call$centre" "mean of the variables"
## 13 "$call$ecart.type" "standard error of the variables"
## 14 "$call$row.w" "weights for the individuals"
## 15 "$call$col.w" "weights for the variables"
La gráfica anterior ilustra las relaciones entre la variable estrato y el precio, así como las interacciones entre el área, el número de habitaciones y el número de baños. Para identificar el número óptimo de componentes principales, se puede utilizar un Scree Plot. Este gráfico muestra los valores propios ordenados de mayor a menor. El número de componentes principales se selecciona observando el punto en el que los valores propios empiezan a estabilizarse y se vuelven relativamente pequeños y similares entre sí.
grafico_pca <- prcomp(dataesc, scale. = TRUE)
fviz_eig(grafico_pca,
addlabels = TRUE,
barfill = "steelblue",
barcolor = "black",
linecolor = "red",
line.size = 1,
title = "Scree Plot de Valores Propios",
xlab = "Número de Componente Principal",
ylab = "Valor Propio",
ggtheme = theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
panel.grid = element_blank()
)) +
geom_vline(xintercept = which.max(diff(fviz_eig(grafico_pca)$data$y)),
linetype = "dashed",
color = "blue") +
annotate("text",
x = which.max(diff(fviz_eig(grafico_pca)$data$y)) + 0.5,
y = max(fviz_eig(grafico_pca)$data$y) * 0.9,
label = "Codo",
color = "blue",
size = 3)## estrato preciom areaconst banios habitaciones
## estrato 1.00000000 0.6098066 0.2743233 0.4203218 -0.07137615
## preciom 0.60980664 1.0000000 0.6873520 0.6691456 0.26409121
## areaconst 0.27432332 0.6873520 1.0000000 0.6484165 0.51691292
## banios 0.42032178 0.6691456 0.6484165 1.0000000 0.58990641
## habitaciones -0.07137615 0.2640912 0.5169129 0.5899064 1.00000000
## [1] "Resumen del PCA:"
## Importance of components:
## PC1 PC2 PC3 PC4 PC5
## Standard deviation 1.7126 1.0901 0.66735 0.49161 0.4376
## Proportion of Variance 0.5866 0.2377 0.08907 0.04834 0.0383
## Cumulative Proportion 0.5866 0.8243 0.91337 0.96170 1.0000
##
## Primeros Componentes Principales:
## PC1 PC2 PC3 PC4 PC5
## estrato 0.3300032 -0.6744363 0.4208934 -0.4795545 0.1706159
## preciom 0.5068715 -0.2807656 -0.3015468 0.2213868 -0.7240921
## areaconst 0.4940473 0.1638135 -0.6525373 -0.2984641 0.4628138
## banios 0.5189619 0.1092831 0.3767649 0.6647648 0.3672488
## habitaciones 0.3475270 0.6538568 0.4051685 -0.4359154 -0.3122700
##
## Proporción de Varianza Explicada por Cada Componente:
## PC1 PC2 PC3 PC4 PC5
## 0.58663 0.23766 0.08907 0.04834 0.03830
##
## Varianza Acumulada por los Componentes Principales:
## PC1 PC2 PC3 PC4 PC5
## 0.58663 0.82430 0.91337 0.96170 1.00000
## [conflicted] Will prefer psych::scree over any other package.
Según el gráfico de sedimentación, lo optimo seria realizar 3 componentes, puesto que este valor se encuentra por encima de la línea aceptable de la grafica .
CP1 = resultado_pca[[2]][,1]
CP2 = resultado_pca[[2]][,2]
CP3 = resultado_pca[[2]][,3]
ComponentePrincipal = cbind(CP1, CP2, CP3)
ComponentePrincipal## CP1 CP2 CP3
## estrato 0.3300032 -0.6744363 0.4208934
## preciom 0.5068715 -0.2807656 -0.3015468
## areaconst 0.4940473 0.1638135 -0.6525373
## banios 0.5189619 0.1092831 0.3767649
## habitaciones 0.3475270 0.6538568 0.4051685
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.
A continuación, se realiza las conversión de la variables cualitativas (tipo y zona) en numéricas, esto con el objetivo de poder utilizar un algoritmo de clustering:
datacon <- mutate_if(datacon, is.character, tolower)
datacon$tipo[datacon$tipo == "apto"] <- "apartamento"
unique(datacon$tipo)## [1] "casa" "apartamento" NA
data_cluster <- datacon
data_cluster <- data_cluster %>% mutate(zona = replace(zona,
zona == "zona centro", 1))
data_cluster <- data_cluster %>% mutate(zona = replace(zona,
zona == "zona norte", 2))
data_cluster <- data_cluster %>% mutate(zona = replace(zona,
zona == "zona oeste", 3))
data_cluster <- data_cluster %>% mutate(zona = replace(zona,
zona == "zona oriente",4))
data_cluster <- data_cluster %>% mutate(zona = replace(zona,
zona == "zona sur", 5))
data_cluster$zona <- as.numeric(data_cluster$zona)
data_cluster <- data_cluster %>% mutate(tipo = replace(tipo,
tipo == "casa", 1))
data_cluster <- data_cluster %>% mutate(tipo = replace(tipo,
tipo == "apartamento", 2))
data_cluster$tipo <- as.numeric(data_cluster$tipo)data_cluster <- na.omit(data_cluster)
scv <- numeric(15)
scv[1] <- (nrow(data_cluster) - 1) * sum(apply(data_cluster, 2, var))
for (i in 2:15) {
scv[i] <- sum(kmeans(data_cluster, centers = i)$withinss)
}
plot(1:15, scv,
type = "b",
pch = 19,
col = "blue",
xlab = "Número de Clusters",
ylab = "Suma de Cuadrados Dentro de los Grupos",
main = "Kmeans",
lwd = 2,
cex = 1.2)
abline(v = which.min(diff(diff(scv))), col = "red", lty = 2, lwd = 2)
abline(h = min(scv), col = "green", lty = 2, lwd = 2)El gráfico muestra la variación dentro de los clusters en función del número de clusters 𝑘 A medida que aumenta 𝑘, la suma de cuadrados dentro de los clusters disminuye. Se observa una inflexión en 𝑘=4, lo que sugiere que agregar más clusters más allá de este punto aporta un valor marginal adicional. Esto indica que el número óptimo de clusters podría ser alrededor de 4.
fviz_nbclust(data_cluster, kmeans, method = "silhouette") + labs(subtitle = "Número Óptimo de Clusters según el Método de Silueta")
La gráfica anterior, basada en el método de silueta, sugiere que el
número óptimo de clusters es 2. Esto contrasta con la recomendación del
método K-means, que indicaba 4 clusters. Después de revisar ambos
métodos, se ha decidido utilizar 2 clusters para el análisis, ya que el
método de silueta proporciona una evaluación más precisa en este
caso.
fviz_cluster(clustering_result, data = data_cluster,
palette = c("#2E9FDF", "#E7B800"),
ellipse.type = "euclid",
star.plot = TRUE,
repel = FALSE,
ggtheme = theme_minimal()
)Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.
data_acm <- data [, c(2,4,10)]
data_acm$estrato = as.factor(data_acm$estrato)
#head(data_acm,3)
str(data_acm)## tibble [8,322 × 3] (S3: tbl_df/tbl/data.frame)
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ estrato: Factor w/ 4 levels "3","4","5","6": 1 1 1 2 3 3 2 3 3 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
# Realizar la prueba de chi-cuadrado
set.seed(7) # Establecer una semilla para reproducibilidad
chi_cuadrado <- data.frame(
estrato = rep(c("3", "4", "5", "6"), times = 50),
tipo = sample(c("zona centro", "zona sur","zona norte", "zona oeste","zona oriente"), size = 100, replace = TRUE)
)
# Crear una tabla de contingencia
tabla_zona_estrato <- table(data_acm$estrato, data_acm$zona)
# Mostrar la tabla de contingencia
print(tabla_zona_estrato)##
## 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
##
## Pearson's Chi-squared test
##
## data: tabla_zona_estrato
## X-squared = 3830.4, df = 12, p-value < 0.00000000000000022
A través de esta representación gráfica, emergen patrones claros y
concretos:
La visualización gráfica obtenida ofrece una perspectiva valiosa sobre las relaciones entre las variables “estrato” y “zona” en el mercado inmobiliario. Esta representación permite identificar patrones significativos, facilitando una segmentación más precisa del mercado y una mejor comprensión de las preferencias de los compradores en distintas áreas geográficas.
El enfoque estadístico empleado valida las tendencias identificadas visualmente y proporciona un sólido fundamento para la toma de decisiones estratégicas en el ámbito inmobiliario.
La reducción de dimensionalidad mediante Análisis de Componentes Principales (PCA) reveló que los dos primeros componentes capturan el 82.43% de la varianza total, destacando las variables numéricas más influyentes en la variabilidad del mercado.
Además, el análisis de correspondencia evidenció relaciones significativas entre las variables categóricas y numéricas. Esto sugiere que factores como el tipo de propiedad y la zona están estrechamente relacionados con características numéricas como el estrato, proporcionando así una comprensión más profunda de las dinámicas del mercado.