1. Introducción

El presente análisis tiene como objetivo comprender de manera integral el mercado mobiliario urbano a partir de una base de datos de propiedades residenciales; con el fin de tomar decisiones estratégicas más informadas sobre compra, venta, valoración y segmentación de propiedades.Para ello, se aplican técnicas multivariadas, que permite desarroillar los siguientes análisis:

  • Análisis exploratorio de datos.
  • Análisis de Componentes Principales, ACP.
  • Análisis de Conglomerados.
  • Análisis de Correspondencias.
  • Visualización de resultados.
  • Conclusiones y recomendaciones estratégicas.

2. Cargue de la base de datos

Se efectúa la carga de la base vivienda del paqueteMODELOS. Así mismo, se realiza validación si el objeto R se llama vivenda o vivienda, para evitar errores por diferencias en el nombre. Finalmente, la base de convierte en data.frame, se limpian los nombres de las columnas y se excluye la variable barrio, para el análisis.

# Cargar la base de datos
data("vivenda")

# Revisar los objetos disponibles en el ambiente
ls()
## character(0)
if (exists("vivienda")) {
  datos <- vivienda
} else if (exists("vivenda")) {
  datos <- vivenda
} else {
  stop("No se encontró el objeto vivienda ni vivenda. Revise el nombre de la base cargada.")
}

datos <- as.data.frame(datos)
datos <- datos %>% clean_names()

# Se excluye la variable barrio del ejercicio
datos <- datos %>%
  select(-matches("^barrio$|barrio"))
head(datos)

3. Revisión de la Estructura de la Base

dim(datos)
## [1] 8322   12

Se identifican 8322 registros con 12 variables de la revisión inicial

names(datos)
##  [1] "id"           "zona"         "piso"         "estrato"      "preciom"     
##  [6] "areaconst"    "parqueaderos" "banios"       "habitaciones" "tipo"        
## [11] "longitud"     "latitud"

Se conocen los nombres de las 12 variables.

str(datos)
## 'data.frame':    8322 obs. of  12 variables:
##  $ id          : num  1147 1169 1350 5992 1212 ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr  NA NA NA "02" ...
##  $ estrato     : num  3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num  250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num  1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num  3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num  6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr  "Casa" "Casa" "Casa" "Casa" ...
##  $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num  3.43 3.43 3.44 3.44 3.46 ...

Asi mismo, se determina el tipo de cada variable para efectuar el analisis, identificando 9 variables numericas y 3 categóricas.

head(datos, 10)

3.1. Tabla de Variables y Tipo de Datos

La siguiente tabla resume la estructura de la base y permite validar las variables que son aptas para análisis numéricos o categóricos.

estructura_variables <- tibble(
  variable = names(datos),
  tipo = sapply(datos, function(x) class(x)[1])
)

kable(
  estructura_variables,
  caption = "Estructura inicial de variables de la base de datos"
) %>%
  kable_styling(full_width = FALSE)
Estructura inicial de variables de la base de datos
variable tipo
id numeric
zona character
piso character
estrato numeric
preciom numeric
areaconst numeric
parqueaderos numeric
banios numeric
habitaciones numeric
tipo character
longitud numeric
latitud numeric

3.2. Resumen de la base de datos

A continuación, se muestra de manera preliminar la calidad y composición de los datos.

skim(datos)
Data summary
Name datos
Number of rows 8322
Number of columns 12
_______________________
Column type frequency:
character 3
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

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 ▃▇▅▇▅

4. Preparación y Limpieza de los datos

Antes de aplicar los análisis multivariados, se revisa la calidad básica de la información, especialmente valores faltantes, tipos de variables y posibles inconsistencias.

4.1. Identificación de datos faltantes

A continuación, se detallan los datos y porcentaje de los datos faltantes por cada variable, identificando que las variables “piso” y “parqueadero”, cuentan con porcentajes representativos del conjunto de datos.

faltantes <- datos %>%
  summarise(across(everything(), ~ sum(is.na(.)))) %>%
  pivot_longer(
    cols = everything(),
    names_to = "variable",
    values_to = "cantidad_faltantes"
  ) %>%
  mutate(
    porcentaje_faltante = round(cantidad_faltantes / nrow(datos) * 100, 2)
  ) %>%
  arrange(desc(porcentaje_faltante))

kable(
  faltantes,
  caption = "Cantidad y porcentaje de valores faltantes por variable"
) %>%
  kable_styling(full_width = FALSE)
Cantidad y porcentaje de valores faltantes por variable
variable cantidad_faltantes porcentaje_faltante
piso 2638 31.70
parqueaderos 1605 19.29
id 3 0.04
zona 3 0.04
estrato 3 0.04
areaconst 3 0.04
banios 3 0.04
habitaciones 3 0.04
tipo 3 0.04
longitud 3 0.04
latitud 3 0.04
preciom 2 0.02

4.2. Tratamiento de datos faltantes

Para este análisis se aplica un tratamiento básico:

  • Variables numéricas: imputación con la mediana, porque es menos sensible a los valores extremos.
  • Variables categóricas: imputación con la moda, porque representa la categória mas reciente.
  • Variables de texto: conversión posterior a factor.
