Este informe analiza 8.322 ofertas de vivienda ubicadas en Cali, obtenidas de la plataforma OLX mediante web scraping. A partir de esta información se aplican análisis exploratorio, Componentes Principales (PCA), conglomerados, correspondencias y visualización geográfica, con el propósito de identificar qué características diferencian los inmuebles, qué segmentos de oferta existen y cómo se relacionan la tipología, la zona y el barrio.
El propósito es construir una visión integral de la oferta inmobiliaria urbana que sirva como soporte para decisiones de valoración, inversión, comercialización y posicionamiento de portafolio.
El análisis se desarrolla en cinco capas:
Nota metodológica. Los valores atípicos de precio y área no se eliminan de forma automática: en un mercado inmobiliario pueden corresponder a propiedades reales de lujo o gran tamaño. En cambio, se controla su influencia mediante estandarización.
La base contiene 8.322 registros y 13 variables, correspondientes a propiedades residenciales y sus principales características físicas, económicas y territoriales.
# Instalar solo una vez si el paquete no está disponible:
# install.packages("devtools")
#if (!requireNamespace("paqueteMODELOS", quietly = TRUE)) {
#
# if (!requireNamespace("devtools", quietly = TRUE)) {
# install.packages("devtools")
# }
#
# devtools::install_github(
# "centromagis/paqueteMODELOS",
# force = TRUE
# )
#}
#devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
#install.packages("remotes")
#
#remotes::install_github(
# "centromagis/paqueteMODELOS",
# force = TRUE
#)
library(paqueteMODELOS)
data("vivienda")
#dim(vivienda)
#str(vivienda)
# Resumen general de la base
resumen_base <- data.frame(
Indicador = c(
"Número de propiedades",
"Número de variables",
"Variables numéricas",
"Variables categóricas"
),
Valor = c(
nrow(vivienda),
ncol(vivienda),
sum(sapply(vivienda, is.numeric)),
sum(sapply(vivienda, function(x) is.character(x) || is.factor(x)))
)
)
kable(
resumen_base,
caption = "Resumen general de la base de datos",
align = c("l", "c")
) |>
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE,
position = "center"
)| Indicador | Valor |
|---|---|
| Número de propiedades | 8322 |
| Número de variables | 13 |
| Variables numéricas | 9 |
| Variables categóricas | 4 |
estructura_base <- data.frame(
Variable = names(vivienda),
Tipo = sapply(vivienda, function(x) class(x)[1]),
`Valores únicos` = sapply(vivienda, function(x) length(unique(x))),
`Datos faltantes` = sapply(vivienda, function(x) sum(is.na(x))),
check.names = FALSE
)
kable(
estructura_base,
caption = "Estructura y características de las variables",
align = c("l", "c", "c", "c")
) |>
kable_styling(
bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = TRUE
)| Variable | Tipo | Valores únicos | Datos faltantes | |
|---|---|---|---|---|
| id | id | numeric | 8320 | 3 |
| zona | zona | character | 6 | 3 |
| piso | piso | character | 13 | 2638 |
| estrato | estrato | numeric | 5 | 3 |
| preciom | preciom | numeric | 540 | 2 |
| areaconst | areaconst | numeric | 653 | 3 |
| parqueaderos | parqueaderos | numeric | 11 | 1605 |
| banios | banios | numeric | 12 | 3 |
| habitaciones | habitaciones | numeric | 12 | 3 |
| tipo | tipo | character | 3 | 3 |
| barrio | barrio | character | 437 | 3 |
| longitud | longitud | numeric | 2929 | 3 |
| latitud | latitud | numeric | 3680 | 3 |
La base contiene 9 variables numéricas y 4 categóricas. Las numéricas corresponden a: id (identificador del inmueble), estrato (estrato socioeconómico), preciom (precio en millones de pesos), areaconst (área construida en m²), parqueaderos (número de parqueaderos), banios (número de baños), habitaciones (número de habitaciones), longitud y latitud (ubicación geográfica). Las categóricas son: zona (zona urbana), piso (piso reportado), tipo (tipo de vivienda) y barrio (barrio de ubicación).
diccionario <- data.frame(
Variable = c(
"id", "zona", "piso", "estrato", "preciom", "areaconst",
"parqueaderos", "banios", "habitaciones", "tipo", "barrio",
"longitud", "latitud"
),
Descripcion = c(
"Identificador del inmueble",
"Zona urbana",
"Piso reportado",
"Estrato socioeconómico",
"Precio del inmueble (millones de pesos)",
"Área construida (m²)",
"Número de parqueaderos",
"Número de baños",
"Número de habitaciones",
"Tipo de vivienda",
"Barrio",
"Longitud geográfica",
"Latitud geográfica"
)
)
kable(diccionario, align = c("l","l"),
caption = "Diccionario básico de variables") |>
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))| Variable | Descripcion |
|---|---|
| id | Identificador del inmueble |
| zona | Zona urbana |
| piso | Piso reportado |
| estrato | Estrato socioeconómico |
| preciom | Precio del inmueble (millones de pesos) |
| areaconst | Área construida (m²) |
| parqueaderos | Número de parqueaderos |
| banios | Número de baños |
| habitaciones | Número de habitaciones |
| tipo | Tipo de vivienda |
| barrio | Barrio |
| longitud | Longitud geográfica |
| latitud | Latitud geográfica |
Inicialmente, se evaluó la presencia de valores faltantes en las variables de la base de datos.
faltantes <- vivienda |>
summarise(across(everything(), ~sum(is.na(.)))) |>
pivot_longer(
cols = everything(),
names_to = "variable",
values_to = "faltantes"
) |>
mutate(
porcentaje = 100 * faltantes / nrow(vivienda)
) |>
arrange(desc(faltantes))
kable(
faltantes,
digits = 2,
col.names = c("Variable", "N faltantes", "% faltantes"),
caption = "Valores faltantes por variable"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover")
)| Variable | N faltantes | % faltantes |
|---|---|---|
| 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 |
| barrio | 3 | 0.04 |
| longitud | 3 | 0.04 |
| latitud | 3 | 0.04 |
| preciom | 2 | 0.02 |
ggplot(
faltantes,
aes(x = reorder(variable, porcentaje), y = porcentaje)
) +
geom_col() +
geom_text(
aes(label = paste0(round(porcentaje, 1), "%")),
hjust = -0.15,
size = 3.5
) +
coord_flip() +
scale_y_continuous(
expand = expansion(mult = c(0, 0.15))
) +
labs(
title = "Valores faltantes por variable",
subtitle = "Porcentaje respecto al total de registros",
x = NULL,
y = "% de valores faltantes"
)Los valores faltantes se concentran principalmente en
piso (31,70 %) y parqueaderos
(19,29 %), mientras que en las demás variables no
superan el 0,04 %. Esta concentración hace necesario
definir un tratamiento que permita conservar la mayor cantidad posible
de información.
Además de los valores faltantes, se verificó la presencia de
registros iguales a cero en habitaciones y
banios. Dado que la base corresponde a inmuebles
residenciales, estos valores se consideran poco plausibles y se analizan
como posibles inconsistencias.
ceros_estrato <- vivienda |>
group_by(estrato) |>
summarise(
Habitaciones = sum(habitaciones == 0, na.rm = TRUE),
Baños = sum(banios == 0, na.rm = TRUE),
.groups = "drop"
) |>
pivot_longer(
cols = c(Habitaciones, Baños),
names_to = "Variable",
values_to = "Cantidad"
)
ggplot(
ceros_estrato,
aes(
x = factor(estrato),
y = Cantidad,
fill = Variable
)
) +
geom_col(
position = position_dodge(width = 0.7),
width = 0.7
) +
geom_text(
aes(label = Cantidad),
position = position_dodge(width = 0.7),
vjust = -0.4,
size = 3.5
) +
scale_y_continuous(
expand = expansion(mult = c(0, 0.12))
) +
labs(
title = "Valores iguales a cero en habitaciones y baños",
subtitle = "Distribución de registros inconsistentes según estrato",
x = "Estrato",
y = "Número de registros",
fill = "Variable"
) +
theme_minimal()Se evaluó la presencia de valores extremos en las principales variables cuantitativas asociadas con las características físicas y económicas de los inmuebles. Para ello se utilizaron diagramas de caja, los cuales permiten identificar observaciones alejadas del comportamiento central de cada variable.
datos_atipicos <- vivienda |>
select(
preciom,
areaconst,
parqueaderos,
banios,
habitaciones
) |>
pivot_longer(
cols = everything(),
names_to = "Variable",
values_to = "Valor"
)
ggplot(
datos_atipicos,
aes(x = Variable, y = Valor)
) +
geom_boxplot(outlier.alpha = 0.4) +
facet_wrap(
~Variable,
scales = "free",
ncol = 3
) +
labs(
title = "Distribución y valores atípicos de las variables cuantitativas",
subtitle = "Diagramas de caja con escalas independientes",
x = NULL,
y = "Valor"
) +
theme_minimal() +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank()
)Los diagramas evidencian la presencia de valores extremos,
principalmente en variables como preciom y
areaconst. Sin embargo, estos registros no se eliminan
automáticamente, dado que pueden corresponder a inmuebles reales de
mayor tamaño, dotación o valor. Para reducir su influencia en los
análisis multivariados posteriores, se utilizarán procedimientos de
estandarización.
A partir del diagnóstico anterior, se adoptan los siguientes criterios de tratamiento:
piso, los valores faltantes se clasifican como
“No aplica / no reportado”, debido a que esta condición
puede estar asociada al tipo de inmueble.parqueaderos, los valores faltantes se imputan
mediante la mediana según el tipo de vivienda.habitaciones y
banios se consideran inconsistentes, se convierten en
valores faltantes y posteriormente se imputan mediante la
mediana según el tipo de vivienda.vivienda_tratada <- vivienda |>
mutate(
# Valores cero considerados inconsistentes
habitaciones = ifelse(habitaciones == 0, NA, habitaciones),
banios = ifelse(banios == 0, NA, banios),
# Tratamiento de piso
piso = ifelse(
is.na(piso),
"No aplica / no reportado",
piso
)
) |>
group_by(tipo) |>
mutate(
# Imputación mediante mediana según tipo de vivienda
parqueaderos = ifelse(
is.na(parqueaderos),
median(parqueaderos, na.rm = TRUE),
parqueaderos
),
habitaciones = ifelse(
is.na(habitaciones),
median(habitaciones, na.rm = TRUE),
habitaciones
),
banios = ifelse(
is.na(banios),
median(banios, na.rm = TRUE),
banios
)
) |>
ungroup() |>
filter(
!is.na(id),
!is.na(zona),
!is.na(estrato),
!is.na(preciom),
!is.na(areaconst),
!is.na(tipo),
!is.na(barrio),
!is.na(longitud),
!is.na(latitud)
) |>
mutate(
zona = as.factor(zona),
tipo = as.factor(tipo),
barrio = as.factor(barrio),
estrato_factor = as.factor(estrato)
)
cat(
"Registros después del tratamiento:",
nrow(vivienda_tratada)
)## Registros después del tratamiento: 8319
precio_mediana <- median(vivienda_tratada$preciom)
area_mediana <- median(vivienda_tratada$areaconst)
estrato_mediana <- median(vivienda_tratada$estrato)
n_barrios <- dplyr::n_distinct(vivienda_tratada$barrio)
cat(
'<div class="kpi-grid">',
sprintf('<div class="kpi"><div class="value">%s</div><div class="label">Inmuebles analizados</div></div>',
format(nrow(vivienda_tratada), big.mark = ".", decimal.mark = ",")),
sprintf('<div class="kpi"><div class="value">$%s M</div><div class="label">Precio mediano</div></div>',
format(round(precio_mediana, 0), big.mark = ".", decimal.mark = ",")),
sprintf('<div class="kpi"><div class="value">%s m²</div><div class="label">Área mediana</div></div>',
format(round(area_mediana, 0), big.mark = ".", decimal.mark = ",")),
sprintf('<div class="kpi"><div class="value">%s</div><div class="label">Barrios observados</div></div>',
n_barrios),
'</div>'
)La distribución de los precios de oferta presenta una asimetría positiva, con una mayor concentración de inmuebles en los rangos de precios bajos y medios. A medida que el precio aumenta, la frecuencia de propiedades disminuye, evidenciando un grupo reducido de inmuebles de alto valor que podría representar un segmento diferenciado dentro del mercado inmobiliario analizado.
p_precio <- ggplot(vivienda_tratada, aes(x = preciom)) +
geom_histogram(bins = 45, boundary = 0) +
scale_x_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
labs(
title = "Distribución del precio de oferta",
subtitle = "La cola derecha evidencia inmuebles de alto valor que representan segmentos reales del mercado",
x = "Precio (millones de pesos)",
y = "Número de inmuebles"
)
ggplotly(p_precio)Se observan diferencias en el precio mediano tanto por zona como por tipo de vivienda. La Zona Oeste presenta los valores más altos para casas y apartamentos, seguida por la Zona Sur. En todas las zonas, las casas registran un precio mediano superior al de los apartamentos. Por su parte, la Zona Oriente presenta los menores precios medianos, lo que evidencia una clara diferenciación territorial de la oferta inmobiliaria.
resumen_zona_tipo <- vivienda_tratada |>
group_by(zona, tipo) |>
summarise(
n = n(),
precio_mediano = median(preciom),
area_mediana = median(areaconst),
.groups = "drop"
)
grafico_zona_tipo <- ggplot(
resumen_zona_tipo,
aes(
x = zona,
y = precio_mediano,
fill = tipo,
text = paste0(
"Zona: ", zona,
"<br>Tipo: ", tipo,
"<br>Oferta: ", n,
"<br>Precio mediano: $", round(precio_mediano, 0), " M",
"<br>Área mediana: ", round(area_mediana, 0), " m²"
)
)
) +
geom_col(position = "dodge") +
coord_flip() +
labs(
title = "Precio mediano según zona y tipo de vivienda",
subtitle = "Una primera lectura de la heterogeneidad territorial y tipológica",
x = NULL,
y = "Precio mediano (millones)",
fill = "Tipo"
)
ggplotly(
grafico_zona_tipo,
tooltip = "text"
)muestra_plot <- vivienda_tratada |>
slice_sample(n = min(3500, nrow(vivienda_tratada)))
ggplot(
muestra_plot,
aes(
x = areaconst,
y = preciom,
color = estrato_factor,
shape = tipo
)
) +
geom_point(alpha = 0.45, size = 2) +
scale_x_continuous(labels = label_number(big.mark = ".")) +
scale_y_continuous(labels = label_number(big.mark = ".")) +
labs(
title = "Área construida y precio: una relación positiva, pero no única",
subtitle = "El estrato y la tipología ayudan a explicar por qué inmuebles de tamaño similar pueden tener precios diferentes",
x = "Área construida (m²)",
y = "Precio (millones)",
color = "Estrato",
shape = "Tipo"
)Lectura preliminar. El mercado presenta una estructura heterogénea: el precio está relacionado con el tamaño, pero la localización, el estrato y la configuración interna del inmueble introducen diferencias que justifican un análisis multivariado.
Para explorar la relación entre las características cuantitativas de los inmuebles, se calcula la matriz de correlaciones entre precio, área construida, estrato, parqueaderos, baños, habitaciones, longitud y latitud.
# Variables numéricas de interés
datos_cor <- vivienda_tratada |>
select(
preciom,
areaconst,
estrato,
parqueaderos,
banios,
habitaciones,
longitud,
latitud
)
# Matriz de correlación
matriz_cor <- cor(
datos_cor,
use = "complete.obs",
method = "pearson"
)
# Nombres más claros para el gráfico
nombres_cor <- c(
"Precio",
"Área",
"Estrato",
"Parqueaderos",
"Baños",
"Habitaciones",
"Longitud",
"Latitud"
)
colnames(matriz_cor) <- nombres_cor
rownames(matriz_cor) <- nombres_cor
# Gráfico de correlaciones
corrplot::corrplot(
matriz_cor,
method = "color",
type = "upper",
order = "hclust",
addCoef.col = "black",
number.cex = 0.75,
tl.col = "black",
tl.srt = 45,
diag = FALSE
)La matriz muestra que el precio se relaciona principalmente con el área construida (r = 0,69), los baños (r = 0,68), los parqueaderos (r = 0,67) y el estrato (r = 0,61). Esto indica que los inmuebles de mayor tamaño, mejor dotación y mayor estrato tienden a presentar precios de oferta más altos. En contraste, las habitaciones (r = 0,27) presentan una relación más débil con el precio. En cuanto a la ubicación, la longitud muestra una correlación negativa moderada con el precio (r = -0,34), mientras que la latitud presenta una relación débil (r = -0,12), lo que sugiere que la ubicación espacial también está asociada con las diferencias de precio, aunque con distinta intensidad.
El PCA se realiza sobre precio, área construida, estrato, habitaciones, baños y parqueaderos, con el propósito de sintetizar las principales características económicas y físicas de los inmuebles. Latitud y longitud se excluyen del análisis factorial y se reservan para el análisis geográfico, donde permiten estudiar la distribución espacial de la oferta y de los segmentos identificados.
vars_pca <- vivienda_tratada |>
select(preciom, areaconst, estrato, habitaciones, banios, parqueaderos)
# PCA con estandarización interna
res_pca <- PCA(
vars_pca,
scale.unit = TRUE,
graph = FALSE
)
eig <- as.data.frame(res_pca$eig)
eig$Componente <- paste0("PC", seq_len(nrow(eig)))
tabla_eig <- eig |>
transmute(
Componente,
Autovalor = eigenvalue,
`Varianza explicada (%)` = `percentage of variance`,
`Varianza acumulada (%)` = `cumulative percentage of variance`
)
kable(tabla_eig, digits = 2,
caption = "Varianza explicada por los componentes principales") |>
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))| Componente | Autovalor | Varianza explicada (%) | Varianza acumulada (%) | |
|---|---|---|---|---|
| comp 1 | PC1 | 3.49 | 58.18 | 58.18 |
| comp 2 | PC2 | 1.21 | 20.23 | 78.41 |
| comp 3 | PC3 | 0.52 | 8.59 | 86.99 |
| comp 4 | PC4 | 0.36 | 6.07 | 93.06 |
| comp 5 | PC5 | 0.23 | 3.87 | 96.94 |
| comp 6 | PC6 | 0.18 | 3.06 | 100.00 |
fviz_eig(
res_pca,
addlabels = TRUE,
barfill = "grey70",
barcolor = "grey30",
linecolor = "grey20"
) +
labs(
title = "¿Cuánta información conserva cada componente?",
subtitle = "El punto de quiebre orienta el número de dimensiones útiles"
)El PCA muestra que los dos primeros componentes explican el 78,41 % de la variabilidad total de los datos. El primer componente (PC1) concentra el 58,18 %, mientras que el segundo (PC2) aporta un 20,23 % adicional. A partir del tercer componente, la contribución disminuye considerablemente (8,59 %), lo que evidencia un punto de quiebre después del segundo componente. Por tanto, dos componentes permiten resumir adecuadamente las seis variables originales, reduciendo la dimensionalidad del análisis con una pérdida de información relativamente baja.
fviz_pca_var(
res_pca,
col.var = "contrib",
gradient.cols = c("#64748b", "#f59e0b", "#b91c1c"),
repel = TRUE
) +
labs(
title = "Variables que estructuran el mercado",
subtitle = "Longitud de la flecha = calidad de representación; color = contribución"
)contrib <- as.data.frame(res_pca$var$contrib) |>
tibble::rownames_to_column("Variable") |>
transmute(
Variable,
PC1 = Dim.1,
PC2 = Dim.2,
Total_PC1_PC2 = Dim.1 + Dim.2
) |>
arrange(desc(Total_PC1_PC2))
kable(contrib, digits = 2,
caption = "Contribución de las variables a los dos primeros componentes") |>
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))| Variable | PC1 | PC2 | Total_PC1_PC2 |
|---|---|---|---|
| habitaciones | 9.14 | 44.81 | 53.95 |
| estrato | 8.94 | 41.89 | 50.83 |
| preciom | 22.22 | 6.16 | 28.38 |
| areaconst | 20.35 | 4.12 | 24.46 |
| banios | 21.81 | 1.81 | 23.62 |
| parqueaderos | 17.54 | 1.21 | 18.75 |
Interpretación. El primer componente (PC1), que explica el 58,18 % de la variabilidad, está determinado principalmente por el precio (22,22 %), los baños (21,81 %), el área construida (20,35 %) y los parqueaderos (17,54 %). Por ello, este eje puede interpretarse como una dimensión de tamaño, dotación y valor del inmueble. El segundo componente (PC2), que explica un 20,23 % adicional, está definido principalmente por habitaciones (44,81 %) y estrato (41,89 %). En este eje, las habitaciones se orientan hacia valores positivos y el estrato hacia valores negativos, por lo que PC2 permite diferenciar inmuebles caracterizados por una mayor cantidad de habitaciones frente a aquellos asociados con estratos más altos. En conjunto, ambos componentes explican el 78,41 % de la variabilidad total.
coords <- as.data.frame(res_pca$ind$coord[, 1:2]) |>
mutate(
zona = vivienda_tratada$zona,
tipo = vivienda_tratada$tipo
)
muestra_coords <- coords |>
slice_sample(n = min(3500, nrow(coords)))
ggplot(
muestra_coords,
aes(Dim.1, Dim.2, color = zona, shape = tipo)
) +
geom_point(alpha = 0.35, size = 1.7) +
labs(
title = "Mapa factorial de la oferta inmobiliaria",
subtitle = "Propiedades próximas presentan perfiles físicos y económicos similares",
x = paste0("PC1 (", round(res_pca$eig[1, 2], 1), "%)"),
y = paste0("PC2 (", round(res_pca$eig[2, 2], 1), "%)"),
color = "Zona",
shape = "Tipo"
)El análisis de conglomerados permite agrupar inmuebles con características similares e identificar perfiles diferenciados dentro de la oferta inmobiliaria. La segmentación se realiza sobre los tres primeros componentes del PCA, que concentran el 86,99 % de la variabilidad total, reduciendo la redundancia entre las variables originales. Esta estrategia reduce ruido, evita duplicar información altamente correlacionada y produce segmentos más fáciles de interpretar.
coords_cluster <- as.data.frame(res_pca$ind$coord[, 1:3])
fviz_nbclust(
coords_cluster,
FUNcluster = kmeans,
method = "wss",
k.max = 8,
nstart = 25
) +
labs(
title = "Método del codo",
subtitle = "Se busca el punto a partir del cual agregar clusters produce mejoras marginales"
)Para mantener una segmentación estratégica y comparable se emplean 4 conglomerados, una solución suficientemente granular para diferenciar perfiles sin fragmentar excesivamente el mercado.
La segmentación identifica cuatro perfiles con diferencias claras en precio, tamaño, estrato y dotación. Se utiliza el precio mediano como referencia, ya que representa mejor el valor típico ante la presencia de precios extremos. El segmento Accesible / compacto (48,2 %) concentra la mayor parte de la oferta, con inmuebles de menor área (89,5 m²), estrato promedio 4,07 y precio mediano de $220 millones. El Amplio / consolidado (29,8 %) corresponde a propiedades más amplias (177,8 m²) y de estratos altos (5,45), con precio mediano de $480 millones. El Residencial medio (11,6 %) se diferencia por su mayor número de habitaciones (6,38) y área (299,6 m²), pese a ubicarse en un estrato promedio menor (3,89). Finalmente, el Premium / alta valorización (10,4 %) reúne los inmuebles de mayor tamaño (423,2 m²), estrato (5,72) y dotación, alcanzando un precio mediano de $1.149 millones. En conjunto, los segmentos muestran que el precio no depende únicamente del estrato, sino de su combinación con el tamaño y las características físicas del inmueble.
set.seed(123)
km <- kmeans(coords_cluster, centers = 4, nstart = 50)
vivienda_cluster <- vivienda_tratada |>
mutate(cluster = factor(km$cluster))
perfil_cluster <- vivienda_cluster |>
group_by(cluster) |>
summarise(
Inmuebles = n(),
Participacion = n() / nrow(vivienda_cluster),
Precio_prom = mean(preciom),
Precio_mediano = median(preciom),
Area_prom = mean(areaconst),
Estrato_prom = mean(estrato),
Habitaciones_prom = mean(habitaciones),
Banios_prom = mean(banios),
Parqueaderos_prom = mean(parqueaderos),
.groups = "drop"
)
# Etiquetas automáticas según el orden del precio promedio.
orden_precio <- perfil_cluster |>
arrange(Precio_prom) |>
pull(cluster)
nombres_segmento <- c(
"Accesible / compacto",
"Residencial medio",
"Amplio / consolidado",
"Premium / alta valorización"
)
mapa_nombres <- setNames(nombres_segmento, orden_precio)
perfil_cluster <- perfil_cluster |>
mutate(
Segmento = unname(mapa_nombres[as.character(cluster)])
)
vivienda_cluster <- vivienda_cluster |>
mutate(
Segmento = factor(
unname(mapa_nombres[as.character(cluster)]),
levels = nombres_segmento
)
)
perfil_mostrar <- perfil_cluster |>
transmute(
Segmento,
Inmuebles,
`Participación` = percent(Participacion, accuracy = 0.1),
`Precio promedio (M)` = round(Precio_prom, 1),
`Precio mediano (M)` = round(Precio_mediano, 1),
`Área promedio (m²)` = round(Area_prom, 1),
`Estrato promedio` = round(Estrato_prom, 2),
`Habitaciones` = round(Habitaciones_prom, 2),
`Baños` = round(Banios_prom, 2),
`Parqueaderos` = round(Parqueaderos_prom, 2)
)
kable(perfil_mostrar,
caption = "Perfil de los segmentos identificados") |>
kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover"))| Segmento | Inmuebles | Participación | Precio promedio (M) | Precio mediano (M) | Área promedio (m²) | Estrato promedio | Habitaciones | Baños | Parqueaderos |
|---|---|---|---|---|---|---|---|---|---|
| Amplio / consolidado | 2483 | 29.8% | 531.0 | 480 | 177.8 | 5.45 | 3.42 | 3.56 | 1.95 |
| Premium / alta valorización | 865 | 10.4% | 1133.8 | 1149 | 423.2 | 5.72 | 4.45 | 5.18 | 3.86 |
| Residencial medio | 964 | 11.6% | 446.1 | 411 | 299.6 | 3.89 | 6.38 | 4.46 | 1.83 |
| Accesible / compacto | 4007 | 48.2% | 219.7 | 220 | 89.5 | 4.07 | 2.93 | 2.10 | 1.17 |
coords_cluster_plot <- as.data.frame(res_pca$ind$coord[, 1:2]) |>
mutate(
Segmento = vivienda_cluster$Segmento
)
coords_cluster_plot <- coords_cluster_plot |>
slice_sample(
n = min(4000, nrow(coords_cluster_plot))
)
ggplot(
coords_cluster_plot,
aes(Dim.1, Dim.2, color = Segmento)
) +
geom_point(alpha = 0.45, size = 1.8) +
stat_ellipse(linewidth = 0.7, alpha = 0.6) +
labs(
title = "Cuatro mercados dentro de un mismo mercado",
subtitle = "Los conglomerados resumen combinaciones distintas de precio, tamaño, dotación y estrato",
x = "Componente principal 1",
y = "Componente principal 2",
color = NULL
)La ubicación geográfica muestra diferencias claras entre los segmentos. La Zona Sur concentra la mayor participación en todos los perfiles, especialmente en Premium / alta valorización (62,3 %) y Accesible / compacto (60,3 %). Sin embargo, el perfil premium también tiene una presencia importante en la Zona Oeste (26,7 %), mientras que el segmento Amplio / consolidado se distribuye principalmente entre Zona Sur (52,1 %), Oeste (28,5 %) y Norte (19,3 %). El Residencial medio presenta la distribución territorial más diversa, con participación relevante de las zonas Sur (49,4 %), Norte (23,0 %) y Oriente (16,8 %). En conjunto, los resultados muestran que una misma zona reúne distintos perfiles de vivienda, por lo que la ubicación por sí sola no define el segmento; también intervienen el precio, el estrato, el tamaño y la dotación del inmueble.
cluster_zona <- vivienda_cluster |>
count(Segmento, zona) |>
group_by(Segmento) |>
mutate(
p = n / sum(n),
etiqueta = ifelse(
p >= 0.03,
percent(p, accuracy = 0.1),
""
)
) |>
ungroup()
ggplot(
cluster_zona,
aes(x = Segmento, y = p, fill = zona)
) +
geom_col() +
geom_text(
aes(label = etiqueta),
position = position_stack(vjust = 0.5),
size = 3.2
) +
scale_y_continuous(
labels = percent_format()
) +
coord_flip() +
labs(
title = "Composición geográfica de cada segmento",
subtitle = "Participación de las zonas dentro de cada perfil de oferta",
x = NULL,
y = "% dentro del segmento",
fill = "Zona"
)Implicación gerencial. Los clusters no deben entenderse solo como grupos estadísticos: pueden convertirse en líneas de portafolio, con estrategias de precio, captación, promoción y negociación distintas.
El reto solicita examinar la relación entre tipo de vivienda, zona y barrio. Como la base contiene una gran cantidad de barrios, mostrar todos simultáneamente produciría un gráfico ilegible. Por ello se conserva el detalle de los barrios con mayor oferta y se agrupa el resto en “Otros barrios”. Esta decisión mejora la visualización sin perder la estructura dominante.
La correspondencia simple muestra que el tipo de vivienda no se distribuye de la misma manera entre las zonas. Los apartamentos tienen una presencia especialmente alta en las zonas Sur, Norte y Oeste, mientras que las casas tienen un mayor peso relativo en las zonas Oriente y Centro. La Zona Sur concentra la mayor cantidad de ofertas de ambos tipos, por lo que su volumen elevado refleja el peso general de esta zona en la base. En conjunto, el análisis sugiere una asociación entre la tipología de vivienda y su ubicación dentro de la ciudad.
tabla_tipo_zona <- table(
vivienda_tratada$tipo,
vivienda_tratada$zona
)
# Tabla de contingencia
kable(
as.data.frame.matrix(tabla_tipo_zona),
caption = "Tabla de contingencia: tipo de vivienda por zona"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover")
)| Zona Centro | Zona Norte | Zona Oeste | Zona Oriente | Zona Sur | |
|---|---|---|---|---|---|
| Apartamento | 24 | 1198 | 1029 | 62 | 2787 |
| Casa | 100 | 722 | 169 | 289 | 1939 |
# Análisis de correspondencias
ca_tipo_zona <- FactoMineR::CA(
tabla_tipo_zona,
graph = FALSE
)
# Número de dimensiones realmente disponibles
ndim <- nrow(ca_tipo_zona$eig)
cat("Número de dimensiones disponibles:", ndim, "\n")## Número de dimensiones disponibles: 1
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08305442 100 100
if (ndim >= 2) {
factoextra::fviz_ca_biplot(
ca_tipo_zona,
axes = c(1, 2),
repel = TRUE,
col.row = "#334155",
col.col = "#b45309"
) +
labs(
title = "Correspondencia entre tipo de vivienda y zona",
subtitle = "La cercanía entre categorías indica perfiles de distribución similares"
)
} else {
# Coordenadas de tipos de vivienda
coord_filas <- as.matrix(ca_tipo_zona$row$coord)
filas <- data.frame(
Categoria = rownames(coord_filas),
Dim1 = coord_filas[, 1],
Grupo = "Tipo de vivienda"
)
# Coordenadas de zonas
coord_columnas <- as.matrix(ca_tipo_zona$col$coord)
columnas <- data.frame(
Categoria = rownames(coord_columnas),
Dim1 = coord_columnas[, 1],
Grupo = "Zona"
)
# Unir categorías
datos_ca <- rbind(filas, columnas)
ggplot(
datos_ca,
aes(
x = Dim1,
y = Grupo,
color = Grupo
)
) +
geom_vline(
xintercept = 0,
linetype = "dashed",
color = "grey60"
) +
geom_point(size = 4) +
geom_text(
aes(label = Categoria),
vjust = -0.8,
check_overlap = TRUE,
show.legend = FALSE
) +
labs(
title = "Correspondencia entre tipo de vivienda y zona",
subtitle = "Asociaciones representadas sobre la única dimensión disponible",
x = "Dimensión 1",
y = NULL,
color = NULL
) +
theme_minimal()
}
El análisis de correspondencia se resume en una única dimensión, que
explica el 100 % de la variación de la asociación entre tipo de vivienda
y zona. Sobre este eje, los apartamentos se ubican más próximos a la
Zona Oeste, mientras que las casas se orientan hacia las zonas Centro y
Oriente. La Zona Norte se encuentra cerca del centro, lo que indica un
perfil menos marcado hacia una tipología específica. En conjunto, el
análisis confirma que la composición entre casas y apartamentos varía
según la zona, mostrando una diferenciación territorial de la
oferta.
El análisis de correspondencia múltiple muestra una diferenciación territorial entre tipo de vivienda, zona y barrio. La Dimensión 1 explica el 9,16 % de la inercia y la Dimensión 2 el 8,27 %, acumulando un 17,43 %. Por tanto, el plano permite reconocer los patrones más marcados, aunque no resume toda la variabilidad presente en las categorías.
La Zona Oeste es uno de los elementos que más se diferencia y, además, presenta una contribución alta a la construcción de las dimensiones, como muestra su coloración más intensa. Se ubica cerca de barrios como Aguacatal, Los Cristales, Santa Teresita y Normandía, que también aparecen alejados del centro. Esto indica que no solo están asociados entre sí, sino que ayudan de manera importante a explicar las diferencias territoriales encontradas.
Algo similar ocurre con la Zona Norte, que se separa principalmente sobre la Dimensión 2 y aparece asociada con La Flora, Prados del Norte y Acopi. La Flora, en particular, presenta una contribución relativamente alta. En contraste, la Zona Sur se ubica en la parte negativa de la segunda dimensión, junto con barrios como Ciudad Jardín, Pance, El Ingenio, El Caney, La Hacienda, Valle del Lili y El Refugio.
Las categorías situadas cerca del origen y con menor contribución tienen menor capacidad para explicar las diferencias del mapa. En este sentido, casa y apartamento muestran una separación, pero su aporte a las dimensiones es menos marcado que el de algunas zonas y barrios.
En conjunto, el ACM sugiere que la zona y ciertos barrios son los elementos que más diferencian la estructura de la oferta inmobiliaria, especialmente Oeste y Norte. No obstante, dado que las dos primeras dimensiones explican 17,43 % de la inercia, estas asociaciones representan los patrones más visibles y no la totalidad de las relaciones existentes.
top_barrios <- vivienda_tratada |>
count(barrio, sort = TRUE) |>
slice_head(n = 15) |>
pull(barrio) |>
as.character()
datos_mca <- vivienda_tratada |>
transmute(
tipo = fct_drop(tipo),
zona = fct_drop(zona),
barrio_grupo = factor(
ifelse(as.character(barrio) %in% top_barrios,
as.character(barrio),
"Otros barrios")
)
)
res_mca <- MCA(datos_mca, graph = FALSE)
mca_eig <- as.data.frame(res_mca$eig)
mca_tabla <- data.frame(
Dimension = paste0("Dim ", seq_len(nrow(mca_eig))),
`Inercia (%)` = round(mca_eig[,2], 2),
`Inercia acumulada (%)` = round(mca_eig[,3], 2)
)
kable(head(mca_tabla, 8),
caption = "Inercia explicada por las primeras dimensiones del ACM") |>
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))| Dimension | Inercia…. | Inercia.acumulada…. |
|---|---|---|
| Dim 1 | 9.16 | 9.16 |
| Dim 2 | 8.27 | 17.43 |
| Dim 3 | 6.90 | 24.33 |
| Dim 4 | 5.33 | 29.67 |
| Dim 5 | 5.03 | 34.70 |
| Dim 6 | 5.00 | 39.70 |
| Dim 7 | 5.00 | 44.70 |
| Dim 8 | 5.00 | 49.70 |
fviz_mca_var(
res_mca,
repel = TRUE,
col.var = "contrib",
gradient.cols = c("#64748b", "#f59e0b", "#b91c1c")
) +
labs(
title = "Mapa de asociaciones: tipología, zona y barrio",
subtitle = "Categorías cercanas tienden a aparecer juntas con mayor frecuencia"
)Cómo leer el mapa. La proximidad entre un barrio, una zona y un tipo de vivienda sugiere una asociación en la estructura de la oferta. Las categorías alejadas del origen son las que más diferencian el mercado, mientras que las cercanas al centro representan comportamientos más promedio.
El componente territorial es especialmente útil para una empresa inmobiliaria porque traduce la segmentación estadística a ubicaciones concretas.
El mapa muestra que los cuatro segmentos presentan patrones de concentración diferentes dentro de Cali, aunque existe una mezcla de perfiles en varias zonas de la ciudad.
El segmento Accesible / compacto, que representa el 48,2 % de los inmuebles (4.007 registros), es el de mayor presencia y dispersión territorial. Se caracteriza por viviendas de menor tamaño (89,5 m² en promedio), precio mediano de $220 millones y estrato promedio de 4,07.
En la Zona Oeste se observa una presencia importante de los segmentos Premium / alta valorización y Amplio / consolidado, coherente con perfiles de mayor precio, área y estrato. Esta concentración también guarda relación con la diferenciación del Oeste encontrada en el análisis de correspondencia múltiple.
En el Norte se observa una presencia importante del segmento Accesible / compacto, acompañada por inmuebles Residencial medio y Amplio / consolidado. A diferencia del Oeste, el mapa muestra una mayor mezcla de perfiles, lo que evidencia una oferta inmobiliaria más heterogénea en sus características.
Hacia el Sur también se presenta una combinación de los cuatro segmentos, aunque se identifican concentraciones puntuales de Premium / alta valorización y Amplio / consolidado, junto con una presencia amplia del segmento Accesible / compacto.
En conjunto, el mapa confirma que los segmentos tienen una expresión territorial, pero también evidencia que las zonas no son homogéneas: dentro del Norte, Sur u Oeste pueden coexistir inmuebles con características muy distintas de precio, área, estrato y configuración física.
mapa_datos <- vivienda_cluster |>
filter(
is.finite(longitud),
is.finite(latitud)
)
mapa_datos <- mapa_datos |>
slice_sample(
n = min(2500, nrow(mapa_datos))
)
pal_segmento <- colorFactor(
palette = "Set2",
domain = mapa_datos$Segmento
)
leaflet(mapa_datos) |>
addProviderTiles(providers$CartoDB.Positron) |>
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 3,
stroke = FALSE,
fillOpacity = 0.55,
color = ~pal_segmento(Segmento),
popup = ~paste0(
"<b>Segmento:</b> ", Segmento,
"<br><b>Zona:</b> ", zona,
"<br><b>Barrio:</b> ", barrio,
"<br><b>Tipo:</b> ", tipo,
"<br><b>Precio:</b> $", round(preciom, 0), " M",
"<br><b>Área:</b> ", round(areaconst, 0), " m²"
)
) |>
addLegend(
position = "bottomright",
pal = pal_segmento,
values = ~Segmento,
title = "Segmento"
)Los diferentes métodos aplicados muestran que la oferta inmobiliaria de Cali no responde a una única lógica de precio, sino a la combinación de características físicas, nivel socioeconómico y localización. A partir de los resultados se destacan los siguientes hallazgos:
El precio presenta asociaciones importantes con el área construida (r = 0,69), los baños (r = 0,68), los parqueaderos (r = 0,67) y el estrato (r = 0,61). En cambio, el número de habitaciones presenta una relación considerablemente menor (r = 0,27).
Esto muestra que dos propiedades con un número similar de habitaciones pueden tener valores muy diferentes dependiendo de su tamaño, dotación, estrato y ubicación. Por tanto, el precio no debería interpretarse a partir de una sola característica del inmueble.
El PCA muestra que los dos primeros componentes concentran el 78,41 % de la variabilidad. El primero (58,18 %) está explicado principalmente por precio, baños, área y parqueaderos, y representa una dimensión asociada con tamaño, dotación y valor.
El segundo componente (20,23 %) está determinado principalmente por habitaciones y estrato, permitiendo diferenciar configuraciones residenciales que no dependen exclusivamente del precio.
Estos resultados muestran que buena parte de las diferencias entre los inmuebles puede sintetizarse en unas pocas dimensiones, facilitando posteriormente su segmentación e interpretación.
La segmentación evidencia que la oferta no debe entenderse como un conjunto homogéneo. El 48,2 % corresponde al segmento Accesible / compacto, con precio mediano de $220 millones, área promedio de 89,5 m² y estrato promedio de 4,07.
El 29,8 % pertenece al perfil Amplio / consolidado, con precio mediano de $480 millones, área promedio de 177,8 m² y estrato promedio de 5,45.
El 11,6 % corresponde al Residencial medio, caracterizado por una mayor área (299,6 m²) y número de habitaciones (6,38), pese a presentar un estrato promedio menor (3,89).
Finalmente, el segmento Premium / alta valorización representa el 10,4 % de la oferta y reúne los inmuebles de mayor tamaño y dotación, con un precio mediano de $1.149 millones, área promedio de 423,2 m² y estrato promedio de 5,72.
Los perfiles encontrados muestran que estrato y precio no son equivalentes. La posición de un inmueble dentro del mercado surge de la combinación entre tamaño, dotación, estrato y demás características.
La Zona Sur concentra la mayor proporción de todos los segmentos, incluyendo el 62,3 % del Premium / alta valorización y el 60,3 % del Accesible / compacto.
Sin embargo, el Premium también presenta una participación relevante en el Oeste (26,7 %). Por su parte, el segmento Amplio / consolidado se distribuye principalmente entre Sur (52,1 %), Oeste (28,5 %) y Norte (19,3 %), mientras que el Residencial medio presenta una distribución más diversa, con presencia en Sur (49,4 %), Norte (23,0 %) y Oriente (16,8 %).
Esto evidencia que una zona no equivale a un segmento de mercado. Dentro de un mismo territorio pueden coexistir propiedades accesibles, amplias, residenciales y premium; por tanto, la ubicación adquiere mayor valor analítico cuando se combina con las características físicas y económicas del inmueble.
El análisis de correspondencia múltiple identifica asociaciones territoriales específicas. La Zona Oeste presenta una diferenciación marcada y se relaciona con barrios como Aguacatal, Los Cristales, Santa Teresita y Normandía.
La Zona Norte se relaciona principalmente con La Flora, Prados del Norte y Acopi, mientras que la Zona Sur aparece asociada con barrios como Ciudad Jardín, Pance, El Ingenio, El Caney, La Hacienda, Valle del Lili y El Refugio.
Las dos primeras dimensiones del análisis de correspondencia múltiple explican el 17,43 % de la inercia, por lo que estas asociaciones representan los patrones territoriales más visibles, pero no la totalidad de las relaciones presentes en la oferta.
En conjunto, los resultados confirman que zona y barrio aportan una dimensión adicional para comprender cómo se estructura el mercado inmobiliario de la ciudad.
Los resultados permiten pasar de una lectura descriptiva de la oferta a decisiones diferenciadas de portafolio, valoración, captación y comercialización.
La empresa debería administrar su inventario a partir de los cuatro perfiles identificados, en lugar de aplicar una estrategia comercial uniforme.
El segmento Accesible / compacto, al representar casi la mitad de la oferta (48,2 %), puede requerir una estrategia orientada a volumen y rotación. En contraste, el segmento Premium / alta valorización, con una participación del 10,4 %, demanda una gestión más selectiva debido a sus mayores niveles de precio, área y dotación.
Esta segmentación puede utilizarse para establecer metas de captación, rangos de precio, canales comerciales y mensajes diferenciados para cada perfil.
Los resultados de correlación y PCA muestran que utilizar únicamente el precio por metro cuadrado puede simplificar excesivamente la valoración de un inmueble.
El área tiene una relación importante con el precio, pero también intervienen baños, parqueaderos, estrato y ubicación. Por tanto, la valoración debería realizarse prioritariamente frente a propiedades de segmento, zona y características similares.
Este enfoque permitiría construir grupos de comparables más homogéneos y reducir el riesgo de utilizar como referencia propiedades que, aunque estén geográficamente próximas, pertenecen a perfiles de mercado diferentes.
La combinación entre segmentos, zonas y barrios permite identificar territorios donde determinados perfiles tienen una presencia relevante.
Por ejemplo, el segmento Premium / alta valorización presenta una concentración importante en el Sur y una participación destacada en el Oeste, mientras que otros perfiles muestran una distribución territorial más amplia.
Esta información puede orientar la captación hacia sectores compatibles con el tipo de portafolio que la empresa busca fortalecer y, al mismo tiempo, ayudar a identificar zonas o segmentos con baja representación dentro de su inventario.
Los cuatro segmentos permiten construir propuestas comerciales distintas:
La comunicación comercial debería incorporar también el contexto del barrio y la zona, ya que los resultados muestran que la dimensión territorial forma parte de la diferenciación de la oferta.
El análisis realizado caracteriza la oferta publicada, pero no permite explicar directamente el cierre de una venta.
Una siguiente etapa podría incorporar variables como antigüedad del inmueble, valor de administración, estado, amenidades, tiempo publicado, cambios de precio y resultado de la negociación.
Con esta información sería posible avanzar hacia modelos de estimación de precios, detección de inmuebles sobrevalorados o subvalorados y estimación de la probabilidad o tiempo esperado de venta, convirtiendo la segmentación actual en una herramienta continua de inteligencia de mercado.
El análisis de las 8.319 ofertas inmobiliarias finalmente utilizadas muestra que el mercado de vivienda analizado presenta una estructura heterogénea. El precio mediano general de $330 millones constituye una referencia del mercado, pero resulta insuficiente para representar la diversidad encontrada entre propiedades, segmentos y territorios.
Los resultados permiten responder al objetivo central del estudio: el valor y la posición de una vivienda dentro de la oferta no dependen de una sola característica. El precio está relacionado con el área, la dotación y el estrato; el PCA confirma que estas variables forman dimensiones comunes de diferenciación; y el clustering transforma esas diferencias en cuatro perfiles de mercado concretos y diferenciables.
La dimensión territorial añade una segunda lectura. El Sur concentra una parte importante de todos los segmentos, mientras que el Oeste presenta una participación destacada de los perfiles Premium y Amplio / consolidado, y el Norte muestra una composición más diversa. A su vez, el análisis de correspondencias evidencia asociaciones específicas entre zonas y barrios.
Esto confirma que la localización importa, pero debe analizarse conjuntamente con las características físicas y económicas del inmueble, y no como un criterio aislado.
Desde una perspectiva empresarial, el principal aporte del análisis es convertir una base extensa de publicaciones en una estructura útil para la toma de decisiones. Los resultados permiten identificar contra qué propiedades debería compararse un inmueble para valorarlo, qué perfiles conforman la oferta, dónde se concentran y qué atributos pueden destacarse en su comercialización.
En consecuencia, la estrategia no debería centrarse en encontrar una única regla de valoración o comercialización para toda Cali. Los resultados apuntan hacia una gestión del mercado por segmentos y territorios, utilizando comparables adecuados para cada perfil y adaptando la captación, valoración y comunicación comercial a las características de cada grupo.
Finalmente, los resultados deben interpretarse como una radiografía de la oferta publicada en OLX y no necesariamente de las transacciones efectivamente cerradas. Los precios analizados corresponden a precios de oferta y no permiten, por sí solos, establecer el valor final de negociación o la velocidad de venta.
Incorporar información transaccional en futuras actualizaciones permitiría avanzar desde la caracterización y segmentación del mercado hacia herramientas predictivas de mayor valor para la toma de decisiones.
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] paqueteMODELOS_0.1.0 summarytools_1.1.5 gridExtra_2.3.1
## [4] GGally_2.4.0 broom_1.0.13 boot_1.3-31
## [7] leaflet_2.2.3 plotly_4.12.0 kableExtra_1.4.0
## [10] knitr_1.51 cluster_2.1.8.1 factoextra_1.0.7
## [13] FactoMineR_2.13 forcats_1.0.1 scales_1.4.0
## [16] ggplot2_4.0.1 tidyr_1.3.2 dplyr_1.1.4
##
## loaded via a namespace (and not attached):
## [1] tidyselect_1.2.1 viridisLite_0.4.3 farver_2.1.2
## [4] S7_0.2.1 fastmap_1.2.0 lazyeval_0.2.2
## [7] digest_0.6.39 estimability_1.5.1 timechange_0.4.0
## [10] lifecycle_1.0.5 multcompView_0.1-10 magrittr_2.0.4
## [13] compiler_4.5.1 rlang_1.1.7 sass_0.4.10
## [16] tools_4.5.1 corrplot_0.95 yaml_2.3.12
## [19] data.table_1.18.0 ggsignif_0.6.4 labeling_0.4.3
## [22] htmlwidgets_1.6.4 scatterplot3d_0.3-44 plyr_1.8.9
## [25] xml2_1.5.1 RColorBrewer_1.1-3 abind_1.4-8
## [28] withr_3.0.3 purrr_1.2.1 grid_4.5.1
## [31] ggpubr_0.6.2 xtable_1.8-8 emmeans_2.0.1
## [34] MASS_7.3-65 flashClust_1.01-2 cli_3.6.5
## [37] mvtnorm_1.3-3 rmarkdown_2.31 generics_0.1.4
## [40] otel_0.2.0 rstudioapi_0.18.0 reshape2_1.4.5
## [43] httr_1.4.7 cachem_1.1.0 pander_0.6.6
## [46] stringr_1.6.0 matrixStats_1.5.0 base64enc_0.1-6
## [49] vctrs_0.6.5 carData_3.0-6 jsonlite_2.0.0
## [52] car_3.1-5 rapportools_1.2 rstatix_0.7.3
## [55] ggrepel_0.9.6 Formula_1.2-5 systemfonts_1.3.1
## [58] magick_2.9.1 crosstalk_1.2.2 jquerylib_0.1.4
## [61] glue_1.8.0 leaflet.providers_3.0.0 ggstats_0.13.0
## [64] DT_0.34.0 lubridate_1.9.5 stringi_1.8.7
## [67] gtable_0.3.6 tibble_3.3.1 pillar_1.11.1
## [70] htmltools_0.5.9 R6_2.6.1 tcltk_4.5.1
## [73] textshaping_1.0.4 evaluate_1.0.5 lattice_0.22-7
## [76] backports_1.5.1 leaps_3.2 bslib_0.12.0
## [79] Rcpp_1.1.1 svglite_2.2.2 coda_0.19-4.1
## [82] checkmate_2.3.4 xfun_0.55 pkgconfig_2.0.3
Fuente de datos: registros de oferta inmobiliaria obtenidos mediante
web scraping y distribuidos en el paquete paqueteMODELOS.
Análisis elaborado con fines académicos.