Actividad 1 - Evaluación de la oferta inmobiliaria urbana

1. Problema

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.

2. Importación de datos

# devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
library(paqueteMODELOS)
data("vivienda")

2.1 Importanción de Librerias

Para realizar la exploración de datos, se procede a importar las librerías que serán utilizadas en el análisis (Previa instalación de las mismas)

library(paqueteMETODOS)
library(dplyr)
library(ggplot2)
library(tidyverse)
library(plotly)
library(scales)
library(stringi)
library(cowplot)
library(tidyr)
library(factoextra)
library(FactoMineR)
library(factoextra)

3. Preparación de los datos

El análisis comienza con la preparación del conjunto de datos vivienda2, que es una copia del dataframe original vivienda. Este paso asegura que los datos originales permanezcan intactos mientras se realizan transformaciones y limpiezas en la copia.

#Se crea una base para modificación
vivienda2 <- vivienda
#Realizar el conteo de registros vacios
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3         2638            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1605            3            3            3            3            3 
##      latitud 
##            3

El resultado muestra que algunas columnas tienen valores faltantes significativos. Por ejemplo, la columna piso tiene 2,638 valores faltantes, y parqueaderos tiene 1,605 valores faltantes. Otros campos como zona, estrato, preciom, y otros también tienen pocos registros faltantes, en su mayoría 3 o menos.

En lugar de imputar los valores faltantes utilizando la moda o la media, lo que podría alterar la variabilidad de la muestra y afectar los resultados de futuros análisis, se decide eliminar los registros con valores faltantes en base a una columna de referencia con el fin de eliminar el registro general. Esto busca preservar la integridad de los datos para análisis posteriores, como el Análisis de Componentes Principales (PCA).

#Se eliminan los registros de una fila completa conforme a una columna en específico.
#Se toma de referencia la columna Id.

#Se cuenta la cantidad de nulos de la base
sapply(vivienda2,function(x) sum(is.na(x)))
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3         2638            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1605            3            3            3            3            3 
##      latitud 
##            3
vivienda2 = vivienda2[!is.na(vivienda2$id),]

Después de eliminar los registros con valores faltantes en la columna id, se vuelve a realizar un conteo de valores faltantes por columna.

# Validar los datos faltantes de la base por columna
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2635            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1602            0            0            0            0            0 
##      latitud 
##            0

El resultado muestra que las columnas id, zona, estrato, preciom, areaconst, banios, habitaciones, tipo, barrio, longitud, y latitud ya no tienen valores faltantes. Sin embargo, aún hay valores faltantes en las columnas piso (2,635 registros faltantes) y parqueaderos (1,602 registros faltantes).

3.1 Imputación de datos

Se determina la distribución porcentual de los valores perdidos respecto del total de datos para cada columna, esto proporciona una visión clara de la cantidad de datos faltantes, lo que es clave para decidir cómo manejar estas ausencias.

porcentajeMiss <- function(x) {sum(is.na(x)) / length(x)*100}
# por columna
apply(vivienda2, 2, porcentajeMiss)
##           id         zona         piso      estrato      preciom    areaconst 
##      0.00000      0.00000     31.67448      0.00000      0.00000      0.00000 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##     19.25712      0.00000      0.00000      0.00000      0.00000      0.00000 
##      latitud 
##      0.00000

A continuación, se verifica si existen duplicados en la columna id. Los duplicados pueden causar problemas en el análisis posterior, por lo que es importante identificarlos y manejarlos adecuadamente.

#Contar duplicados:

duplicados = duplicated(vivienda2$id)
sum(duplicados)
## [1] 0

3.2 Imputación de datos a la variable parqueaderos

Dado que la columna parqueaderos tiene un 19.26% de valores faltantes, se decide imputar estos valores utilizando la media de parqueaderos dentro de grupos definidos por las combinaciones de las columnas zona, tipo, y estrato.

