##Cargue de datos y librerias
#Cargue de datos
data("vivienda")
summary(vivienda)
## 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
De la data inicial se logra identificar que existen datos faltantes en todas las variables, por tanto se procede realizar la identificación de la proporción de faltantes.
porcentaje_faltantes <- colSums(is.na(vivienda)) / nrow(vivienda) * 100
df_faltantes <- data.frame(
Variable = names(porcentaje_faltantes),
Porcentaje = porcentaje_faltantes
)
ggplot(df_faltantes, aes(x = reorder(Variable, -Porcentaje), y = Porcentaje)) +
geom_bar(stat = "identity", fill = "steelblue") +
coord_flip() +
labs(
title = "Porcentaje de Datos Faltantes por Variable",
x = "Variable",
y = "Porcentaje de Datos Faltantes"
) +
theme_minimal()
Se logra identificar que existe un porcentaje alto de datos faltantes para las variables piso con más del 30% de datos faltantes y parqueadero con cerca del 20% y una proporción minima correspondiente a maximo tres datos en las demás variables que corresponden a tres filas sin datos, las cuales se proceden a eliminar de la base de datos.
vivienda_sin_na <- vivienda[!is.na(vivienda$id), ]
summary(vivienda_sin_na)
## id zona piso estrato
## Min. : 1 Length:8319 Length:8319 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
##
## 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 :1602
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8319 Length:8319 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
##
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
##
Si bien gran parte de los datos de las variables piso y parqueadero se encuentran faltantes se considera que deben mantenerse en el analisis e imputarse para lograr realizar un analisis completo de las caracteristicas provistas de las viviendas, por tanto se procede a realizar la imputación por la media de las variables piso y parqueadero.
# Crear un nuevo dataframe 'vivienda2' con los datos originales para mantener la base anterior sin modificaciones
vivienda2 <- vivienda_sin_na
# Convertir la columna piso a numérico
vivienda2$piso <- as.numeric(vivienda2$piso)
# Imputar la columna 'piso' con la media
vivienda2$piso[is.na(vivienda2$piso)] <- mean(vivienda2$piso, na.rm = TRUE)
# Imputar la columna 'parqueaderos' con la media
vivienda2$parqueaderos[is.na(vivienda2$parqueaderos)] <- mean(vivienda2$parqueaderos, na.rm = TRUE)
Se verifica que no haya datos faltantes en el dataframe y los cambios que se generaron de forma correcta.
vivienda2 = vivienda2[!is.na(vivienda2$piso),]
colSums(is.na(vivienda2))
## 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
Se procede a realizar el analisis de correlación
# Se seleccionan unicamente las variables numéricas para proceder a realizar el analisis de correlación
vivienda_numerico <- vivienda2[, c("preciom", "areaconst", "banios", "habitaciones","piso","parqueaderos")]
# Verificar las columnas seleccionadas
str(vivienda_numerico)
## spc_tbl_ [8,319 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ 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 ...
## $ 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 ...
## $ piso : num [1:8319] 3.77 3.77 3.77 2 1 ...
## $ parqueaderos: num [1:8319] 1 1 2 3 1 1 2 2 2 2 ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
# Calcular la matriz de correlación
correlation_matrix <- cor(vivienda_numerico, use = "complete.obs")
# Crear el gráfico de correlación (heatmap)
corrplot(correlation_matrix, method = "color", type = "upper",
tl.col = "black", tl.srt = 45,
addCoef.col = "black", # Añadir los coeficientes de correlación en el gráfico
title = "Mapa de Correlación", mar = c(0, 0, 1, 0))
Del mapa de correlación se logra apreciar que existe una fuerte correlación entre las variables precio y areaconstruida,baños y parqueaderos, y tiene poca o nula correlación con las variables habitaciones y piso, tambien se observa una alta correlación entre las variables baños y habitaciones, asi como area construida y habitaciones.
# Escalar los datos en el dataframe 'vivienda_numerico'
vivienda_scaled <- scale(vivienda_numerico)
# Realizar el PCA
pca_result <- prcomp(vivienda_scaled, center = TRUE, scale. = TRUE)
# Resumen de los resultados del PCA
summary(pca_result)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6
## Standard deviation 1.7797 1.0313 0.9033 0.65217 0.57971 0.43830
## Proportion of Variance 0.5279 0.1772 0.1360 0.07089 0.05601 0.03202
## Cumulative Proportion 0.5279 0.7051 0.8411 0.91197 0.96798 1.00000
# Graficar la variabilidad explicada por cada componente
fviz_eig(pca_result)
# Graficar los individuos (puntos) en el espacio de los componentes principales
fviz_pca_ind(pca_result, col.ind = "cos2",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE) # Evita la superposición de textos
# Graficar las variables (vectores) en el espacio de los componentes principales
fviz_pca_var(pca_result, col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE) # Evita la superposición de textos
Del analisis de componentes PCA se logra apreciar que las dos primeras dimensiones recogen gran parte de la variación en los datos (70%) en donde las variables area, baños, habitaciones, y parqueaderos están más alineadas con Dim1, mientras Dim2 Captura la siguiente mayor cantidad de variabilidad, donde piso tiene una fuerte contribución.
Del grafico de individuos PCA se logra observar tambien que muchas observaciones están representadas de manera similar en estos componentes, por tanto se determina usar PC1 y PC2.
de aqui se tiene que las características clave que influyen en la variación de precios son el área construida, Baños y parqueaderos que están alineados con precio en la dimensión 1, lo que indica que estas variables están positivamente correlacionadas con el precio, mientras la variable piso tiene una mayor influencia en la dimensión 2, podria estar más relacionada con otras variables diferentes al precio, como el tipo de vivienda.
# Retener solo los primeros dos componentes principales del PCA
vivienda_reducida <- as.data.frame(pca_result$x[, 1:2])
# Renombrar las columnas para mayor claridad
colnames(vivienda_reducida) <- c("PC1", "PC2")
# Mostrar el nuevo dataframe reducido
head(vivienda_reducida)
## PC1 PC2
## 1 -0.4068780 0.9925099
## 2 -1.2149951 0.1314387
## 3 -0.1797563 0.1620015
## 4 1.3593443 0.2880263
## 5 -1.2713043 1.2480141
## 6 -0.9700388 1.2599687
# Determinar el número óptimo de conglomerados utilizando el método del codo
fviz_nbclust(vivienda_reducida, kmeans, method = "wss") +
geom_vline(xintercept = 3, linetype = 2)
set.seed(123) #
kmeans_result <- kmeans(vivienda_reducida, centers = 3, nstart = 25)
# Añadir los clusters al dataframe reducido
vivienda_reducida$cluster <- kmeans_result$cluster
# Mostrar los primeros resultados
head(vivienda_reducida)
## PC1 PC2 cluster
## 1 -0.4068780 0.9925099 2
## 2 -1.2149951 0.1314387 1
## 3 -0.1797563 0.1620015 2
## 4 1.3593443 0.2880263 2
## 5 -1.2713043 1.2480141 1
## 6 -0.9700388 1.2599687 1
Del grafico anterior se logra determinar que el número optimo de clusters corresponde a tres, por tanto se procede a realizar la graficación de los cluster o conglomerados.
# Visualización de los conglomerados en el espacio de los dos primeros componentes principales
fviz_cluster(kmeans_result, data = vivienda_reducida,
geom = "point", stand = FALSE,
ellipse.type = "convex",
palette = "jco", ggtheme = theme_minimal())
Se identfica que las viviendas pueden representarse en tres clusters que estan divididos en donde el culster tres agurpa la mayoría de las propiedades con características más distribuidas.
vivienda_mapa <- cbind(vivienda2[, c("longitud", "latitud")], cluster = factor(vivienda_reducida$cluster))
# Crear una paleta de colores específica para los clusters
pal <- colorFactor(palette = c("red", "blue", "green"), domain = vivienda_mapa$cluster)
# Crear el mapa
leaflet(vivienda_mapa) %>%
addTiles() %>% # Agregar el fondo del mapa
addCircleMarkers(
~longitud, ~latitud,
color = ~pal(cluster), # Aplicar la paleta de colores a los clusters
popup = ~paste("Cluster:", cluster), # Información emergente al hacer clic
radius = 5, # Tamaño de los puntos
stroke = FALSE, fillOpacity = 0.8
) %>%
addLegend("bottomright", pal = pal,
values = ~cluster, title = "Cluster", opacity = 1)
En cuanto a la ubicación de los cluster en la ciudad se identifica que los cluster 1 y 2 son los más predominantes, con el cluster 1 presentando mayor concentración en la zona suroriental y nororiental de la ciudad, mientras el cluster 3 presenta una clara concentración en la zona más al sur de la ciudad, por su parte el cluster 2 no presenta concentración en una zona especifica.
# Combinar los datos originales con la información de los clusters
vivienda_clustered <- cbind(vivienda2, cluster = vivienda_reducida$cluster)
# Filtrar filas con clusters válidos (excluir NA)
vivienda_clustered <- vivienda_clustered %>% filter(!is.na(cluster))
# Seleccionar solo las variables numéricas relevantes (excluyendo id, longitud, latitud)
vivienda_relevant <- vivienda_clustered %>%
select(-id, -longitud, -latitud)
# Calcular estadísticas descriptivas por cluster
cluster_summary <- vivienda_relevant %>%
group_by(cluster) %>%
summarise(across(where(is.numeric), list(mean = mean, median = median, sd = sd), na.rm = TRUE))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(...)`.
## ℹ In group 1: `cluster = 1`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
# Mostrar el resumen
print(cluster_summary)
## # A tibble: 3 × 22
## cluster piso_mean piso_median piso_sd estrato_mean estrato_median estrato_sd
## <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 4.22 3.77 2.38 4.33 4 0.913
## 2 2 3.07 3 1.45 4.84 5 1.07
## 3 3 3.53 3.77 2.16 5.48 6 0.809
## # ℹ 15 more variables: preciom_mean <dbl>, preciom_median <dbl>,
## # preciom_sd <dbl>, areaconst_mean <dbl>, areaconst_median <dbl>,
## # areaconst_sd <dbl>, parqueaderos_mean <dbl>, parqueaderos_median <dbl>,
## # parqueaderos_sd <dbl>, banios_mean <dbl>, banios_median <dbl>,
## # banios_sd <dbl>, habitaciones_mean <dbl>, habitaciones_median <dbl>,
## # habitaciones_sd <dbl>
Cuando observamos las caracteristicas especificas de los cluster identificamos que el cluster 1 corresponde a viviendas de menor valor, mientras la zona 3 son las de mayor valor, tambien que existen diferencias notables en cuantro al área, en donde existe la misma relación el cluster 1 es el de menor area construida en promedio, mientras el 3 el de mayor, el cluster 3 se compone de acuerdo a la mediana por viviendas estrato 6, mientras el cluster 2 son viviendas estrato 5 y el 1 viviendas estrato 4.
De aqui se logra identificar que los tres cluster obedecen a caracteristicas diferentes de viviendas que aplican para sectores economicos distintos en cuanto a estrato, precio y caracterisitcas propias de la vivienda.
Se realiza un analisis de correspondencia entre zona y estrato teniendo en cuenta los resultados previos en donde los cluster muestran una marcada diferencia entre los estratos y una concentración en ciertas zonas.
# Seleccionar las variables categóricas 'zona' y 'estrato'
vivienda_zona_estrato <- vivienda2 %>%
select(zona, estrato)
# Asegurarse de que las variables estén en formato categórico
vivienda_zona_estrato <- vivienda_zona_estrato %>%
mutate(across(everything(), as.factor))
# Crear una tabla de contingencia entre 'zona' y 'estrato'
tabla_contingencia_zona_estrato <- table(vivienda_zona_estrato$zona, vivienda_zona_estrato$estrato)
# Realizar el Análisis de Correspondencia
ca_result_zona_estrato <- CA(tabla_contingencia_zona_estrato, graph = FALSE)
# Visualizar los resultados
fviz_ca_biplot(ca_result_zona_estrato, repel = TRUE)
El Biplot sugiere asociaciones claras entre ciertas zonas y estratos, la Zona Oriente y centro estan fuertemente asociadas con Estrato 3 , mientras que la Zona Oeste está más asociada con Estrato 6 y la zona sur y norte con el estrato 4 y 5.
Del analisis de correlación y PCA se logró identificar que existen caracteristicas clave que influyen en el precio de las viviendas en la Ciudad de en Cali mayor medida, como lo son el área construida,el número de Baños y parqueaderos que están alineados con precio en la dimensión 1, estas variables son factores clave.
Tambien se logró identificar que existen caracteristicas de las viviendas que pueden ser agrupadas o segmentadas, se encontraron tres clusters en donde existe una relación clara entre las caracteristicas de precio, estrató y área variando de forma incremental del cluster 1 al 3.
Se recomienda implementar estrategias de segmentación de mercado teniendo en cuenta las zonas que estan relacionadas en gran medida con los estratos y los cluster identificados, en donde si bien por toda la ciudad hay distribución de cada uno de los cluster como se logro apreciar existen ciertas concentraciones de estos cluster que puden ser aprovechadas si se focalizan los esfuerzos de mercadeo.
El analisis realizado nos permite identificar como se agrupan las diferentes caracteristicas de las viviendas en la ciudad de Cali y es util para tomar decisiones estratégicas referentes al nicho de mercado o segmento que la empresa desee atender de acuerdo a la oferta de vivienda en la ciudad..