moda <- function(x) {
  x <- x[!is.na(x)]
  if (length(x) == 0) {
    return(NA)
  }
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
datos_limpios <- datos %>%
  mutate(
    across(
      where(is.numeric),
      ~ ifelse(is.na(.), median(., na.rm = TRUE), .)
    ),
    across(
      where(is.character),
      ~ ifelse(is.na(.), moda(.), .)
    ),
    across(
      where(is.factor),
      ~ ifelse(is.na(.), moda(.), .)
    )
  )

datos_limpios <- datos_limpios %>%
  mutate(across(where(is.character), as.factor))

faltantes_despues <- datos_limpios %>%
  summarise(across(everything(), ~ sum(is.na(.)))) %>%
  pivot_longer(
    cols = everything(),
    names_to = "variable",
    values_to = "cantidad_faltantes"
  ) %>%
  arrange(desc(cantidad_faltantes))

kable(
  faltantes_despues,
  caption = "Valores faltantes después del tratamiento"
) %>%
  kable_styling(full_width = FALSE)
Valores faltantes después del tratamiento
variable cantidad_faltantes
id 0
zona 0
piso 0
estrato 0
preciom 0
areaconst 0
parqueaderos 0
banios 0
habitaciones 0
tipo 0
longitud 0
latitud 0

5. Analisis Exploratorio de los Datos

El análisis exploratorio permite conocer el comportamiento inicial de las variables, identificar relaciones y detectar patrones preliminares dentro de la base de viviendas.

5.1 Identificación de variables numéricas y categóricas

Se separan las variables en dos grupos:

  • variables numéricas: para correlacoines, ACP, y clustering
  • Variables Categóricas: para frecuencias y análisis de correspondencia.
variables_numericas <- datos_limpios %>%
  select(where(is.numeric))

variables_categoricas <- datos_limpios %>%
  select(where(is.factor))

names(variables_numericas)
## [1] "id"           "estrato"      "preciom"      "areaconst"    "parqueaderos"
## [6] "banios"       "habitaciones" "longitud"     "latitud"
names(variables_categoricas)
## [1] "zona" "piso" "tipo"

5.2 Resumen descriptivo de variables numéricas

La siguiente tabla permite conocer la tendencia central y dispersión de las variables

summary(variables_numericas)
##        id          estrato         preciom         areaconst     
##  Min.   :   1   Min.   :3.000   Min.   :  58.0   Min.   :  30.0  
##  1st Qu.:2081   1st Qu.:4.000   1st Qu.: 220.0   1st Qu.:  80.0  
##  Median :4160   Median :5.000   Median : 330.0   Median : 123.0  
##  Mean   :4160   Mean   :4.634   Mean   : 433.9   Mean   : 174.9  
##  3rd Qu.:6239   3rd Qu.:5.000   3rd Qu.: 540.0   3rd Qu.: 229.0  
##  Max.   :8319   Max.   :6.000   Max.   :1999.0   Max.   :1745.0  
##   parqueaderos        banios        habitaciones       longitud     
##  Min.   : 1.000   Min.   : 0.000   Min.   : 0.000   Min.   :-76.59  
##  1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.: 3.000   1st Qu.:-76.54  
##  Median : 2.000   Median : 3.000   Median : 3.000   Median :-76.53  
##  Mean   : 1.867   Mean   : 3.111   Mean   : 3.605   Mean   :-76.53  
##  3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 4.000   3rd Qu.:-76.52  
##  Max.   :10.000   Max.   :10.000   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
resumen_numericas <- variables_numericas %>%
  summarise(
    across(
      everything(),
      list(
        minimo = ~ min(.x, na.rm = TRUE),
        promedio = ~ mean(.x, na.rm = TRUE),
        mediana = ~ median(.x, na.rm = TRUE),
        maximo = ~ max(.x, na.rm = TRUE),
        desviacion = ~ sd(.x, na.rm = TRUE)
      )
    )
  ) %>%
  pivot_longer(
    cols = everything(),
    names_to = "indicador",
    values_to = "valor"
  )

kable(
  resumen_numericas,
  digits = 2,
  caption = "Resumen estadístico de variables numéricas"
) %>%
  kable_styling(full_width = FALSE)
Resumen estadístico de variables numéricas
indicador valor
id_minimo 1.00
id_promedio 4160.00
id_mediana 4160.00
id_maximo 8319.00
id_desviacion 2401.20
estrato_minimo 3.00
estrato_promedio 4.63
estrato_mediana 5.00
estrato_maximo 6.00
estrato_desviacion 1.03
preciom_minimo 58.00
preciom_promedio 433.87
preciom_mediana 330.00
preciom_maximo 1999.00
preciom_desviacion 328.61
areaconst_minimo 30.00
areaconst_promedio 174.92
areaconst_mediana 123.00
areaconst_maximo 1745.00
areaconst_desviacion 142.94
parqueaderos_minimo 1.00
parqueaderos_promedio 1.87
parqueaderos_mediana 2.00
parqueaderos_maximo 10.00
parqueaderos_desviacion 1.01
banios_minimo 0.00
banios_promedio 3.11
banios_mediana 3.00
banios_maximo 10.00
banios_desviacion 1.43
habitaciones_minimo 0.00
habitaciones_promedio 3.61
habitaciones_mediana 3.00
habitaciones_maximo 10.00
habitaciones_desviacion 1.46
longitud_minimo -76.59
longitud_promedio -76.53
longitud_mediana -76.53
longitud_maximo -76.46
longitud_desviacion 0.02
latitud_minimo 3.33
latitud_promedio 3.42
latitud_mediana 3.42
latitud_maximo 3.50
latitud_desviacion 0.04

5.3 Distribución de variables numéricas

if (ncol(variables_numericas) > 0) {
  variables_numericas %>%
    pivot_longer(
      cols = everything(),
      names_to = "variable",
      values_to = "valor"
    ) %>%
    ggplot(aes(x = valor)) +
    geom_histogram(
      bins = 30,
      fill = "blue",
      color = "gray"
    ) +
    facet_wrap(~ variable, scales = "free") +
    labs(
      title = "Distribución de variables numéricas",
      x = "Valor",
      y = "Frecuencia"
    ) +
    theme_minimal()
} else {
  print("No se identificaron variables numéricas para graficar.")
}

Interpretación

  • areaconst: La distribución está fuertemente sesgada hacia la derecha; la mayoría de las viviendas tienen áreas construidas relativamente pequeñas, mientras que hay pocas viviendas con áreas muy grandes. Esto indica posibles valores extremos (outliers).
  • banios: La mayor concentración está alrededor de 2–3 baños. A medida que aumenta el número de baños, la frecuencia disminuye considerablemente;por tanto, las viviendas con muchos baños son poco frecuentes.
  • estrato: Se observa que predominan los estratos 4, 5 y 6, especialmente el estrato 5. Los estratos más bajos tienen una representación menor en los datos.
  • habitaciones: La mayor frecuencia corresponde a viviendas con aproximadamente 3 habitaciones, seguida de 4 y 5. Las viviendas con muy pocas o muchas habitaciones son menos comunes.
  • id: La distribución es prácticamente uniforme, porque el identificador simplemente enumera los registros.
  • latitud y longitud: Ambas variables presentan concentraciones en determinados rangos, lo que indica que las viviendas están ubicadas en zonas geográficas relativamente específicas. En latitud se aprecia una concentración importante alrededor de ciertos valores, mientras que longitud presenta una distribución más agrupada.
  • parqueaderos: La mayoría de las viviendas tienen 1 o 2 parqueaderos, mientras que tener 3 o más es mucho menos frecuente. También hay una cantidad importante de viviendas sin parqueadero.
  • preciom: Es una de las variables más importantes y presenta un sesgo positivo bastante marcado. La mayoría de las propiedades tienen precios relativamente bajos o medios, mientras que existe un grupo pequeño de viviendas con precios muy altos. Esto puede indicar la presencia de valores atípicos y una gran dispersión en los precios.

5.4 Diagramas de caja para variables numéricas

if (ncol(variables_numericas) > 0) {
  variables_numericas %>%
    pivot_longer(
      cols = everything(),
      names_to = "variable",
      values_to = "valor"
    ) %>%
    ggplot(aes(x = variable, y = valor)) +
    geom_boxplot(fill = "blue", alpha = 0.8) +
    facet_wrap(~ variable, scales = "free") +
    labs(
      title = "Diagramas de caja de variables numéricas",
      x = "Variable",
      y = "Valor"
    ) +
    theme_minimal() +
    theme(axis.text.x = element_blank())
} else {
  print("No se identificaron variables numéricas para construir diagramas de caja.")
}

Interpretación

Los anteriores boxplots premiten visualizar lo mencionado en el numeral anterior, es decir, la presencia de valores atipicos en variables como área o previo.

5.5 Matriz de correlación

if (ncol(variables_numericas) >= 2) {
  matriz_correlacion <- cor(
    variables_numericas,
    use = "complete.obs"
  )

  corrplot(
    matriz_correlacion,
    method = "color",
    type = "upper",
    tl.cex = 0.8,
    addCoef.col = "black",
    number.cex = 0.6,
    title = "Matriz de correlación entre variables numéricas",
    mar = c(0, 0, 2, 0)
  )
} else {
  print("No hay suficientes variables numéricas para calcular una matriz de correlación.")
}

Interpretación

Lo más relevante de la matriz es que el precio de la vivienda está principalmente relacionado con sus características físicas y socioeconómicas. En particular, el área construida (0.69), número de baños (0.67), parqueaderos (0.62) y estrato (0.61) presentan las correlaciones positivas más importantes con el precio.

Además, las variables área, baños, habitaciones y parqueaderos están relacionadas entre sí, lo que indica que las viviendas de mayor tamaño generalmente tienen mejores características y, en consecuencia, tienden a ser más costosas.

Por otro lado, la correlación extremadamente alta entre id y longitud (-0.96) debe tomarse con precaución, ya que probablemente refleja el orden de los registros y no una relación causal o significativa entre ambas variables.

5.6 Análisis descriptivo de variables categóricas

if (ncol(variables_categoricas) > 0) {
  for (var in names(variables_categoricas)) {
    cat("\n\nVariable:", var, "\n")
    print(table(variables_categoricas[[var]]))
  }
} else {
  print("No se identificaron variables categóricas en la base.")
}
## 
## 
## Variable: zona 
## 
##  Zona Centro   Zona Norte   Zona Oeste Zona Oriente     Zona Sur 
##          124         1920         1198          351         4729 
## 
## 
## Variable: piso 
## 
##   01   02   03   04   05   06   07   08   09   10   11   12 
##  860 4088 1097  607  567  245  204  211  146  130   84   83 
## 
## 
## Variable: tipo 
## 
## Apartamento        Casa 
##        5103        3219

5.7 Gráficos de frecuencia para variables categóricas

if (ncol(variables_categoricas) > 0) {
  variables_categoricas %>%
    pivot_longer(
      cols = everything(),
      names_to = "variable",
      values_to = "categoria"
    ) %>%
    ggplot(aes(x = categoria)) +
    geom_bar(fill = "lightgreen") +
    facet_wrap(~ variable, scales = "free") +
    coord_flip() +
    labs(
      title = "Distribución de variables categóricas",
      x = "Categoría",
      y = "Frecuencia"
    ) +
    theme_minimal()
} else {
  print("No existen variables categóricas para graficar.")
}

Interpretación

Se evidencia que existe una mayor concentración de la oferta inmobiliaria en el piso No. 2, apartamento en la zona sur.

5.8 Interpretación inicial del análisis exploratorio

A partir de esta primera revisión se pueden identificar algunos elementos relevantes para el análisis posterior:

  1. Estructura de la base: la base contiene variables numéricas y categóricas que permiten analizar características físicas, económicas y de ubicación de las viviendas.
  2. Calidad de datos: la revisión de valores faltantes permite determinar si la base requiere tratamientos adicionales antes de aplicar modelos multivariados.
  3. Variables numéricas: las variables cuantitativas permiten analizar dimensiones como precio, área, habitaciones, baños, antigüedad o estrato, dependiendo de las variables disponibles.
  4. Variables categóricas: las variables cualitativas permiten estudiar patrones por tipo de vivienda, zona, barrio, estrato u otras clasificaciones relevantes.
  5. Relaciones preliminares: la matriz de correlación permite identificar asociaciones iniciales entre variables, útiles para interpretar el Análisis de Componentes Principales.

6. Análisis de Componentes Principales, ACP

El Análisis de Componentes Principales permite reducir la dimensionalidad del conjunto de datos numéricos, conservando la mayor cantidad posible de información. Esta técnica ayuda a identificar las variables que explican la mayor variabilidad de las propiedades residenciales.

6.1 Preparación de datos para ACP

Se seleccionan unicamente las variables numéricas y se eliminan variables con varianza 0.

datos_acp <- datos_limpios %>%
  select(where(is.numeric))

# Eliminar variables con varianza cero
if (ncol(datos_acp) > 0) {
  datos_acp <- datos_acp[, apply(datos_acp, 2, stats::var, na.rm = TRUE) > 0, drop = FALSE]
}

# Validación mínima
if (ncol(datos_acp) < 2) {
  stop("No hay suficientes variables numéricas con varianza para ejecutar el ACP.")
}

summary(datos_acp)
##        id          estrato         preciom         areaconst     
##  Min.   :   1   Min.   :3.000   Min.   :  58.0   Min.   :  30.0  
##  1st Qu.:2081   1st Qu.:4.000   1st Qu.: 220.0   1st Qu.:  80.0  
##  Median :4160   Median :5.000   Median : 330.0   Median : 123.0  
##  Mean   :4160   Mean   :4.634   Mean   : 433.9   Mean   : 174.9  
##  3rd Qu.:6239   3rd Qu.:5.000   3rd Qu.: 540.0   3rd Qu.: 229.0  
##  Max.   :8319   Max.   :6.000   Max.   :1999.0   Max.   :1745.0  
##   parqueaderos        banios        habitaciones       longitud     
##  Min.   : 1.000   Min.   : 0.000   Min.   : 0.000   Min.   :-76.59  
##  1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.: 3.000   1st Qu.:-76.54  
##  Median : 2.000   Median : 3.000   Median : 3.000   Median :-76.53  
##  Mean   : 1.867   Mean   : 3.111   Mean   : 3.605   Mean   :-76.53  
##  3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 4.000   3rd Qu.:-76.52  
##  Max.   :10.000   Max.   :10.000   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

6.2 Ejecución del ACP

Con el ACP se reduce la dimensionalidad de la base, identificando combinaciones de variables que explican las principales diferencias entre las variables analizadas.

acp <- PCA(
  datos_acp,
  scale.unit = TRUE,
  graph = FALSE
)

summary(acp)
## 
## Call:
## PCA(X = datos_acp, scale.unit = TRUE, graph = FALSE) 
## 
## 
## Eigenvalues
##                       Dim.1  Dim.2  Dim.3  Dim.4  Dim.5
## Variance              3.820  1.887  0.972  0.901  0.580
## % of var.            42.442 20.962 10.797 10.013  6.440
## Cumulative % of var. 42.442 63.404 74.201 84.214 90.655
## 
## Individuals (the 10 first)
##                  Dist    Dim.1    ctr   cos2    Dim.2    ctr   cos2    Dim.3
## 1            |  3.077 | -1.771  0.010  0.331 |  1.835  0.021  0.355 |  1.175
## 2            |  2.620 | -2.268  0.016  0.749 |  0.871  0.005  0.111 |  0.034
## 3            |  2.347 | -1.403  0.006  0.357 |  1.469  0.014  0.392 |  0.198
## 4            |  2.298 |  1.250  0.005  0.296 |  0.123  0.000  0.003 |  0.526
## 5            |  2.339 | -1.824  0.010  0.608 |  0.400  0.001  0.029 | -0.486
## 6            |  2.122 | -1.173  0.004  0.306 | -0.121  0.000  0.003 | -1.009
## 7            |  1.795 | -1.616  0.008  0.810 |  0.145  0.000  0.007 | -0.352
## 8            |  1.061 |  0.101  0.000  0.009 | -0.387  0.001  0.133 | -0.180
## 9            |  2.787 | -0.284  0.000  0.010 |  2.090  0.028  0.562 |  0.534
## 10           |  2.786 |  0.298  0.000  0.011 |  1.525  0.015  0.300 | -0.656
##                 ctr   cos2  
## 1             0.017  0.146 |
## 2             0.000  0.000 |
## 3             0.000  0.007 |
## 4             0.003  0.052 |
## 5             0.003  0.043 |
## 6             0.013  0.226 |
## 7             0.002  0.038 |
## 8             0.000  0.029 |
## 9             0.004  0.037 |
## 10            0.005  0.055 |
## 
## Variables
##                 Dim.1    ctr   cos2    Dim.2    ctr   cos2    Dim.3    ctr
## id           |  0.621 10.087  0.385 | -0.668 23.635  0.446 |  0.367 13.843
## estrato      |  0.659 11.366  0.434 | -0.340  6.139  0.116 | -0.404 16.767
## preciom      |  0.866 19.639  0.750 |  0.124  0.818  0.015 | -0.237  5.766
## areaconst    |  0.747 14.591  0.557 |  0.437 10.116  0.191 |  0.047  0.231
## parqueaderos |  0.664 11.537  0.441 |  0.253  3.394  0.064 | -0.309  9.843
## banios       |  0.803 16.870  0.644 |  0.342  6.210  0.117 |  0.083  0.711
## habitaciones |  0.430  4.851  0.185 |  0.598 18.926  0.357 |  0.510 26.809
## longitud     | -0.600  9.410  0.359 |  0.694 25.551  0.482 | -0.359 13.269
## latitud      | -0.251  1.650  0.063 |  0.314  5.210  0.098 |  0.352 12.760
##                cos2  
## id            0.135 |
## estrato       0.163 |
## preciom       0.056 |
## areaconst     0.002 |
## parqueaderos  0.096 |
## banios        0.007 |
## habitaciones  0.261 |
## longitud      0.129 |
## latitud       0.124 |

6.3 Varianza explicada

fviz_eig(
  acp,
  addlabels = TRUE,
  ylim = c(0, 60),
  barfill = "#2E86AB",
  barcolor = "#2E86AB"
) +
  labs(
    title = "Porcentaje de varianza explicada por componente principal",
    x = "Componentes principales",
    y = "Porcentaje de varianza explicada"
  ) +
  theme_minimal()

Interpretación

Se evidencia cuánto aporta cada componente al principal, para el caso los dos primeros componentes explican un % importante de la varianza.

varianza <- as.data.frame(acp$eig)
varianza <- varianza %>%
  rownames_to_column("componente") %>%
  rename(
    eigenvalor = eigenvalue,
    porcentaje_varianza = `percentage of variance`,
    porcentaje_acumulado = `cumulative percentage of variance`
  )

kable(
  varianza,
  digits = 2,
  caption = "Varianza explicada por cada componente"
) %>%
  kable_styling(full_width = FALSE)
Varianza explicada por cada componente
componente eigenvalor porcentaje_varianza porcentaje_acumulado
comp 1 3.82 42.44 42.44
comp 2 1.89 20.96 63.40
comp 3 0.97 10.80 74.20
comp 4 0.90 10.01 84.21
comp 5 0.58 6.44 90.65

6.4 Mapa de variables del ACP

fviz_pca_var(
  acp,
  col.var = "contrib",
  gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
  repel = TRUE
) +
  labs(
    title = "Mapa de variables en el ACP",
    subtitle = "Color según contribución a los componentes principales"
  ) +
  theme_minimal()

Interpretración

En términos sencillos, este ACP muestra que la principal fuente de variabilidad de los datos está relacionada con las características físicas y económicas de las viviendas. El primer componente está fuertemente asociado con precio, área construida, baños, habitaciones y parqueaderos, mientras que el segundo componente recoge diferencias adicionales relacionadas principalmente con estrato y ubicación geográfica.

Además, el gráfico confirma los resultados obtenidos anteriormente mediante la matriz de correlación: las viviendas más grandes tienden a tener más habitaciones, baños y parqueaderos y, generalmente, un precio más elevado.

6.5 Contribución de variables a los componentes principales

fviz_contrib(
  acp,
  choice = "var",
  axes = 1,
  top = min(10, ncol(datos_acp)),
  fill = "#2E86AB",
  color = "#2E86AB"
) +
  labs(
    title = "Variables con mayor contribución al componente principal 1"
  ) +
  theme_minimal()

Interpretación

Las variables que explican mas el primer componente 1, corresponden a precio, área y baños.

fviz_contrib(
  acp,
  choice = "var",
  axes = 2,
  top = min(10, ncol(datos_acp)),
  fill = "#F18F01",
  color = "#F18F01"
) +
  labs(
    title = "Variables con mayor contribución al componente principal 2"
  ) +
  theme_minimal()

Interpretación

Las variables que aportan mas al componente 2 corresponden a longitud, id, y habotaciones.

6.6 Visualización de propiedades en el plano factorial

fviz_pca_ind(
  acp,
  geom.ind = "point",
  col.ind = "cos2",
  gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
  repel = TRUE
) +
  labs(
    title = "Representación de propiedades en el plano factorial",
    subtitle = "Color según calidad de representación"
  ) +
  theme_minimal()

Interpretación

La distribución de las propiedades permite evidenciar agrupaciones naturales y posibles casos atipicos, insumo para el análisis de conglomerados.

6.7 Interpretación del ACP

Los primeros componentes principales concentran la mayor parte de la variabilidad de las variables numéricas. Si los dos o tres primeros componentes explican un porcentaje importante de la variabilidad acumulada, es posible representar la estructura del mercado inmobiliario en un plano reducido sin perder demasiada información.

La interpretación debe centrarse en las variables con mayor contribución:

  • Si variables como precio, área, número de habitaciones o baños tienen alta contribución al primer componente, este puede representar una dimensión de tamaño y valor económico de la vivienda.
  • Si variables como antigüedad, estrato, garajes u otras características específicas contribuyen al segundo componente, este puede representar una dimensión de calidad, ubicación o diferenciación del inmueble.
  • Las variables alejadas del centro en el mapa factorial tienen mayor poder explicativo.
  • Las variables cercanas entre sí indican asociación positiva.
  • Las variables en dirección opuesta pueden indicar comportamientos inversos.

7. Análisis de Conglomerados

El análisis de conglomerados permite agrupar las propiedades en segmentos homogéneos. Estos segmentos ayudan a interpretar diferentes perfiles de vivienda y pueden ser utilizados para decisiones comerciales, inversión, valoración y estrategia territorial.

7.1 Preparación de datos para clustering

Se utilizan variables numéricas estandarizadas.

datos_cluster <- scale(datos_acp)

sum(is.na(datos_cluster))
## [1] 0

7.2 Determinación del número óptimo de conglomerados

7.2.1 Método del codo

fviz_nbclust(
  datos_cluster,
  kmeans,
  method = "wss"
) +
  labs(
    title = "Método del codo para determinar número óptimo de clusters",
    x = "Número de clusters",
    y = "Suma de cuadrados intra-cluster"
  ) +
  theme_minimal()

Interpretación

Este gráfico muestra una reducción de hasta 3 clusters y luego la curva se aplana, es decir que disminuye la variabilidad a medida que aumenta el numero de cluster.

7.2.2 Método de la silueta

fviz_nbclust(
  datos_cluster,
  kmeans,
  method = "silhouette"
) +
  labs(
    title = "Método de la silueta para determinar número óptimo de clusters",
    x = "Número de clusters",
    y = "Ancho promedio de silueta"
  ) +
  theme_minimal()

Interpretación

Este gráfico muestra que tan bien separados están los cluster

7.3 Estimación automática sugerida del número de clusters

set.seed(123)

sil_width <- map_dbl(2:8, function(k) {
  km <- kmeans(datos_cluster, centers = k, nstart = 25)
  ss <- silhouette(km$cluster, dist(datos_cluster))
  mean(ss[, 3])
})

k_silueta <- tibble(
  k = 2:8,
  silueta_promedio = sil_width
)

kable(
  k_silueta,
  digits = 3,
  caption = "Ancho promedio de silueta por número de clusters"
) %>%
  kable_styling(full_width = FALSE)
Ancho promedio de silueta por número de clusters
k silueta_promedio
2 0.283
3 0.218
4 0.233
5 0.211
6 0.210
7 0.218
8 0.232
k_optimo <- k_silueta$k[which.max(k_silueta$silueta_promedio)]
k_optimo
## [1] 2

Interpretación

La tabla permite comparar objetivamente las alternativas de segmentación y seleccionar los conglomerados que ofrecen mayor calidad estasdistica, para el caso el numéro sugerido de cluster es 2.

7.4 Ejecución del modelo K-means

Se ejecuta el modelo k-mens con los 2 cluster óptimos, lo que permite segmentar el mercado en grupos homogéneos de propiedades.

set.seed(123)

modelo_kmeans <- kmeans(
  datos_cluster,
  centers = k_optimo,
  nstart = 25
)

datos_segmentados <- datos_limpios %>%
  mutate(cluster = as.factor(modelo_kmeans$cluster))

table(datos_segmentados$cluster)
## 
##    1    2 
## 2975 5347

7.5 Visualización de clusters

fviz_cluster(
  modelo_kmeans,
  data = datos_cluster,
  geom = "point",
  ellipse.type = "convex",
  palette = "jco",
  ggtheme = theme_minimal()
) +
  labs(
    title = "Segmentación de propiedades mediante K-means",
    subtitle = "Clusters proyectados en el plano factorial"
  )

Interpretación

Se evidencia la existencia de grupos de vivienda con caracteristicas diferenciales, lo que permite orientar estrategias comerciales y de inversión de segmento.

7.6 Perfilamiento numérico de los conglomerados

perfil_numerico <- datos_segmentados %>%
  group_by(cluster) %>%
  summarise(
    across(
      where(is.numeric),
      list(
        promedio = ~ mean(.x, na.rm = TRUE),
        mediana = ~ median(.x, na.rm = TRUE)
      )
    ),
    cantidad = n(),
    .groups = "drop"
  )

kable(
  perfil_numerico,
  digits = 2,
  caption = "Perfil numérico de los clusters"
) %>%
  kable_styling(full_width = FALSE)
Perfil numérico de los clusters
cluster id_promedio id_mediana estrato_promedio estrato_mediana preciom_promedio preciom_mediana areaconst_promedio areaconst_mediana parqueaderos_promedio parqueaderos_mediana banios_promedio banios_mediana habitaciones_promedio habitaciones_mediana longitud_promedio longitud_mediana latitud_promedio latitud_mediana cantidad
1 5779.55 6025 5.41 6 747.62 650 288.61 250 2.59 2 4.42 4 4.31 4 -76.54 -76.54 3.41 3.42 2975
2 3258.90 2852 4.20 4 259.30 250 111.66 90 1.47 1 2.38 2 3.21 3 -76.52 -76.52 3.42 3.41 5347

El perfil numérico permite asignar una interpretación de negocio de cada cluster, identificando caracteristicas en términos de precio, área, etc.

7.7 Perfilamiento categórico de los conglomerados

variables_categoricas_segmentadas <- datos_segmentados %>%
  select(where(is.factor)) %>%
  select(-cluster)

if (ncol(variables_categoricas_segmentadas) > 0) {
  for (var in names(variables_categoricas_segmentadas)) {
    cat("\n\nVariable categórica:", var, "\n")
    tabla <- datos_segmentados %>%
      tabyl(cluster, !!sym(var)) %>%
      adorn_percentages("row") %>%
      adorn_pct_formatting(digits = 1)
    print(tabla)
  }
} else {
  print("No existen variables categóricas adicionales para perfilar los clusters.")
}
## 
## 
## Variable categórica: zona 
##  cluster Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
##        1        0.7%      13.6%      29.3%         0.8%    55.5%
##        2        1.9%      28.3%       6.1%         6.1%    57.5%
## 
## 
## Variable categórica: piso 
##  cluster    01    02    03   04   05   06   07   08   09   10   11   12
##        1  9.3% 57.9% 12.8% 5.1% 3.2% 2.9% 2.2% 1.9% 1.7% 1.3% 1.1% 0.7%
##        2 10.9% 44.2% 13.4% 8.5% 8.8% 3.0% 2.6% 2.9% 1.8% 1.7% 1.0% 1.2%
## 
## 
## Variable categórica: tipo 
##  cluster Apartamento  Casa
##        1       43.3% 56.7%
##        2       71.4% 28.6%

7.8 Interpretación de los conglomerados

Se identifica lo siguiente en función de los resultados observados:

  • Cluster de vivienda económica: propiedades con menor valor relativo, menor área o características más básicas. Puede representar oferta accesible o de alta rotación.
  • Cluster de vivienda intermedia: propiedades con equilibrio entre precio, área y atributos. Puede representar el mercado de mayor volumen comercial.
  • Cluster de vivienda premium: propiedades con mayor valor, mejores características físicas o ubicaciones más valorizadas. Puede representar oportunidades de inversión selectiva o comercialización especializada.

Esta segmentación permite diseñar estrategias diferenciadas por tipo de propiedad, precio esperado, ubicación, perfil de comprador y objetivo comercial.

8. Análisis de Correspondencias

El Análisis de Correspondencias permite examinar asociaciones entre variables categóricas. En este caso, se utiliza para identificar patrones entre variables como tipo de vivienda, zona, barrio, estrato u otras variables cualitativas disponibles.

8.1 Identificación de variables categóricas válidas

se seleccionan las variables categóricas con un numero razonable de categórias; para el caso, estas corresponde a “zona”, “piso” y “tipo”.

cat_validas <- datos_limpios %>%
  select(where(is.factor)) %>%
  select(where(~ n_distinct(.) >= 2 & n_distinct(.) <= 25))

names(cat_validas)
## [1] "zona" "piso" "tipo"

8.2 Análisis automático de correspondencias entre pares de variables

if (ncol(cat_validas) >= 2) {

  pares <- combn(names(cat_validas), 2, simplify = FALSE)
  pares_mostrar <- pares[1:min(length(pares), 5)]

  for (par in pares_mostrar) {

    cat("\n\nAnalizando correspondencias entre:", par[1], "y", par[2], "\n")

    tabla <- table(
      datos_limpios[[par[1]]],
      datos_limpios[[par[2]]]
    )

    # Eliminar filas y columnas con frecuencia cero
    tabla <- tabla[rowSums(tabla) > 0, colSums(tabla) > 0]

    # Validar que la tabla tenga al menos 2 filas y 2 columnas
    if (all(dim(tabla) > 1)) {

      tryCatch({

        ac <- CA(tabla, graph = FALSE)

        # Validar que el análisis tenga al menos dos dimensiones para graficar
        if (
          !is.null(ac$row$coord) &&
          !is.null(ac$col$coord) &&
          ncol(as.matrix(ac$row$coord)) >= 2 &&
          ncol(as.matrix(ac$col$coord)) >= 2
        ) {

          print(
            fviz_ca_biplot(
              ac,
              axes = c(1, 2),
              repel = TRUE
            ) +
              labs(
                title = paste("Análisis de Correspondencias:", par[1], "vs", par[2]),
                x = "Dimensión 1",
                y = "Dimensión 2"
              ) +
              theme_minimal()
          )

        } else {

          cat(
            "No se genera gráfico para", par[1], "vs", par[2],
            "porque el análisis tiene menos de dos dimensiones.\n"
          )

          print(ac$eig)
        }

      }, error = function(e) {

        cat(
          "No fue posible ejecutar el análisis para", par[1], "vs", par[2], "\n"
        )
        cat("Detalle del error:", e$message, "\n")

      })

    } else {

      cat(
        "No se analiza", par[1], "vs", par[2],
        "porque la tabla no tiene al menos 2 filas y 2 columnas válidas.\n"
      )

    }
  }

} else {

  print("No existen suficientes variables categóricas válidas para el análisis de correspondencias.")

}
## 
## 
## Analizando correspondencias entre: zona y piso

## 
## 
## Analizando correspondencias entre: zona y tipo 
## No se genera gráfico para zona vs tipo porque el análisis tiene menos de dos dimensiones.
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08300733                    100                               100
## 
## 
## Analizando correspondencias entre: piso y tipo 
## No se genera gráfico para piso vs tipo porque el análisis tiene menos de dos dimensiones.
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1  0.2036611                    100                               100

Interpretación

Este análisis permite observar si ciertas categórias tienden a relacionarse ente sí.

8.3 Correspondencia específica entre variables de interés

En este caso se intenta identificar nombres probables.

nombres_base <- names(datos_limpios)

variable_tipo <- nombres_base[str_detect(nombres_base, "tipo")][1]
variable_zona <- nombres_base[str_detect(nombres_base, "zona|sector|localidad")][1]

variable_tipo
## [1] "tipo"
variable_zona
## [1] "zona"

8.3.1 Tipo de vivienda vs zona

if (!is.na(variable_tipo) & !is.na(variable_zona)) {

  tabla_tipo_zona <- table(
    datos_limpios[[variable_tipo]],
    datos_limpios[[variable_zona]]
  )

  # Eliminar filas y columnas sin frecuencia
  tabla_tipo_zona <- tabla_tipo_zona[
    rowSums(tabla_tipo_zona) > 0,
    colSums(tabla_tipo_zona) > 0
  ]

  # Mostrar tabla de contingencia
  print(tabla_tipo_zona)

  if (all(dim(tabla_tipo_zona) > 1)) {

    tryCatch({

      ac_tipo_zona <- CA(tabla_tipo_zona, graph = FALSE)

      # Mostrar eigenvalores para evaluar número de dimensiones disponibles
      print(ac_tipo_zona$eig)

      n_dim_filas <- ncol(as.matrix(ac_tipo_zona$row$coord))
      n_dim_columnas <- ncol(as.matrix(ac_tipo_zona$col$coord))
      n_dim <- min(n_dim_filas, n_dim_columnas)

      if (n_dim >= 2) {

        # Caso 1: existen dos dimensiones, se grafica biplot tradicional
        print(
          fviz_ca_biplot(
            ac_tipo_zona,
            axes = c(1, 2),
            repel = TRUE
          ) +
            labs(
              title = "Análisis de Correspondencias: Tipo de vivienda vs Zona",
              x = "Dimensión 1",
              y = "Dimensión 2"
            ) +
            theme_minimal()
        )

      } else if (n_dim == 1) {

        # Caso 2: solo existe una dimensión, se grafica representación unidimensional
        coord_filas <- as.data.frame(ac_tipo_zona$row$coord)
        coord_columnas <- as.data.frame(ac_tipo_zona$col$coord)

        coord_filas$categoria <- rownames(coord_filas)
        coord_columnas$categoria <- rownames(coord_columnas)

        names(coord_filas)[1] <- "Dim1"
        names(coord_columnas)[1] <- "Dim1"

        coord_filas$variable <- variable_tipo
        coord_columnas$variable <- variable_zona

        coords_1d <- bind_rows(
          coord_filas %>% select(categoria, Dim1, variable),
          coord_columnas %>% select(categoria, Dim1, variable)
        )

        print(
          ggplot(coords_1d, aes(x = Dim1, y = variable, color = variable, label = categoria)) +
            geom_point(size = 4) +
            geom_text_repel(size = 3.5, show.legend = FALSE) +
            geom_vline(xintercept = 0, linetype = "dashed", color = "gray50") +
            labs(
              title = "Análisis de Correspondencias Unidimensional: Tipo de vivienda vs Zona",
              subtitle = "El cruce solo genera una dimensión factorial; por eso se presenta en un eje",
              x = "Dimensión 1",
              y = "Variable"
            ) +
            theme_minimal()
        )

      } else {

        print("El análisis no generó dimensiones suficientes para graficar.")

      }

      # Gráfico alternativo: mapa de calor de frecuencias relativas
      # Este gráfico siempre es útil para interpretar la distribución del tipo de vivienda por zona.
      tabla_prop <- prop.table(tabla_tipo_zona, margin = 1) * 100

      tabla_prop_df <- as.data.frame(tabla_prop)
      names(tabla_prop_df) <- c("tipo_vivienda", "zona", "porcentaje")

      print(
        ggplot(tabla_prop_df, aes(x = zona, y = tipo_vivienda, fill = porcentaje)) +
          geom_tile(color = "white") +
          geom_text(aes(label = paste0(round(porcentaje, 1), "%")), size = 3) +
          scale_fill_gradient(low = "#E8F4F8", high = "#2E86AB") +
          labs(
            title = "Distribución porcentual de tipo de vivienda por zona",
            subtitle = "Porcentaje calculado dentro de cada tipo de vivienda",
            x = "Zona",
            y = "Tipo de vivienda",
            fill = "%"
          ) +
          theme_minimal() +
          theme(axis.text.x = element_text(angle = 45, hjust = 1))
      )

    }, error = function(e) {

      print(paste("Error en el análisis tipo vs zona:", e$message))

    })

  } else {

    print("La tabla tipo vs zona no tiene suficientes filas y columnas válidas.")

  }

} else {

  print("No se encontraron automáticamente las variables tipo y zona. Ajuste manualmente los nombres.")

}
##              
##               Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
##   Apartamento          24       1198       1029           62     2790
##   Casa                100        722        169          289     1939
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08300733                    100                               100

Interpretación

El análisis de correspondencia mostró que la relación entre tipo de vivienda y zona se representa principalmente en una dimensión. Por esta razón, se utilizó una visualización unidimensional que permite identificar la posición de cada categória sobre el eje principal de diferenciación. Por ejemplo se observa una mayor participación en la zona sur de tipo casa y apartamento.

8.4 Interpretación del Análisis de Correspondencias

En los mapas de correspondencia:

  • Categorías cercanas entre sí indican asociación o comportamiento similar.
  • Categorías lejanas al centro tienen mayor capacidad explicativa.
  • Categorías ubicadas cerca del origen tienen comportamiento promedio o poco diferenciador.
  • Si un tipo de vivienda aparece cercano a una zona específica, puede interpretarse como una concentración relativa de ese tipo de oferta en dicha zona.

Este análisis es útil para identificar patrones de localización, concentración de oferta y perfiles inmobiliarios por sector de la ciudad.

9. Visualizaciones estratégicas

9.1 Identificación automática de variables clave

variable_precio <- names(datos_segmentados)[str_detect(names(datos_segmentados), "precio|valor|venta")][1]
variable_area <- names(datos_segmentados)[str_detect(names(datos_segmentados), "area|área|metros|m2")][1]
variable_zona <- names(datos_segmentados)[str_detect(names(datos_segmentados), "zona|sector|localidad")][1]
variable_estrato <- names(datos_segmentados)[str_detect(names(datos_segmentados), "estrato")][1]

variable_precio
## [1] "preciom"
variable_area
## [1] "areaconst"
variable_zona
## [1] "zona"
variable_estrato
## [1] "estrato"

9.2 Precio por cluster

if (!is.na(variable_precio)) {
  ggplot(datos_segmentados, aes(x = cluster, y = .data[[variable_precio]], fill = cluster)) +
    geom_boxplot(alpha = 0.8) +
    labs(
      title = "Distribución del precio por segmento de vivienda",
      x = "Cluster",
      y = "Precio"
    ) +
    theme_minimal() +
    theme(legend.position = "none")
} else {
  print("No se identificó automáticamente una variable de precio. Ajuste variable_precio manualmente.")
}

Interpretación

El gráfico permite comparar el comportamiento económico de cada segmento, para el cluster 1 se puede identificar un segmento premium por tener precios mas altos. Diferente al cluster 2 que puede representar vivienda mas económica.

9.3 Área por cluster

if (!is.na(variable_area)) {
  ggplot(datos_segmentados, aes(x = cluster, y = .data[[variable_area]], fill = cluster)) +
    geom_boxplot(alpha = 0.8) +
    labs(
      title = "Distribución del área por segmento de vivienda",
      x = "Cluster",
      y = "Área"
    ) +
    theme_minimal() +
    theme(legend.position = "none")
} else {
  print("No se identificó automáticamente una variable de área. Ajuste variable_area manualmente.")
}

Interpretación

El gráfico muestra que a mayor área en el cluster 1 pueden ser viviendas familiares o premium, y el cluster 2 pueden representar apartamentos pequeños o viviendas de menor costo

9.4 Relación entre precio y área por cluster

if (!is.na(variable_precio) & !is.na(variable_area)) {
  ggplot(
    datos_segmentados,
    aes(
      x = .data[[variable_area]],
      y = .data[[variable_precio]],
      color = cluster
    )
  ) +
    geom_point(alpha = 0.7) +
    geom_smooth(method = "lm", se = FALSE) +
    labs(
      title = "Relación entre precio y área por segmento",
      x = "Área",
      y = "Precio",
      color = "Cluster"
    ) +
    theme_minimal()
} else {
  print("No se identificaron automáticamente precio y área. Ajuste los nombres manualmente.")
}

Interpretación

La relación entre previo y área confirma que el tamaño de la vivienda es un facor relevante en el precio del inmueble.

9.5 Oferta por zona y cluster

if (!is.na(variable_zona)) {
  ggplot(datos_segmentados, aes(x = .data[[variable_zona]], fill = cluster)) +
    geom_bar(position = "dodge") +
    coord_flip() +
    labs(
      title = "Distribución de la oferta por zona y cluster",
      x = "Zona",
      y = "Número de propiedades",
      fill = "Cluster"
    ) +
    theme_minimal()
} else {
  print("No se identificó automáticamente una variable de zona. Ajuste variable_zona manualmente.")
}

Interpretación

Se identifica que las zonas con mayor concentración de ofertas corresponde a la zona sur y Norte para el cluster 2

9.6 Precio promedio por zona

if (!is.na(variable_precio) & !is.na(variable_zona)) {
  datos_segmentados %>%
    group_by(.data[[variable_zona]]) %>%
    summarise(
      precio_promedio = mean(.data[[variable_precio]], na.rm = TRUE),
      cantidad = n(),
      .groups = "drop"
    ) %>%
    arrange(desc(precio_promedio)) %>%
    ggplot(aes(x = reorder(.data[[variable_zona]], precio_promedio), y = precio_promedio)) +
    geom_col(fill = "#2E86AB") +
    coord_flip() +
    labs(
      title = "Precio promedio por zona",
      x = "Zona",
      y = "Precio promedio"
    ) +
    theme_minimal()
} else {
  print("No se identificaron automáticamente precio y zona. Ajuste los nombres manualmente.")
}

Interpretación

El precio promedio por zona permite identificar diferencias en la valorización de los inmuebles, lo cual implica decisiones de inversión, compara y venta.

10. Conclusiones

A partir del análisis realizado, se plantean las siguientes conclusiones generales:

  1. El mercado inmobiliario presenta estructuras diferenciadas según características físicas, económicas y de ubicación: El ACP permite identificar las variables que explican la mayor variabilidad de la oferta inmobiliaria, tales como precio, área, número de habitaciones, número de baños, estrato o ubicación, dependiendo de las variables disponibles en la base.

  2. La segmentación mediante conglomerados permite clasificar las viviendas en grupos homogéneos: Estos grupos pueden interpretarse como segmentos de mercado, por ejemplo vivienda económica, vivienda intermedia y vivienda premium; esta clasificación facilita decisiones comerciales más focalizadas.

  3. Las variables categóricas muestran patrones de asociación relevantes: El análisis de correspondencias permite identificar si ciertos tipos de vivienda se concentran en determinadas zonas, ayudando a entender la dinámica territorial de la oferta.

  4. La visualización de resultados fortalece la toma de decisiones: Los gráficos de componentes principales, clusters, distribuciones de precio y análisis por zona permiten comunicar los hallazgos de forma clara a la dirección de la empresa.

  5. El análisis ofrece una base para decisiones estratégicas: La combinación de ACP, clustering y correspondencias permite comprender el mercado desde varias dimensiones: atributos físicos, segmentación económica y comportamiento territorial.

11. Recomendaciones estratégicas

Con base en los resultados del análisis, se proponen las siguientes recomendaciones:

  1. Diseñar estrategias comerciales diferenciadas por segmento: Cada cluster debe abordarse con una estrategia distinta de precio, promoción, canal comercial y perfil de cliente objetivo.

  2. Priorizar zonas con alta concentración de oferta y mejor relación precio-características: Las zonas con propiedades bien posicionadas en términos de área, precio y atributos pueden representar oportunidades de inversión o comercialización.

  3. Utilizar el ACP como herramienta de monitoreo del mercado: La empresa puede actualizar periódicamente el análisis para identificar cambios en los factores que explican el comportamiento del mercado inmobiliario.

  4. Incorporar el análisis de correspondencias en la estrategia territorial: Las asociaciones entre tipo de vivienda, zona y barrio permiten detectar nichos de mercado y oportunidades específicas por ubicación.

  5. Profundizar el análisis predictivo de precios: Como siguiente etapa, se recomienda construir modelos de regresión, árboles de decisión o modelos de machine learning para estimar el precio esperado de una vivienda según sus características.

  6. Crear tableros ejecutivos de seguimiento: Se recomienda implementar visualizaciones en Power BI, Tableau o Shiny para monitorear indicadores como precio promedio, oferta por zona, comportamiento por cluster y evolución de segmentos.

  7. Validar la calidad de datos de forma periódica:La empresa debe establecer controles sobre completitud, consistencia y actualización de la información inmobiliaria para mejorar la confiabilidad de los análisis.