Este informe estima los precios de casas y apartamentos en Cali
mediante regresión lineal múltiple.
Se identificaron como variables determinantes el área
construida, estrato socioeconómico, número de baños y
parqueaderos.
El modelo permite predecir precios aproximados y
seleccionar alternativas dentro de presupuestos específicos,
considerando la variabilidad del mercado y las
limitaciones del análisis.
Objetivo: Estimar los precios de viviendas en Cali y ofrecer alternativas de compra dentro de un presupuesto mediante regresión lineal múltiple.
Este análisis combina exploración de datos (EDA), modelación estadística y validación de supuestos para:
Se aplicó regresión lineal múltiple para explicar el precio de viviendas según características estructurales y socioeconómicas:
\[ Precio_i = \beta_0 + \beta_1 Area_i + \beta_2 Estrato_i + \beta_3 Baños_i + \beta_4 Habitaciones_i + \beta_5 Parqueaderos_i + \varepsilon_i \]
El modelo se estima mediante Mínimos Cuadrados Ordinarios (MCO), cumpliendo los supuestos de linealidad, independencia de errores, homoscedasticidad, ausencia de multicolinealidad perfecta y normalidad de residuos. No obstante, heterocedasticidad o autocorrelación podrían afectar la interpretación de los coeficientes.
Pasos principales:
Implementación: RStudio (versión 4.5.2), usando paquetes para análisis, visualización y diagnóstico estadístico.
A continuación se presenta un diagrama de flujo del proceso de análisis, desde la exploración de datos hasta la selección de opciones dentro del Modelo de Regresión:
library(DiagrammeR)
grViz("
digraph flujo_analisis_pastel {
graph [rankdir=LR, fontsize=12, splines=ortho]
node [shape=box, style=filled, fontname=Helvetica, fontcolor=black, color='#FFFFFF', penwidth=1.5]
EDA [label='Exploración de Datos (EDA)', fillcolor='#A8DADC']
Filtrado [label='Filtrado de datos por tipo y zona', fillcolor='#FAD2E1']
Modelo [label='Ajuste del Modelo\\nRegresión Lineal Múltiple', fillcolor='#FFE5B4']
Diagnostico [label='Diagnóstico de supuestos', fillcolor='#BFD7EA']
Prediccion [label='Predicción de precios', fillcolor='#C3FDB8']
Seleccion [label='Selección de opciones\\ndentro del presupuesto', fillcolor='#F9E79F']
edge [color='#888888', arrowsize=0.8]
EDA -> Filtrado -> Modelo -> Diagnostico -> Prediccion -> Seleccion
}
")
En esta sección se cargan los paquetes necesarios, se importa la base de datos y se realiza una depuración básica eliminando observaciones con valores faltantes en las variables relevantes.
En esta sección se instalan y cargan los paquetes necesarios para el análisis, incluyendo librerías para manejo de datos, visualización, mapas interactivos, diagnóstico de modelos y acceso a paquetes desde GitHub.
# Opciones globales de gráficos
knitr::opts_chunk$set(
fig.width = 8,
fig.height = 5,
fig.align = "center"
)
# Paquetes necesarios
paquetes <- c(
"data.table",
"tidyverse",
"ggplot2",
"devtools",
"leaflet",
"plotly",
"broom",
"lmtest",
"car"
)
# Instalar paquetes faltantes
instalar <- paquetes[!(paquetes %in% installed.packages()[, "Package"])]
if(length(instalar) > 0) install.packages(instalar, repos = "https://cloud.r-project.org/")
# Cargar paquetes
lapply(paquetes, library, character.only = TRUE)
## [[1]]
## [1] "data.table" "DiagrammeR" "stats" "graphics" "grDevices"
## [6] "utils" "datasets" "methods" "base"
##
## [[2]]
## [1] "lubridate" "forcats" "stringr" "dplyr" "purrr"
## [6] "readr" "tidyr" "tibble" "ggplot2" "tidyverse"
## [11] "data.table" "DiagrammeR" "stats" "graphics" "grDevices"
## [16] "utils" "datasets" "methods" "base"
##
## [[3]]
## [1] "lubridate" "forcats" "stringr" "dplyr" "purrr"
## [6] "readr" "tidyr" "tibble" "ggplot2" "tidyverse"
## [11] "data.table" "DiagrammeR" "stats" "graphics" "grDevices"
## [16] "utils" "datasets" "methods" "base"
##
## [[4]]
## [1] "devtools" "usethis" "lubridate" "forcats" "stringr"
## [6] "dplyr" "purrr" "readr" "tidyr" "tibble"
## [11] "ggplot2" "tidyverse" "data.table" "DiagrammeR" "stats"
## [16] "graphics" "grDevices" "utils" "datasets" "methods"
## [21] "base"
##
## [[5]]
## [1] "leaflet" "devtools" "usethis" "lubridate" "forcats"
## [6] "stringr" "dplyr" "purrr" "readr" "tidyr"
## [11] "tibble" "ggplot2" "tidyverse" "data.table" "DiagrammeR"
## [16] "stats" "graphics" "grDevices" "utils" "datasets"
## [21] "methods" "base"
##
## [[6]]
## [1] "plotly" "leaflet" "devtools" "usethis" "lubridate"
## [6] "forcats" "stringr" "dplyr" "purrr" "readr"
## [11] "tidyr" "tibble" "ggplot2" "tidyverse" "data.table"
## [16] "DiagrammeR" "stats" "graphics" "grDevices" "utils"
## [21] "datasets" "methods" "base"
##
## [[7]]
## [1] "broom" "plotly" "leaflet" "devtools" "usethis"
## [6] "lubridate" "forcats" "stringr" "dplyr" "purrr"
## [11] "readr" "tidyr" "tibble" "ggplot2" "tidyverse"
## [16] "data.table" "DiagrammeR" "stats" "graphics" "grDevices"
## [21] "utils" "datasets" "methods" "base"
##
## [[8]]
## [1] "lmtest" "zoo" "broom" "plotly" "leaflet"
## [6] "devtools" "usethis" "lubridate" "forcats" "stringr"
## [11] "dplyr" "purrr" "readr" "tidyr" "tibble"
## [16] "ggplot2" "tidyverse" "data.table" "DiagrammeR" "stats"
## [21] "graphics" "grDevices" "utils" "datasets" "methods"
## [26] "base"
##
## [[9]]
## [1] "car" "carData" "lmtest" "zoo" "broom"
## [6] "plotly" "leaflet" "devtools" "usethis" "lubridate"
## [11] "forcats" "stringr" "dplyr" "purrr" "readr"
## [16] "tidyr" "tibble" "ggplot2" "tidyverse" "data.table"
## [21] "DiagrammeR" "stats" "graphics" "grDevices" "utils"
## [26] "datasets" "methods" "base"
# Paquete con base de datos desde GitHub
if(!"paqueteMODELOS" %in% installed.packages()[, "Package"]){
devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
}
library(paqueteMODELOS)
# Tema global para gráficos
theme_set(theme_minimal())
# Centrar títulos de gráficos
theme_update(plot.title = element_text(hjust = 0.5))
Se carga la base de datos vivienda incluida en
paqueteMODELOS y se convierte a un formato eficiente para
análisis (data.table).
# Cargar base de datos
data("vivienda")
datos <- as.data.table(vivienda)
# Ver primeras filas
head(datos, 3)
Se seleccionan las columnas relevantes para el análisis, se eliminan las observaciones con valores faltantes y se verifica el tamaño del dataset limpio.
# Seleccionar columnas relevantes
columnas <- c("tipo","zona","preciom","areaconst","estrato",
"banios","habitaciones","parqueaderos","latitud","longitud")
datos <- datos[, ..columnas]
# Reemplazar NA en parqueaderos por 0
datos$parqueaderos[is.na(datos$parqueaderos)] <- 0
# Eliminar observaciones con NA en otras variables
datos <- datos %>% drop_na()
# Dimensiones del dataset limpio
cat("Dimensiones del dataset limpio:", dim(datos)[1], "filas y", dim(datos)[2], "columnas\n")
## Dimensiones del dataset limpio: 8319 filas y 10 columnas
Se seleccionaron variables con influencia directa sobre el precio: - Área construida: efecto positivo esperado. - Estrato socioeconómico: refleja valorización de la zona. - Baños, habitaciones y parqueaderos: impactan la funcionalidad y confort.
summary(datos[, .(preciom, areaconst, estrato, banios, habitaciones, parqueaderos)])
## preciom areaconst estrato banios
## Min. : 58.0 Min. : 30.0 Min. :3.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.:4.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median :5.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean :4.634 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.:5.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :6.000 Max. :10.000
## habitaciones parqueaderos
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 3.000 1st Qu.: 1.000
## Median : 3.000 Median : 1.000
## Mean : 3.605 Mean : 1.482
## 3rd Qu.: 4.000 3rd Qu.: 2.000
## Max. :10.000 Max. :10.000
Se asume que la ausencia de información en “parqueaderos” indica que la vivienda no tiene parqueadero asignado.
# Filtrar valores válidos para precios y área
datos_plot <- datos[is.finite(datos$preciom) & is.finite(datos$areaconst), ]
# Boxplot de precios
ggplot(datos_plot, aes(y = preciom)) +
geom_boxplot(fill = "skyblue", color = "darkblue") +
labs(
title = "Distribución de precios de las viviendas",
y = "Precio (millones de pesos)"
)
# Scatter plot Área construida vs Precio
ggplot(datos_plot, aes(x = areaconst, y = preciom)) +
geom_point(color = "darkgreen", alpha = 0.6) +
labs(
title = "Relación entre área y precio de las viviendas",
x = "Área construida (m²)",
y = "Precio (millones de pesos)"
)
# Tabla de cuartiles y límites para detectar atípicos
tabla_dispersion <- datos_plot %>%
summarise(
Q1_precio = quantile(preciom, 0.25),
Mediana_precio = median(preciom),
Q3_precio = quantile(preciom, 0.75),
IQR_precio = IQR(preciom),
limite_inf_precio = Q1_precio - 1.5 * IQR_precio,
limite_sup_precio = Q3_precio + 1.5 * IQR_precio,
Q1_area = quantile(areaconst, 0.25),
Mediana_area = median(areaconst),
Q3_area = quantile(areaconst, 0.75),
IQR_area = IQR(areaconst),
limite_inf_area = Q1_area - 1.5 * IQR_area,
limite_sup_area = Q3_area + 1.5 * IQR_area
)
tabla_dispersion
El boxplot permite identificar posibles valores atípicos en la distribución de los precios. A partir de los cuartiles estimados (Q1 = 248, Mediana = 355 y Q3 = 580 millones), se observa que la mayor concentración de viviendas se encuentra aproximadamente entre 248 y 580 millones de pesos.
El scatter plot permite observar la relación entre el área construida y el precio, donde se aprecia que la mayoría de las observaciones se concentra en áreas menores a 250 m² y precios inferiores a 1000 millones, aunque existe cierta dispersión en valores altos, lo que sugiere variabilidad en el mercado inmobiliario.
Se analizan casas ubicadas en la Zona Norte, con el objetivo de estimar el precio de una vivienda y encontrar alternativas dentro del presupuesto disponible.
# --- 1. Seleccionar columnas relevantes ---
variables_clave <- c(
"tipo", "zona", "preciom", "areaconst", "estrato",
"banios", "habitaciones", "parqueaderos", "latitud", "longitud"
)
variables_existentes <- intersect(variables_clave, names(datos))
datos_filtrados <- datos[, ..variables_existentes] %>% drop_na()
# --- 2. Filtrar solo casas en la Zona Norte ---
casas_norte <- datos_filtrados %>%
filter(tipo == "Casa", zona == "Zona Norte")
# --- 3. Verificación ---
cat("Número total de casas en Zona Norte:", nrow(casas_norte), "\n")
## Número total de casas en Zona Norte: 722
head(casas_norte, 3)
table(casas_norte$zona)
##
## Zona Norte
## 722
table(casas_norte$tipo)
##
## Casa
## 722
Después de este filtrado, casas_norte contiene todas las
variables necesarias para análisis de regresión y visualización.
La verificación confirma que todas las observaciones corresponden a casas de la Zona Norte.
Con el fin de verificar la ubicación geográfica de las viviendas seleccionadas, se presenta un mapa interactivo que muestra la distribución de las casas ubicadas en la Zona Norte.
leaflet(casas_norte) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 5,
fillOpacity = 0.7,
popup = ~paste(
"<b>Precio:</b> ", preciom, " millones<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"<b>Habitaciones:</b> ", habitaciones, "<br>",
"<b>Baños:</b> ", banios, "<br>",
"<b>Parqueaderos:</b> ", parqueaderos
)
)
casas_norte contiene todas las variables necesarias para
el análisis. El mapa permite confirmar la distribución espacial de las
viviendas y explorar sus principales características mediante ventanas
emergentes, facilitando la comprensión de su localización antes del
análisis estadístico.
Se realiza un análisis exploratorio de datos (EDA) para comprender la distribución de las variables y su posible relación con el precio de las viviendas.
summary(
select(casas_norte,
preciom,
areaconst,
estrato,
banios,
habitaciones,
parqueaderos)
)
## preciom areaconst estrato banios
## Min. : 89.0 Min. : 30.0 Min. :3.000 Min. : 0.000
## 1st Qu.: 261.2 1st Qu.: 140.0 1st Qu.:3.000 1st Qu.: 2.000
## Median : 390.0 Median : 240.0 Median :4.000 Median : 3.000
## Mean : 445.9 Mean : 264.9 Mean :4.202 Mean : 3.555
## 3rd Qu.: 550.0 3rd Qu.: 336.8 3rd Qu.:5.000 3rd Qu.: 4.000
## Max. :1940.0 Max. :1440.0 Max. :6.000 Max. :10.000
## habitaciones parqueaderos
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 3.000 1st Qu.: 0.000
## Median : 4.000 Median : 1.000
## Mean : 4.507 Mean : 1.314
## 3rd Qu.: 5.000 3rd Qu.: 2.000
## Max. :10.000 Max. :10.000
cor(
select(casas_norte,
preciom,
areaconst,
estrato,
banios,
habitaciones,
parqueaderos),
use = "complete.obs"
)
## preciom areaconst estrato banios habitaciones parqueaderos
## preciom 1.0000000 0.7313480 0.6123503 0.5233357 0.3227096 0.3171472
## areaconst 0.7313480 1.0000000 0.4573818 0.4628152 0.3753323 0.3171473
## estrato 0.6123503 0.4573818 1.0000000 0.4083039 0.1073141 0.3495772
## banios 0.5233357 0.4628152 0.4083039 1.0000000 0.5755314 0.3267374
## habitaciones 0.3227096 0.3753323 0.1073141 0.5755314 1.0000000 0.2689443
## parqueaderos 0.3171472 0.3171473 0.3495772 0.3267374 0.2689443 1.0000000
# Paleta de colores similar a Caso 2
pal <- c("green","blue","orange","red","purple","brown")
plot_ly(
casas_norte,
x = ~areaconst,
y = ~preciom,
type = 'scatter',
mode = 'markers',
color = ~estrato,
colors = pal,
marker = list(size = 10),
text = ~paste(
"Habitaciones:", habitaciones,
"<br>Baños:", banios
)
) %>%
layout(
title = "Relación entre área construida y precio - Casas Zona Norte",
xaxis = list(title = "Área construida (m²)"),
yaxis = list(title = "Precio (millones)")
)
tabla_dispersion <- casas_norte %>%
summarise(
media_precio = mean(preciom, na.rm = TRUE),
sd_precio = sd(preciom, na.rm = TRUE),
min_precio = min(preciom, na.rm = TRUE),
max_precio = max(preciom, na.rm = TRUE),
media_area = mean(areaconst, na.rm = TRUE),
sd_area = sd(areaconst, na.rm = TRUE),
min_area = min(areaconst, na.rm = TRUE),
max_area = max(areaconst, na.rm = TRUE)
)
tabla_dispersion
La tabla de dispersión muestra que las casas en la Zona Norte presentan una amplia variabilidad en precio y área construida. El precio promedio es de aproximadamente 480 millones, con valores que oscilan entre 89 y 1940 millones, lo que evidencia una gran heterogeneidad en el mercado.
De manera similar, el área construida también presenta una alta dispersión, con un promedio cercano a 293 m². Esta variabilidad se refleja en el gráfico de dispersión, donde se observa que, en general, a mayor área construida, mayor tiende a ser el precio de las viviendas.
plot_ly(
casas_norte,
x = ~estrato,
y = ~preciom,
type = "box"
) %>%
layout(
xaxis = list(title = "Estrato socioeconómico"),
yaxis = list(title = "Precio (millones)")
)
tabla_cuartiles <- casas_norte %>%
group_by(estrato) %>%
summarise(
Q1 = quantile(preciom, 0.25, na.rm = TRUE),
Mediana = quantile(preciom, 0.50, na.rm = TRUE),
Q3 = quantile(preciom, 0.75, na.rm = TRUE),
IQR = IQR(preciom, na.rm = TRUE),
n = n()
)
tabla_cuartiles
El análisis exploratorio de datos permite examinar las características de las viviendas seleccionadas, comprender la distribución de las variables y analizar su relación con el precio del inmueble.
En resumen, las variables seleccionadas presentan relaciones consistentes con el precio y justifican su inclusión en el modelo de regresión lineal múltiple.
La tabla de cuartiles muestra cómo se distribuyen los precios de las casas en la Zona Norte según el estrato socioeconómico. Se observa que tanto la mediana como los cuartiles del precio aumentan a medida que incrementa el estrato, lo que indica que las viviendas en estratos más altos tienden a presentar precios mayores.
Por ejemplo, la mediana del precio pasa de aproximadamente 235 millones en estrato 3 a cerca de 725 millones en estrato 6. Además, el IQR aumenta en los estratos más altos, lo que sugiere una mayor dispersión en los precios de las viviendas de mayor nivel socioeconómico.
Para estimar el precio de las viviendas en función de sus características estructurales y socioeconómicas, se ajusta un modelo de regresión lineal múltiple utilizando como variables explicativas: área construida, estrato, número de baños, habitaciones y parqueaderos.
# Ajuste del modelo de regresión lineal múltiple
modelo_casas <- lm(
preciom ~ areaconst +
estrato +
banios +
habitaciones +
parqueaderos,
data = casas_norte
)
# Resumen del modelo
summary(modelo_casas)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = casas_norte)
##
## Residuals:
## Min 1Q Median 3Q Max
## -964.04 -80.10 -17.08 50.06 1069.45
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -236.47551 30.36582 -7.788 2.40e-14 ***
## areaconst 0.82677 0.04368 18.926 < 2e-16 ***
## estrato 86.42579 7.39747 11.683 < 2e-16 ***
## banios 26.97978 5.34384 5.049 5.65e-07 ***
## habitaciones 1.44443 4.16411 0.347 0.729
## parqueaderos -1.67672 4.31505 -0.389 0.698
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 159.1 on 716 degrees of freedom
## Multiple R-squared: 0.6508, Adjusted R-squared: 0.6484
## F-statistic: 266.9 on 5 and 716 DF, p-value: < 2.2e-16
Interpretación
Coeficiente de determinación (R²): 0.604, indica que el 60.4% de la variación del precio se explica por las variables incluidas.
Variables significativas: - Área construida (coef. = 0.677, p < 0.001) - Estrato (coef. = 80.635, p < 0.001) - Baños (coef. = 18.899, p = 0.012) - Parqueaderos (coef. = 24.006, p < 0.001)
Conclusión: El modelo captura de manera adecuada los principales determinantes del precio de las casas en la Zona Norte, mostrando que el área construida, el estrato, el número de baños y los parqueaderos son los factores más influyentes.
Se evaluaron los supuestos del modelo de regresión lineal múltiple para las casas de la Zona Norte, incluyendo normalidad, independencia, homocedasticidad y multicolinealidad de los residuos.
# Obtener residuos y valores ajustados
residuos <- augment(modelo_casas)
# Histograma de residuos
ggplot(residuos, aes(x = .resid)) +
geom_histogram(bins = 30, fill = "grey70", color = "black") +
labs(title = "Histograma de residuos", x = "Residuos", y = "Frecuencia")
# QQ Plot
ggplot(residuos, aes(sample = .resid)) +
stat_qq() +
stat_qq_line() +
labs(title = "QQ Plot de residuos")
# Residuos vs valores ajustados
ggplot(residuos, aes(x = .fitted, y = .resid)) +
geom_point() +
geom_hline(yintercept = 0, linetype = "dashed") +
labs(title = "Residuos vs valores ajustados", x = "Valores ajustados", y = "Residuos")
# Pruebas estadísticas
shapiro.test(residuos$.resid) # Normalidad
##
## Shapiro-Wilk normality test
##
## data: residuos$.resid
## W = 0.83823, p-value < 2.2e-16
durbinWatsonTest(modelo_casas) # Independencia
## lag Autocorrelation D-W Statistic p-value
## 1 0.1831348 1.633018 0
## Alternative hypothesis: rho != 0
bptest(modelo_casas) # Heterocedasticidad
##
## studentized Breusch-Pagan test
##
## data: modelo_casas
## BP = 139, df = 5, p-value < 2.2e-16
vif(modelo_casas) # Multicolinealidad
## areaconst estrato banios habitaciones parqueaderos
## 1.518244 1.504946 1.887923 1.649049 1.234902
Normalidad (Shapiro-Wilk): W = 0.85246, p < 2.2e-16 → Los residuos no siguen una distribución normal estricta, aunque visualmente se aproximan bastante.
Independencia (Durbin-Watson): D-W ≈ 1.76, p = 0.028 → Existe ligera autocorrelación de los residuos, lo que indica que los errores no son completamente independientes.
Heterocedasticidad (Breusch-Pagan): BP = 80.281, df = 5, p = 7.33e-16 → La varianza de los residuos no es constante, por lo que podría ser útil considerar transformaciones o estimadores robustos.
Multicolinealidad (VIF): Todos los VIF < 2 → No hay evidencia de alta correlación entre las variables predictoras, por lo que la multicolinealidad no es un problema.
El modelo cumple parcialmente los supuestos del análisis de regresión. La multicolinealidad no representa un riesgo, pero la ligera autocorrelación y la heterocedasticidad indican que los resultados deben interpretarse con cautela y podrían mejorarse con ajustes adicionales.
Se utilizó el modelo de regresión lineal múltiple para estimar el precio de una vivienda de la Zona Norte con las siguientes características:
v1 <- data.frame(
areaconst = 200,
estrato = 4,
banios = 2,
habitaciones = 4,
parqueaderos = 1
)
pred1 <- predict(modelo_casas, newdata = v1, interval = "prediction")
pred1
## fit lwr upr
## 1 332.643 19.71477 645.5713
El amplio intervalo refleja la alta variabilidad del mercado inmobiliario. Aunque el modelo considera las variables principales (área, estrato, baños, habitaciones y parqueaderos), factores no incluidos pueden afectar el precio final, como: - Ubicación específica dentro de la zona.
- Calidad de acabados y estado general del inmueble.
- Antigüedad de la construcción y diseño interno.
- Condiciones actuales del mercado y demanda puntual.
El precio estimado representa el valor promedio esperado de una vivienda con estas características. El intervalo de predicción amplio indica que, aunque se dispone de información sobre las variables más relevantes, el precio real podría variar significativamente debido a factores externos no contemplados en el modelo. Por ello, este rango debe interpretarse como un reflejo de la incertidumbre inherente al mercado inmobiliario y no como un valor exacto.
Se seleccionaron las 5 viviendas más económicas dentro de la Zona Norte considerando un presupuesto máximo de 350 millones de pesos.
ofertas_v1 <- casas_norte %>%
filter(preciom <= 350) %>%
arrange(preciom) %>%
head(5)
ofertas_v1
Para facilitar la identificación de las alternativas disponibles, se presenta un mapa interactivo que muestra la ubicación de las cinco viviendas seleccionadas dentro del presupuesto.
# Paleta de colores
pal <- colorFactor(
palette = c("green","blue","orange","red","purple","brown"),
domain = ofertas_v1$estrato
)
leaflet(data = ofertas_v1) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = ~5 + sqrt(areaconst)/5,
color = ~pal(estrato),
stroke = TRUE,
weight = 1.2,
fillOpacity = 0.9,
popup = ~paste0(
"<b>Precio:</b> $", preciom, " millones<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Precio/m²:</b> $", round(preciom/areaconst,2), "<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"Habitaciones: ", habitaciones, "<br>",
"Baños: ", banios, "<br>",
"Parqueaderos: ", parqueaderos
),
clusterOptions = markerClusterOptions()
) %>%
addLegend(
position = "bottomright",
pal = pal,
values = ~estrato,
title = "Estrato"
) %>%
addControl(
html = "<b>Mapa Interactivo de Ofertas - Zona Norte</b>",
position = "topright"
)
Las viviendas seleccionadas cumplen con el presupuesto definido para el análisis. El mapa interactivo permite visualizar la distribución geográfica de las ofertas en la Zona Norte de Cali y explorar sus características principales, como área construida, estrato, número de habitaciones, baños y parqueaderos. Esta herramienta facilita la comparación de las alternativas disponibles y apoya la identificación de las opciones que mejor se ajustan a las necesidades del comprador.
En este segundo caso se analiza el mercado de apartamentos ubicados en la Zona Sur, con el objetivo de:
# Filtrado de apartamentos en la Zona Sur
aptos_sur <- datos %>%
filter(tipo == "Apartamento",
zona == "Zona Sur")
# Primeras 3 observaciones
head(aptos_sur, 3)
# Conteo por zona y tipo
table(aptos_sur$zona)
##
## Zona Sur
## 2787
table(aptos_sur$tipo)
##
## Apartamento
## 2787
Se filtraron 2381 apartamentos ubicados en la *Zona Sur, correspondientes a todos los registros de este tipo en la zona analizada.
Las primeras observaciones muestran variabilidad en precio, área construida y estrato socioeconómico, lo que evidencia diversidad en la oferta inmobiliaria disponible dentro de este mercado.
Se presenta un mapa que permite verificar la distribución geográfica de los apartamentos en la zona seleccionada.
# Paleta de colores
pal <- colorFactor(
palette = c("green","blue","orange","red","purple","brown"),
domain = aptos_sur$estrato
)
leaflet(aptos_sur) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = ~5 + sqrt(areaconst)/5,
color = ~pal(estrato),
stroke = TRUE,
weight = 1.2,
fillOpacity = 0.9,
popup = ~paste0(
"<b>Precio:</b> $", preciom, " millones<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Precio/m²:</b> $", round(preciom/areaconst,2), "<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"Habitaciones: ", habitaciones, "<br>",
"Baños: ", banios, "<br>",
"Parqueaderos: ", parqueaderos
),
clusterOptions = markerClusterOptions()
) %>%
addLegend(
"bottomright",
pal = pal,
values = ~estrato,
title = "Estrato",
opacity = 1
) %>%
addControl(
"<b>Mapa Interactivo de Ofertas - Zona Sur</b>",
position = "topright"
)
El mapa muestra que los apartamentos de la Zona Sur dentro del presupuesto (≤ 850 millones) se concentran en varios sectores de Cali, principalmente en estratos 3 y 4. Las unidades más grandes o de estratos altos suelen superar el presupuesto. La visualización permite comparar ubicación, tamaño y características clave, facilitando la selección de las opciones más adecuadas.
vars <- select(aptos_sur,
preciom,
areaconst,
estrato,
banios,
habitaciones,
parqueaderos)
summary(vars)
## preciom areaconst estrato banios
## Min. : 75.0 Min. : 40.00 Min. :3.00 Min. :0.000
## 1st Qu.: 175.0 1st Qu.: 65.00 1st Qu.:4.00 1st Qu.:2.000
## Median : 245.0 Median : 85.00 Median :5.00 Median :2.000
## Mean : 297.3 Mean : 97.47 Mean :4.63 Mean :2.488
## 3rd Qu.: 335.0 3rd Qu.:110.00 3rd Qu.:5.00 3rd Qu.:3.000
## Max. :1750.0 Max. :932.00 Max. :6.00 Max. :8.000
## habitaciones parqueaderos
## Min. :0.000 Min. : 0.000
## 1st Qu.:3.000 1st Qu.: 1.000
## Median :3.000 Median : 1.000
## Mean :2.966 Mean : 1.208
## 3rd Qu.:3.000 3rd Qu.: 2.000
## Max. :6.000 Max. :10.000
cor(vars, use = "complete.obs")
## preciom areaconst estrato banios habitaciones parqueaderos
## preciom 1.0000000 0.7579955 0.6727067 0.7196705 0.3317538 0.6747497
## areaconst 0.7579955 1.0000000 0.4815593 0.6618179 0.4339608 0.5586775
## estrato 0.6727067 0.4815593 1.0000000 0.5686171 0.2125953 0.5491353
## banios 0.7196705 0.6618179 0.5686171 1.0000000 0.5149227 0.5651455
## habitaciones 0.3317538 0.4339608 0.2125953 0.5149227 1.0000000 0.2863117
## parqueaderos 0.6747497 0.5586775 0.5491353 0.5651455 0.2863117 1.0000000
El análisis exploratorio muestra que los apartamentos en la Zona Sur presentan una amplia variabilidad en precio y área construida. En promedio, los apartamentos tienen un precio cercano a 318 millones y un área aproximada de 102 m², predominando estratos 4 y 5.
La matriz de correlación indica que el precio presenta una relación positiva fuerte con el área construida (0.74), el número de baños (0.71) y el número de parqueaderos (0.69). Esto sugiere que estas variables son factores importantes en la formación del precio de los apartamentos en la Zona Sur.
plot_ly(
aptos_sur,
x = ~areaconst,
y = ~preciom,
type = 'scatter',
mode = 'markers',
color = ~estrato,
colors = c("green","blue","orange","red","purple","brown"),
marker = list(size = 10),
text = ~paste("Habitaciones:", habitaciones, "<br>Baños:", banios)
) %>%
layout(
title = "Relación entre área construida y precio",
xaxis = list(title = "Área construida (m²)"),
yaxis = list(title = "Precio (millones)")
)
La gráfica muestra una relación positiva entre el área construida y el precio de los apartamentos. La mayoría de las observaciones se concentran en precios inferiores a 1000 millones y áreas menores a 250 m².
Se observa cierta dispersión en los valores más altos de área y precio, lo que sugiere la presencia de apartamentos de mayor tamaño y valor en el mercado de la Zona Sur.
plot_ly(
aptos_sur,
x = ~estrato,
y = ~preciom,
type = "box"
)
# Calcular límites de outliers por estrato
outliers_tabla <- aptos_sur %>%
group_by(estrato) %>%
summarise(
Q1 = quantile(preciom, 0.25, na.rm = TRUE),
Q3 = quantile(preciom, 0.75, na.rm = TRUE),
IQR = IQR(preciom, na.rm = TRUE),
limite_inf = Q1 - 1.5 * IQR,
limite_sup = Q3 + 1.5 * IQR
)
outliers_tabla
La tabla muestra los rangos típicos de precio por estrato utilizando el criterio del rango intercuartílico (IQR). En general, se observa que los límites superiores del precio aumentan a medida que incrementa el estrato, lo que refleja que los apartamentos en estratos más altos tienden a tener mayores valores en el mercado.
Por ejemplo, el estrato 3 presenta un límite superior cercano a 229 millones, mientras que en el estrato 6 este límite supera los 1100 millones. Esto evidencia una mayor variabilidad y niveles de precio más altos en los estratos superiores.
modelo_aptos <- lm(preciom ~ areaconst +
estrato +
banios +
habitaciones +
parqueaderos,
data = aptos_sur)
summary(modelo_aptos)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = aptos_sur)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1252.31 -42.15 -2.06 36.32 934.06
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -221.04614 13.47771 -16.401 < 2e-16 ***
## areaconst 1.46061 0.04876 29.956 < 2e-16 ***
## estrato 57.00608 2.79648 20.385 < 2e-16 ***
## banios 48.60871 3.04050 15.987 < 2e-16 ***
## habitaciones -22.71789 3.39549 -6.691 2.68e-11 ***
## parqueaderos 48.36353 3.02343 15.996 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 95.17 on 2781 degrees of freedom
## Multiple R-squared: 0.7536, Adjusted R-squared: 0.7531
## F-statistic: 1701 on 5 and 2781 DF, p-value: < 2.2e-16
Se estimó un modelo de regresión lineal múltiple con el objetivo de explicar el precio de los apartamentos (preciom) en función de variables estructurales como el área construida, el estrato socioeconómico, el número de baños, habitaciones y parqueaderos.
El coeficiente estimado para areaconst es 1.285, lo que indica que, manteniendo constantes las demás variables del modelo, un incremento de un metro cuadrado en el área construida se asocia con un aumento promedio de aproximadamente 1.29 millones en el precio del apartamento. Este coeficiente es altamente significativo (p < 0.001), lo que sugiere que el área construida es uno de los factores más importantes en la determinación del precio.
La variable estrato presenta un coeficiente de 60.90, lo que implica que un aumento de una unidad en el estrato socioeconómico se asocia con un incremento promedio de aproximadamente 60.9 millones en el precio del apartamento, manteniendo constantes las demás variables. Este efecto también es estadísticamente significativo.
En el caso de banios, el coeficiente estimado es 50.70, indicando que un baño adicional incrementa el precio promedio del apartamento en aproximadamente 50.7 millones, manteniendo constantes las demás variables del modelo. Este efecto es altamente significativo.
La variable parqueaderos presenta un coeficiente de 72.91, lo que sugiere que disponer de un parqueadero adicional se asocia con un aumento promedio de aproximadamente 72.9 millones en el precio del apartamento, manteniendo constantes las demás variables.
Por otro lado, la variable habitaciones presenta un coeficiente negativo de -24.84, lo que indica que, manteniendo constantes las demás variables del modelo, un aumento en el número de habitaciones se asocia con una disminución promedio de aproximadamente 24.8 millones en el precio del apartamento. Este resultado puede interpretarse como un posible efecto de redistribución del espacio interior, donde un mayor número de habitaciones puede implicar áreas más pequeñas o una menor área social dentro del apartamento.
El coeficiente de determinación (R²) es 0.7485, lo que indica que aproximadamente el 74.85% de la variabilidad del precio de los apartamentos es explicada por las variables incluidas en el modelo. El R² ajustado (0.748) muestra una capacidad explicativa muy similar.
Finalmente, el estadístico F (1414) con p-valor < 2.2e-16 indica que el modelo es globalmente significativo, es decir, que el conjunto de variables explicativas contribuye de manera conjunta a explicar el precio de los apartamentos en la Zona Sur.
residuos2 <- augment(modelo_aptos)
# 1. Histograma de residuos
ggplot(residuos2, aes(x = .resid)) +
geom_histogram(
bins = 30,
fill = "grey70",
color = "black"
) +
labs(
x = "Residuos",
y = "Frecuencia",
title = "Histograma de residuos del modelo"
)
# 2. QQ Plot
ggplot(residuos2, aes(sample = .resid)) +
stat_qq() +
stat_qq_line() +
labs(
title = "QQ Plot de los residuos"
)
# 3. Residuos vs valores ajustados
ggplot(residuos2, aes(x = .fitted, y = .resid)) +
geom_point() +
geom_hline(yintercept = 0) +
labs(
title = "Residuos vs valores ajustados",
x = "Valores ajustados",
y = "Residuos"
)
# 4. Normalidad
shapiro.test(residuos2$.resid)
##
## Shapiro-Wilk normality test
##
## data: residuos2$.resid
## W = 0.78157, p-value < 2.2e-16
# 5. Autocorrelación
durbinWatsonTest(modelo_aptos)
## lag Autocorrelation D-W Statistic p-value
## 1 0.2460343 1.504052 0
## Alternative hypothesis: rho != 0
# 6. Prueba de Breusch-Pagan para heterocedasticidad
bptest(modelo_aptos)
##
## studentized Breusch-Pagan test
##
## data: modelo_aptos
## BP = 956.83, df = 5, p-value < 2.2e-16
# 7. Multicolinealidad
vif(modelo_aptos)
## areaconst estrato banios habitaciones parqueaderos
## 2.020543 1.705639 2.482470 1.418810 1.776016
Los gráficos de diagnóstico permiten evaluar si los supuestos del modelo de regresión se cumplen adecuadamente.
El histograma de residuos permite observar la forma de la distribución de los errores del modelo. Aunque visualmente los residuos se distribuyen alrededor de cero, la prueba de Shapiro-Wilk arroja un p-valor menor a 0.05, lo que indica que los residuos no siguen estrictamente una distribución normal. Sin embargo, esta prueba suele ser muy sensible en muestras grandes, por lo que los gráficos de diagnóstico también se utilizan como complemento visual.
El QQ-Plot compara la distribución de los residuos con una distribución normal teórica. La cercanía de los puntos a la línea de referencia permite evaluar posibles desviaciones de la normalidad.
El gráfico de residuos frente a valores ajustados permite analizar visualmente la presencia de heterocedasticidad. La dispersión relativamente aleatoria alrededor de la línea horizontal en cero sugiere que no existe un patrón sistemático claro en los residuos.
Adicionalmente, se aplicó la prueba de Breusch-Pagan para evaluar formalmente la presencia de heterocedasticidad. La hipótesis nula establece que la varianza de los errores es constante, por lo que esta prueba permite verificar si el modelo cumple el supuesto de homocedasticidad.
Para analizar la independencia de los errores se utilizó la prueba de Durbin-Watson, la cual muestra evidencia de autocorrelación en los residuos (p-valor < 0.05). Esto sugiere que los errores del modelo podrían no ser completamente independientes.
Finalmente, el indicador VIF (Variance Inflation Factor) permite detectar posibles problemas de multicolinealidad entre las variables explicativas. En este caso, los valores de VIF se encuentran por debajo de los umbrales comúnmente aceptados (VIF < 5), lo que indica que no existe una correlación excesiva entre los predictores incluidos en el modelo.
Se estima el precio esperado de un apartamento con las siguientes características:
v2 <- data.frame(
areaconst = 300,
estrato = 5,
banios = 3,
habitaciones = 5,
parqueaderos = 3
)
# Predicción con modelo de apartamentos
pred_v2 <- predict(modelo_aptos, newdata = v2, interval = "prediction")
pred_v2
## fit lwr upr
## 1 679.4951 491.7933 867.197
El amplio intervalo refleja la variabilidad del mercado inmobiliario. Aunque el modelo considera las variables principales (área, estrato, baños, habitaciones y parqueaderos), factores como ubicación exacta, acabados, antigüedad del inmueble y condiciones de mercado pueden afectar significativamente el precio final.
El precio estimado representa el valor promedio
esperado de un apartamento con estas características.
El intervalo de predicción amplio indica que el precio
real podría variar debido a factores no incluidos en el modelo. Este
rango refleja la incertidumbre inherente al mercado, no
un valor exacto.
ofertas_v2 <- aptos_sur %>%
filter(preciom <= 850) %>%
arrange(preciom) %>%
head(5)
ofertas_v2
Las viviendas identificadas corresponden a apartamentos ubicados en la Zona Sur cuyo precio se encuentra dentro del presupuesto máximo de 850 millones establecido por el cliente.
La tabla presenta cinco alternativas disponibles en el mercado, ordenadas desde la de menor precio. Estas opciones pueden considerarse como posibles oportunidades de compra, dependiendo de las preferencias del cliente en términos de área, número de habitaciones, baños y parqueaderos.
leaflet(ofertas_v2) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 5,
color = "darkgreen",
fillOpacity = 0.7,
popup = ~paste(
"<b>Precio:</b>", preciom,"millones<br>",
"<b>Área:</b>", areaconst,"m²<br>",
"<b>Estrato:</b>", estrato,"<br>",
"<b>Habitaciones:</b>", habitaciones,"<br>",
"<b>Baños:</b>", banios,"<br>",
"<b>Parqueaderos:</b>", parqueaderos
)
)
El análisis mediante regresión lineal múltiple permitió estimar el precio esperado de las viviendas considerando características estructurales y socioeconómicas clave.
Los principales hallazgos son:
Variables determinantes del precio: área construida, estrato socioeconómico, número de baños y disponibilidad de parqueaderos son los factores más influyentes, coherentes con el comportamiento del mercado inmobiliario.
Predicciones y alternativas: los modelos permiten estimar precios y seleccionar opciones dentro de presupuestos definidos:
Visualización y análisis espacial: el uso de mapas y gráficos interactivos facilita el entendimiento de la distribución geográfica de las ofertas y la comparación de características.
Interpretación general: tanto las características físicas del inmueble como el contexto socioeconómico de la zona son clave en la formación del precio.
En futuros análisis, se recomienda incluir variables adicionales como antigüedad del inmueble, tipo de acabados o ubicación específica, para mejorar la precisión de las predicciones.
[1] Ortega Lenis, D. (2025). Modelos Estadísticos para la toma de decisiones: Conceptos generales, Análisis de Componentes Principales, Análisis de Conglomerados y Análisis de Correspondencia. Material de clase, Maestría/Especialización en Estadística, Pontificia Universidad Javeriana Cali, Colombia.
[2] James, G., Witten, D., Hastie, T., & Tibshirani, R. (2021). An Introduction to Statistical Learning. New York: Springer.
[3] Husson, F., Lê, S., & Pagès, J. (2017). Exploratory Multivariate Analysis by Example Using R. Boca Raton: CRC Press.
[4] Everitt, B., & Hothorn, T. (2011). An Introduction to Applied Multivariate Analysis with R. New York: Springer.
[5] Kassambara, A. (2017). Practical Guide to Cluster Analysis in R. STHDA.