María fundó C&A (Casas y Apartamentos), una agencia de bienes raíces en Cali. Ha recibido una solicitud de una empresa internacional que desea adquirir dos viviendas para ubicar a sus empleados en la ciudad. Este informe aplica técnicas de Regresión Lineal Múltiple (RLM) para predecir los precios de vivienda y apoyar la toma de decisión.
Las condiciones de las solicitudes son:
| Característica | Vivienda 1 | Vivienda 2 |
|---|---|---|
| Tipo | Casa | Apartamento |
| Área construida (m²) | 200 | 300 |
| Parqueaderos | 1 | 3 |
| Baños | 2 | 3 |
| Habitaciones | 4 | 5 |
| Estrato | 4 o 5 | 5 o 6 |
| Zona | Norte | Sur |
| Crédito pre-aprobado | 350 millones COP | 850 millones COP |
library(tidyverse)
library(plotly)
library(leaflet)
library(corrplot)
library(lmtest)
library(car)
library(nortest)
library(knitr)
library(kableExtra)
# Cargar datos desde CSV (debe estar en la misma carpeta que este .Rmd)
vivienda <- read.csv("vivienda.csv")
# Convertir columnas numéricas
vivienda <- vivienda %>%
mutate(
preciom = as.numeric(preciom),
areaconst = as.numeric(areaconst),
estrato = as.numeric(estrato),
banios = as.numeric(banios),
habitaciones = as.numeric(habitaciones),
parqueaderos = as.numeric(parqueaderos),
longitud = as.numeric(longitud),
latitud = as.numeric(latitud)
)# Filtrar: Casas en Zona Norte
base1 <- vivienda %>%
filter(tipo == "Casa", zona == "Zona Norte")
# Primeros 3 registros
base1 %>%
head(3) %>%
kable(caption = "Primeros 3 registros – Casas Zona Norte") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE)| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1209 | Zona Norte | 2 | 5 | 320 | 150 | 2 | 4 | 6 | Casa | acopi | -76.51341 | 3.47968 |
| 1592 | Zona Norte | 2 | 5 | 780 | 380 | 2 | 3 | 3 | Casa | acopi | -76.51674 | 3.48721 |
| 4057 | Zona Norte | 2 | 6 | 750 | 445 | NA | 7 | 6 | Casa | acopi | -76.52950 | 3.38527 |
# Tabla de verificación por tipo
table(base1$tipo) %>%
kable(col.names = c("Tipo", "Frecuencia"),
caption = "Verificación: solo Casas") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Tipo | Frecuencia |
|---|---|
| Casa | 722 |
# Tabla de verificación por zona
table(base1$zona) %>%
kable(col.names = c("Zona", "Frecuencia"),
caption = "Verificación: solo Zona Norte") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Zona | Frecuencia |
|---|---|
| Zona Norte | 722 |
## Total de registros en base1: 722
# Mapa interactivo con leaflet
leaflet(base1) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 5,
color = "#2c7bb6",
fillOpacity = 0.7,
popup = ~paste0(
"<b>Barrio:</b> ", barrio, "<br>",
"<b>Precio:</b> $", preciom, " millones<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"<b>Zona:</b> ", zona
)
) %>%
addLegend("bottomright",
colors = "#2c7bb6",
labels = "Casas – Zona Norte",
title = "Base 1")Se pueden observar algunos puntos fuera del área geográfica de la Zona Norte. Esto puede deberse a errores en la clasificación de la zona en la base de datos, o a viviendas ubicadas en límites fronterizos entre zonas. Es importante tenerlo en cuenta para interpretar los resultados con cautela.
# Resumen estadístico de variables clave
base1 %>%
select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
summary() %>%
kable(caption = "Resumen estadístico – Variables del modelo (base1)") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| preciom | areaconst | estrato | banios | habitaciones | parqueaderos | |
|---|---|---|---|---|---|---|
| Min. : 89.0 | Min. : 30.0 | Min. :3.000 | Min. : 0.000 | Min. : 0.000 | Min. : 1.000 | |
| 1st Qu.: 261.2 | 1st Qu.: 140.0 | 1st Qu.:3.000 | 1st Qu.: 2.000 | 1st Qu.: 3.000 | 1st Qu.: 1.000 | |
| Median : 390.0 | Median : 240.0 | Median :4.000 | Median : 3.000 | Median : 4.000 | Median : 2.000 | |
| Mean : 445.9 | Mean : 264.9 | Mean :4.202 | Mean : 3.555 | Mean : 4.507 | Mean : 2.182 | |
| 3rd Qu.: 550.0 | 3rd Qu.: 336.8 | 3rd Qu.:5.000 | 3rd Qu.: 4.000 | 3rd Qu.: 5.000 | 3rd Qu.: 3.000 | |
| Max. :1940.0 | Max. :1440.0 | Max. :6.000 | Max. :10.000 | Max. :10.000 | Max. :10.000 | |
| NA | NA | NA | NA | NA | NA’s :287 |
p1 <- plot_ly(base1,
x = ~areaconst, y = ~preciom,
type = "scatter", mode = "markers",
marker = list(color = "#2c7bb6", opacity = 0.6),
text = ~paste("Barrio:", barrio,
"<br>Precio:", preciom,
"<br>Área:", areaconst)) %>%
layout(title = "Precio vs Área Construida – Casas Zona Norte",
xaxis = list(title = "Área construida (m²)"),
yaxis = list(title = "Precio (millones COP)"))
p1Se observa una relación positiva entre el área construida y el precio. A mayor área, mayor precio, lo cual es lógico en el mercado inmobiliario.
p2 <- plot_ly(base1,
x = ~factor(estrato), y = ~preciom,
type = "box",
color = ~factor(estrato),
colors = "Blues") %>%
layout(title = "Distribución de Precio por Estrato – Casas Zona Norte",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones COP)"))
p2Los estratos más altos presentan precios medianos superiores, confirmando que el estrato socioeconómico es un determinante importante del precio.
p3 <- plot_ly(base1,
x = ~factor(banios), y = ~preciom,
type = "box",
color = ~factor(banios),
colors = "Greens") %>%
layout(title = "Precio por Número de Baños – Casas Zona Norte",
xaxis = list(title = "Número de baños"),
yaxis = list(title = "Precio (millones COP)"))
p3Las viviendas con más baños tienden a tener precios más altos, lo cual refleja mayor tamaño y lujo.
p4 <- plot_ly(base1,
x = ~factor(habitaciones), y = ~preciom,
type = "box",
color = ~factor(habitaciones),
colors = "Oranges") %>%
layout(title = "Precio por Número de Habitaciones – Casas Zona Norte",
xaxis = list(title = "Número de habitaciones"),
yaxis = list(title = "Precio (millones COP)"))
p4Mayor número de habitaciones se asocia con precios más elevados, aunque con mayor variabilidad en los rangos altos.
vars_cor <- base1 %>%
select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
na.omit()
cor_matrix <- cor(vars_cor)
corrplot(cor_matrix,
method = "color",
type = "upper",
tl.col = "black",
addCoef.col = "black",
number.cex = 0.8,
title = "Correlación entre variables – base1",
mar = c(0,0,1,0))El precio presenta correlaciones positivas con todas las variables. El área construida y el estrato son las que muestran mayor correlación con el precio. Esto sugiere que son los predictores más relevantes en el modelo.
modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones +
parqueaderos + banios,
data = base1)
summary(modelo1)##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -784.29 -77.56 -16.03 47.67 978.61
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -238.17090 44.40551 -5.364 1.34e-07 ***
## areaconst 0.67673 0.05281 12.814 < 2e-16 ***
## estrato 80.63495 9.82632 8.206 2.70e-15 ***
## habitaciones 7.64511 5.65873 1.351 0.177
## parqueaderos 24.00598 5.86889 4.090 5.14e-05 ***
## banios 18.89938 7.48800 2.524 0.012 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 155.1 on 429 degrees of freedom
## (287 observations deleted due to missingness)
## Multiple R-squared: 0.6041, Adjusted R-squared: 0.5995
## F-statistic: 130.9 on 5 and 429 DF, p-value: < 2.2e-16
# Tabla de coeficientes
coef_df <- as.data.frame(summary(modelo1)$coefficients)
coef_df$Variable <- rownames(coef_df)
coef_df <- coef_df[, c("Variable", "Estimate", "Std. Error", "t value", "Pr(>|t|)")]
coef_df %>%
kable(digits = 4,
caption = "Coeficientes del Modelo 1 – Casas Zona Norte",
col.names = c("Variable", "Estimado", "Error Estándar",
"Estadístico t", "Valor p")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)| Variable | Estimado | Error Estándar | Estadístico t | Valor p | |
|---|---|---|---|---|---|
| (Intercept) | (Intercept) | -238.1709 | 44.4055 | -5.3635 | 0.0000 |
| areaconst | areaconst | 0.6767 | 0.0528 | 12.8140 | 0.0000 |
| estrato | estrato | 80.6349 | 9.8263 | 8.2060 | 0.0000 |
| habitaciones | habitaciones | 7.6451 | 5.6587 | 1.3510 | 0.1774 |
| parqueaderos | parqueaderos | 24.0060 | 5.8689 | 4.0904 | 0.0001 |
| banios | banios | 18.8994 | 7.4880 | 2.5240 | 0.0120 |
Interpretación de coeficientes:
- Intercepto: Valor base del precio cuando todas las variables son cero (referencia matemática, no interpretable directamente en este contexto).
- areaconst: Por cada metro cuadrado adicional de área construida, el precio aumenta en promedio
β₁millones de pesos, manteniendo las demás variables constantes. Lógico: más área = más precio.- estrato: Por cada unidad que sube el estrato, el precio aumenta en
β₂millones. Lógico: estratos altos tienen propiedades más costosas.- habitaciones: Por cada habitación adicional, el precio varía en
β₃millones. Resultado esperado positivo.- parqueaderos: Cada parqueadero adicional agrega
β₄millones al precio. Razonable en mercado urbano.- banios: Cada baño adicional incrementa el precio en
β₅millones. Refleja mayor tamaño y confort.Solo se interpretan como estadísticamente significativos los coeficientes con p-valor < 0.05.
r2 <- summary(modelo1)$r.squared
r2_adj <- summary(modelo1)$adj.r.squared
cat("R² =", round(r2, 4), "\n")## R² = 0.6041
## R² ajustado = 0.5995
Interpretación del R²: El modelo explica aproximadamente el 60.4% de la variabilidad en el precio de las casas de la Zona Norte. Un R² cercano a 1 indica mejor ajuste. Para mejorarlo se podrían incluir variables adicionales como antigüedad, estado de la vivienda, distancia a zonas comerciales, entre otras.
residuales1 <- residuals(modelo1)
# Test de Lilliefors (Kolmogorov-Smirnov)
lillie_test <- lillie.test(residuales1)
cat("Test de Lilliefors:\n")## Test de Lilliefors:
## Estadístico D = 0.1325
## p-valor = 0
# QQ-plot interactivo
qq_data <- data.frame(
teoricos = qqnorm(residuales1, plot.it = FALSE)$x,
muestra = qqnorm(residuales1, plot.it = FALSE)$y
)
plot_ly(qq_data, x = ~teoricos, y = ~muestra,
type = "scatter", mode = "markers",
marker = list(color = "#2c7bb6", size = 5)) %>%
add_lines(x = ~teoricos, y = ~teoricos,
line = list(color = "red", dash = "dash")) %>%
layout(title = "QQ-Plot de Residuales – Modelo 1",
xaxis = list(title = "Cuantiles teóricos"),
yaxis = list(title = "Cuantiles muestrales"))Si el p-valor del test de Lilliefors es mayor a 0.05, no se rechaza la normalidad de los residuales. En caso contrario, se sugiere aplicar transformaciones (logarítmica, Box-Cox) a la variable respuesta.
## Test de Breusch-Pagan:
## Estadístico BP = 80.2808
## p-valor = 0
Si p-valor > 0.05 no hay evidencia de heterocedasticidad. En caso de detectarla, se recomienda usar errores estándar robustos o transformar la variable dependiente.
## Test de Durbin-Watson:
## DW = 1.7615
## p-valor = 0.0055
Valores de DW cercanos a 2 indican independencia de residuales. Valores muy alejados sugieren autocorrelación, más común en datos de series de tiempo que en datos de corte transversal como este.
vif_values <- vif(modelo1)
vif_df <- data.frame(Variable = names(vif_values), VIF = round(vif_values, 3))
vif_df %>%
kable(caption = "Factor de Inflación de Varianza (VIF) – Modelo 1") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Variable | VIF | |
|---|---|---|
| areaconst | areaconst | 1.461 |
| estrato | estrato | 1.308 |
| habitaciones | habitaciones | 1.721 |
| parqueaderos | parqueaderos | 1.226 |
| banios | banios | 1.967 |
VIF > 10 indica multicolinealidad problemática. Si se detecta, se recomienda eliminar variables redundantes, usar Ridge Regression o aplicar PCA.
# Características de la vivienda solicitada
nueva_v1 <- data.frame(
areaconst = 200,
estrato = 4, # estrato 4 (dentro del rango 4-5 solicitado)
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
pred_v1 <- predict(modelo1, newdata = nueva_v1, interval = "prediction",
level = 0.95)
cat("=== Predicción para Vivienda 1 ===\n")## === Predicción para Vivienda 1 ===
## Precio estimado: $ 312.1 millones
## Límite inferior (95%): $ 6.2 millones
## Límite superior (95%): $ 618 millones
##
## Crédito pre-aprobado: $350 millones
if (pred_v1[1] <= 350) {
cat("✔ La vivienda estimada está DENTRO del presupuesto.\n")
} else {
cat("✘ La vivienda estimada SUPERA el presupuesto.\n")
}## ✔ La vivienda estimada está DENTRO del presupuesto.
El intervalo de predicción al 95% indica el rango de precios plausibles para una casa con estas características en la Zona Norte. La comparación con el crédito pre-aprobado de 350 millones orienta la búsqueda de opciones reales.
# Filtrar ofertas que cumplan condiciones y estén dentro del presupuesto
ofertas_v1 <- base1 %>%
filter(
preciom <= 350,
estrato %in% c(4, 5),
habitaciones >= 3,
banios >= 2,
parqueaderos >= 1
) %>%
arrange(abs(areaconst - 200)) %>% # ordenar por cercanía al área solicitada
head(10)
ofertas_v1 %>%
select(barrio, preciom, areaconst, estrato,
habitaciones, banios, parqueaderos, zona) %>%
kable(caption = "Top 10 ofertas potenciales – Vivienda 1") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)| barrio | preciom | areaconst | estrato | habitaciones | banios | parqueaderos | zona |
|---|---|---|---|---|---|---|---|
| el bosque | 350 | 200 | 5 | 4 | 3 | 3 | Zona Norte |
| la flora | 320 | 200 | 5 | 4 | 4 | 2 | Zona Norte |
| la merced | 320 | 200 | 4 | 4 | 4 | 2 | Zona Norte |
| el bosque | 335 | 202 | 5 | 5 | 4 | 1 | Zona Norte |
| el bosque | 350 | 203 | 5 | 5 | 2 | 2 | Zona Norte |
| vipasa | 340 | 203 | 5 | 4 | 3 | 2 | Zona Norte |
| vipasa | 300 | 205 | 5 | 6 | 5 | 2 | Zona Norte |
| la flora | 350 | 190 | 5 | 3 | 3 | 1 | Zona Norte |
| urbanización la merced | 320 | 210 | 5 | 5 | 3 | 2 | Zona Norte |
| la merced | 350 | 216 | 5 | 4 | 2 | 2 | Zona Norte |
# Tomar las primeras 5 para el mapa
top5_v1 <- ofertas_v1 %>% head(5)
# Paleta de colores por precio
pal1 <- colorNumeric(palette = "Blues", domain = top5_v1$preciom)
leaflet(top5_v1) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 8,
color = ~pal1(preciom),
fillOpacity = 0.9,
popup = ~paste0(
"<b>Barrio:</b> ", barrio, "<br>",
"<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
),
label = ~paste0("$", preciom, "M – ", barrio)
) %>%
addLegend("bottomright",
pal = pal1,
values = ~preciom,
title = "Precio (millones)",
labFormat = labelFormat(prefix = "$")) %>%
addLegend("topright",
colors = "#2c7bb6",
labels = "Ofertas potenciales V1",
title = "Vivienda 1 (≤$350M)")Análisis de ofertas: Se presentan 5 propiedades que cumplen con los requisitos de la solicitud (casa, Zona Norte, estrato 4-5, mínimo 3 habitaciones, 2 baños, 1 parqueadero) y se encuentran dentro del presupuesto pre-aprobado de 350 millones. Se recomienda a María priorizar las que más se acercan a las 4 habitaciones y 200 m² solicitados.
# Filtrar: Apartamentos en Zona Sur
base2 <- vivienda %>%
filter(tipo == "Apartamento", zona == "Zona Sur")
# Primeros 3 registros
base2 %>%
head(3) %>%
kable(caption = "Primeros 3 registros – Apartamentos Zona Sur") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE)| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5098 | Zona Sur | 5 | 4 | 290 | 96 | 1 | 2 | 3 | Apartamento | acopi | -76.53464 | 3.44987 |
| 698 | Zona Sur | 2 | 3 | 78 | 40 | 1 | 1 | 2 | Apartamento | aguablanca | -76.50100 | 3.40000 |
| 8199 | Zona Sur | NA | 6 | 875 | 194 | 2 | 5 | 3 | Apartamento | aguacatal | -76.55700 | 3.45900 |
table(base2$tipo) %>%
kable(col.names = c("Tipo", "Frecuencia"),
caption = "Verificación: solo Apartamentos") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Tipo | Frecuencia |
|---|---|
| Apartamento | 2787 |
table(base2$zona) %>%
kable(col.names = c("Zona", "Frecuencia"),
caption = "Verificación: solo Zona Sur") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Zona | Frecuencia |
|---|---|
| Zona Sur | 2787 |
## Total de registros en base2: 2787
leaflet(base2) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 5,
color = "#d7191c",
fillOpacity = 0.7,
popup = ~paste0(
"<b>Barrio:</b> ", barrio, "<br>",
"<b>Precio:</b> $", preciom, " millones<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"<b>Zona:</b> ", zona
)
) %>%
addLegend("bottomright",
colors = "#d7191c",
labels = "Apartamentos – Zona Sur",
title = "Base 2")Al igual que en la base anterior, pueden aparecer puntos geográficamente fuera de la Zona Sur, posiblemente por errores de clasificación o por viviendas en zonas limítrofes entre sectores de la ciudad.
base2 %>%
select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
summary() %>%
kable(caption = "Resumen estadístico – Variables del modelo (base2)") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| preciom | areaconst | estrato | banios | habitaciones | parqueaderos | |
|---|---|---|---|---|---|---|
| Min. : 75.0 | Min. : 40.00 | Min. :3.00 | Min. :0.000 | Min. :0.000 | Min. : 1.000 | |
| 1st Qu.: 175.0 | 1st Qu.: 65.00 | 1st Qu.:4.00 | 1st Qu.:2.000 | 1st Qu.:3.000 | 1st Qu.: 1.000 | |
| Median : 245.0 | Median : 85.00 | Median :5.00 | Median :2.000 | Median :3.000 | Median : 1.000 | |
| Mean : 297.3 | Mean : 97.47 | Mean :4.63 | Mean :2.488 | Mean :2.966 | Mean : 1.415 | |
| 3rd Qu.: 335.0 | 3rd Qu.:110.00 | 3rd Qu.:5.00 | 3rd Qu.:3.000 | 3rd Qu.:3.000 | 3rd Qu.: 2.000 | |
| Max. :1750.0 | Max. :932.00 | Max. :6.00 | Max. :8.000 | Max. :6.000 | Max. :10.000 | |
| NA | NA | NA | NA | NA | NA’s :406 |
p1b <- plot_ly(base2,
x = ~areaconst, y = ~preciom,
type = "scatter", mode = "markers",
marker = list(color = "#d7191c", opacity = 0.6),
text = ~paste("Barrio:", barrio,
"<br>Precio:", preciom,
"<br>Área:", areaconst)) %>%
layout(title = "Precio vs Área Construida – Apartamentos Zona Sur",
xaxis = list(title = "Área construida (m²)"),
yaxis = list(title = "Precio (millones COP)"))
p1bp4b <- plot_ly(base2,
x = ~factor(habitaciones), y = ~preciom,
type = "box",
color = ~factor(habitaciones),
colors = "Oranges") %>%
layout(title = "Precio por Número de Habitaciones – Apartamentos Zona Sur",
xaxis = list(title = "Número de habitaciones"),
yaxis = list(title = "Precio (millones COP)"))
p4bvars_cor2 <- base2 %>%
select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
na.omit()
cor_matrix2 <- cor(vars_cor2)
corrplot(cor_matrix2,
method = "color",
type = "upper",
tl.col = "black",
addCoef.col = "black",
number.cex = 0.8,
title = "Correlación entre variables – base2",
mar = c(0,0,1,0))En los apartamentos de la Zona Sur se esperan correlaciones similares a la base1. El área y el estrato suelen dominar como predictores del precio. Nótese que en estratos altos (5-6) la variabilidad de precios es mayor.
modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones +
parqueaderos + banios,
data = base2)
summary(modelo2)##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1092.02 -42.28 -1.33 40.58 926.56
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -261.62501 15.63220 -16.736 < 2e-16 ***
## areaconst 1.28505 0.05403 23.785 < 2e-16 ***
## estrato 60.89709 3.08408 19.746 < 2e-16 ***
## habitaciones -24.83693 3.89229 -6.381 2.11e-10 ***
## parqueaderos 72.91468 3.95797 18.422 < 2e-16 ***
## banios 50.69675 3.39637 14.927 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 98.02 on 2375 degrees of freedom
## (406 observations deleted due to missingness)
## Multiple R-squared: 0.7485, Adjusted R-squared: 0.748
## F-statistic: 1414 on 5 and 2375 DF, p-value: < 2.2e-16
coef_df2 <- as.data.frame(summary(modelo2)$coefficients)
coef_df2$Variable <- rownames(coef_df2)
coef_df2 <- coef_df2[, c("Variable", "Estimate", "Std. Error", "t value", "Pr(>|t|)")]
coef_df2 %>%
kable(digits = 4,
caption = "Coeficientes del Modelo 2 – Apartamentos Zona Sur",
col.names = c("Variable", "Estimado", "Error Estándar",
"Estadístico t", "Valor p")) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)| Variable | Estimado | Error Estándar | Estadístico t | Valor p | |
|---|---|---|---|---|---|
| (Intercept) | (Intercept) | -261.6250 | 15.6322 | -16.7363 | 0 |
| areaconst | areaconst | 1.2850 | 0.0540 | 23.7853 | 0 |
| estrato | estrato | 60.8971 | 3.0841 | 19.7457 | 0 |
| habitaciones | habitaciones | -24.8369 | 3.8923 | -6.3811 | 0 |
| parqueaderos | parqueaderos | 72.9147 | 3.9580 | 18.4223 | 0 |
| banios | banios | 50.6967 | 3.3964 | 14.9267 | 0 |
r2b <- summary(modelo2)$r.squared
r2b_adj <- summary(modelo2)$adj.r.squared
cat("R² =", round(r2b, 4), "\n")## R² = 0.7485
## R² ajustado = 0.748
El modelo para apartamentos de la Zona Sur explica el 74.9% de la variabilidad en el precio. Se interpretan solo los coeficientes con p-valor < 0.05 como estadísticamente significativos.
residuales2 <- residuals(modelo2)
lillie_test2 <- lillie.test(residuales2)
cat("Test de Lilliefors:\n")## Test de Lilliefors:
## Estadístico D = 0.1244
## p-valor = 0
qq_data2 <- data.frame(
teoricos = qqnorm(residuales2, plot.it = FALSE)$x,
muestra = qqnorm(residuales2, plot.it = FALSE)$y
)
plot_ly(qq_data2, x = ~teoricos, y = ~muestra,
type = "scatter", mode = "markers",
marker = list(color = "#d7191c", size = 5)) %>%
add_lines(x = ~teoricos, y = ~teoricos,
line = list(color = "red", dash = "dash")) %>%
layout(title = "QQ-Plot de Residuales – Modelo 2",
xaxis = list(title = "Cuantiles teóricos"),
yaxis = list(title = "Cuantiles muestrales"))## Test de Breusch-Pagan:
## Estadístico BP = 754.8051
## p-valor = 0
## Test de Durbin-Watson:
## DW = 1.5333
## p-valor = 0
vif_values2 <- vif(modelo2)
vif_df2 <- data.frame(Variable = names(vif_values2), VIF = round(vif_values2, 3))
vif_df2 %>%
kable(caption = "Factor de Inflación de Varianza (VIF) – Modelo 2") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Variable | VIF | |
|---|---|---|
| areaconst | areaconst | 2.067 |
| estrato | estrato | 1.545 |
| habitaciones | habitaciones | 1.429 |
| parqueaderos | parqueaderos | 1.738 |
| banios | banios | 2.529 |
nueva_v2 <- data.frame(
areaconst = 300,
estrato = 5, # estrato 5 (dentro del rango 5-6 solicitado)
habitaciones = 5,
parqueaderos = 3,
banios = 3
)
pred_v2 <- predict(modelo2, newdata = nueva_v2, interval = "prediction",
level = 0.95)
cat("=== Predicción para Vivienda 2 ===\n")## === Predicción para Vivienda 2 ===
## Precio estimado: $ 675 millones
## Límite inferior (95%): $ 481.5 millones
## Límite superior (95%): $ 868.6 millones
##
## Crédito pre-aprobado: $850 millones
if (pred_v2[1] <= 850) {
cat("✔ La vivienda estimada está DENTRO del presupuesto.\n")
} else {
cat("✘ La vivienda estimada SUPERA el presupuesto.\n")
}## ✔ La vivienda estimada está DENTRO del presupuesto.
ofertas_v2 <- base2 %>%
filter(
preciom <= 850,
estrato %in% c(5, 6),
habitaciones >= 4,
banios >= 3,
parqueaderos >= 2
) %>%
arrange(abs(areaconst - 300)) %>%
head(10)
ofertas_v2 %>%
select(barrio, preciom, areaconst, estrato,
habitaciones, banios, parqueaderos, zona) %>%
kable(caption = "Top 10 ofertas potenciales – Vivienda 2") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)| barrio | preciom | areaconst | estrato | habitaciones | banios | parqueaderos | zona |
|---|---|---|---|---|---|---|---|
| seminario | 670 | 300.00 | 5 | 6 | 5 | 3 | Zona Sur |
| cuarto de legua | 410 | 295.55 | 5 | 4 | 4 | 2 | Zona Sur |
| cuarto de legua | 520 | 320.00 | 5 | 4 | 4 | 2 | Zona Sur |
| ciudadela pasoancho | 650 | 275.00 | 5 | 5 | 5 | 2 | Zona Sur |
| capri | 350 | 270.00 | 5 | 4 | 3 | 3 | Zona Sur |
| san fernando | 500 | 330.00 | 5 | 4 | 4 | 2 | Zona Sur |
| san fernando viejo | 485 | 259.00 | 5 | 4 | 4 | 2 | Zona Sur |
| San Fernando | 350 | 258.00 | 5 | 5 | 4 | 2 | Zona Sur |
| seminario | 530 | 256.00 | 5 | 5 | 5 | 3 | Zona Sur |
| el ingenio | 700 | 250.00 | 6 | 5 | 4 | 2 | Zona Sur |
top5_v2 <- ofertas_v2 %>% head(5)
pal2 <- colorNumeric(palette = "Reds", domain = top5_v2$preciom)
leaflet(top5_v2) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 8,
color = ~pal2(preciom),
fillOpacity = 0.9,
popup = ~paste0(
"<b>Barrio:</b> ", barrio, "<br>",
"<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
),
label = ~paste0("$", preciom, "M – ", barrio)
) %>%
addLegend("bottomright",
pal = pal2,
values = ~preciom,
title = "Precio (millones)",
labFormat = labelFormat(prefix = "$")) %>%
addLegend("topright",
colors = "#d7191c",
labels = "Ofertas potenciales V2",
title = "Vivienda 2 (≤$850M)")Se identifican apartamentos en la Zona Sur que cumplen con los criterios de la segunda solicitud: estrato 5 o 6, mínimo 4 habitaciones, 3 baños y 2 parqueaderos, dentro del presupuesto de 850 millones. Se recomienda priorizar los más cercanos a los 300 m² y con mayor número de parqueaderos dado el tamaño de la familia.
Informe elaborado con R y RMarkdown | Modelos Estadísticos para la Toma de Decisiones