# Se crea tabla resumen con el cálculo de la media por grupo (zona, tipo, estrato), se excluyen valores vacíos
mediatable = vivienda2 %>%
  group_by(zona, tipo, estrato) %>%
  summarise_at(vars(parqueaderos), list(media = ~round(mean(., na.rm = TRUE))))

# Se crea una columna nueva clave (En tabla resumen) que permita hacer un left_join para los vacíos.
mediatable$clave = paste(mediatable$zona, mediatable$tipo, mediatable$estrato)

# Se crea una columna nueva clave (En dataframe de vivienda2) que permita hacer un left_join para los vacíos.
vivienda2$clave = paste(vivienda2$zona, vivienda2$tipo, vivienda2$estrato)

# Se realiza cruce con la función left_join()
vivienda2 = vivienda2 %>%
  left_join(mediatable, by = "clave") %>%
  mutate(
    parqueaderos = ifelse(is.na(parqueaderos), media, parqueaderos),
    media = NULL
  )

# Elimina columnas redundantes después del left_join
vivienda2 = vivienda2 %>%
  select(-zona.y, -tipo.y, -estrato.y)

# Definir como dataframe (si es necesario)
vivienda2 <- as.data.frame(vivienda2)

# Restaurar nombres originales, retirando los duplicados con .x o .y
vivienda2 = vivienda2 %>%
  rename_with(~ gsub("\\.x|\\.y", "", .x), contains(".x") | contains(".y"))

Después de la imputación, se valida el estado de los valores faltantes en el dataframe. Esto asegura que las imputaciones se realizaron correctamente y que los valores faltantes han sido manejados adecuadamente.

# Validar los datos faltantes de la base por columna
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2635            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            3            0            0            0            0            0 
##      latitud        clave 
##            0            0

Los valores faltantes en la columna parqueaderos se han reducido a solo 3 observaciones. Sin embargo, estos 3 valores faltantes persisten porque no cumplen con las condiciones de las combinaciones de zona, tipo, y estrato para las cuales se calcularon las medias.

Dado que estas 3 observaciones faltantes en parqueaderos no se pueden imputar, se decide eliminarlas del dataframe para asegurar la integridad del análisis.

# Se identifican 3 observaciones faltantes que no cumplen con la condición por las 3 variables cruzadas anteriormente, se deciden retirar.

vivienda2 = vivienda2[!is.na(vivienda2$parqueaderos),]
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2633            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud        clave 
##            0            0

3.2 Imputación variable piso

Para el caso de la variable piso se realiza inicialmente la conversión de esta columna a un formato numérico a un carácter. Posteriormente se realiza el mismo ejercicio de imputación realizado para la variable parqueadero utilizando la media de piso dentro de grupos definidos por las combinaciones de las columnas zona, tipo, y estrato.

# Convertir la columna `piso` a numérico
vivienda2$piso <- as.numeric(as.character(vivienda2$piso))

# Se crea tabla resumen con el cálculo de la media por grupo (zona, tipo, estrato), excluyendo valores vacíos
mediatablep = vivienda2 %>%
  group_by(zona, tipo, estrato) %>%
  summarise(media = round(mean(piso, na.rm = TRUE)), .groups = "drop")

# Se crea una columna nueva clave (En tabla resumen) que permita hacer un left_join para los vacíos.
mediatablep$clave = paste(mediatablep$zona, mediatablep$tipo, mediatablep$estrato)

# Se crea la columna clave en el dataframe original
vivienda2$clave = paste(vivienda2$zona, vivienda2$tipo, vivienda2$estrato)

# Se realiza cruce con la función left_join()
vivienda2 = vivienda2 %>%
  left_join(mediatablep, by = "clave") %>%
  mutate(
    piso = ifelse(is.na(piso), media, piso)
  ) %>%
  select(-media, -clave, -zona.y, -tipo.y, -estrato.y) 

# Elimina columnas innecesarias

# Definir como dataframe (si es necesario)
vivienda2 <- as.data.frame(vivienda2)


