Descripción de la actividad
Objetivo
Filtrado inicial y depuración de datos
Análisis exploratorio de datos
Estimación e interpretación del modelo
Validación de supuestos
Estudio para los apartamento del sur
8 Discusión de resultados
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.
Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:
library(paqueteMODELOS)
library(scales)
library(dplyr)
library(ggplot2)
library(kableExtra)
library(knitr)
Caracteristicas <- c("Tipo","área construida","parqueaderos","baños","habitaciones","estrato","zona","crédito preaprobado")
Vivienda_1 <- c("Casa","200","1","2","4","4 o 5","Norte","350 millones")
Vivienda_2 <-c("Apartamento","300","3","3","5","5 o 6","Sur","850 millones")
Tabla_enunciado <-data.frame(Caracteristicas,Vivienda_1,Vivienda_2)
colnames(Tabla_enunciado) <-c("Característica","Vivienda 1","Vivienda 2")
knitr::kable(Tabla_enunciado,
booktabs = TRUE,
caption = "<center><b><span style='font-size:18px'> Inmuebles de interés </span></b></center>",
) %>%
kable_styling(full_width = FALSE)| Característica | Vivienda 1 | Vivienda 2 |
|---|---|---|
| Tipo | Casa | Apartamento |
| área construida | 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 |
Proporcionar un análisis sustentado mediante técnicas modelación para aconsejar a los ejecutivos en la toma de desiciones en el sector inmobiliario.
Los datos proporcionados para realizar el estudio se presentan a continuación:
knitr::kable(vivienda[1:3,],
booktabs = TRUE,
format = "html",
caption = "<center><b><span style='font-size:18px'> Datos de oferta inmobiliaria </span></b></center>",
) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE)| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1147 | Zona Oriente | NA | 3 | 250 | 70 | 1 | 3 | 6 | Casa | 20 de julio | -76.51168 | 3.43382 |
| 1169 | Zona Oriente | NA | 3 | 320 | 120 | 1 | 2 | 3 | Casa | 20 de julio | -76.51237 | 3.43369 |
| 1350 | Zona Oriente | NA | 3 | 350 | 220 | 2 | 2 | 4 | Casa | 20 de julio | -76.51537 | 3.43566 |
Inicialmente como lo propone el ejercicio se realiza el filtrado de datos basándose en la zona norte de la ciudad y en el tipo de vivienda, adicionalmente se filtran las coordenadas teniendo en cuenta las delimitaciones de la zona proporcionadas por el IDESC.
La zona norte de Cali está comprendida desde la KR 1 entre la CL 1 OESTE hasta el separador vial ubicado entre las CL 25 y CL 26, vía por la cual se continúa hasta la KR 7, y desde este punto, siguiendo el trazado del corredor férreo hasta llegar a la CL 88, a partir de la cual, se continua hacia el norte por el límite del suelo urbano hasta finalizar en el punto de inicio en la KR 1. Aproximadamente, esto corresponde a longitudes mayores a “-76.54” y latitudes menores o iguales a 3.45.
Obteniendo como resultado la siguiente distribución
mapa_cali <- read_sf("C:/Users/AnalíticaSilice/Documents/Maestría en ciencia de datos/Segundo semestre 2026_I/Modelos/Actividad Caso C&A/mc_barrios/mc_barrios.shp")
vivienda_puntos <- st_as_sf(datos_filtrado2,
coords = c("longitud", "latitud"),
crs = 4326)
ggplot() +
geom_sf(data = mapa_cali, fill = "white", color = "gray80") +
geom_sf(data = vivienda_puntos, aes(color = "lightcoral"), size = 1.2, alpha = 0.6) +
theme_minimal() +
labs(title = "Mapa de Casas en la Zona Norte",
caption = "Fuente: IDESC",
color = "Inmuebles") +
theme(legend.position = "none")Preliminarmente, se realiza un estudio con indicadores de tendencia central, posición y varianza para comprender los datos, esta información se ilustra en la sección de anexos, por otra parte, el siguiente gráfico de cajas y bigotes permite identificar de manera visual los datos atípicos.
datos_f <- datos_filtrado2[names(datos_filtrado2) %in% c("preciom","areaconst","parqueaderos","banios","habitaciones")]
datos_long <- datos_f %>%
pivot_longer(
cols = everything(),
names_to = "Variable",
values_to = "Valor"
)
ggplot(datos_long, aes(x = Variable, y = Valor)) +
geom_boxplot(fill = "lightcoral", alpha = 0.6) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)
) +
labs(
title = "Diagramas de caja de variables continuas",
x = "Variables",
y = "Valor"
) +
scale_y_continuous(labels = function(x) format(x, scientific = FALSE))Los datos atípicos están presentes mayoritariamente en las variables de precio y el área construida estos datos pueden ofrecer información valiosa para el modelo y sus estimaciones, por lo que se opta por conservarlos. Las variables predictoras para el precio son “estrato”,“areaconst”,“parqueaderos”,“banios” y “habitaciones”. A continuación, se realiza un análisis entre estas y la variable objetivo que es el precio.
Al graficar cada una de las variables con respecto al precio del inmueble no es posible determinar una clara relación entre estas, hay una tendencia entre área construida y precio que posee una amplia varianza en sus puntos, por lo que es necesario determinar los coeficientes de correlación de Pearson o Spearman según corresponda.
vars_continuas <- c("preciom","estrato","areaconst","parqueaderos","banios","habitaciones")
library(ggplot2)
library(patchwork)
plots <- list()
for (var in vars_continuas) {
p2 <- ggplot(datos_filtrado2, aes_string(x = var, y = "preciom")) +
geom_point(fill = "lightcoral", alpha = 0.6) +
labs(
title = paste(var,"vs preciom"),
x = var,
y = "preciom") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5)
) +
scale_y_continuous(labels = function(x) format(x, scientific = FALSE))
plots[[var]] <- p2
}
wrap_plots(plots)Para las variables “estrato”,“areaconst”,“parqueaderos”, “baños” y “habitaciones” se emplea un coeficiente de Spearman, mientras que para el área construida se procede a usar un coeficiente de Pearson. En la siguiente tabla se representan los resultados de estos coeficientes.
pearsonareas <- round(cor(datos_filtrado2$areaconst, datos_filtrado2$preciom, method = "pearson"),digits = 2)
Spearmanestrato <-round(cor(datos_filtrado2$estrato, datos_filtrado2$preciom, method = "spearman"),digits = 2)
Spearmanhabitaciones <- round(cor(datos_filtrado2$habitaciones,datos_filtrado2$preciom, method = "spearman"),digits = 2)
Spearmanbanios <- round(cor(datos_filtrado2$banios,datos_filtrado2$preciom, method = "spearman"),digits = 2)
Spearmanparqueaderos <- round(cor(datos_filtrado2$parqueaderos,datos_filtrado2$preciom, method = "spearman"),digits = 2)
r <-c("r")
Tabla_enunciado <-data.frame(r,pearsonareas,Spearmanestrato,Spearmanhabitaciones,Spearmanbanios,Spearmanparqueaderos)
colnames(Tabla_enunciado) <-c(" - ","Área construida","Estrato","Habitaciones","Baños","Parqueaderos")
knitr::kable(Tabla_enunciado,
booktabs = TRUE,
caption = "<center><b><span style='font-size:18px'> Coeficientes de correlación </span></b></center>",
) %>%
kable_styling(full_width = FALSE)|
|
Área construida | Estrato | Habitaciones | Baños | Parqueaderos |
|---|---|---|---|---|---|
| r | 0.69 | 0.72 | 0.38 | 0.64 | 0.5 |
De lo anterior, podemos destacar que hay una relación positiva entre el área construida, estrato y baños con respecto al precio del inmueble por lo que se opta por usar estas tres dentro del modelo, para el caso de las variables como habitaciones y parqueaderos no estarían dentro de este dado que la correlación no es lo suficientemente alta con respecto a las otras, además para las casas la mayor parte no posee un registro por lo que esta variable puede tener un impacto en inmuebles de tipo apartamento.
Previamente, se realizan los siguientes supuestos y posteriormente se validan estos conforme los resultados.
\[ \text{Los errores son variables aleatorias normales.} \] \[ \text{Los errores tienen media cero.} \] \[ \text{Los errores tienen una varianza constante (homocedasticidad).} \] \[ \text{No hay correlación entre los errores, lo que se conoce como independencia de los errores.} \]
Se procede a emplear un modelo de regresión lineal múltiple con las siguientes variables:
Cabe resaltar que este modelo emplea los siguientes parámetros:
\[ Y_i = \beta_0 + \beta_1 X_{i1} + \beta_2 X_{i2} + \cdots + \beta_k X_{ik} + \varepsilon_i, \qquad i = 1,2,\ldots,n \] En este caso es necesario implementar una escala logarítmica para las variables como precio y área construida dado que son variables continuas con magnitudes distantes con respecto a los valores de las variables categóricas. Aplicando, el modelo se obtiene los siguientes resultados:
modelo_multiple <- lm(log(datos_filtrado2$preciom) ~ log(datos_filtrado2$areaconst) + datos_filtrado2$estrato + datos_filtrado2$banios, data = datos_filtrado2)
summary(modelo_multiple)##
## Call:
## lm(formula = log(datos_filtrado2$preciom) ~ log(datos_filtrado2$areaconst) +
## datos_filtrado2$estrato + datos_filtrado2$banios, data = datos_filtrado2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.72581 -0.17979 -0.02112 0.14152 1.24376
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.137217 0.098335 21.734 < 2e-16 ***
## log(datos_filtrado2$areaconst) 0.497274 0.022669 21.936 < 2e-16 ***
## datos_filtrado2$estrato 0.216185 0.012462 17.348 < 2e-16 ***
## datos_filtrado2$banios 0.059291 0.008512 6.966 7.42e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2838 on 716 degrees of freedom
## Multiple R-squared: 0.7724, Adjusted R-squared: 0.7715
## F-statistic: 810.1 on 3 and 716 DF, p-value: < 2.2e-16
Se obtiene un \(R^2\) de 0.77 el cual resulta en un modelo que explica el 77% del comportamiento de los datos obtenidos, el otro 23% puede ser producto de otras variables que no se están cuantificando como, lujo antigüedad o tipo y calidad de acabados, es importante considerar que el intercepto cuando las variables predictoras son 0 es de 2.13, además los resultados nos permiten identificar que la variación en el precio en un 50% depende del área construida, 21% del estrato y un 6% es con respecto a la cantidad de baños.
Inicialmente es necesario verificar si el modelo es válido y esto implica el comportamiento de los residuos al azar, como se muestra en la siguiente gráfica este no tiene sesgos ni una amplia dispersión, además la línea roja es casi que horizontal sobre el valor de 0.
Al realizar la prueba de Shapiro-Wilk, se obtuvo un p-valor inferior a 0.05, lo que indica que el supuesto de normalidad de los errores no se cumple estrictamente producto de valores atípicos como lo pueden ser inmuebles con una gran área construida o con un elevado número de baños.
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_multiple)
## W = 0.97465, p-value = 7.673e-10
Realizando el test de Goldfeld-Quandt se obtiene un p-valor superior a 0.33, lo que termina de confirmar que los errores tienen una varianza constante.
##
## Goldfeld-Quandt test
##
## data: modelo_multiple
## GQ = 1.0467, df1 = 356, df2 = 356, p-value = 0.3335
## alternative hypothesis: variance increases from segment 1 to 2
Finalmente, se aplicó la prueba de Durbin-Watson obteniendo un estadístico de 1.578 esto indica una correlación positiva, sin embargo el p-valor es menor que 0.05 lo cual quiere decir que hay una dependencia entre los errores dado que por lo que son del mismo tipo de inmueble y de la misma zona de la ciudad.
## lag Autocorrelation D-W Statistic p-value
## 1 0.2103679 1.578567 0
## Alternative hypothesis: rho != 0
Por último, se puede concluir que solo se están cumpliendo dos supuestos de los cuatro planteados. Es necesario depurar aún más la base de datos, principalmente inmuebles con una gran área construida y con una cantidad atípica de baños, habitaciones y parqueaderos, esto podría mejorar el \(R^2\) del modelo y a su vez la exactitud de las predicciones.
Recopilando los valores de la primera solicitud se procede a predecir el posible valor del inmueble, es importante considerar que estas estimaciones se realizaron si la casa fuera estrato 4 obteniendo como resultado 316 millones de pesos. Este valor está dentro del margen del crédito hipotecario preaprobado.
modelo_multiple <- lm(
log(preciom) ~ log(areaconst) + estrato + banios,
data = datos_filtrado2
)
nuevo_inmueble <- data.frame(
areaconst = 200,
estrato = 4,
banios = 2
)
precio_predicho <- exp(predict(modelo_multiple, newdata = nuevo_inmueble))
precio_predicho## 1
## 315.846
Finalmente, se procede a aplicar los mismos pasos empleados anteriormente, pero para la segunda propiedad, iniciando con el filtrado y verificación de las coordenadas suministradas.
mapa_cali <- read_sf("C:/Users/AnalíticaSilice/Documents/Maestría en ciencia de datos/Segundo semestre 2026_I/Modelos/Actividad Caso C&A/mc_barrios/mc_barrios.shp")
vivienda_puntos <- st_as_sf(datos_filtrado3,
coords = c("longitud", "latitud"),
crs = 4326)
ggplot() +
geom_sf(data = mapa_cali, fill = "white", color = "gray80") +
geom_sf(data = vivienda_puntos, aes(color = "lightcoral"), size = 1.2, alpha = 0.6) +
theme_minimal() +
labs(title = "Mapa de Apartamentos en la Zona Sur",
caption = "Fuente: IDESC",
color = "Inmuebles") +
theme(legend.position = "none")Para el modelo de regresión múltiple para el caso de los apartamentos se incluye la variable parqueadero dado que este atributo tiene más valores en los apartamentos que en las casas. Como resultado se obtiene un \(R^2\) de 0.84 el cual es significativamente mayor que el anterior. El peso de las variables incrementa un poco más en donde el 68% dependen de del área construida, el 22% del estrato, 8% de los baños y el 3% depende del parqueadero.
modelo_multiple2 <- lm(log(datos_filtrado3$preciom) ~ log(datos_filtrado3$areaconst) + datos_filtrado3$estrato + datos_filtrado3$banios + datos_filtrado3$parqueaderos, data = datos_filtrado3)
summary(modelo_multiple2)##
## Call:
## lm(formula = log(datos_filtrado3$preciom) ~ log(datos_filtrado3$areaconst) +
## datos_filtrado3$estrato + datos_filtrado3$banios + datos_filtrado3$parqueaderos,
## data = datos_filtrado3)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.77346 -0.12229 0.00789 0.13419 0.79954
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.235815 0.063169 19.563 <2e-16 ***
## log(datos_filtrado3$areaconst) 0.683535 0.017900 38.187 <2e-16 ***
## datos_filtrado3$estrato 0.226686 0.006822 33.228 <2e-16 ***
## datos_filtrado3$banios 0.077165 0.007384 10.451 <2e-16 ***
## datos_filtrado3$parqueaderos 0.003746 0.002874 1.303 0.193
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2106 on 2299 degrees of freedom
## Multiple R-squared: 0.8401, Adjusted R-squared: 0.8398
## F-statistic: 3019 on 4 and 2299 DF, p-value: < 2.2e-16
Para concluir se procede a estimar la predicción del precio del inmueble 2 que da como resultado un valor de 668 millones de pesos el cual se ajusta al margen del crédito hipotecario preaprobado.
modelo_multiple2 <- lm(
log(preciom) ~ log(areaconst) + estrato + banios,
data = datos_filtrado3
)
nuevo_inmueble <- data.frame(
areaconst = 300,
estrato = 5,
banios = 3,
parqueaderos = 3
)
precio_predicho <- exp(predict(modelo_multiple2, newdata = nuevo_inmueble))
precio_predicho## 1
## 668.4182
Antes de aconsejar por la compra de un inmueble sobre otro es necesario mejorar la calidad de los datos, dado que hay muchas inconsistencias entre la zona en donde dicen estar los inmuebles y sus coordenadas, por lo que al filtrar y revisar estos datos se pierden. Se recomienda la compra del apartamento en la Zona Sur dado que el modelo empleado para la predicción de su valor presenta un mejor \(R^2\) además cabe resaltar que las estimaciones se realizaron para inmuebles del estrato 4 para la casa y 5 para el caso del apartamento, por lo que inmuebles con un estrato mayor, repercutiría en un mayor precio.
library(kableExtra)
var_continuas <- datos_filtrado2[names(datos_filtrado2) %in% c("preciom","areaconst","parqueaderos","banios","habitaciones")]
var_continuas <- names(var_continuas)[sapply(var_continuas, is.numeric)]
#Indicadores de Tendencia central, dispersiC3n y posiciC3n
calc_stats <- function(columna){
media <- round(mean(columna, na.rm = TRUE), 3)
mediana <- round(median(columna, na.rm = TRUE), 3)
maximo <- max(columna, na.rm = TRUE)
minimo <- min(columna, na.rm = TRUE)
rango <- round((max(columna, na.rm = TRUE) - min(columna, na.rm = TRUE)), 3)
varianza <- round(var(columna, na.rm = TRUE), 3)
Desv_Estandar <- round(sd(columna, na.rm = TRUE), 3)
Coef_Variacion <- round(((Desv_Estandar / media) * 100), 3)
Q1 <- quantile(columna, 0.25, na.rm = TRUE)
Q3 <- quantile(columna, 0.75, na.rm = TRUE)
IQR_val <- Q3 - Q1
lim_inf <- Q1 - 1.5 * IQR_val
lim_sup <- Q3 + 1.5 * IQR_val
datos_atipicos <- columna[columna < lim_inf | columna > lim_sup]
if(length(datos_atipicos) == 0){
valores_txt <- "Ninguno"
} else {
valores_unicos <- unique(datos_atipicos)
valores_txt <- paste(head(valores_unicos, 5), collapse = ", ")
if(length(valores_unicos) > 5){
valores_txt <- paste0(valores_txt, ", ...")
}
}
return(list(
Media = media,
Mediana = mediana,
Minimo = minimo,
Maximo = maximo,
Rango = rango,
Varianza = varianza,
Desv_Estandar = Desv_Estandar,
Coef_Variacion = Coef_Variacion,
Cantidad_de_valores_atipicos = length(datos_atipicos),
Valores_atipicos = valores_txt
))
}
resultados <- lapply(datos_filtrado2[, var_continuas, drop = FALSE], calc_stats)
resultados_df <- do.call(rbind, lapply(resultados, as.data.frame))
resultados_df <- cbind(Variable = names(resultados), resultados_df)
resultados_df$Varianza <- format(resultados_df$Varianza, scientific = FALSE)
outliers_df <- resultados_df[, c("Variable", "Cantidad_de_valores_atipicos")]
resultados_df %>%
kable(
col.names = c(
"Variable",
"Media",
"Mediana",
"Mínimo",
"Máximo",
"Rango",
"Varianza",
"Desv. Estándar",
"Coef. Variación",
"Cantidad de valores atípicos",
"Valores atípicos"
),
align = "c",
caption = "<center><b><span style='font-size:18px'> Indicadores de tendencia central, dispersión, posición y valores atípicos</span></b></center>",
row.names = FALSE)%>%
kable_styling(full_width = FALSE, position = "center")| Variable | Media | Mediana | Mínimo | Máximo | Rango | Varianza | Desv. Estándar | Coef. Variación | Cantidad de valores atípicos | Valores atípicos |
|---|---|---|---|---|---|---|---|---|---|---|
| preciom | 442.292 | 369.5 | 85 | 1940 | 1855 | 87082.975 | 295.098 | 66.720 | 34 | 1100, 1350, 1590, 1800, 1200, … |
| areaconst | 258.540 | 230.5 | 30 | 1500 | 1470 | 28866.708 | 169.902 | 65.716 | 22 | 750, 700, 1188, 736, 920, … |
| parqueaderos | 1.432 | 1.0 | 0 | 10 | 10 | 2.454 | 1.567 | 109.427 | 20 | 6, 7, 8, 10, 9 |
| banios | 3.556 | 3.0 | 0 | 10 | 10 | 2.356 | 1.535 | 43.166 | 17 | 8, 9, 10 |
| habitaciones | 4.601 | 4.0 | 0 | 10 | 10 | 3.472 | 1.863 | 40.491 | 36 | 10, 9 |