Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.
Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.
Descripción de las variables:
El cliente solicita asesoria para adquirir una casa de 200 m^2 1 parqueadero, 2 baños, 4 habitaciones en la zona norte estrato 4 o 5.
library(paqueteMODELOS)
library(dplyr)
data("vivienda")
df=vivienda
#LIMPIEZA DE DATOS
base <- df %>%
select(-id)
base <- base |> dplyr::filter(!is.na(zona))
# Calcular la moda (función auxiliar)
get_mode <- function(v) {
uniqv <- na.omit(unique(v))
uniqv[which.max(tabulate(match(v, uniqv)))]
}
# Imputar NA en parqueaderos con la moda
base$parqueaderos[is.na(base$parqueaderos)] <- get_mode(base$parqueaderos)
#Imputar NA en piso con "Other"
base$piso[is.na(base$piso)] <- "Other"
base <- base %>%
mutate(across(where(is.character),as.factor))1.1. A continuación se realizará un filtro a la base de datos que incluya solo las ofertas de: casas, de la zona norte de la ciudad. Y se mostrará los primeros 3 registros de las bases y algunas tablas que comprueben la consulta.Adicional a lo anterior, se muestra un mapa con los puntos donde se ubican las casa zona norte. Además se presenta un análisis de la ubicación de los puntos y si se estos corresponden o no a la zona filtrada y por qué?.
library(dplyr)
library(mice)
library(ggplot2)
base$D_norte = as.numeric(base$zona=="Zona Norte")
base$D_sur = as.numeric(base$zona=="Zona Sur")
base$D_casa = as.numeric(base$tipo=="Casa")
base$preciom = as.numeric(base$preciom)
base$areaconst = as.numeric(base$areaconst)
base$habitaciones = as.numeric(base$habitaciones)
base$latitud = as.numeric(base$latitud)
base$longitud = as.numeric(base$longitud)
#Escenario analizando las casas de la zona norte
base1 <- base %>%
filter(tipo == "Casa",
zona == "Zona Norte")
head(base1, 3)## # A tibble: 3 × 15
## zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo
## <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <fct>
## 1 Zona N… 02 5 320 150 2 4 6 Casa
## 2 Zona N… 02 5 780 380 2 3 3 Casa
## 3 Zona N… 02 6 750 445 1 7 6 Casa
## # ℹ 6 more variables: barrio <fct>, longitud <dbl>, latitud <dbl>,
## # D_norte <dbl>, D_sur <dbl>, D_casa <dbl>
##
## Apartamento Casa
## Zona Centro 0 0
## Zona Norte 0 722
## Zona Oeste 0 0
## Zona Oriente 0 0
## Zona Sur 0 0
##
## Apartamento Casa
## 3 0 235
## 4 0 161
## 5 0 271
## 6 0 55
#Generar mapa con los puntos de las ofertas
ggplot(base1, aes(x = longitud, y = latitud)) +
geom_point(color = "blue", size = 3, alpha = 0.6) +
labs(title = "Ubicación de casas en Zona Norte",
x = "Longitud", y = "Latitud") +
theme_minimal()
En las tablas se confirma que la base de datos fue correctamente
filtrada según las variables de interés: Casas ubicadas en la Zona
Norte, junto con los estratos correspondientes. Sin embargo, al analizar
el mapa de ubicación geográfica se observa que algunos inmuebles
aparecen fuera de los límites esperados de la zona norte. Esta situación
puede explicarse por posibles errores en la digitación de las
coordenadas de latitud y longitud, o por una asignación inadecuada de la
zona a ciertos registros.
Para mejorar la consistencia de la información, se definió un rango representativo de la Zona Norte a partir de los valores de latitud y longitud, tomando como referencia la distribución de todas las zonas. Con este criterio fue posible clasificar los registros en dos grupos: aquellos que se encuentran dentro del rango válido de la Zona Norte y los que se ubican por fuera, lo que permitirá depurar la base de datos y garantizar un análisis más confiable.
Esto se muestra a continuación:
# --- 1) Rangos por zona usando TODA la base (robusto 1%-99%) ---
rangos_por_zona <- base %>%
group_by(zona) %>%
summarise(
lat_min = quantile(latitud, 0.01, na.rm = TRUE),
lat_max = quantile(latitud, 0.99, na.rm = TRUE),
lon_min = quantile(longitud, 0.01, na.rm = TRUE),
lon_max = quantile(longitud, 0.99, na.rm = TRUE),
.groups = "drop"
)
# --- 2) Extraer los límites de la Zona Norte (definidos con toda la base) ---
lim_norte <- rangos_por_zona %>% filter(zona == "Zona Norte")
lat_min <- lim_norte$lat_min; lat_max <- lim_norte$lat_max
lon_min <- lim_norte$lon_min; lon_max <- lim_norte$lon_max
# --- 3) Clasificar base1 en Dentro/Fuera ---
base1_flag <- base1 %>%
mutate(
latitud = as.numeric(latitud),
longitud = as.numeric(longitud),
grupo_rango = if_else(
latitud >= lat_min & latitud <= lat_max &
longitud >= lon_min & longitud <= lon_max,
"Dentro del rango (def. global)", "Fuera del rango (def. global)"
)
)
# --- 4) Tabla de validación ---
tab_resumen <- base1_flag %>%
count(grupo_rango, name = "n") %>%
mutate(pct = round(100 * n / sum(n), 1))
print(tab_resumen)## # A tibble: 2 × 3
## grupo_rango n pct
## <chr> <int> <dbl>
## 1 Dentro del rango (def. global) 691 95.7
## 2 Fuera del rango (def. global) 31 4.3
# --- 5) Mapa coloreado + rectángulo de referencia ---
ggplot(base1_flag, aes(x = longitud, y = latitud)) +
annotate("rect",
xmin = lon_min, xmax = lon_max,
ymin = lat_min, ymax = lat_max,
fill = NA, linetype = 2, linewidth = 0.9) +
geom_point(aes(color = grupo_rango), alpha = 0.8, size = 2) +
labs(
title = "Casas - Zona Norte: Dentro/Fuera del rango (definido con toda la base)",
subtitle = paste0("Lat[", round(lat_min,4), ", ", round(lat_max,4),
"] • Lon[", round(lon_min,4), ", ", round(lon_max,4), "]"),
x = "Longitud", y = "Latitud", color = ""
) +
theme_minimal() +
theme(legend.position = "top")1.2. Se muestra un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, numero de baños y numero de habitaciones. Los gráficos interactivos con el paquete plotly e interprete los resultados, nos ayudarán a visualizar mejor este análisis.
# Paquetes
library(dplyr)
library(ggplot2)
library(plotly)
# Datos (ajusta el nombre si es distinto)
df <- base1_in %>%
rename(precio = preciom,
area = areaconst,
banios = banios,
habitaciones = habitaciones,
estrato = estrato) %>%
mutate(estrato = as.numeric(estrato)) %>%
filter(!is.na(precio), !is.na(area), !is.na(estrato),
!is.na(banios), !is.na(habitaciones))
# 1) Heatmap de correlaciones (numéricas)
numvars <- df %>% select(precio, area, banios, habitaciones, estrato)
M <- round(cor(numvars), 2)
plot_ly(z = M, x = colnames(M), y = rownames(M),
type = "heatmap", colors = "RdBu", reversescale = TRUE,
hovertemplate = "<b>%{y} vs %{x}</b><br>Corr = %{z}<extra></extra>") %>%
layout(title = "Matriz de correlación (interactiva)", xaxis = list(title=""), yaxis = list(title=""))# 2) Matriz de dispersión (splom)
plot_ly(df, type = "splom",
dimensions = list(
list(label="Precio", values=~precio),
list(label="Área", values=~area),
list(label="Baños", values=~banios),
list(label="Habitaciones", values=~habitaciones)
),
text = ~paste("Estrato:", estrato),
marker = list(size = 4, line = list(width = 0.5, color = 'rgba(0,0,0,0.5)')),
showupperhalf = FALSE) %>%
layout(title = "Matriz de dispersión interactiva")La matriz de correlación evidencia que el precio de la vivienda presenta asociación positiva con varias variables: en particular con el estrato, el número de baños y, de forma más fuerte, con el área construida. Esta relación podría sugerir la presencia de multicolinealidad entre las variables explicativas.
1.3. Se estima un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) y se interpreta los coeficientes si son estadísticamente significativos. Las interpretaciones están contextualizadas y se discuten si los resultados son lógicos. Adicionalmente se interpreta el coeficiente R2 ajustado del modelo y sus implicaciones. Adicional se presentan estrategias para mejorar el modelo.
mod <- lm(preciom~areaconst+estrato+banios+habitaciones+parqueaderos, data = base1_in)
car::vif(mod)## areaconst estrato banios habitaciones parqueaderos
## 1.519405 1.442281 1.933616 1.657192 1.219939
Para verificarlo, se aplicó el análisis del Factor de Inflación de la Varianza (VIF), cuyos valores obtenidos se ubicaron entre 1 y 2 para todas las variables evaluadas. Estos resultados indican que, si bien existen correlaciones relativamente altas, no se presenta un riesgo significativo de multicolinealidad en el modelo, ya que los valores de VIF se mantienen dentro de rangos aceptables.
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = base1_in)
##
## Residuals:
## Min 1Q Median 3Q Max
## -914.93 -75.19 -17.28 42.12 1095.01
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -207.05930 29.67341 -6.978 7.08e-12 ***
## areaconst 0.77957 0.04377 17.810 < 2e-16 ***
## estrato 76.00725 7.28081 10.439 < 2e-16 ***
## banios 24.11788 5.38355 4.480 8.75e-06 ***
## habitaciones 3.28582 4.08226 0.805 0.4212
## parqueaderos 12.06433 5.16577 2.335 0.0198 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 153.7 on 685 degrees of freedom
## Multiple R-squared: 0.64, Adjusted R-squared: 0.6374
## F-statistic: 243.6 on 5 and 685 DF, p-value: < 2.2e-16
El modelo es globalmente significativo (F = 243.6 y p-valor < 2.2e-16) y si bien explica un 63.7% de la variabilidad del precio de la vivienda (R² ajustado,todavía queda un 36.3% de variabilidad explicada por factores no incluidos.
En cuanto a los coeficientes, el intercepto (-207.05, p < 0.001) es estadísticamente significativo pero carece de interpretación práctica, ya que implicaría un precio negativo. Por otro lado, el área construida (0.779, p < 2e-16), el estrato (76.0, p < 2e-16), el número de baños (24.11, p = 8.75e-06) y los parqueaderos (12.06, p = 0.0198) son predictores altamente significativos. En contraste, el número de habitaciones (3.28, p = 0.4212) no resulta significativo, lo que indica que su efecto ya está explicado por las demás variables.
En conclusión, el modelo es estadísticamente sólido y útil para explicar el precio de la vivienda. No obstante, se recomienda excluir la variable habitaciones para obtener un modelo más óptimo y con mejor interpretabilidad.Así que a continuación se trabajará con el modelo ajustado (mod1).
## areaconst estrato banios parqueaderos
## 1.456003 1.355952 1.409565 1.212836
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + parqueaderos,
## data = base1_in)
##
## Residuals:
## Min 1Q Median 3Q Max
## -907.88 -75.94 -17.52 43.86 1102.92
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -196.65281 26.70186 -7.365 5.10e-13 ***
## areaconst 0.78676 0.04284 18.366 < 2e-16 ***
## estrato 74.57350 7.05774 10.566 < 2e-16 ***
## banios 26.37375 4.59530 5.739 1.43e-08 ***
## parqueaderos 12.38161 5.14938 2.404 0.0165 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 153.6 on 686 degrees of freedom
## Multiple R-squared: 0.6397, Adjusted R-squared: 0.6376
## F-statistic: 304.4 on 4 and 686 DF, p-value: < 2.2e-16
\[precio=-196.65.1+0.79*area+74.57*estrato+26.3*baños+12.38*parqueaderos\] #### PASO 4 1.4. Se rRealizar la validación de supuestos del modelo y se interpreta los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).
library(lmtest)
library(DescTools)
library(nortest)
res<- residuals(mod1)
# 1) Gráficos base
par(mfrow=c(2,2)); plot(mod1); par(mfrow=c(1,1)) # Resid vs Fitted, QQ, Scale-Location, Cook's##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: res
## D = 0.14461, p-value < 2.2e-16
##
## Anderson-Darling normality test
##
## data: res
## A = 27.467, p-value < 2.2e-16
##
## Shapiro-Francia normality test
##
## data: res
## W = 0.80854, p-value < 2.2e-16
## areaconst estrato banios parqueaderos
## 1.456003 1.355952 1.409565 1.212836
# 4) Observaciones influyentes
cd <- cooks.distance(mod1)
influ <- which(cd > 4/(nrow(base1_in) - length(coef(mod1)))) # regla de dedo
length(influ); sort(cd[influ], decreasing = TRUE)[1:10]## [1] 41
## 603 168 673 485 187 484 63
## 1.11252278 0.22457677 0.19830981 0.14950212 0.12588493 0.11817209 0.09391606
## 563 473 100
## 0.08421283 0.06243313 0.06110122
##
## RESET test
##
## data: mod1
## RESET = 22.027, df1 = 2, df2 = 684, p-value = 5.36e-10
La normalidad de los residuos se evaluó con Lilliefors (D=0.144, p<2.2e−16), Anderson–Darling (A²=27.467, p<2.2e−16) y Shapiro–Francia (W=0.808, p<2.2e−16). En los tres casos se rechaza la hipótesis de normalidad, con evidencia de colas pesadas. Para la inferencia se recomienda reportar errores estándar robustos (HC3) y, como análisis complementario, considerar la transformación logarítmica de la respuesta y/o la inclusión de términos no lineales en ‘areaconst’
1.5.Se predice el precio de la vivienda con las características de la primera solicitud.
library(lmtest)
library(sandwich)
new4 <- data.frame(areaconst=200, estrato=4, banios=2, parqueaderos=1)
new5 <- data.frame(areaconst=200, estrato=5, banios=2, parqueaderos=1)
# Predicción + intervalo de predicción (caso individual)
predict(mod1, new4, interval="prediction")## fit lwr upr
## 1 324.123 22.00617 626.2398
## fit lwr upr
## 1 398.6965 96.13148 701.2615
# Probabilidad de estar ≤ 350 millones
p4 <- predict(mod1, new4, se.fit=TRUE)
p5 <- predict(mod1, new5, se.fit=TRUE)
sigma <- summary(mod1)$sigma
sd4 <- sqrt(p4$se.fit^2 + sigma^2); prob4 <- pnorm(350, mean=p4$fit, sd=sd4)
sd5 <- sqrt(p5$se.fit^2 + sigma^2); prob5 <- pnorm(350, mean=p5$fit, sd=sd5)
prob4; prob5 # ~0.57 y ~0.38## [1] 0.5667761
## [1] 0.3759994
Para una casa de \[200 m^2\]2 , 2 baños y 1 parqueadero, la probabilidad de que el precio sea inferior a 350 millones es \[56,7 %\] en estrato 4 y \[37,5 %\] en estrato 5, según el modelo estimado (predicción para un inmueble individual).
1.6. Con las predicciones del modelo se sugiere potenciales ofertas que responda a la solicitud de la vivienda “Casas zona Norte”. Teniendo en cuentaa que la empresa tiene crédito pre-aprobado de máximo 350 millones de pesos. Se presentaa un análisis y un mapa con 5 ofertas potenciales.
# Paquetes
library(dplyr)
library(leaflet)
library(scales)
# Parámetros
credito <- 350
area_obj <- 200; ban_obj <- 2; parq_obj <- 1
estr_validos <- c(4,5) # numéricos
# Candidatos desde base1_in (asegura estrato numérico)
cand <- base1_in %>%
filter(!is.na(latitud), !is.na(longitud)) %>%
mutate(
estrato = as.numeric(as.character(estrato)), # fuerza numérico (seguro si venía factor)
area_gap = abs(areaconst - area_obj)
) %>%
filter(estrato %in% estr_validos,
banios >= ban_obj,
parqueaderos >= parq_obj)
# Predicción y probabilidad ≤ 350 (usa tu mod1 entrenado con estrato numérico)
p <- predict(mod1, cand, se.fit = TRUE)
sigma <- summary(mod1)$sigma
cand <- cand %>%
mutate(
fit = as.numeric(p$fit),
sd_pred = sqrt(p$se.fit^2 + sigma^2),
prob_le350 = pnorm(credito, mean = fit, sd = sd_pred),
score = 0.6*prob_le350 + 0.4*rescale(-area_gap, to = c(0,1))
) %>%
arrange(desc(score))
# Top 5 y mapa
oferta <- cand %>%
slice_head(n = 5) %>%
transmute(
lat = latitud,
long = longitud,
popup = sprintf(
"<b>Área:</b> %s m²<br><b>Baños:</b> %s<br><b>Parq.:</b> %s<br><b>Estrato:</b> %s<br>
<b>Precio pred.:</b> %.1f<br><b>Prob(≤350 M):</b> %s",
areaconst, banios, parqueaderos, estrato,
fit, percent(prob_le350, 0.1))
)
leaflet(oferta) %>% addTiles() %>%
addMarkers(lng=~long, lat=~lat, popup=~popup)El cliente solicita asesoria para adquirir un apartamento de 300 m^2, 3 parqueadero, 3 baños, 5 habitaciones en la zona Sur en estrato 5 o 6.
1.1. A continuación se realizará un filtro a la base de datos que incluya solo las ofertas de: casas, de la zona norte de la ciudad. Y se mostrará los primeros 3 registros de las bases y algunas tablas que comprueben la consulta.Adicional a lo anterior, se muestra un mapa con los puntos donde se ubican las casa zona norte. Además se presenta un análisis de la ubicación de los puntos y si se estos corresponden o no a la zona filtrada y por qué?.
#Escenario analizando los apartamentos de la zona Sur
base2 <- base %>%
filter(tipo == "Apartamento",
zona == "Zona Sur")
head(base2, 3)## # A tibble: 3 × 15
## zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo
## <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <fct>
## 1 Zona S… 05 4 290 96 1 2 3 Apar…
## 2 Zona S… 02 3 78 40 1 1 2 Apar…
## 3 Zona S… Other 6 875 194 2 5 3 Apar…
## # ℹ 6 more variables: barrio <fct>, longitud <dbl>, latitud <dbl>,
## # D_norte <dbl>, D_sur <dbl>, D_casa <dbl>
##
## Apartamento Casa
## Zona Centro 0 0
## Zona Norte 0 0
## Zona Oeste 0 0
## Zona Oriente 0 0
## Zona Sur 2787 0
##
## Apartamento Casa
## 3 201 0
## 4 1091 0
## 5 1033 0
## 6 462 0
#Generar mapa con los puntos de las ofertas
ggplot(base2, aes(x = longitud, y = latitud)) +
geom_point(color = "blue", size = 3, alpha = 0.6) +
labs(title = "Ubicación de apartamentos en Zona Sur",
x = "Longitud", y = "Latitud") +
theme_minimal()En las tablas se confirma que la base de datos fue correctamente filtrada según las variables de interés: Apartamentos ubicados en la Zona Sur, junto con los estratos correspondientes. Sin embargo, al analizar el mapa de ubicación geográfica se observa que algunos inmuebles aparecen fuera de los límites esperados de la zona sur.
Para mejorar la consistencia de la información, se tomará el análisis previo donde se realiza un rango representativo de la Zona Sur a partir de los valores de latitud y longitud, tomando como referencia la distribución de todas las zonas.En la gráfica siguiente se podrá visualizar la clasificaicón de los registros en dos grupos: aquellos que se encuentran dentro del rango válido de la Zona Sur y los que se ubican por fuera, lo que permitirá depurar la base de datos y garantizar un análisis más confiable.
# --- 1) Extraer los límites de la Zona Sur ---
lim_sur <- rangos_por_zona %>% filter(zona == "Zona Sur")
lat_min_s <- lim_sur$lat_min; lat_max_s <- lim_sur$lat_max
lon_min_s <- lim_sur$lon_min; lon_max_s <- lim_sur$lon_max
# --- 2) Clasificar base2 en Dentro/Fuera ---
base2_flag <- base2 %>%
mutate(
latitud = as.numeric(latitud),
longitud = as.numeric(longitud),
grupo_rango = if_else(
latitud >= lat_min_s & latitud <= lat_max_s &
longitud >= lon_min_s & longitud <= lon_max_s,
"Dentro del rango (def. global)", "Fuera del rango (def. global)"
)
)
# --- 3) Tabla de validación ---
tab_resumen <- base2_flag %>%
count(grupo_rango, name = "n") %>%
mutate(pct = round(100 * n / sum(n), 1))
print(tab_resumen)## # A tibble: 2 × 3
## grupo_rango n pct
## <chr> <int> <dbl>
## 1 Dentro del rango (def. global) 2681 96.2
## 2 Fuera del rango (def. global) 106 3.8
# --- 4) Mapa coloreado + rectángulo de referencia ---
ggplot(base2_flag, aes(x = longitud, y = latitud)) +
annotate("rect",
xmin = lon_min_s, xmax = lon_max_s,
ymin = lat_min_s, ymax = lat_max_s,
fill = NA, linetype = 2, linewidth = 0.9) +
geom_point(aes(color = grupo_rango), alpha = 0.8, size = 2) +
labs(
title = "Apartamentos - Zona Sur: Dentro/Fuera del rango",
subtitle = paste0("Lat[", round(lat_min_s,4), ", ", round(lat_max_s,4),
"] • Lon[", round(lon_min_s,4), ", ", round(lon_max_s,4), "]"),
x = "Longitud", y = "Latitud", color = ""
) +
theme_minimal() +
theme(legend.position = "top")2.2. Se muestra un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, numero de baños y numero de habitaciones. Los gráficos interactivos con el paquete plotly e interprete los resultados, nos ayudarán a visualizar mejor este análisis.
library(dplyr)
library(ggplot2)
library(plotly)
# Datos
df1 <- base2_in %>%
rename(precio = preciom,
area = areaconst,
banios = banios,
habitaciones = habitaciones,
estrato = estrato,
parqueaderos=parqueaderos) %>%
mutate(estrato = as.numeric(estrato))
# 1) Heatmap de correlaciones (numericas)
numvars <- df1 %>% select(precio, area, banios, habitaciones, estrato, parqueaderos)
M <- round(cor(numvars), 2)
plot_ly(z = M, x = colnames(M), y = rownames(M),
type = "heatmap", colors = "RdBu", reversescale = TRUE,
hovertemplate = "<b>%{y} vs %{x}</b><br>Corr = %{z}<extra></extra>") %>%
layout(title = "Matriz de correlación (interactiva)", xaxis = list(title=""), yaxis = list(title=""))# 2) Matriz de dispersión (splom)
plot_ly(df1, type = "splom",
dimensions = list(
list(label="Precio", values=~precio),
list(label="Área", values=~area),
list(label="Baños", values=~banios),
list(label="Habitaciones", values=~habitaciones)
),
text = ~paste("Estrato:", estrato),
marker = list(size = 4, line = list(width = 0.5, color = 'rgba(0,0,0,0.5)')),
showupperhalf = FALSE) %>%
layout(title = "Matriz de dispersión interactiva")La matriz de correlación evidencia que el precio de la vivienda presenta asociación positiva con varias variables: en particular con el area (0.75) el estrato (0.67), el número de baños (0.72), el número de parqueaderos (0.69) y, en contraste el precio presenta una correclación débil con respecto a las habitaciones(0.33). Es necesari realizar un análisis de los datos con el objetivo de revisar la ausencia de multicolinealidad entre las variables explicativas.
2.3. Se estima un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) y se interpreta los coeficientes si son estadísticamente significativos. Las interpretaciones están contextualizadas y se discuten si los resultados son lógicos. Adicionalmente se interpreta el coeficiente R2 ajustado del modelo y sus implicaciones. Adicional se presentan estrategias para mejorar el modelo.
mod_as <- lm(preciom~areaconst+estrato+banios+habitaciones+parqueaderos, data = base2_in)
car::vif(mod_as)## areaconst estrato banios habitaciones parqueaderos
## 2.065086 1.605103 2.488858 1.430858 1.728139
Para verificarlo, se aplicó el análisis del Factor de Inflación de la Varianza (VIF), cuyos valores obtenidos se ubicaron entre 1 y 3 para todas las variables evaluadas. Estos resultados indican que, si bien existen correlaciones relativamente altas, no se presenta un riesgo significativo de multicolinealidad en el modelo, ya que los valores de VIF se mantienen dentro de rangos aceptables.
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = base2_in)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1126.16 -38.70 -3.12 37.50 926.79
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -265.51276 13.07641 -20.305 < 2e-16 ***
## areaconst 1.32968 0.04887 27.209 < 2e-16 ***
## estrato 59.27404 2.70806 21.888 < 2e-16 ***
## banios 45.50576 3.05295 14.906 < 2e-16 ***
## habitaciones -18.30889 3.37951 -5.418 6.58e-08 ***
## parqueaderos 73.45822 3.71351 19.781 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 93.04 on 2675 degrees of freedom
## Multiple R-squared: 0.7621, Adjusted R-squared: 0.7617
## F-statistic: 1714 on 5 and 2675 DF, p-value: < 2.2e-16
El modelo es globalmente significativo (F = 1714 y p-valor < 2.2e-16) y si bien explica un 76.21% de la variabilidad del precio de la vivienda (R² ajustado,todavía queda un 23.8% de variabilidad explicada por factores no incluidos.
En cuanto a los coeficientes, el intercepto (-265.5, p <2e-16) es estadísticamente significativo pero carece de interpretación práctica, ya que implicaría un precio negativo. Todos los coeficientes son altamente significativos, ya que todos los p-valor están muy cercanos al cero; área construida (1.33, p < 2e-16), el estrato (59.27, p < 2e-16), el número de baños (45.5, p < 2e-16), el número de habitaciones (-18.31, p < 6.58e-08) y los parqueaderos (73.46, p < 2e-16)) son predictores altamente significativos.
En conclusión, el modelo es estadísticamente sólido y útil para explicar el precio de la vivienda.El signo negativo de habitaciones requiere un análisis más profundo (puede ser un indicador de redundancia o un efecto real del mercado).
2.4. Se rRealizar la validación de supuestos del modelo y se interpreta los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).
# Carga las librerías
library(lmtest)
library(car)
res_2<- residuals(mod_as)
# 1) Gráficos base
par(mfrow=c(2,2)); plot(mod_as); par(mfrow=c(1,1)) # Resid vs Fitted, QQ, Scale-Location, Cook's##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: res_2
## D = 0.12258, p-value < 2.2e-16
##
## Anderson-Darling normality test
##
## data: res_2
## A = 87.74, p-value < 2.2e-16
##
## Shapiro-Francia normality test
##
## data: res_2
## W = 0.77979, p-value < 2.2e-16
##
## studentized Breusch-Pagan test
##
## data: mod_as
## BP = 847.34, df = 5, p-value < 2.2e-16
# 3) Observaciones influyentes
cd_2 <- cooks.distance(mod_as)
influ <- which(cd_2 > 4/(nrow(base2_in) - length(coef(mod_as)))) # regla de dedo
length(influ); sort(cd_2[influ], decreasing = TRUE)[1:10]## [1] 140
## 2292 2466 943 1071 851 977 330 329
## 7.1111955 1.2512521 1.1582207 0.2719995 0.2040933 0.1205729 0.1175533 0.1161038
## 1477 443
## 0.1123411 0.1091275
##
## RESET test
##
## data: mod_as
## RESET = 293.64, df1 = 2, df2 = 2673, p-value < 2.2e-16
En conclusión, el modelo actual presenta heterocedasticidad significativa (BP test con p < 2.2e-16), falta de normalidad en los residuos y problemas de especificación funcional (RESET test con p < 2.2e-16). Esto indica que el modelo lineal, en su forma actual, no logra capturar adecuadamente la relación entre las variables independientes y el precio de la vivienda, comprometiendo la validez de las inferencias.
Para mejorar el desempeño, se recomienda:
Reestimar el modelo aplicando una transformación logarítmica sobre la variable dependiente (log(preciom)), con el fin de estabilizar la varianza y aproximar la normalidad de los residuos.
Explorar modelos robustos y menos sensibles a outliers, tales como Random Forest, Árboles de Decisión o modelos robustos de regresión, que permiten capturar relaciones no lineales y posibles interacciones entre predictores.
Comparar los resultados de ajuste y diagnóstico entre el modelo transformado y los modelos alternativos, evaluando especialmente la mejora en la normalidad de los residuos, la homocedasticidad y la capacidad explicativa global.
2.5. Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.
library(lmtest)
library(sandwich)
new2 <- data.frame(areaconst=300, estrato=5, banios=3, parqueaderos=3, habitaciones=5)
new3 <- data.frame(areaconst=300, estrato=6, banios=3, parqueaderos=3, habitaciones=5)
# Predicción + intervalo de predicción (caso individual)
predict(mod_as, new2, interval="prediction")## fit lwr upr
## 1 695.1076 511.5487 878.6664
## fit lwr upr
## 1 754.3816 570.8095 937.9538
# Probabilidad de estar ≤ 850 millones
p2 <- predict(mod_as, new2, se.fit=TRUE)
p3 <- predict(mod_as, new3, se.fit=TRUE)
sigma <- summary(mod_as)$sigma
sd2 <- sqrt(p2$se.fit^2 + sigma^2); prob2 <- pnorm(850, mean=p2$fit, sd=sd2)
sd3 <- sqrt(p3$se.fit^2 + sigma^2); prob3 <- pnorm(850, mean=p3$fit, sd=sd3)
prob2; prob3 # ~0.57 y ~0.38## [1] 0.9509997
## [1] 0.8464582
Para un apartamento de 300 m^2 , 3 baños y 3 parqueadero, la probabilidad de que el precio sea inferior a 850 millones es 95.1 % en estrato 5 y 84,6% en estrato 6, según el modelo estimado (predicción para un inmueble individual).
2.6. Con las predicciones del modelo se sugiere potenciales ofertas que responda a la solicitud de la vivienda “Apartamentos zona Sur”. Teniendo en cuentaa que la empresa tiene crédito pre-aprobado de máximo 850 millones de pesos. Se presentaa un análisis y un mapa con 5 ofertas potenciales.
# Paquetes
library(dplyr)
library(leaflet)
library(scales)
# Parámetros
credito <- 850
area_obj <- 300; ban_obj <- 3; parq_obj <- 3
estr_validos <- c(4,5) # numéricos
# Candidatos desde base1_in (asegura estrato numérico)
cand <- base2_in %>%
filter(!is.na(latitud), !is.na(longitud)) %>%
mutate(
estrato = as.numeric(as.character(estrato)), # fuerza numérico (seguro si venía factor)
area_gap = abs(areaconst - area_obj)
) %>%
filter(estrato %in% estr_validos,
banios >= ban_obj,
parqueaderos >= parq_obj)
# Predicción y probabilidad ≤ 850 (usa tu mod2 entrenado con estrato numérico)
p <- predict(mod_as, cand, se.fit = TRUE)
sigma <- summary(mod_as)$sigma
cand <- cand %>%
mutate(
fit = as.numeric(p$fit),
sd_pred = sqrt(p$se.fit^2 + sigma^2),
prob_le350 = pnorm(credito, mean = fit, sd = sd_pred),
score = 0.6*prob_le350 + 0.4*rescale(-area_gap, to = c(0,1))
) %>%
arrange(desc(score))
# Top 5 y mapa
oferta <- cand %>%
slice_head(n = 5) %>%
transmute(
lat = latitud,
long = longitud,
popup = sprintf(
"<b>Área:</b> %s m²<br><b>Baños:</b> %s<br><b>Parq.:</b> %s<br><b>Estrato:</b> %s<br>
<b>Precio pred.:</b> %.1f<br><b>Prob(≤350 M):</b> %s",
areaconst, banios, parqueaderos, estrato,
fit, percent(prob_le350, 0.1))
)
leaflet(oferta) %>% addTiles() %>%
addMarkers(lng=~long, lat=~lat, popup=~popup)El análisis permitió depurar y validar la información, identificar las variables con mayor incidencia en el precio de las viviendas (área, estrato, baños y parqueaderos) y ajustar modelos con buen poder explicativo. Aunque se evidenciaron problemas en los supuestos clásicos, se plantearon alternativas de mejora mediante transformaciones y modelos robustos. Finalmente, se seleccionaron y mapearon las ofertas con mayor probabilidad de ajustarse al presupuesto disponible, entregando un insumo práctico y confiable para la toma de decisiones inmobiliarias.