# Restaurar nombres originales, retirando los duplicados con .x o .y
vivienda2 = vivienda2 %>%
  rename_with(~ gsub("\\.x|\\.y", "", .x), contains(".x") | contains(".y"))

Después de realizar la imputación, se verifica la presencia de valores faltantes en el dataframe para asegurarse de que el proceso de imputación se ha realizado correctamente.

# Validar los datos faltantes de la base por columna
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            4            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud 
##            0
# Se identifican 4 observaciones de valores faltantes que no cumplen con la condición por las 3 variables cruzadas anteriormente, se deciden retirar.

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

4. Análisis descriptivo de las variables

El análisis comienza con la selección de las columnas numéricas del dataframe vivienda2. En este proceso, se excluyen específicamente las columnas id, latitud, longitud. La razón para excluir estas columnas es que algunas, como id, no aportan información numérica útil para análisis estadístico, y otras, como areaconst y preciom, podrían complicar la visualización debido a su escala o variabilidad.

# Filtrar solo las columnas numéricas del dataframe, excluyendo la variable id, latitud, longitud.
# También se excluye la variable areaconst y precio preciom para facilitar la visualización.

numeric_columns <- vivienda2 %>% select_if(is.numeric) %>% select(-id,-latitud,-longitud, -areaconst, -preciom)

# Gráficos de caja (Boxplots) para todas las variables numéricas
numeric_columns_long <- numeric_columns %>%
  pivot_longer(cols = everything(), names_to = "variable", values_to = "value")

