library(ggplot2) # Para gráficos
library(dplyr) # Para manipulación de datos
##
## Adjuntando el paquete: 'dplyr'
## The following object is masked from 'package:gridExtra':
##
## combine
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(cluster) # Para clustering
library(factoextra) # Para visualización
## Warning: package 'factoextra' was built under R version 4.4.2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(tidyr)
library(tinytex)
library(GGally)
library(summarytools)
library(leaflet)
## Warning: package 'leaflet' was built under R version 4.4.3
El presente informe ejecutivo tiene como finalidad brindar un análisis estratégico y recomendaciones fundamentadas en técnicas de modelación para atender la solicitud de asesoría en la adquisición de dos viviendas por parte de una compañía internacional, que busca reubicar a dos de sus empleados en Cali.
María, quien inició su carrera en bienes raíces en Cali hace 10 años y ha acumulado experiencia trabajando en distintas ciudades, fundó su propia agencia, C&A (Casas y Apartamentos), tras obtener la licencia de intermediaria. Con el respaldo de un equipo consolidado, conformado actualmente por ocho agentes, María ha logrado posicionarse en el mercado local, lo que le confiere un amplio conocimiento y una sólida reputación en el sector.
En respuesta a la reciente solicitud de María, este informe analiza dos casos de estudio, aplicando técnicas de modelación para estimar y validar el precio de las viviendas en función de variables clave como el área construida, estrato, número de baños, habitaciones y parqueaderos. Además, se presentan recomendaciones estratégicas para identificar ofertas potenciales que se ajusten a los creditos pre-aprobados de los clientes y a las necesidades específicas de la compañía.
El informe se estructura en dos secciones principales: el “Análisis exploratorio”, en el que se sintetizan los detalles del mercado inmobiliario en Cali y el de “Ofertas de viviendas” donde se muestran los hallazgos, se proponen las recomendaciones y que contienen el detalle de las estimaciones, validaciones y comparación de modelos utilizados en el análisis. Esta metodología integral busca proporcionar una base sólida para la toma de decisiones en un momento crucial para el sector inmobiliario en Cali.
Inicialmente se realiza un EDA sobre la base de datos que se tiene para conocer la información con lo que se está trabajando
str(vivienda)
## 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")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Primeramente,se realizará un tratamiento de los NA’s por la naturaleza del análisis.
summary(vivienda)
## 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 tiene NAs en las variables de Estrato, precio, area, parqueaderos, baños y habitaciones
# Eliminar todas las filas que tengan al menos un NA
vivienda_clean <- na.omit(vivienda)
# Verificar el resultado
summary(vivienda_clean)
## id zona piso estrato
## Min. : 1 Length:4808 Length:4808 Min. :3.000
## 1st Qu.:2479 Class :character Class :character 1st Qu.:4.000
## Median :4474 Mode :character Mode :character Median :5.000
## Mean :4427 Mean :4.838
## 3rd Qu.:6413 3rd Qu.:6.000
## Max. :8316 Max. :6.000
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 40.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 244.5 1st Qu.: 85.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 350.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 457.2 Mean : 174.8 Mean : 1.815 Mean : 3.219
## 3rd Qu.: 560.0 3rd Qu.: 225.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1500.0 Max. :10.000 Max. :10.000
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:4808 Length:4808 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.564 Mean :-76.53
## 3rd Qu.: 4.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.46
## latitud
## Min. :3.333
## 1st Qu.:3.378
## Median :3.408
## Mean :3.414
## 3rd Qu.:3.451
## Max. :3.498
Se tienen 4.808 observaciones y las siguientes variables:
Identificación y ubicación: Los registros se pueden identificar con id individual. Como variables de tipo carácter están los registros de “zona” y “barrio”, adicionalmente se encuentra la “longitud” y “latitud” que indican la ubicación geográfica de las propiedades.
Dentro de las características físicas de los inmuebles podemos encontrar: el piso, estrato, área construida (areconst), habitaciones, baños (banios), parqueaderos y tipo (posiblemente casa o apartamento)
En cuanto al precio de las viviendas, los valores van desde 58 hasta 1.999 (posiblemente la unidad de medida es millones de pesos). La mediana de los precios es 350 y la distribución sugiere una amplia variación en los precios, al igual que con el área construida lo que podría estár fuertemente relacionado.
Análisis de componentes principales
# Variables numéricas relevantes
vars_numericas <- c( "preciom", "areaconst", "parqueaderos",
"banios", "habitaciones")
# Nuevo data frame con las variables seleccionadas
df_numeric <- vivienda[, vars_numericas]
# Se eliminan filas con NA
df_numeric <- na.omit(df_numeric)
head(df_numeric)
## # A tibble: 6 × 5
## preciom areaconst parqueaderos banios habitaciones
## <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 250 70 1 3 6
## 2 320 120 1 2 3
## 3 350 220 2 2 4
## 4 400 280 3 5 3
## 5 260 90 1 2 3
## 6 240 87 1 3 3
# Ejecutar el PCA con centrado y escalado
pca_result <- prcomp(df_numeric, center = TRUE, scale. = TRUE)
# Resumen del PCA para ver la varianza explicada
summary(pca_result)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5
## Standard deviation 1.8035 0.9280 0.60371 0.57319 0.43938
## Proportion of Variance 0.6505 0.1722 0.07289 0.06571 0.03861
## Cumulative Proportion 0.6505 0.8228 0.89568 0.96139 1.00000
fviz_eig(pca_result, addlabels = TRUE, ylim = c(0, 80)) +
ggtitle("Scree Plot: Varianza Explicada por Componentes")
fviz_pca_biplot(pca_result,
geom.ind = "point", # Representa las observaciones como puntos
col.ind = "blue", # Color para las observaciones
repel = TRUE, # Evita que las etiquetas se solapen
title = "Biplot de PCA")
fviz_pca_var(pca_result,
col.var = "contrib", # Colorea según la contribución
gradient.cols = c("blue", "yellow", "red"),
repel = TRUE, # Evita solapamientos en las etiquetas
title = "Contribución de Variables en el PCA")
El precio (preciom) está fuertemente influenciado por características
estructurales como área construida, cantidad de baños, parqueaderos y
estrato.
El PCA ha reducido la dimensionalidad a dos componentes que explican el 65.1% de la variabilidad, lo que sugiere que estas dimensiones capturan gran parte de la estructura del mercado inmobiliario.
La primera solicitud tiene un credito preaprobado de COP 350 mm para una vivienda con las siguientes características:
De acuerdo a esto, se realizó un filtro de la base de datos para que las viviendas evaluadas tenga todas las características anteriores.
# Filtrar las viviendas que cumplen con las condiciones del cliente
viviendas_filtradas <- vivienda %>%
filter(tipo == "Casa",
areaconst >= 200,
parqueaderos >= 1,
banios >= 2,
habitaciones >= 4,
estrato %in% c(4, 5),
zona == "Zona Norte",
preciom <= 350) # Filtrar por precio máximo de 350 millones
# Mostrar las primeras filas del resultado
head(viviendas_filtradas, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4210 Zona N… 01 5 350 200 3 3 4
## 2 4267 Zona N… 01 5 335 202 1 4 5
## 3 4800 Zona N… 01 5 340 250 2 4 4
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
summary(viviendas_filtradas)
## id zona piso estrato
## Min. : 766 Length:34 Length:34 Min. :4.000
## 1st Qu.:1154 Class :character Class :character 1st Qu.:4.000
## Median :1928 Mode :character Mode :character Median :5.000
## Mean :2743 Mean :4.647
## 3rd Qu.:4210 3rd Qu.:5.000
## Max. :7470 Max. :5.000
## preciom areaconst parqueaderos banios
## Min. :230.0 Min. :200.0 Min. :1.000 Min. :2.000
## 1st Qu.:320.2 1st Qu.:219.5 1st Qu.:2.000 1st Qu.:3.000
## Median :340.0 Median :250.0 Median :2.000 Median :4.000
## Mean :328.5 Mean :254.6 Mean :1.882 Mean :3.618
## 3rd Qu.:350.0 3rd Qu.:275.0 3rd Qu.:2.000 3rd Qu.:4.000
## Max. :350.0 Max. :355.0 Max. :3.000 Max. :5.000
## habitaciones tipo barrio longitud
## Min. :4.000 Length:34 Length:34 Min. :-76.55
## 1st Qu.:4.000 Class :character Class :character 1st Qu.:-76.53
## Median :4.500 Mode :character Mode :character Median :-76.52
## Mean :4.824 Mean :-76.52
## 3rd Qu.:5.000 3rd Qu.:-76.51
## Max. :8.000 Max. :-76.50
## latitud
## Min. :3.376
## 1st Qu.:3.468
## Median :3.480
## Mean :3.472
## 3rd Qu.:3.483
## Max. :3.489
# Tablas de frecuencia para comprobar la consulta
# Tabla de frecuencia para la variable 'zona'
tabla_zona <- table(viviendas_filtradas$zona)
print(tabla_zona)
##
## Zona Norte
## 34
# Tabla de frecuencia para 'tipo' (debe ser solo "Casa")
tabla_tipo <- table(viviendas_filtradas$tipo)
print(tabla_tipo)
##
## Casa
## 34
De acuerdo a la base de datos, hay 34 potenciales ofertas que se ajustan a lo que el cliente busca.
# Tabla para 'estrato'
tabla_estrato <- table(viviendas_filtradas$estrato)
print(tabla_estrato)
##
## 4 5
## 12 22
En la zona se tiene 12 viviendas de estrato 4 y 22 de estrato 5
# Mapa con los puntos de las viviendas filtradas
# Usamos ggplot para una visualización rápida
mapa_gg <- ggplot(viviendas_filtradas, aes(x = longitud, y = latitud)) +
geom_point(color = "blue", size = 2, alpha = 0.7) +
labs(title = "Ubicación de Viviendas Filtradas (Zona Norte)",
x = "Longitud", y = "Latitud") +
theme_minimal()
print(mapa_gg)
Se puede observar que en la base de datos hay 4 ofertas (casas) que se
salen de la zona en la que el cliente está solicitando la busqueda, por
lo que se procede a filtrarlo para que solo esten las viviendas que
apliquen a lo que el cliente busca.
# Ordenar por latitud de menor a mayor
df_filtrado <- viviendas_filtradas[order(viviendas_filtradas$latitud), ]
# Eliminar las tres con menor latitud (son las casas que se salen de la zona pedida)
df_filtrado <- df_filtrado[-c(1:4), ]
# Tabla de frecuencia para 'tipo' (debe ser solo "Casa")
tabla_tipo <- table(df_filtrado$tipo)
print(tabla_tipo)
##
## Casa
## 30
# se grafica nuevamente
ggplot(df_filtrado, aes(x = longitud, y = latitud)) +
geom_point(color = "blue") +
ggtitle("Ubicación de Viviendas Filtradas (Zona Norte)") +
xlab("Longitud") + ylab("Latitud") +
theme_minimal()
# Alternativamente, un mapa interactivo con leaflet
mapa_leaflet <- leaflet(df_filtrado) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud,
color = "blue", fillOpacity = 0.7,
radius = 5, popup = ~paste("Tipo:", tipo, "<br>",
"Precio:", preciom, "mm<br>",
"Zona:", zona))
mapa_leaflet # Esto abre el mapa interactivo en el visor de RStudio o en el navegador
# Calcular la correlación entre precio y las variables numéricas
correlaciones <- cor(df_filtrado[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")], use = "complete.obs")
print(correlaciones)
## preciom areaconst estrato banios habitaciones
## preciom 1.00000000 0.14757614 0.2221428 -0.19469118 -0.07287675
## areaconst 0.14757614 1.00000000 -0.1983761 0.01275372 0.28256251
## estrato 0.22214283 -0.19837613 1.0000000 0.18544106 0.30241165
## banios -0.19469118 0.01275372 0.1854411 1.00000000 0.49800449
## habitaciones -0.07287675 0.28256251 0.3024116 0.49800449 1.00000000
ggpairs(correlaciones[,1:5], title=" ")
library(ggplot2)
ggplot(df_filtrado, aes(x = areaconst, y = preciom)) +
geom_point(color = "blue", alpha = 0.6) +
labs(title = "Precio vs Área Construida",
x = "Área Construida (m²)",
y = "Precio (COP)") +
theme_minimal()
ggplot(df_filtrado, aes(x = factor(estrato), y = preciom)) +
geom_boxplot(fill = "red", alpha = 0.6) +
labs(title = "Distribución del Precio por Estrato",
x = "Estrato",
y = "Precio (COP)") +
theme_minimal()
ggplot(df_filtrado, aes(x = factor(banios), y = preciom)) +
geom_boxplot(fill = "green", alpha = 0.6) +
labs(title = "Distribución del Precio por Número de Baños",
x = "Número de Baños",
y = "Precio (COP)") +
theme_minimal()
ggplot(df_filtrado, aes(x = factor(habitaciones), y = preciom)) +
geom_boxplot(fill = "orange", alpha = 0.6) +
labs(title = "Distribución del Precio por Número de Habitaciones",
x = "Número de Habitaciones",
y = "Precio (COP)") +
theme_minimal()
ggplot(df_filtrado, aes(x = factor(barrio), y = preciom)) +
geom_boxplot(fill = "orange", alpha = 0.6) +
labs(title = "Distribución del Precio por Barrio",
x = "Barrio",
y = "Precio (COP)") +
theme_minimal()
ggplot(df_filtrado, aes(x = longitud, y = latitud, color = preciom)) +
geom_point(size = 2, alpha = 0.7) +
scale_color_gradient(low = "yellow", high = "red") +
labs(title = "Ubicación y Precio de las Viviendas",
x = "Longitud",
y = "Latitud",
color = "Precio (COP)") +
theme_minimal()
Dentro de la zona no hay ningún barrio cuyas viviendas tengan un valor
preferencial en el mercado.
# Ajuste del modelo
modelo <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = df_filtrado)
# Resumen del modelo para ver los coeficientes y sus pruebas de significancia
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = df_filtrado)
##
## Residuals:
## Min 1Q Median 3Q Max
## -86.264 -7.336 4.728 14.842 27.015
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 187.4631 72.4655 2.587 0.0162 *
## areaconst 0.2036 0.1370 1.487 0.1501
## estrato 22.8987 12.2184 1.874 0.0731 .
## habitaciones -5.0277 6.5585 -0.767 0.4508
## parqueaderos 13.7770 9.8580 1.398 0.1750
## banios -5.3729 6.1962 -0.867 0.3945
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 26.79 on 24 degrees of freedom
## Multiple R-squared: 0.2297, Adjusted R-squared: 0.06923
## F-statistic: 1.431 on 5 and 24 DF, p-value: 0.249
Interpretación de coeficientes:
Intercepto (187.46): Representa el precio base (en millones de COP) cuando todas las variables predictoras son cero. Aunque en el contexto inmobiliario este valor no tiene una interpretación real, sirve como referencia en el modelo.
Área Construida (coef. 0.2036): Sugiere que, manteniendo constantes las demás variables, por cada metro cuadrado adicional se incrementa el precio en aproximadamente 0.20 millones de COP. Sin embargo, el valor p (0.1501) indica que este coeficiente no es estadísticamente significativo al 5% (aunque pudiera ser relevante en un análisis práctico).
Estrato (coef. 22.8987): Indica que por cada incremento en el estrato, se espera un aumento de alrededor de 22.90 millones de COP en el precio, con una significancia marginal (p = 0.0731). Esto es coherente con que viviendas en estratos más altos suelen tener precios mayores.
-Habitaciones (coef. -5.0277): Curiosamente, el coeficiente es negativo, lo que sugiere que, manteniendo constantes otras variables, un mayor número de habitaciones se asocia con un precio inferior. Sin embargo, este resultado no es estadísticamente significativo (p = 0.4508) y podría reflejar problemas de colinealidad o que esta variable esté capturando efectos complejos (por ejemplo, viviendas con muchas habitaciones pero de menor calidad).
-Parqueaderos (coef. 13.7770): Cada parqueadero adicional se asocia con un aumento de 13.78 millones de COP, pero con p = 0.1750, no es estadísticamente significativo.
-Baños (coef. -5.3729): También se observa un coeficiente negativo (–5.37), sin significancia estadística (p = 0.3945). Esto podría deberse a que la cantidad de baños en este conjunto de datos no varía de forma consistente con el precio, o puede estar relacionado con otras características.
R² = 0.2297 y R² Ajustado = 0.06923: Esto indica que el modelo explica aproximadamente el 23% de la variabilidad en el precio (o solo el 7% cuando se ajusta por el número de predictores). El ajuste es bajo, lo que sugiere que hay otros factores importantes no incluidos en el modelo o que la relación es no lineal.
F-Statistic p-valor = 0.249: El modelo global es estadísticamente significativo, lo que implica que, en conjunto, las variables incluidas explican la variación en el precio.
Discusión del ajuste:
Si bien es razonable esperar que las variables utilizadas como área construida y el estrato influyan en el precio, la falta de significancia de algunas variables y el bajo R² sugieren que el modelo podría mejorarse. Se podría incluir otras variables como la ubicación detallada (barrio), antigüedad (variable que no se tendría en esta base de datos) y el estado de la propiedad (tampoco se tiene este detalle).
Adicionalmente, se podría considerar transformaciones (como logaritmos) o interacciones entre variables.
par(mfrow = c(2,2))
plot(modelo)
En el gráfico de Residuals vs Fitted, los residuos parecen estar
relativamente alineados, pero se aprecia cierta variación (quizás
algunos puntos se concentran en un rango y otros se salen). No se ve un
patrón muy marcado, pero sí podría haber una ligera pendiente
descendente. Podría no ser grave, pero conviene vigilar si hay un ligero
sesgo de linealidad.
En el Q-Q Residuals, hay valores en las colas que muestran asimetrías en la distribución. Si bien la desviación no es drástica, se podría revisar si hay outliers que estén influyendo en la distribución.
El Scale-Location evalúa la homocedasticidad (constancia de la varianza). En este caso, la línea muestra una pequeña disminución, lo que podría indicar heterocedasticidad, es decir la varianza de los residuos no son completamente constante. Aunque la disminución no es muy grande por lo que no parece ser un problema grave.
Residuals vs leverage: no parece haber puntos extremadamente lejanos que crucen la línea de Cook’s distance, aunque sí hay algunos casos un poco apartados. Esto indica que no hay un gran problema de puntos con influencia desmedida, pero también se podría revisar manualmente aquellos con mayor leverage o residuo estandarizado.
nueva_vivienda <- data.frame(
areaconst = 200,
estrato = 4,
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
# Predecir el precio:
precio_predicho <- predict(modelo, newdata = nueva_vivienda)
precio_predicho
## 1
## 302.7058
nueva_vivienda1.1 <- data.frame(
areaconst = 200,
estrato = 5,
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
# Predecir el precio:
precio_predicho1.1 <- predict(modelo, newdata = nueva_vivienda)
precio_predicho1.1
## 1
## 302.7058
El valor de la predicción se encuentra por debajo del crédito preaprobado.
# Ya se había filtrado viviendas que son "Casa" en "Zona Norte"
ofertas <- df_filtrado %>%
filter(tipo == "Casa", zona == "Zona Norte")
# Predecir precios con el modelo
ofertas$precio_predicho <- predict(modelo, newdata = ofertas)
ofertas_potenciales <- ofertas %>%
filter(precio_predicho <= 350)
ofertas_seleccionadas <- ofertas_potenciales %>%
arrange(350 - precio_predicho) %>%
head(5)
ofertas_seleccionadas
## # A tibble: 5 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4210 Zona N… 01 5 350 200 3 3 4
## 2 4209 Zona N… 02 5 350 300 3 5 6
## 3 819 Zona N… 02 5 350 264 2 3 4
## 4 3043 Zona N… <NA> 5 330 275 2 3 5
## 5 1163 Zona N… <NA> 5 350 216 2 2 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # precio_predicho <dbl>
Teniendo en cuenta el credito preaprobado, se dan 5 ofertas que estan en con un precio de 350 mm. Entre las opciones hay casas entre 200 a 300 metros cuadrados. Si la elección se realiza solo por tamaño, entonces la mejor opción sería la número dos, que se encuentra en el barrio El bosque (-76.53010 3.48577 ) y tiene 5 baños y 6 habitaciones.
No obstante, es bueno que se tenga en cuenta otros temas como el barrio y su seguridad, los años de construcción y los alrededores de la vivienda (vías de acceso, parques cercanes, escuelas, centros comerciales, etc). Por eso, es importante que el cliente visite las 5 opciones antes de elegir la definitiva.
A continuación se ve la ubicación de las 5 opciones:
# Alternativamente, un mapa interactivo con leaflet
mapa_leaflet <- leaflet(ofertas_seleccionadas) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud,
color = "blue", fillOpacity = 0.7,
radius = 5, popup = ~paste("Tipo:", tipo, "<br>",
"Precio:", preciom, "mm<br>",
"Zona:", zona))
mapa_leaflet # Esto abre el mapa interactivo en el visor de RStudio o en el navegador
La primera solicitud tiene un credito preaprobado de COP 850 mm para una vivienda con las siguientes características:
De acuerdo a esto, se realizó un filtro de la base de datos para que las viviendas evaluadas tenga todas las características anteriores.
# Filtrar las viviendas que cumplen con las condiciones del cliente
viviendas_filtradas2 <- vivienda %>%
filter(tipo == "Apartamento",
areaconst >= 300,
parqueaderos >= 3,
banios >= 3,
habitaciones >= 5,
estrato %in% c(5, 6),
zona == "Zona Sur",
preciom <= 850)
# Mostrar las primeras filas del resultado
head(viviendas_filtradas2, 5)
## # A tibble: 2 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 7182 Zona S… <NA> 5 730 573 3 8 5
## 2 7512 Zona S… <NA> 5 670 300 3 5 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Filtrar las viviendas que cumplen con las condiciones del cliente
viviendas_filtradas2 <- vivienda %>%
filter(tipo == "Apartamento",
areaconst >= 300,
parqueaderos >= 3,
banios >= 3,
habitaciones >= 5,
estrato %in% c(5, 6),
latitud < 3.418,
preciom <= 850)
# Mostrar las primeras filas del resultado
head(viviendas_filtradas2, 5)
## # A tibble: 2 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 7182 Zona S… <NA> 5 730 573 3 8 5
## 2 7512 Zona S… <NA> 5 670 300 3 5 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Siendo que el filtro de la zona sur dio que solo había dos ofertas que cumplian con las caracteristicas buscadas, se valido realizando el mismo filtro pero en vez de usar la “Zona” se usó la latitud, donde se dejó que fuera menor que la media que da la base de datos de viviendas. Se comprobó de esta manera que solo hay dos viviendas que cumplen.
# Tablas de frecuencia para comprobar la consulta
# Tabla de frecuencia para la variable 'zona'
tabla_zona <- table(viviendas_filtradas2$zona)
print(tabla_zona)
##
## Zona Sur
## 2
# Tabla de frecuencia para 'tipo' (debe ser solo "Casa")
tabla_tipo <- table(viviendas_filtradas2$tipo)
print(tabla_tipo)
##
## Apartamento
## 2
# Tabla para 'estrato'
tabla_estrato <- table(viviendas_filtradas2$estrato)
print(tabla_estrato)
##
## 5
## 2
Las dos viviendas son de estrato 5
# Alternativamente, un mapa interactivo con leaflet
mapa_leaflet <- leaflet(viviendas_filtradas2) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud,
color = "blue", fillOpacity = 0.7,
radius = 5, popup = ~paste("Tipo:", tipo, "<br>",
"Precio:", preciom, "mm<br>",
"Zona:", zona))
mapa_leaflet # Esto abre el mapa interactivo en el visor de RStudio o en el navegador
Siendo dos opciones no tendría sentido hacer análisis de correlaciones.
# Calcular la correlación entre precio y las variables numéricas
correlaciones <- cor(viviendas_filtradas2[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")], use = "complete.obs")
## Warning in cor(viviendas_filtradas2[, c("preciom", "areaconst", "estrato", : La
## desviación estándar es cero
print(correlaciones)
## preciom areaconst estrato banios habitaciones
## preciom 1 1 NA 1 -1
## areaconst 1 1 NA 1 -1
## estrato NA NA 1 NA NA
## banios 1 1 NA 1 -1
## habitaciones -1 -1 NA -1 1
library(ggplot2)
ggplot(viviendas_filtradas2, aes(x = areaconst, y = preciom)) +
geom_point(color = "blue", alpha = 0.6) +
labs(title = "Precio vs Área Construida",
x = "Área Construida (m²)",
y = "Precio (COP)") +
theme_minimal()
ggplot(viviendas_filtradas2, aes(x = factor(estrato), y = preciom)) +
geom_boxplot(fill = "red", alpha = 0.6) +
labs(title = "Distribución del Precio por Estrato",
x = "Estrato",
y = "Precio (COP)") +
theme_minimal()
# Ajuste del modelo
modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = viviendas_filtradas2)
# Resumen del modelo para ver los coeficientes y sus pruebas de significancia
summary(modelo2)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = viviendas_filtradas2)
##
## Residuals:
## ALL 2 residuals are 0: no residual degrees of freedom!
##
## Coefficients: (4 not defined because of singularities)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 604.0659 NaN NaN NaN
## areaconst 0.2198 NaN NaN NaN
## estrato NA NA NA NA
## habitaciones NA NA NA NA
## parqueaderos NA NA NA NA
## banios NA NA NA NA
##
## Residual standard error: NaN on 0 degrees of freedom
## Multiple R-squared: 1, Adjusted R-squared: NaN
## F-statistic: NaN on 1 and 0 DF, p-value: NA
Interpretación de coeficientes:
No hay suficientes datos para estimar los parámetros de la regresión. En otras palabras, R no puede calcular los coeficientes, se debería tener más observaciones (ofertas de apartamentos) para poder estimar un modelo con esta cantidad de variables. Como solo hay dos datos R da 1 porque no habría error residual.
#par(mfrow = c(2,2))
#plot(modelo2)
No se puede validar los supuestos del modelo.
nueva_vivienda2 <- data.frame(
areaconst = 300,
estrato = 5,
habitaciones = 5,
parqueaderos = 3,
banios = 3
)
# Predecir el precio:
precio_predicho2 <- predict(modelo2, newdata = nueva_vivienda2)
## Warning in predict.lm(modelo2, newdata = nueva_vivienda2): prediction from
## rank-deficient fit; attr(*, "non-estim") has doubtful cases
precio_predicho2
## 1
## 670
## attr(,"non-estim")
## 1
## 1
El rango es deficiente para el modelo
# Ya se había filtrado viviendas que son "Casa" en "Zona Norte"
ofertas <- viviendas_filtradas2 %>%
filter(tipo == "Apartamento", zona == "Zona Sur")
# Predecir precios con el modelo
ofertas$precio_predicho <- predict(modelo2, newdata = ofertas)
ofertas_potenciales <- ofertas %>%
filter(precio_predicho <= 850)
ofertas_seleccionadas2 <- ofertas_potenciales %>%
arrange(350 - precio_predicho) %>%
head(5)
ofertas_seleccionadas2
## # A tibble: 2 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 7182 Zona S… <NA> 5 730 573 3 8 5
## 2 7512 Zona S… <NA> 5 670 300 3 5 6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # precio_predicho <dbl>
Teniendo en cuenta el credito preaprobado, se dan 2 ofertas que estan en con un precio menor a 850 mm. Entre las opciones hay un apartamento de 573 y otro de 300 metros cuadrados. Si la elección se realiza solo por tamaño, entonces la mejor opción sería la primera, que se encuentra en el barrio Guadalupe (-76.548 3.408) y tiene 8 baños y 5 habitaciones. Pero teniendo en cuenta que es mucho más grande de lo que se buscaba y que la diferencia de precio entre los dos apartamentos es 8.9% la opción más inteligente sería la segunda.
No obstante, es bueno que se tenga en cuenta otros temas como el barrio (seguridad), los años de construcción y los alrededores de la vivienda (vías de acceso, parques cercanes, escuelas, centros comerciales, etc). Por eso, es importante que el cliente visite ambas opciones antes de elegir la definitiva.
A continuación se ve la ubicación de las 2 opciones:
# Alternativamente, un mapa interactivo con leaflet
mapa_leaflet <- leaflet(ofertas_seleccionadas2) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud,
color = "blue", fillOpacity = 0.7,
radius = 5, popup = ~paste("Tipo:", tipo, "<br>",
"Precio:", preciom, "mm<br>",
"Zona:", zona))
mapa_leaflet # Esto abre el mapa interactivo en el visor de RStudio o en el navegador