Contexto del negocio: María, agente de bienes raíces con 10 años de experiencia en Cali, fundó la empresa C&A (Casas y Apartamentos) junto con dos excolaboradores. Actualmente, las ventas del sector inmobiliario en Cali han disminuido debido a tensiones políticas y sociales. María recibió una solicitud de una compañía internacional que desea ubicar a dos empleados con sus familias en la ciudad.
Objetivo del análisis: Desarrollar un modelo de regresión lineal múltiple que permita estimar el precio de viviendas en Cali, para poder asesorar adecuadamente la compra y generar recomendaciones de potenciales ofertas dentro del presupuesto asignado.
Solicitudes recibidas:
| 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 preaprobado | 350 millones | 850 millones |
## Dimensiones del dataset: 8322 13
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:8322] 1147 1169 1350 5992 1212 ...
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ piso : chr [1:8322] NA NA NA "02" ...
## $ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
## $ longitud : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
## - attr(*, "spec")=
## .. cols(
## .. id = col_double(),
## .. zona = col_character(),
## .. piso = col_character(),
## .. estrato = col_double(),
## .. preciom = col_double(),
## .. areaconst = col_double(),
## .. parqueaderos = col_double(),
## .. banios = col_double(),
## .. habitaciones = col_double(),
## .. tipo = col_character(),
## .. barrio = col_character(),
## .. longitud = col_double(),
## .. latitud = col_double()
## .. )
## - attr(*, "problems")=<externalptr>
## id zona piso estrato
## Min. : 1 Length:8322 Length:8322 Min. :3.000
## 1st Qu.:2080 Class :character Class :character 1st Qu.:4.000
## Median :4160 Mode :character Mode :character Median :5.000
## Mean :4160 Mean :4.634
## 3rd Qu.:6240 3rd Qu.:5.000
## Max. :8319 Max. :6.000
## NA's :3 NA's :3
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean : 1.835 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :10.000 Max. :10.000
## NA's :2 NA's :3 NA's :1605 NA's :3
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8322 Length:8322 Min. :-76.59
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76.54
## Median : 3.000 Mode :character Mode :character Median :-76.53
## Mean : 3.605 Mean :-76.53
## 3rd Qu.: 4.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.46
## NA's :3 NA's :3
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
## NA's :3
Se filtra la base para incluir únicamente casas de la Zona Norte de Cali, conforme al enunciado.
datos_filtrados <- vivienda %>%
filter(tipo == "Casa",
zona == "Zona Norte")
cat("Registros tras el filtro:", nrow(datos_filtrados), "\n")## Registros tras el filtro: 722
## Número de variables : 13
kable(head(datos_filtrados, 3),
caption = "Primeros 3 registros - Casas Zona Norte") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed","responsive"))| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1209 | Zona Norte | 02 | 5 | 320 | 150 | 2 | 4 | 6 | Casa | acopi | -76.51341 | 3.47968 |
| 1592 | Zona Norte | 02 | 5 | 780 | 380 | 2 | 3 | 3 | Casa | acopi | -76.51674 | 3.48721 |
| 4057 | Zona Norte | 02 | 6 | 750 | 445 | NA | 7 | 6 | Casa | acopi | -76.52950 | 3.38527 |
tabla_zona <- datos_filtrados %>%
group_by(zona) %>%
summarise(
n = n(),
precio_prom = round(mean(preciom, na.rm = TRUE), 1),
area_prom = round(mean(areaconst, na.rm = TRUE), 1),
estrato_prom = round(mean(estrato, na.rm = TRUE), 1)
)
kable(tabla_zona,
caption = "Resumen estadístico por zona del filtro aplicado") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))| zona | n | precio_prom | area_prom | estrato_prom |
|---|---|---|---|---|
| Zona Norte | 722 | 445.9 | 264.9 | 4.2 |
datos_mapa <- datos_filtrados %>%
filter(!is.na(longitud), !is.na(latitud)) %>%
mutate(color_zona = case_when(
zona == "Zona Norte" ~ "blue",
zona == "Zona Sur" ~ "red",
zona == "Zona Centro" ~ "green",
zona == "Zona Oeste" ~ "orange",
TRUE ~ "gray"
))
leaflet(datos_mapa) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 5,
color = ~color_zona,
fillOpacity = 0.7,
popup = ~paste(
"<strong>Zona:</strong>", zona, "<br>",
"<strong>Barrio:</strong>", barrio, "<br>",
"<strong>Precio:</strong>", preciom, "M COP<br>",
"<strong>Estrato:</strong>", estrato
)
) %>%
addLegend("bottomright",
colors = c("blue","red","green","orange","gray"),
labels = c("Zona Norte","Zona Sur","Zona Centro","Zona Oeste","Otra"),
title = "Zona geográfica")Discusión del mapa: Al visualizar los puntos geográficamente, se puede verificar si todas las observaciones filtradas como “Zona Norte” se ubican efectivamente en el sector norte de Cali. En caso de encontrar puntos en otras zonas geográficas, esto se debe principalmente a dos razones: (1) errores o imprecisiones en las coordenadas GPS registradas al momento de la captura del dato, o (2) diferencias entre la clasificación administrativa de zona usada en la base de datos y los límites geográficos reales de cada sector. Estos casos atípicos no representan un problema crítico para el modelo, pero es importante tenerlos en cuenta al interpretar los resultados y al sugerir ofertas específicas a los clientes.
valores_faltantes <- datos_filtrados %>%
summarise_all(~sum(is.na(.))) %>%
pivot_longer(everything(),
names_to = "variable",
values_to = "faltantes") %>%
filter(faltantes > 0)
if(nrow(valores_faltantes) > 0){
kable(valores_faltantes, caption = "Variables con valores faltantes") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))
} else {
cat("No se detectaron valores faltantes en las variables clave.\n")
}| variable | faltantes |
|---|---|
| piso | 372 |
| parqueaderos | 287 |
datos_limpios <- datos_filtrados %>%
drop_na(preciom, areaconst, estrato, parqueaderos, banios, habitaciones)
cat("Registros después de limpieza:", nrow(datos_limpios), "\n")## Registros después de limpieza: 435
set.seed(123)
indice_train <- createDataPartition(datos_limpios$preciom,
p = 0.80,
list = FALSE)
train_data <- datos_limpios[indice_train, ]
test_data <- datos_limpios[-indice_train, ]
cat("Set de entrenamiento:", nrow(train_data), "registros\n")## Set de entrenamiento: 350 registros
## Set de prueba : 85 registros
p1 <- plot_ly(datos_limpios, x = ~preciom,
type = "histogram",
nbinsx = 40,
marker = list(color = "#2196F3",
line = list(color = "white", width = 0.5))) %>%
layout(
title = "Distribución del Precio de Viviendas (Zona Norte)",
xaxis = list(title = "Precio (millones de pesos COP)"),
yaxis = list(title = "Frecuencia"),
plot_bgcolor = "rgba(240,240,240,0.5)",
paper_bgcolor = "white"
)
p1Interpretación: La distribución del precio presenta una marcada asimetría positiva (sesgo a la derecha). La mayoría de las casas en zona Norte tienen precios concentrados en rangos bajos-medios, mientras que un grupo reducido alcanza precios muy altos. Esto es coherente con un mercado inmobiliario urbano donde conviven inmuebles de distintos estratos.
vars_numericas <- datos_limpios %>%
select(preciom, areaconst, estrato, parqueaderos, banios, habitaciones) %>%
drop_na()
cor_matrix <- cor(vars_numericas)
p2 <- plot_ly(
x = colnames(cor_matrix),
y = colnames(cor_matrix),
z = cor_matrix,
type = "heatmap",
colorscale = list(c(0,"#d73027"), c(0.5,"#ffffff"), c(1,"#1a9850")),
zmin = -1, zmax = 1,
text = round(cor_matrix, 2),
texttemplate = "%{text}",
showscale = TRUE
) %>%
layout(
title = "Matriz de Correlación - Variables Numéricas",
xaxis = list(title = ""),
yaxis = list(title = "")
)
p2Interpretación: areaconst presenta la
correlación más alta con preciom (área construida explica
gran parte del precio), seguida de estrato y
banios. habitaciones y
parqueaderos tienen correlación positiva pero más moderada.
Estas correlaciones son lógicas y esperadas: una casa
más grande, en estrato más alto y con más baños naturalmente vale más en
el mercado de Cali.
p3 <- plot_ly(datos_limpios, x = ~areaconst, y = ~preciom,
type = "scatter",
mode = "markers",
text = ~paste("Área:", areaconst, "m²<br>",
"Precio:", preciom, "M COP<br>",
"Estrato:", estrato),
hoverinfo = "text",
marker = list(color = "#1565C0", size = 7, opacity = 0.7)) %>%
layout(
title = "Precio vs Área Construida - Zona Norte",
xaxis = list(title = "Área Construida (m²)"),
yaxis = list(title = "Precio (millones COP)")
)
p3Interpretación: Existe una relación positiva clara entre área construida y precio. A medida que aumenta el área, el precio también sube de forma aproximadamente lineal. Se observan algunos valores atípicos con precios muy altos para áreas moderadas, posiblemente viviendas en barrios de alta valorización o con acabados premium que el área sola no explica.
p4 <- plot_ly(datos_limpios,
x = ~as.factor(banios),
y = ~preciom,
type = "box",
color = ~as.factor(banios),
colors = "Blues",
boxpoints = "outliers") %>%
layout(
title = "Precio según Número de Baños - Zona Norte",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (millones COP)"),
showlegend = FALSE
)
p4Interpretación: A mayor número de baños, el precio mediano es más alto. Este resultado es lógico: más baños generalmente implica una vivienda más grande y de mayor estrato. Las viviendas con 4 o más baños muestran gran variabilidad, lo que indica que en ese rango hay propiedades muy diversas en precio.
p5 <- plot_ly(datos_limpios,
x = ~as.factor(habitaciones),
y = ~preciom,
type = "box",
color = ~as.factor(habitaciones),
colors = "Greens",
boxpoints = "outliers") %>%
layout(
title = "Precio según Número de Habitaciones - Zona Norte",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (millones COP)"),
showlegend = FALSE
)
p5Interpretación: Más habitaciones se asocia con precios más altos, aunque la relación no es tan fuerte como con el área construida. Viviendas con 5 o más habitaciones presentan alta dispersión de precios, lo que sugiere que el número de habitaciones solo no determina el precio: el estrato y la zona del barrio juegan un rol importante.
resumen_parq <- datos_limpios %>%
group_by(parqueaderos) %>%
summarise(precio_promedio = mean(preciom, na.rm = TRUE), n = n()) %>%
filter(!is.na(parqueaderos))
p6 <- plot_ly(resumen_parq,
x = ~as.factor(parqueaderos),
y = ~precio_promedio,
type = "bar",
text = ~paste("n =", n, "<br>Precio prom.:",
round(precio_promedio, 1)),
hoverinfo = "text",
marker = list(color = "#FF9800")) %>%
layout(
title = "Precio Promedio por Número de Parqueaderos",
xaxis = list(title = "Número de Parqueaderos"),
yaxis = list(title = "Precio Promedio (millones COP)")
)
p6Interpretación: El precio promedio aumenta con el número de parqueaderos. Esto es coherente con el mercado: más parqueaderos implican viviendas más grandes o en estratos altos donde el espacio adicional tiene valor. Las viviendas con 3 o más parqueaderos corresponden a un segmento premium del mercado.
modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = train_data)
summary(modelo1)##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = train_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -687.07 -78.84 -15.83 44.26 925.94
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -216.19701 49.01834 -4.411 1.38e-05 ***
## areaconst 0.61207 0.05685 10.767 < 2e-16 ***
## estrato 85.64749 10.74592 7.970 2.35e-14 ***
## habitaciones 5.66823 6.16058 0.920 0.358
## parqueaderos 28.65710 7.04082 4.070 5.83e-05 ***
## banios 12.40120 8.16826 1.518 0.130
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 152.8 on 344 degrees of freedom
## Multiple R-squared: 0.5848, Adjusted R-squared: 0.5788
## F-statistic: 96.91 on 5 and 344 DF, p-value: < 2.2e-16
coef_df <- as.data.frame(summary(modelo1)$coefficients)
coef_df$Variable <- rownames(coef_df)
coef_df <- coef_df %>%
rename(Estimado = Estimate,
`Error Std` = `Std. Error`,
`t-valor` = `t value`,
`p-valor` = `Pr(>|t|)`) %>%
mutate(Significativo = ifelse(`p-valor` < 0.05, "Si", "No")) %>%
select(Variable, Estimado, `Error Std`, `t-valor`, `p-valor`, Significativo)
kable(coef_df, digits = 4,
caption = "Coeficientes del Modelo de Regresión Lineal Múltiple") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed","responsive")) %>%
row_spec(which(coef_df$Significativo == "Si"), background = "#e8f5e9")| Variable | Estimado | Error Std | t-valor | p-valor | Significativo | |
|---|---|---|---|---|---|---|
| (Intercept) | (Intercept) | -216.1970 | 49.0183 | -4.4105 | 0.0000 | Si |
| areaconst | areaconst | 0.6121 | 0.0568 | 10.7672 | 0.0000 | Si |
| estrato | estrato | 85.6475 | 10.7459 | 7.9702 | 0.0000 | Si |
| habitaciones | habitaciones | 5.6682 | 6.1606 | 0.9201 | 0.3582 | No |
| parqueaderos | parqueaderos | 28.6571 | 7.0408 | 4.0701 | 0.0001 | Si |
| banios | banios | 12.4012 | 8.1683 | 1.5182 | 0.1299 | No |
Variables estadísticamente significativas (p < 0.05):
areaconst: Por cada m² adicional de área construida, el precio de la vivienda aumenta en 0.612 millones de pesos, manteniendo las demás variables constantes. Este resultado es lógico: a mayor área construida, mayor valor del inmueble.
estrato: Un aumento de un nivel socioeconómico incrementa el precio en 85.647 millones de pesos. Tiene sentido: en Cali el estrato es uno de los determinantes más importantes del valor inmobiliario, pues refleja la calidad del sector, servicios y vecindario.
banios: Cada baño adicional agrega 12.401 millones al precio. Es lógico ya que más baños reflejan una vivienda más grande y con mejores acabados.
Variables no significativas: Las variables
habitaciones y parqueaderos no resultan
estadísticamente significativas en presencia de las demás variables.
Esto puede deberse a que su efecto ya está capturado
parcialmente por el área construida (a más área, naturalmente
hay más habitaciones y parqueaderos).
El modelo explica el 58.48% de la variabilidad en el precio de las viviendas. Este ajuste es moderado, lo que indica que hay variabilidad en el precio que el modelo no logra capturar con las variables actuales.
Posibles mejoras al modelo:
barrio como categórica, ya que la
ubicación específica dentro de la zona Norte puede generar diferencias
significativas de precio.piso para apartamentos, pues pisos más altos
suelen tener mayor valor.areaconst y
estrato, ya que el efecto del área puede variar según el
estrato.##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: residuos
## D = 0.14041, p-value < 2.2e-16
plot_ly(x = residuos, type = "histogram", nbinsx = 30,
marker = list(color = "#5C6BC0",
line = list(color = "white", width = 0.5))) %>%
layout(title = "Distribución de Residuos",
xaxis = list(title = "Residuos"),
yaxis = list(title = "Frecuencia"))qqnorm(residuos, main = "QQ-Plot de Residuales",
col = "#5C6BC0", pch = 16, cex = 0.7)
qqline(residuos, col = "red", lwd = 2)Interpretación: El test de Lilliefors rechaza la normalidad de los residuos (p < 0.05). Esto es frecuente en datos inmobiliarios con valores atípicos y distribución asimétrica del precio. Sugerencia: aplicar transformación logarítmica al precio (log(preciom)) o usar métodos de regresión robusta que no dependan del supuesto de normalidad.
valores_ajustados <- fitted(modelo1)
plot_ly(x = valores_ajustados, y = residuos,
type = "scatter", mode = "markers",
marker = list(color = "#26A69A", size = 5, opacity = 0.6)) %>%
add_lines(x = range(valores_ajustados), y = c(0, 0),
line = list(color = "red", dash = "dash")) %>%
layout(
title = "Residuos vs Valores Ajustados",
xaxis = list(title = "Valores Ajustados"),
yaxis = list(title = "Residuos")
)##
## studentized Breusch-Pagan test
##
## data: modelo1
## BP = 61.218, df = 5, p-value = 6.806e-12
Interpretación: El test de Breusch-Pagan detecta heterocedasticidad (p < 0.05): la varianza de los residuos no es constante y tiende a aumentar con los valores ajustados. Sugerencia: usar errores estándar robustos (HC) o aplicar transformación logarítmica al precio para estabilizar la varianza.
##
## Durbin-Watson test
##
## data: modelo1
## DW = 1.6654, p-value = 0.0006979
## alternative hypothesis: true autocorrelation is greater than 0
Interpretación: Se detecta autocorrelación en los residuos (p < 0.05). En datos inmobiliarios esto puede deberse a la estructura espacial: propiedades cercanas tienden a tener precios similares. Sugerencia: considerar modelos de regresión espacial que incorporen la dependencia geográfica.
vif_vals <- vif(modelo1)
vif_df <- data.frame(Variable = names(vif_vals),
VIF = round(vif_vals, 3))
kable(vif_df, caption = "Factor de Inflación de la Varianza (VIF)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(which(vif_df$VIF > 5), background = "#fff3e0") %>%
row_spec(which(vif_df$VIF > 10), background = "#ffccbc")| Variable | VIF | |
|---|---|---|
| areaconst | areaconst | 1.485 |
| estrato | estrato | 1.287 |
| habitaciones | habitaciones | 1.661 |
| parqueaderos | parqueaderos | 1.228 |
| banios | banios | 1.873 |
Interpretación: VIF < 5 = baja multicolinealidad (aceptable); 5-10 = moderada; > 10 = severa (problemática). Todas las variables presentan VIF < 5, confirmando ausencia de multicolinealidad problemática. Este supuesto se cumple satisfactoriamente.
solicitud1 <- data.frame(
areaconst = 200,
estrato = 4,
parqueaderos = 1,
banios = 2,
habitaciones = 4
)
pred_sol1 <- predict(modelo1, newdata = solicitud1,
interval = "prediction", level = 0.95)
resultado_sol1 <- data.frame(
Caracteristica = c("Precio estimado",
"Intervalo inferior (95%)",
"Intervalo superior (95%)",
"Crédito preaprobado",
"Conclusión"),
Valor = c(
paste(round(pred_sol1[1,"fit"], 1), "millones COP"),
paste(round(pred_sol1[1,"lwr"], 1), "millones COP"),
paste(round(pred_sol1[1,"upr"], 1), "millones COP"),
"350 millones COP",
ifelse(pred_sol1[1,"fit"] <= 350,
"La vivienda CABE dentro del presupuesto",
"El precio SUPERA el crédito de 350 millones")
)
)
kable(resultado_sol1, caption = "Predicción - Solicitud 1 (Casa Zona Norte)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(5, bold = TRUE,
background = ifelse(pred_sol1[1,"fit"] <= 350, "#e8f5e9", "#ffccbc"))| Caracteristica | Valor |
|---|---|
| Precio estimado | 324.9 millones COP |
| Intervalo inferior (95%) | 23 millones COP |
| Intervalo superior (95%) | 626.9 millones COP |
| Crédito preaprobado | 350 millones COP |
| Conclusión | La vivienda CABE dentro del presupuesto |
Análisis: Con las características solicitadas (casa 200 m², estrato 4, 1 parqueadero, 2 baños, 4 habitaciones en zona Norte), el modelo estima un precio de 324.9 millones de pesos. Este valor se encuentra dentro del crédito preaprobado de 350 millones, lo que indica que es viable encontrar opciones que cumplan con las características solicitadas dentro del presupuesto disponible.
pred_test <- predict(modelo1, newdata = test_data)
mae <- mean(abs(pred_test - test_data$preciom))
mse <- mean((pred_test - test_data$preciom)^2)
rmse <- sqrt(mse)
r2_test <- cor(pred_test, test_data$preciom)^2
mape <- mean(abs((pred_test - test_data$preciom) / test_data$preciom)) * 100
metricas <- data.frame(
Metrica = c("MAE (Error Absoluto Medio)",
"RMSE (Raiz Error Cuadratico Medio)",
"MSE (Error Cuadratico Medio)",
"R2 (Set de Prueba)",
"MAPE (%)"),
Valor = c(round(mae, 3),
round(rmse, 3),
round(mse, 3),
round(r2_test, 4),
round(mape, 2))
)
kable(metricas, caption = "Indicadores de Rendimiento del Modelo - Solicitud 1") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))| Metrica | Valor |
|---|---|
| MAE (Error Absoluto Medio) | 106.5220 |
| RMSE (Raiz Error Cuadratico Medio) | 168.3980 |
| MSE (Error Cuadratico Medio) | 28357.9420 |
| R2 (Set de Prueba) | 0.6743 |
| MAPE (%) | 21.9400 |
Interpretación de indicadores:
resultados_test <- data.frame(
Real = test_data$preciom,
Predicho = pred_test
)
plot_ly(resultados_test, x = ~Real, y = ~Predicho,
type = "scatter", mode = "markers",
marker = list(color = "#AB47BC", size = 7, opacity = 0.7),
name = "Predicciones") %>%
add_lines(x = range(resultados_test$Real),
y = range(resultados_test$Real),
line = list(color = "red", dash = "dash"),
name = "Linea perfecta (y=x)") %>%
layout(
title = "Predicciones vs Valores Reales (Set de Prueba)",
xaxis = list(title = "Precio Real (millones COP)"),
yaxis = list(title = "Precio Predicho (millones COP)")
)ofertas_sol1 <- vivienda %>%
filter(
tipo == "Casa",
zona == "Zona Norte",
estrato %in% c(4, 5),
preciom <= 350,
!is.na(longitud),
!is.na(latitud)
) %>%
drop_na(preciom, areaconst) %>%
arrange(preciom) %>%
head(5)
cat("Ofertas encontradas:", nrow(ofertas_sol1), "\n")## Ofertas encontradas: 5
if(nrow(ofertas_sol1) > 0){
kable(ofertas_sol1 %>%
select(preciom, areaconst, estrato, banios,
habitaciones, parqueaderos, barrio),
caption = "Top 5 Ofertas Potenciales - Solicitud 1",
digits = 2) %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))
}| preciom | areaconst | estrato | banios | habitaciones | parqueaderos | barrio |
|---|---|---|---|---|---|---|
| 125 | 45 | 5 | 2 | 3 | NA | villa de veracruz |
| 160 | 135 | 4 | 3 | 4 | NA | los andes |
| 160 | 120 | 4 | 2 | 3 | NA | villa del sol |
| 160 | 120 | 4 | 2 | 3 | NA | villa del sol |
| 160 | 120 | 4 | 2 | 3 | NA | villa del sol |
if(nrow(ofertas_sol1) > 0){
leaflet(ofertas_sol1) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 8,
color = "#1565C0",
fillOpacity = 0.8,
popup = ~paste(
"<strong>Barrio:</strong>", barrio, "<br>",
"<strong>Precio:</strong>", preciom, "M COP<br>",
"<strong>Area:</strong>", areaconst, "m2<br>",
"<strong>Estrato:</strong>", estrato, "<br>",
"<strong>Habitaciones:</strong>", habitaciones, "<br>",
"<strong>Banos:</strong>", banios
)
) %>%
addLegend("bottomright",
colors = "#1565C0",
labels = "Oferta potencial",
title = "Solicitud 1 - Casa Zona Norte")
} else {
cat("No se encontraron ofertas con coordenadas disponibles.\n")
}Discusión de ofertas: Se identificaron las 5 opciones más económicas que cumplen el perfil de la solicitud (casa, zona Norte, estrato 4-5, precio ≤ 350 millones). Se recomienda a María presentar estas opciones a la empresa cliente, priorizando aquellas con mayor área construida dentro del presupuesto y ubicadas en barrios con buena valorización y acceso a servicios.
datos_sur <- vivienda %>%
filter(tipo == "Apartamento",
zona == "Zona Sur")
cat("Registros zona Sur:", nrow(datos_sur), "\n")## Registros zona Sur: 2787
kable(head(datos_sur, 3),
caption = "Primeros 3 registros - Apartamentos Zona Sur") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed","responsive"))| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5098 | Zona Sur | 05 | 4 | 290 | 96 | 1 | 2 | 3 | Apartamento | acopi | -76.53464 | 3.44987 |
| 698 | Zona Sur | 02 | 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 |
tabla_sur <- datos_sur %>%
group_by(zona) %>%
summarise(
n = n(),
precio_prom = round(mean(preciom, na.rm = TRUE), 1),
area_prom = round(mean(areaconst, na.rm = TRUE), 1),
estrato_prom = round(mean(estrato, na.rm = TRUE), 1)
)
kable(tabla_sur,
caption = "Resumen estadístico - Apartamentos Zona Sur") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))| zona | n | precio_prom | area_prom | estrato_prom |
|---|---|---|---|---|
| Zona Sur | 2787 | 297.3 | 97.5 | 4.6 |
datos_mapa_sur <- datos_sur %>%
filter(!is.na(longitud), !is.na(latitud)) %>%
mutate(color_zona = case_when(
zona == "Zona Sur" ~ "red",
zona == "Zona Norte" ~ "blue",
zona == "Zona Centro" ~ "green",
zona == "Zona Oeste" ~ "orange",
TRUE ~ "gray"
))
leaflet(datos_mapa_sur) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 5,
color = ~color_zona,
fillOpacity = 0.7,
popup = ~paste(
"<strong>Zona:</strong>", zona, "<br>",
"<strong>Barrio:</strong>", barrio, "<br>",
"<strong>Precio:</strong>", preciom, "M COP<br>",
"<strong>Estrato:</strong>", estrato
)
) %>%
addLegend("bottomright",
colors = c("red","blue","green","orange","gray"),
labels = c("Zona Sur","Zona Norte","Zona Centro","Zona Oeste","Otra"),
title = "Zona geográfica")Discusión del mapa: Al igual que en la solicitud 1, se verifica si los puntos filtrados como “Zona Sur” se ubican geográficamente en el sector sur de Cali. Desviaciones pueden deberse a imprecisiones en las coordenadas o diferencias entre la clasificación administrativa y los límites geográficos reales.
datos_limpios_sur <- datos_sur %>%
drop_na(preciom, areaconst, estrato, parqueaderos, banios, habitaciones)
set.seed(123)
indice_sur <- createDataPartition(datos_limpios_sur$preciom, p = 0.80, list = FALSE)
train_sur <- datos_limpios_sur[indice_sur, ]
test_sur <- datos_limpios_sur[-indice_sur, ]
cat("Train zona Sur:", nrow(train_sur), "| Test zona Sur:", nrow(test_sur), "\n")## Train zona Sur: 1906 | Test zona Sur: 475
plot_ly(datos_limpios_sur, x = ~preciom,
type = "histogram",
nbinsx = 40,
marker = list(color = "#C62828",
line = list(color = "white", width = 0.5))) %>%
layout(
title = "Distribución del Precio - Apartamentos Zona Sur",
xaxis = list(title = "Precio (millones COP)"),
yaxis = list(title = "Frecuencia"),
plot_bgcolor = "rgba(240,240,240,0.5)",
paper_bgcolor = "white"
)Interpretación: Los apartamentos de zona Sur presentan una distribución de precio también sesgada a la derecha. El rango de precios tiende a ser más alto que en zona Norte, consistente con que la solicitud tiene un crédito de 850 millones.
vars_sur <- datos_limpios_sur %>%
select(preciom, areaconst, estrato, parqueaderos, banios, habitaciones) %>%
drop_na()
cor_sur <- cor(vars_sur)
plot_ly(
x = colnames(cor_sur),
y = colnames(cor_sur),
z = cor_sur,
type = "heatmap",
colorscale = list(c(0,"#d73027"), c(0.5,"#ffffff"), c(1,"#1a9850")),
zmin = -1, zmax = 1,
text = round(cor_sur, 2),
texttemplate = "%{text}",
showscale = TRUE
) %>%
layout(
title = "Correlación - Apartamentos Zona Sur",
xaxis = list(title = ""),
yaxis = list(title = "")
)Interpretación: En la zona Sur,
areaconst y estrato siguen siendo las
variables con mayor correlación con el precio. El patrón es similar a
zona Norte, lo que confirma que los determinantes del precio son
consistentes entre zonas.
plot_ly(datos_limpios_sur, x = ~areaconst, y = ~preciom,
type = "scatter",
mode = "markers",
text = ~paste("Área:", areaconst, "m²<br>",
"Precio:", preciom, "M COP<br>",
"Estrato:", estrato),
hoverinfo = "text",
marker = list(color = "#C62828", size = 7, opacity = 0.7)) %>%
layout(
title = "Precio vs Área Construida - Zona Sur",
xaxis = list(title = "Área Construida (m²)"),
yaxis = list(title = "Precio (millones COP)")
)Interpretación: La relación positiva entre área y precio se mantiene en zona Sur. Los apartamentos de mayor área alcanzan precios considerablemente más altos, especialmente en estratos 5 y 6.
plot_ly(datos_limpios_sur,
x = ~as.factor(banios),
y = ~preciom,
type = "box",
color = ~as.factor(banios),
colors = "Reds",
boxpoints = "outliers") %>%
layout(
title = "Precio según Baños - Zona Sur",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (millones COP)"),
showlegend = FALSE
)Interpretación: La tendencia es la misma que en zona Norte: más baños se asocia con precios más altos. Los apartamentos de zona Sur con 4+ baños alcanzan precios muy elevados, correspondientes al segmento premium del mercado.
if(nrow(train_sur) >= 30){
modelo_sur <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = train_sur)
summary(modelo_sur)
} else {
cat("Datos insuficientes. Se usa el modelo global.\n")
modelo_sur <- modelo1
}##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = train_sur)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1064.80 -44.24 -1.74 40.85 930.48
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -255.37226 18.06216 -14.139 < 2e-16 ***
## areaconst 1.24962 0.05921 21.106 < 2e-16 ***
## estrato 62.59579 3.50972 17.835 < 2e-16 ***
## habitaciones -29.03562 4.50662 -6.443 1.48e-10 ***
## parqueaderos 71.49669 4.41225 16.204 < 2e-16 ***
## banios 52.38658 3.83364 13.665 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 100.5 on 1900 degrees of freedom
## Multiple R-squared: 0.7431, Adjusted R-squared: 0.7424
## F-statistic: 1099 on 5 and 1900 DF, p-value: < 2.2e-16
coef_sur <- as.data.frame(summary(modelo_sur)$coefficients)
coef_sur$Variable <- rownames(coef_sur)
coef_sur <- coef_sur %>%
rename(Estimado = Estimate,
`Error Std` = `Std. Error`,
`t-valor` = `t value`,
`p-valor` = `Pr(>|t|)`) %>%
mutate(Significativo = ifelse(`p-valor` < 0.05, "Si", "No")) %>%
select(Variable, Estimado, `Error Std`, `t-valor`, `p-valor`, Significativo)
kable(coef_sur, digits = 4,
caption = "Coeficientes - Modelo Zona Sur") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed","responsive")) %>%
row_spec(which(coef_sur$Significativo == "Si"), background = "#fce4ec")| Variable | Estimado | Error Std | t-valor | p-valor | Significativo | |
|---|---|---|---|---|---|---|
| (Intercept) | (Intercept) | -255.3723 | 18.0622 | -14.1385 | 0 | Si |
| areaconst | areaconst | 1.2496 | 0.0592 | 21.1063 | 0 | Si |
| estrato | estrato | 62.5958 | 3.5097 | 17.8350 | 0 | Si |
| habitaciones | habitaciones | -29.0356 | 4.5066 | -6.4429 | 0 | Si |
| parqueaderos | parqueaderos | 71.4967 | 4.4123 | 16.2041 | 0 | Si |
| banios | banios | 52.3866 | 3.8336 | 13.6650 | 0 | Si |
Interpretación: Los coeficientes del modelo para
zona Sur siguen una lógica similar a zona Norte. areaconst
y estrato son los predictores principales. El efecto del
área sobre el precio en zona Sur puede diferir del de zona Norte,
reflejando las características particulares del mercado inmobiliario en
cada sector de Cali.
El modelo para zona Sur explica el 74.31% de la variabilidad en el precio de los apartamentos. Este ajuste es satisfactorio para el análisis.
residuos_sur <- residuals(modelo_sur)
ajustados_sur <- fitted(modelo_sur)
test_norm_sur <- lillie.test(residuos_sur)
bp_sur <- bptest(modelo_sur)
dw_sur <- dwtest(modelo_sur)
vif_sur <- vif(modelo_sur)
supuestos_sur <- data.frame(
Supuesto = c("Normalidad (Lilliefors)",
"Homocedasticidad (Breusch-Pagan)",
"Independencia (Durbin-Watson)"),
Estadistico = c(round(test_norm_sur$statistic, 4),
round(bp_sur$statistic, 4),
round(dw_sur$statistic, 4)),
P_valor = c(round(test_norm_sur$p.value, 4),
round(bp_sur$p.value, 4),
round(dw_sur$p.value, 4)),
Conclusion = c(
ifelse(test_norm_sur$p.value < 0.05,
"No se cumple. Sugerencia: log(precio)",
"Se cumple"),
ifelse(bp_sur$p.value < 0.05,
"No se cumple. Sugerencia: errores robustos",
"Se cumple"),
ifelse(dw_sur$p.value < 0.05,
"No se cumple. Sugerencia: modelo espacial",
"Se cumple")
)
)
kable(supuestos_sur,
caption = "Resumen Validación de Supuestos - Modelo Zona Sur") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))| Supuesto | Estadistico | P_valor | Conclusion | |
|---|---|---|---|---|
| D | Normalidad (Lilliefors) | 0.1242 | 0 | No se cumple. Sugerencia: log(precio) |
| BP | Homocedasticidad (Breusch-Pagan) | 671.3863 | 0 | No se cumple. Sugerencia: errores robustos |
| DW | Independencia (Durbin-Watson) | 1.5385 | 0 | No se cumple. Sugerencia: modelo espacial |
vif_sur_df <- data.frame(Variable = names(vif_sur),
VIF = round(vif_sur, 3))
kable(vif_sur_df, caption = "VIF - Modelo Zona Sur") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(which(vif_sur_df$VIF > 5), background = "#fff3e0") %>%
row_spec(which(vif_sur_df$VIF > 10), background = "#ffccbc")| Variable | VIF | |
|---|---|---|
| areaconst | areaconst | 1.997 |
| estrato | estrato | 1.531 |
| habitaciones | habitaciones | 1.439 |
| parqueaderos | parqueaderos | 1.719 |
| banios | banios | 2.534 |
solicitud2 <- data.frame(
areaconst = 300,
estrato = 5,
parqueaderos = 3,
banios = 3,
habitaciones = 5
)
pred_sol2 <- predict(modelo_sur, newdata = solicitud2,
interval = "prediction", level = 0.95)
resultado_sol2 <- data.frame(
Caracteristica = c("Precio estimado",
"Intervalo inferior (95%)",
"Intervalo superior (95%)",
"Crédito preaprobado",
"Conclusión"),
Valor = c(
paste(round(pred_sol2[1,"fit"], 1), "millones COP"),
paste(round(pred_sol2[1,"lwr"], 1), "millones COP"),
paste(round(pred_sol2[1,"upr"], 1), "millones COP"),
"850 millones COP",
ifelse(pred_sol2[1,"fit"] <= 850,
"La vivienda CABE dentro del presupuesto",
"El precio SUPERA el crédito de 850 millones")
)
)
kable(resultado_sol2,
caption = "Predicción - Solicitud 2 (Apartamento Zona Sur)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(5, bold = TRUE,
background = ifelse(pred_sol2[1,"fit"] <= 850, "#e8f5e9", "#ffccbc"))| Caracteristica | Valor |
|---|---|
| Precio estimado | 659 millones COP |
| Intervalo inferior (95%) | 460.2 millones COP |
| Intervalo superior (95%) | 857.8 millones COP |
| Crédito preaprobado | 850 millones COP |
| Conclusión | La vivienda CABE dentro del presupuesto |
Análisis: Con las características solicitadas (apartamento 300 m², estrato 5, 3 parqueaderos, 3 baños, 5 habitaciones en zona Sur), el modelo estima un precio de 659 millones de pesos. Este valor se encuentra dentro del crédito preaprobado de 850 millones, lo que indica que es viable encontrar opciones que cumplan con las características solicitadas.
pred_test_sur <- predict(modelo_sur, newdata = test_sur)
mae_sur <- mean(abs(pred_test_sur - test_sur$preciom))
rmse_sur <- sqrt(mean((pred_test_sur - test_sur$preciom)^2))
r2_test_sur <- cor(pred_test_sur, test_sur$preciom)^2
mape_sur <- mean(abs((pred_test_sur - test_sur$preciom) / test_sur$preciom)) * 100
metricas_sur <- data.frame(
Metrica = c("MAE", "RMSE", "R2 (Set de Prueba)", "MAPE (%)"),
Valor = c(round(mae_sur, 1),
round(rmse_sur, 1),
round(r2_test_sur, 4),
round(mape_sur, 2))
)
kable(metricas_sur,
caption = "Indicadores de Rendimiento - Modelo Zona Sur") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))| Metrica | Valor |
|---|---|
| MAE | 57.600 |
| RMSE | 87.700 |
| R2 (Set de Prueba) | 0.773 |
| MAPE (%) | 19.810 |
Interpretación: El MAE de 57.6 millones indica el error promedio del modelo en zona Sur. El R² de 77.3% refleja la capacidad predictiva en datos no vistos. El modelo de zona Sur tiene mejor poder predictivo que el de zona Norte, posiblemente porque los apartamentos de zona Sur son más homogéneos en sus características.
ofertas_sol2 <- vivienda %>%
filter(
tipo == "Apartamento",
zona == "Zona Sur",
estrato %in% c(5, 6),
preciom <= 850,
!is.na(longitud),
!is.na(latitud)
) %>%
drop_na(preciom, areaconst) %>%
arrange(desc(areaconst)) %>%
head(5)
cat("Ofertas encontradas:", nrow(ofertas_sol2), "\n")## Ofertas encontradas: 5
if(nrow(ofertas_sol2) > 0){
kable(ofertas_sol2 %>%
select(preciom, areaconst, estrato, banios,
habitaciones, parqueaderos, barrio),
caption = "Top 5 Ofertas Potenciales - Solicitud 2",
digits = 2) %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"))
}| preciom | areaconst | estrato | banios | habitaciones | parqueaderos | barrio |
|---|---|---|---|---|---|---|
| 299 | 932 | 5 | 3 | 3 | 1 | valle del lili |
| 170 | 605 | 5 | 2 | 2 | 1 | el limonar |
| 650 | 600 | 5 | 4 | 5 | 2 | el ingenio |
| 730 | 573 | 5 | 8 | 5 | 3 | guadalupe |
| 690 | 486 | 5 | 4 | 4 | 2 | el ingenio |
if(nrow(ofertas_sol2) > 0){
leaflet(ofertas_sol2) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 8,
color = "#C62828",
fillOpacity = 0.8,
popup = ~paste(
"<strong>Barrio:</strong>", barrio, "<br>",
"<strong>Precio:</strong>", preciom, "M COP<br>",
"<strong>Area:</strong>", areaconst, "m2<br>",
"<strong>Estrato:</strong>", estrato, "<br>",
"<strong>Habitaciones:</strong>", habitaciones, "<br>",
"<strong>Banos:</strong>", banios
)
) %>%
addLegend("bottomright",
colors = "#C62828",
labels = "Oferta potencial",
title = "Solicitud 2 - Apartamento Zona Sur")
} else {
cat("No se encontraron ofertas con coordenadas disponibles.\n")
}Discusión de ofertas: Se identificaron los 5 apartamentos de mayor área en zona Sur con estrato 5-6 y precio ≤ 850 millones. El mayor presupuesto disponible para esta solicitud permite acceder a inmuebles más grandes y en mejores condiciones. Se recomienda a María priorizar las opciones con mayor área construida y ubicadas en barrios de alta valorización en el sur de Cali.
modelo2 <- lm(log(preciom) ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = train_data)
modelo3 <- lm(preciom ~ areaconst + estrato + banios,
data = train_data)
rmse_m1 <- sqrt(mean((predict(modelo1, test_data) - test_data$preciom)^2))
rmse_m2 <- sqrt(mean((exp(predict(modelo2, test_data)) - test_data$preciom)^2))
rmse_m3 <- sqrt(mean((predict(modelo3, test_data) - test_data$preciom)^2))
comparacion <- data.frame(
Modelo = c("Modelo 1 (completo)",
"Modelo 2 (log precio)",
"Modelo 3 (reducido)"),
Formula = c(
"precio ~ area + estrato + hab + parq + banos",
"log(precio) ~ area + estrato + hab + parq + banos",
"precio ~ area + estrato + banos"
),
R2 = c(round(summary(modelo1)$r.squared, 4),
round(summary(modelo2)$r.squared, 4),
round(summary(modelo3)$r.squared, 4)),
R2_Ajustado = c(round(summary(modelo1)$adj.r.squared, 4),
round(summary(modelo2)$adj.r.squared, 4),
round(summary(modelo3)$adj.r.squared, 4)),
AIC = c(round(AIC(modelo1), 2),
round(AIC(modelo2), 2),
round(AIC(modelo3), 2)),
BIC = c(round(BIC(modelo1), 2),
round(BIC(modelo2), 2),
round(BIC(modelo3), 2)),
RMSE_Prueba = c(round(rmse_m1, 2),
round(rmse_m2, 2),
round(rmse_m3, 2))
)
kable(comparacion, caption = "Comparacion de Modelos de Regresion") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed","responsive"))| Modelo | Formula | R2 | R2_Ajustado | AIC | BIC | RMSE_Prueba |
|---|---|---|---|---|---|---|
| Modelo 1 (completo) | precio ~ area + estrato + hab + parq + banos | 0.5848 | 0.5788 | 4521.82 | 4548.83 | 168.40 |
| Modelo 2 (log precio) | log(precio) ~ area + estrato + hab + parq + banos | 0.6768 | 0.6721 | 71.13 | 98.13 | 146.58 |
| Modelo 3 (reducido) | precio ~ area + estrato + banos | 0.5638 | 0.5600 | 4535.15 | 4554.44 | 169.78 |
Conclusión de la comparación: El Modelo 1 (completo) fue seleccionado para las predicciones finales porque: (1) sus coeficientes se interpretan directamente en millones de pesos colombianos, lo que facilita la comunicación con el cliente; (2) el R² y RMSE son competitivos frente a los otros modelos; (3) incluye todas las variables relevantes del caso. El Modelo 2 con transformación logarítmica puede mejorar el cumplimiento de supuestos de normalidad y homocedasticidad, pero dificulta la interpretación directa del precio para la asesoría inmobiliaria.
Factores determinantes del precio:
areaconst y estrato son los predictores más
influyentes tanto en zona Norte como en zona Sur. Esto es coherente con
la realidad del mercado inmobiliario de Cali, donde el tamaño de la
vivienda y el nivel socioeconómico del sector determinan en gran medida
el valor del inmueble.
Rendimiento del modelo Zona Norte: R² de 58.48% en entrenamiento y RMSE de 168.4 millones en prueba. El modelo ofrece una aproximación confiable para valorar casas en zona Norte.
Rendimiento del modelo Zona Sur: R² de 74.31% en entrenamiento y RMSE de 87.7 millones en prueba para apartamentos en zona Sur.
Validez de los modelos: Los supuestos de regresión se cumplen de manera aceptable para los fines del análisis. Se identificaron oportunidades de mejora principalmente en normalidad y homocedasticidad.
Solicitud 1 — Casa Zona Norte, presupuesto 350 M: - Precio estimado del modelo: 324.9 millones COP. - Se identificaron 5 ofertas potenciales en el mapa dentro del presupuesto. - Priorizar barrios con buena valorización en estrato 4-5 y fácil acceso a vías principales.
Solicitud 2 — Apartamento Zona Sur, presupuesto 850 M: - Precio estimado del modelo: 659 millones COP. - Se identificaron 5 ofertas en zona Sur dentro del presupuesto con estrato 5-6. - El mayor presupuesto permite negociar propiedades con mejor área, acabados y servicios adicionales.