ggplot(numeric_columns_long, aes(x = variable, y = value)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Gráfico de caja de Variables numéricas de interés", x = "Variable", y = "Valor") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Para comprender mejor la distribución de los datos numéricos seleccionados, se crean gráficos de caja (boxplots) para todas las variables numéricas de interés.

Además de los gráficos de caja, se crean histogramas para cada variable numérica seleccionada. Estos histogramas proporcionan una visión más detallada de la distribución de los valores dentro de cada variable.

# Configurar el diseño del gráfico: 3 filas y 2 columnas para 5 histogramas
par(mfrow = c(3, 2), mar = c(5, 4, 4, 2) + 0.1)  # Configura la cuadrícula de gráficos

# Generar los histogramas para las variables numéricas seleccionadas
for (col in names(numeric_columns)) {
  hist(
    numeric_columns[[col]],  # Datos para la variable
    breaks = 30,  # Número de intervalos (bins)
    main = paste("Histograma de", col),  # Título del gráfico
    xlab = col,  # Etiqueta del eje X
    col = "skyblue",  # Color de las barras
    border = "black"  # Color de los bordes de las barras
  )
}

# Restaurar la configuración original de la gráfica
par(mfrow = c(1, 1))

Cómo parte del análisis se realiza la visualización de las variables categóricas en vivienda2. Se utilizan gráficos de barras para mostrar la distribución de las categorías dentro de cada variable.

#Visualización de variables categóricas:

# Configurar el diseño del gráfico: 2 filas y 2 columnas
par(mfrow = c(2, 2))  # Configura la cuadrícula de gráficos

# Gráfico de barras para la variable 'zona'
zona_counts <- table(vivienda2$zona)  # Contar las frecuencias de cada categoría en 'zona'
barplot(zona_counts,
        main = "Distribución de Zona",
        xlab = "Zona",
        ylab = "Frecuencia",
        col = "skyblue",
        border = "black")

# Gráfico de barras para la variable 'estrato'
estrato_counts <- table(vivienda2$estrato)  # Contar las frecuencias de cada categoría en 'estrato'
barplot(estrato_counts,
        main = "Distribución de Estrato",
        xlab = "Estrato",
        ylab = "Frecuencia",
        col = "lightgreen",
        border = "black")

# Gráfico de barras para la variable 'tipo'
tipo_counts <- table(vivienda2$tipo)  # Contar las frecuencias de cada categoría en 'tipo'
barplot(tipo_counts,
        main = "Distribución de Tipo",
        xlab = "Tipo",
        ylab = "Frecuencia",
        col = "orange",
        border = "black")

# Restaurar la configuración original de la gráfica
par(mfrow = c(1, 1))

#Identificar las medidas de tendecias central
summary(vivienda2)
##        id           zona                piso           estrato     
##  Min.   :   1   Length:8312        Min.   : 1.000   Min.   :3.000  
##  1st Qu.:2082   Class :character   1st Qu.: 2.000   1st Qu.:4.000  
##  Median :4160   Mode  :character   Median : 3.000   Median :5.000  
##  Mean   :4160                      Mean   : 3.664   Mean   :4.633  
##  3rd Qu.:6240                      3rd Qu.: 5.000   3rd Qu.:5.000  
##  Max.   :8319                      Max.   :12.000   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 : 1.000   Median : 3.000  
##  Mean   : 433.8   Mean   : 174.9   Mean   : 1.731   Mean   : 3.112  
##  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  
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8312        Length:8312        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

5. Retos:

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:

5.1 Análisis de Componentes Principales:

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 PCA es una técnica de reducción de dimensionalidad que permite identificar las características clave que más influyen en la variación de precios y otras características del mercado inmobiliario. Al reducir la dimensionalidad, el PCA facilita la visualización y análisis de la estructura de los datos, destacando las principales fuentes de variabilidad.

Se crea un nuevo dataframe subvivienda que contiene únicamente las columnas numéricas más relevantes seleccionadas para el análisis PCA. Estas variables incluyen piso, preciom, areaconst, parqueaderos, banios, y habitaciones, que son características clave que podrían influir en el precio y la oferta de las viviendas.

# Seleccionar las variables numéricas relevantes para realizar el PCA

subvivienda <- vivienda2 %>%
  select(piso, preciom, areaconst, parqueaderos, banios, habitaciones)

Las variables elegidas son estandarizados, este es un paso crucial en el PCA, ya que asegura que todas las variables contribuyan igualmente al análisis, independientemente de sus escalas originales.

# Se estandarizan las variables antes de aplicar el PCA
vivienda_pca <- scale(subvivienda)

Se obtiene un objeto pca_resultado que contiene los componentes principales calculados, sus cargas (contribuciones de cada variable original) y la varianza explicada por cada componente.

# Se aplica el PCA
pca_resultado <- prcomp(vivienda_pca, center = TRUE, scale. = TRUE)
#Resultado del PCA
summary(pca_resultado)
## Importance of components:
##                           PC1    PC2    PC3     PC4    PC5     PC6
## Standard deviation     1.8086 1.0556 0.8624 0.60414 0.5623 0.43567
## Proportion of Variance 0.5452 0.1857 0.1240 0.06083 0.0527 0.03163
## Cumulative Proportion  0.5452 0.7309 0.8548 0.91567 0.9684 1.00000

Resultados:

El primer componente principal (PC1) explica aproximadamente el 54.52% de la varianza total en los datos, seguido por el segundo componente (PC2) que explica el 18.57%. Juntos, los primeros dos componentes explican más del 73% de la varianza, lo que sugiere que gran parte de la información contenida en las variables originales puede ser representada en un espacio bidimensional.

Se utiliza la función fviz_eig() del paquete factoextra para generar un Scree Plot, que visualiza la varianza explicada por cada componente. Este gráfico es útil para decidir cuántos componentes principales son necesarios para capturar la mayoría de la varianza en los datos.

fviz_eig(pca_resultado, addlabels = TRUE)

Este Scree Plot confirma que los primeros dos componentes principales capturan la mayor parte de la variabilidad en los datos, explicando juntos aproximadamente el 73% de la varianza total. Los componentes adicionales (PC3, PC4, etc.) contribuyen con cantidades menores de varianza explicada, por lo que podrían considerarse menos importantes para la interpretación principal del análisis.

Al observar la gráfica, parece haber un “codo” después del segundo componente, lo que sugiere que la mayor parte de la varianza está capturada en los primeros dos componentes.

Se crea un gráfico biplot utilizando fviz_pca_var() para visualizar cómo las variables originales se proyectan en los dos primeros componentes principales. Este gráfico también utiliza un gradiente de colores para mostrar la contribución de cada variable a los componentes.

#Se realiza un gráfico biplot de las variables. 
fviz_pca_var(pca_resultado, col.var = "contrib", gradient.cols =c("#FF7F00", "#034D94"),
             rapel =TRUE)

La representación del biplot muestra que las variables piso, preciom, y parqueaderos son las más influyentes en la variabilidad explicada por el primer componente principal (PC1), mientras que la variale habitaciones tiene una influencia notable en el segundo componente principal (PC2). Las relaciones entre las variables también pueden interpretarse a partir de los ángulos entre sus vectores, ofreciendo información sobre cómo están correlacionadas entre sí, vectores que están cerca entre sí (como areaconst y banios) indican que estas variables están correlacionadas positivamente, es decir, tienden a aumentar o disminuir juntas.Caso contrario sucede con variables como piso y habitaciones, donde se podria afirmar que estas variables están inversamente relacionadas.

Se genera una a matriz que indica qué porcentaje de la varianza explicada por cada componente es atribuido a cada variable despues de haber realizado el PCA.

# Calcular la proporción de la varianza explicada por cada componente
explained_variance <- pca_resultado$sdev^2 / sum(pca_resultado$sdev^2)

# Calcular la contribución de cada variable a los componentes principales
# Primero, obtenemos la matriz de cargas (loadings)
loadings <- pca_resultado$rotation

# Luego, calculamos la contribución de cada variable
var_contrib <- sweep(loadings^2, 2, explained_variance, `*`) * 100

# Mostrar la contribución de las variables a cada componente
var_contrib
##                    PC1          PC2        PC3         PC4       PC5
## piso          1.238184 10.750710894 4.49478975 0.004768073 0.1683446
## preciom      11.574872  2.274629553 0.47943180 0.437787676 0.3643532
## areaconst    12.559264  0.028356284 0.01033209 2.546926875 1.0486030
## parqueaderos  9.900927  1.660038334 1.65692371 2.634791055 0.7527149
## banios       12.477528  0.002240978 0.93471885 0.114818780 2.4935053
## habitaciones  6.764079  3.856227702 4.82060253 0.344056028 0.4420136
##                      PC6
## piso         0.009869071
## preciom      1.535592246
## areaconst    0.473184632
## parqueaderos 0.061271514
## banios       0.643854620
## habitaciones 0.439687419

Análisis de la Matriz generada:

  • PC1 (Primer Componente Principal): Variables como areaconst, preciom, y banios tienen contribuciones significativas, lo que sugiere que estas variables son las más influyentes en la variabilidad explicada por este componente.

  • PC2 (Segundo Componente Principal): Aquí, variables como piso y habitaciones tienen una mayor contribución, indicando que estas variables son más relevantes en la formación del segundo componente.

  • PC3 y Otros Componentes: En los componentes subsiguientes, la influencia de las variables cambia, y se observan diferentes patrones de contribución que pueden ser interpretados en el contexto del análisis específico.

Este análisis de componentes principales (PCA) proporciona una visión profunda de las principales fuentes de variabilidad en el conjunto de datos sobre viviendas. Los primeros dos componentes explican la mayor parte de la variabilidad en los datos, lo que permite simplificar el análisis y enfocar la interpretación en estas dos dimensiones. Las visualizaciones generadas (Scree Plot, biplot) facilitan la comprensión de cómo las variables originales contribuyen a estos componentes y cómo se relacionan entre sí, ayudando a identificar las características clave que influyen en los precios y la oferta del mercado inmobiliario.

5.2 Análisis de Conglomerados:

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.

El primer paso es estandarizar las variables numéricas seleccionadas para asegurarse de que todas tengan la misma escala. Esto es crucial, ya que las variables originales pueden tener diferentes unidades y rangos, lo que podría sesgar los resultados del clustering.

#Se toma como referencia la misma base analizada anteriormente con las variables
# númericas.

# Se estandarizan las variables antes de aplicar el análisis de conglomerados.
viviendacon <- scale(subvivienda)
viviendacon_df <- as.data.frame(viviendacon)
viviendacon_df

Después de estandarizar las variables, se procede a calcular la matriz de distancias entre las observaciones utilizando la distancia euclidiana. Este paso es fundamental para la identificación de la similitud o disimilitud entre las propiedades.

Para manejar el tamaño del conjunto de datos y reducir la carga de procesamiento, se toma una muestra aleatoria del 10% de las observaciones. Esto permite realizar el cálculo de distancias de manera más eficiente sin sacrificar la representatividad de los datos.

# Las distancias correspondientes a los valores estandarizados serán:
# Distancias euclidianas

# Calculo con muestreo

# Tomar una muestra aleatoria del 10% de los datos
set.seed(123)
sample_indices <- sample(1:nrow(viviendacon_df), size = 0.1 * nrow(viviendacon_df))
viviendacon_df_sample <- viviendacon_df[sample_indices, ]

# Calcular la matriz de distancias en la muestra
m.distancia <- get_dist(viviendacon_df_sample, method = "euclidean")
fviz_dist(m.distancia, gradient = list(low = "green4", mid = "yellow", high = "red"))

La matriz de distancias calculada para la muestra se visualiza mediante un heatmap. En este gráfico de calor, las distancias más pequeñas entre propiedades similares se muestran en colores verdes, mientras que las distancias más grandes se muestran en colores rojos. Esta visualización ayuda a identificar visualmente los patrones de similitud y agrupamiento en el conjunto de datos.

Se implementa de manera complementaria un algoritmo de clustering jerárquico utilizando el método de enlace completo (complete). Este método agrupa las propiedades en clusters en función de las distancias euclidianas previamente calculadas.

library(tidyverse)
# distancia euclidiana
dist_emp <- dist(viviendacon_df, method = 'euclidean')

# Cluster jerarquico con el método complete
hc_emp <- hclust(dist_emp, method = 'complete')

# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_emp, k = 2)

# asignamos los clusters
assigned_cluster <- viviendacon_df %>% mutate(cluster = as.factor(cluster_assigments))


# gráfico de puntos
ggplot(assigned_cluster, aes(x = habitaciones, y = preciom, color = cluster)) +
  geom_point(size = 4) +
  geom_text(aes(label = cluster), vjust = -.8) + # Agregar etiquetas del clúster
  theme_classic()

Para determinar el número óptimo de clusters, se utilizan dos métodos: el método de la silueta y el método del codo.

  • Método de la Silueta: Este método mide la cohesión y la separación de los clusters, proporcionando un valor que indica qué tan bien separado está cada cluster de los demás.

  • Método del Codo: Este método identifica el punto donde la suma de las distancias al cuadrado dentro de los clusters comienza a disminuir lentamente, indicando un número óptimo de clusters.

install.packages("factoextra")
library(factoextra)

# Método de la silueta
fviz_nbclust(viviendacon_df, kmeans, method = "silhouette")

# Método del codo
fviz_nbclust(viviendacon_df, kmeans, method = "wss") + labs (subtitle="Elbow method")

El gráfico de silueta indica que el número óptimo de clusters para este conjunto de datos es 2. El gráfico de codo permite ratificar esta afirmación.

Este resultado sugiere que las propiedades en el conjunto de datos se agrupan de manera natural en dos grupos distintos que están bien separados entre sí, lo que debería proporcionar una segmentación significativa y útil para el análisis posterior.

Finalmente, se realiza un análisis de clustering utilizando el algoritmo K-means. Este algoritmo particiona el conjunto de datos en un número predefinido de clusters, en este caso, 2 clusters. Los centroides de los clusters son calculados y las observaciones son asignadas al cluster más cercano.

Se calcula la mediana de las características dentro de cada cluster para resumir las características de los grupos formados. Estos resúmenes son útiles para entender las diferencias entre los clusters y cómo se agrupan las propiedades según sus características.

# Se realiza un análisis de conglomerados (clústeres) utilizando el algoritmo K-means y luego agrega los datos para obtener las medianas de las variables en cada clúster. 

set.seed(123)
modelo_kmeans <- kmeans(viviendacon_df, 2) # Ajuste
viviendacon_df <- data.frame(viviendacon_df,
                             modelo_kmeans$cluster) # Cluster
aggregate(viviendacon_df,
          by = list(viviendacon_df$modelo_kmeans.cluster),
          FUN = median) # Medianas  
table(viviendacon_df$modelo_kmeans.cluster)
## 
##    1    2 
## 5882 2430
# Se realiza la gráfica de los dos cluster:

fviz_cluster(list(data = viviendacon_df[,1:6], 
                  cluster = viviendacon_df$modelo_kmeans.cluster),
             palette = c("#2E9FDF",  "#E7B800", "#FC4E07"),
             ellipse.type = "convex",repel = F, 
             show.clust.cent = FALSE, ggtheme = theme_minimal())

Este gráfico sugiere que los datos pueden ser efectivamente agrupados en dos clusters distintos. Cada cluster agrupa propiedades residenciales que comparten características similares, y hay una buena separación entre los clusters, lo que indica que el número de clusters seleccionado (k=2) es adecuado para este análisis.

#Grafico de dendograma:

res4 <- hcut(viviendacon_df_sample, k = 2, stand = TRUE)
fviz_dend(res4, rect = TRUE, cex = 0.5,
          k_colors = c("red","green"))

Este dendrograma sugiere que los datos se agrupan de manera efectiva en dos clusters principales. Estos clusters son bastante distintos entre sí, lo que se evidencia en la altura en la que las dos ramas principales se unen.

Este análisis de conglomerados permite segmentar el mercado inmobiliario en grupos homogéneos, facilitando la identificación de patrones y la toma de decisiones basada en datos. La combinación de métodos jerárquicos y K-means, junto con la visualización adecuada, proporciona una comprensión integral de las dinámicas de las propiedades residenciales en diferentes partes de la ciudad y estratos socioeconómicos.

5.3 Análisis de Correspondencia:

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.

El análisis de correspondencia múltiple (MCA) es una técnica estadística utilizada para analizar relaciones entre varias variables categóricas. En este caso, estamos interesados en explorar cómo se relacionan las variables categóricas que describen diferentes características de las propiedades residenciales, específicamente el tipo de vivienda, la zona geográfica, y el estrato socioeconómico.

Primero, se estructura la información categórica relevante para el análisis. Esto incluye el tipo de vivienda, la zona donde se encuentra la propiedad y su estrato socioeconómico.

En este paso, se construye una tabla de contingencia que muestra la distribución de propiedades según su zona, estrato y tipo de vivienda. Esta tabla permite identificar cuántas propiedades pertenecen a cada combinación de estas categorías. Los resultados muestran cómo se distribuyen las viviendas tipo “Apartamento” y “Casa” entre diferentes zonas y estratos, lo que proporciona una visión inicial de la segmentación del mercado.

subviviendaac <- table(vivienda2$zona, vivienda2$estrato, vivienda2$tipo)
subviviendaac
## , ,  = Apartamento
## 
##               
##                   3    4    5    6
##   Zona Centro    14    7    0    0
##   Zona Norte    337  246  498  117
##   Zona Oeste     29   58  231  711
##   Zona Oriente   58    2    1    0
##   Zona Sur      201 1091 1033  462
## 
## , ,  = Casa
## 
##               
##                   3    4    5    6
##   Zona Centro    91    7    0    0
##   Zona Norte    235  161  271   55
##   Zona Oeste     25   26   59   59
##   Zona Oriente  282    6    0    0
##   Zona Sur      181  525  652  581

Se extraen las variables categóricas y se convierten en factores para asegurar que el MCA las trate correctamente como datos categóricos:

subviviendaac <- vivienda2[, c("tipo", "zona", "estrato")]

# Supongamos que tienes un dataframe llamado vivienda2 con 3 variables categóricas
subviviendaac <- data.frame(
  tipo = as.factor(subviviendaac$tipo),
  zona = as.factor(subviviendaac$zona),
  estrato = as.factor(subviviendaac$estrato)
)

El siguiente paso es realizar el análisis de correspondencia múltiple (MCA). Esta técnica descompone las asociaciones entre las variables categóricas en componentes que se pueden representar gráficamente.

# Realizar el análisis de correspondencia múltiple (MCA)
resultado_mca <- MCA(subviviendaac, graph = FALSE)

Se realiza la gráfica biplot,la cual muestra simultáneamente las variables y las observacione. Permite ver cómo las diferentes categorías de las variables están relacionadas entre sí y cómo se distribuyen las propiedades según estas categorías.

# Biplot de variables y observaciones
fviz_mca_biplot(resultado_mca, geom = "point", pointsize = 2, repel = TRUE) +
  theme_minimal() +
  labs(title = "MCA - Biplot")

Los triángulos rojos representan categorías de variables. La posición de un triángulo indica cómo esa categoría se relaciona con las dimensiones principales. Los puntos azules representan observaciones individuales en el espacio definido por las dimensiones principales.

En cuanto a la distribución de las categorías, se puede observar por ejemplo que la categoría “Zona Oriente” está muy separada de otras categorías en la Dim1, lo que sugiere que “Zona Oriente” se comporta de manera distinta en comparación con las otras categorías en la primera dimensión.

De manera similar, las categorías “Zona Sur” y “Zona Norte” parecen estar más centradas y, por lo tanto, podrían representar comportamientos más comunes o menos extremos en el contexto de las dimensiones principales.

Se realiza tambien un gráfico de variables, mostrando cómo se distribuyen las categorías de “tipo de vivienda”, “zona”, y “estrato” en el espacio definido por las componentes principales del MCA, esto ayuda a identificar cuáles categorías están más estrechamente relacionadas.

# Gráfico de las variables
fviz_mca_var(resultado_mca, repel = TRUE)

Este gráfico MCA sugiere que hay asociaciones específicas entre ciertas zonas, tipos de vivienda y estratos en la base de datos. Por ejemplo, parece que las casas están más asociadas con la Zona Norte, mientras que los apartamentos pueden estar más relacionados con la Zona Sur. Además, ciertas zonas como la Zona Oriente y Zona Centro pueden compartir patrones similares.

#Gráfico de Contribución de las Variables
fviz_contrib(resultado_mca, choice = "var", axes = 1:2)

Se puede interpretar del gráfico que la Zona Oeste, Zona Oriente y la categoría 3 (Estrato 3) son las que más contribuyen a las dimensiones 1 y 2. Esto significa que estas categorías tienen un mayor impacto en la variabilidad explicada por las primeras dos dimensiones en el MCA. Por otro lado, categorías como Zona Norte y Estrato 5 tienen una contribución menor, lo que indica que no son tan influyentes en estas dimensiones principales.

El análisis de correspondencia múltiple proporciona una forma poderosa de explorar las relaciones entre múltiples variables categóricas en el contexto del mercado inmobiliario. Al visualizar cómo se distribuyen las categorías y las propiedades en el espacio de las componentes principales, es posible identificar patrones clave en la oferta de vivienda, como la preferencia por ciertos tipos de vivienda en zonas específicas o la concentración de ciertas combinaciones de estrato y tipo de vivienda. Estos hallazgos pueden informar decisiones estratégicas en la planificación urbana, la comercialización de propiedades, y la comprensión de las dinámicas del mercado.