Ante la solicitud del análisis inmobiliario, se realizó un modelo de regresión lineal múltiple que nos permitió obtener valores estimados de posibles precios para las solicitudes realizada por parte de la compañía internacional a la inmobiiliaia C&A . A continuación se muestran las posibles “ofertas potenciales” para cada escenario.
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 3586 | Zona Norte | 01 | 4 | 330 | 240 | 1 | 2 | 3 | Casa | la merced | -76.52720 | 3.48433 |
| 1924 | Zona Norte | 01 | 4 | 320 | 264 | 1 | 2 | 3 | Casa | vipasa | -76.51840 | 3.48459 |
| 1943 | Zona Norte | NA | 5 | 350 | 346 | 1 | 2 | 4 | Casa | vipasa | -76.51847 | 3.47503 |
Como se puede evidenciar, para la primera solicitud se muestran 3 opciones:
Casa de 240 metros de área, 1 parqueadero, 2 baños, 3 habitaciones y estrato 4 por un precio de 330 millones de pesos.
Casa de 264 metros de área, 1 parqueadero, 2 baños, 3 habitaciones y estrato 4 por un precio de 320 millones de pesos.
Casa de 346 metros de área, 1 parqueadero, 2 baños, 4 habitaciones y estrato 5 por un precio de 350 millones de pesos.
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5574 | Zona Sur | NA | 6 | 850 | 352 | 4 | 3 | 3 | Apartamento | pance | -76.53729 | 3.34265 |
Como se puede evidenciar, para la segunda solicitud se muestra 1 opción:
Debido a que solo obtuvimos una propuesta inicial, agregamos las siguientes opciones por un precio un poco mayor al presupuesto inicial.
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5248 | Zona Sur | 08 | 6 | 1150 | 344 | 4 | 5 | 5 | Apartamento | ciudad jardín | -76.53533 | 3.36971 |
| 6023 | Zona Sur | 08 | 6 | 1150 | 464 | 4 | 6 | 5 | Apartamento | ciudad jardín | -76.54000 | 3.36800 |
Casa de 344 metros de área, 4 parqueaderos, 5 baños, 5 habitaciones y estrato 6 por un precio de 1150 millones de pesos.
Casa de 464 metros de área, 4 parqueaderos, 6 baños, 5 habitaciones y estrato 6 por un precio de 1150 millones de pesos.
Se decide trabajar con un modelo de regresión lineal múltiple, debido a que este ofrece ventajas clave al permitirnos analizar y predecir datos considerando múltiples variables predictoras, lo que resulta en un mejor ajuste a los datos, mayor precisión en los pronosticos, capacidad de controlar variables de confusión y la evaluación de la importancia de las variables predictoras.
METODOLOGÍA QUE USAREMOS
1- Limpieza de datos: En esta fase, se eliminan valores atípicos, datos faltantes o erróneos, se asumen decisiones y se realiza una preparación inicial de los datos para garantizar que estén listos para el análisis.
2- Análisis de variables: Se examinan las variables disponibles para comprender su comportamiento y relaciones.
3- Creación de modelo y validación de supuestos: Aquí se construye un modelo de regresión lineal múltiple que explique la relación entre las variables. Luego, se validan los supuestos del modelo, como la linealidad, la independencia de errores y la normalidad de los residuos.
4- Pronósticos con el modelo: Una vez que el modelo ha sido validado, se utiliza para hacer pronósticos sobre futuros eventos o resultados basados en los datos disponibles.
5- Toma de decisiones: Finalmente, se utilizan los pronósticos y resultados del modelo para tomar decisiones informadas.
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 trasladó 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:
Características 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
Instalamos las librerías y paquetes que usaremos
Revisamos las primeras 10 filas de nuestra data frame y verificamos si existen datos faltantes en nuestra base
head(df,10)
## # A tibble: 10 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona … <NA> 3 250 70 1 3 6
## 2 1169 Zona … <NA> 3 320 120 1 2 3
## 3 1350 Zona … <NA> 3 350 220 2 2 4
## 4 5992 Zona … 02 4 400 280 3 5 3
## 5 1212 Zona … 01 5 260 90 1 2 3
## 6 1724 Zona … 01 5 240 87 1 3 3
## 7 2326 Zona … 01 4 220 52 2 2 3
## 8 4386 Zona … 01 5 310 137 2 3 4
## 9 1209 Zona … 02 5 320 150 2 4 6
## 10 1592 Zona … 02 5 780 380 2 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
md.pattern(df)
## preciom id zona estrato areaconst banios habitaciones tipo barrio longitud
## 4808 1 1 1 1 1 1 1 1 1 1
## 1909 1 1 1 1 1 1 1 1 1 1
## 876 1 1 1 1 1 1 1 1 1 1
## 726 1 1 1 1 1 1 1 1 1 1
## 1 1 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0
## 2 3 3 3 3 3 3 3 3 3
## latitud parqueaderos piso
## 4808 1 1 1 0
## 1909 1 1 0 1
## 876 1 0 1 1
## 726 1 0 0 2
## 1 0 0 0 12
## 2 0 0 0 13
## 3 1605 2638 4275
Notamos que hay varios N/1, procedemos a eliminarlos
df <- df[!is.na(df$id), ]
Llenamos los registros de NA en la columna “piso” con el número 1 asumiendo que es una vivienda de una planta y de parqueadero con el número 0 asumiendo que esa vivienda no tiene parquederos.
df$piso <- ifelse(is.na(df$piso), 1, df$piso)
df$parqueaderos <- ifelse(is.na(df$parqueaderos), 0, df$parqueaderos)
Verificamos nuevamente si existen datos faltantes en nuestra base
md.pattern(df)
## /\ /\
## { `---' }
## { O O }
## ==> V <== No need for mice. This data set is completely observed.
## \ \|/ /
## `-----'
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## 8319 1 1 1 1 1 1 1 1 1
## 0 0 0 0 0 0 0 0 0
## tipo barrio longitud latitud
## 8319 1 1 1 1 0
## 0 0 0 0 0
head(df,10)
## # A tibble: 10 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona … 1 3 250 70 1 3 6
## 2 1169 Zona … 1 3 320 120 1 2 3
## 3 1350 Zona … 1 3 350 220 2 2 4
## 4 5992 Zona … 02 4 400 280 3 5 3
## 5 1212 Zona … 01 5 260 90 1 2 3
## 6 1724 Zona … 01 5 240 87 1 3 3
## 7 2326 Zona … 01 4 220 52 2 2 3
## 8 4386 Zona … 01 5 310 137 2 3 4
## 9 1209 Zona … 02 5 320 150 2 4 6
## 10 1592 Zona … 02 5 780 380 2 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Notamos que ya no hay datos faltantes
Corregimos la zona de acuerdo con la posición geográfica para un análisis más fiable.
df$zona <- NA
df$zona <- ifelse(df$longitud < -76.537138, "Zona Oeste", df$zona)
df$zona <- ifelse(df$longitud > -76.485196, "Zona Oriente", df$zona)
df$zona <- ifelse(df$latitud > 3.460322, "Zona Norte", df$zona)
df$zona <- ifelse(df$latitud < 3.404536, "Zona Sur", df$zona)
df$zona <- ifelse(is.na(df$zona), "Zona Centro", df$zona)
head(df, n = 5)
## # A tibble: 5 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona C… 1 3 250 70 1 3 6
## 2 1169 Zona C… 1 3 320 120 1 2 3
## 3 1350 Zona C… 1 3 350 220 2 2 4
## 4 5992 Zona O… 02 4 400 280 3 5 3
## 5 1212 Zona C… 01 5 260 90 1 2 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
base1 = subset(df, tipo =="Casa" & zona=="Zona Norte")
# Mostrar los primeros 3 registros de la base filtrada
head(base1, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 504 Zona N… 1 3 180 120 0 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
leaflet() %>% addTiles() %>%
setView(lng = -76.51595234451665, lat = 3.436834062816008, zoom = 12) %>%
addCircleMarkers(lng = base1$longitud,
lat = base1$latitud,
label = as.character(paste0(base1$tipo, " est:", base1$estrato, " Precio:",base1$preciom, "'000.000", " Pisos:", base1$piso)),
stroke = FALSE,
fillOpacity = 0.5,
radius = 4,
color = 'green'
)
Debido a que anteriormente en nuestra limpieza de datos corregimos la latitud y longitud, podemos observar que todas nuestras observaciones se encuentran dentro del rango de zona norte.
Creo mi nueva df con la información solo de la solicitud 1
df1 = base1
head(df1,10)
## # A tibble: 10 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona … 02 5 320 150 2 4 6
## 2 1592 Zona … 02 5 780 380 2 3 3
## 3 504 Zona … 1 3 180 120 0 3 3
## 4 604 Zona … 1 5 520 455 0 5 4
## 5 1003 Zona … 1 3 380 300 0 5 8
## 6 1840 Zona … 1 5 395 165 0 4 4
## 7 2730 Zona … 1 5 460 319 0 5 4
## 8 2875 Zona … 1 5 390 357 0 3 6
## 9 2908 Zona … 1 5 780 380 0 3 3
## 10 3182 Zona … 1 4 420 265 0 6 7
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Reordeno mi df
df1 <- df1 %>% select(id, zona, tipo, barrio, piso, estrato, preciom, areaconst, parqueaderos, banios, habitaciones, longitud, latitud)
ggpairs(df1[,7:11], title="Análisis exploratorio de datos")
Podemos observar una correlación de 0.743 entre la variable precio con areaconst lo cual es positivo para nuestro análisis. También con la variable baños de 0.558
Convertimos la variable “estrato” en factor, creamos nuestro modelo y mostramos un resumen de él
df1$estrato <- factor(df1$estrato)
str(df1)
## tibble [551 × 13] (S3: tbl_df/tbl/data.frame)
## $ id : num [1:551] 1209 1592 504 604 1003 ...
## $ zona : chr [1:551] "Zona Norte" "Zona Norte" "Zona Norte" "Zona Norte" ...
## $ tipo : chr [1:551] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:551] "acopi" "acopi" "acopi" "acopi" ...
## $ piso : chr [1:551] "02" "02" "1" "1" ...
## $ estrato : Factor w/ 4 levels "3","4","5","6": 3 3 1 3 1 3 3 3 3 2 ...
## $ preciom : num [1:551] 320 780 180 520 380 395 460 390 780 420 ...
## $ areaconst : num [1:551] 150 380 120 455 300 165 319 357 380 265 ...
## $ parqueaderos: num [1:551] 2 2 0 0 0 0 0 0 0 0 ...
## $ banios : num [1:551] 4 3 3 5 5 4 5 3 3 6 ...
## $ habitaciones: num [1:551] 6 3 3 4 8 4 4 6 3 7 ...
## $ longitud : num [1:551] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:551] 3.48 3.49 3.47 3.46 3.47 ...
modelo <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = df1)
residuos <- resid(modelo)
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = df1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -860.47 -57.76 -13.63 35.43 942.86
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 54.33921 18.44101 2.947 0.003350 **
## areaconst 0.74602 0.04583 16.277 < 2e-16 ***
## estrato4 63.82907 17.84550 3.577 0.000379 ***
## estrato5 102.93561 16.93853 6.077 2.31e-09 ***
## estrato6 356.93216 33.57898 10.630 < 2e-16 ***
## habitaciones -0.61195 4.66376 -0.131 0.895655
## parqueaderos 18.33363 4.44531 4.124 4.30e-05 ***
## banios 22.41181 6.23423 3.595 0.000354 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 138.5 on 543 degrees of freedom
## Multiple R-squared: 0.6956, Adjusted R-squared: 0.6916
## F-statistic: 177.2 on 7 and 543 DF, p-value: < 2.2e-16
Estadísticamente notamos que obtenemos un R2 de 0.69, lo cual indica que nuestro modelo es relativamente bueno para explicar variabilidad en la variable de precio y que la variable “habitaciones” es estadísticamente no significativa debido al valor p 0.89 y que la inclusión de esta, no sería justificada.
Procedemos a revisar la media y mediana de esta variable junto a una prueba de normalidad.
mediahabitaciones <- mean(df1$habitaciones)
medianahabitaciones <- median(df1$habitaciones)
mediahabitaciones
## [1] 4.519056
medianahabitaciones
## [1] 4
normalidadhabitaciones <- shapiro.test(df1$habitaciones)
normalidadhabitaciones
##
## Shapiro-Wilk normality test
##
## data: df1$habitaciones
## W = 0.88102, p-value < 2.2e-16
Obtenemos un valor W de 0.88 por lo cual confirmamos que la variable habitaciones sigue una distribución normal con media 4.51 y mediana de 4. Lo que nos indica que independientemente las otras variables de las casas en zona norte, las habitaciones en el 95% de los casos, estarán en un valor cercano a la media
Creamos un nuevo modelo sin la variable “habitaciones”
modelo1 <- lm(preciom ~ areaconst + estrato + parqueaderos + banios, data = df1)
residuos1 <- resid(modelo1)
summary(modelo1)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios,
## data = df1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -861.26 -57.48 -13.45 35.65 942.99
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 53.14172 16.00996 3.319 0.000963 ***
## areaconst 0.74443 0.04417 16.855 < 2e-16 ***
## estrato4 64.38269 17.32388 3.716 0.000223 ***
## estrato5 103.62007 16.10075 6.436 2.70e-10 ***
## estrato6 358.18405 32.16594 11.136 < 2e-16 ***
## parqueaderos 18.29520 4.43164 4.128 4.23e-05 ***
## banios 21.95978 5.19119 4.230 2.74e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 138.4 on 544 degrees of freedom
## Multiple R-squared: 0.6956, Adjusted R-squared: 0.6922
## F-statistic: 207.1 on 6 and 544 DF, p-value: < 2.2e-16
Obtenemos el modelo: y= 53.14 + 0.74*areaconst + 64.38*estrato4 + 103.62*estrato5 + 358.18*estrato6 + 18.29*parqueaderos + 21.95*banios
plot(modelo1, which = 1)
Análisis. Podemos observar el gráfico de dispersión de residuos vs. valores ajustados. En este gráfico, se puede apreciar que los puntos de dispersión se distribuyen aparentemente de manera aleatoria alrededor de la línea horizontal en y=0, sin mostrar patrones no lineales evidentes. La falta de una forma clara de embudo, curva o estructura en forma de U en los datos respalda la noción de que la relación entre las variables es lineal.
2. Homocedasticidad. Gráfico de residuos estandarizados vs. valores ajustados
plot(modelo1, which = 3)
Análisis. Se cumple el supuesto de homocedasticidad al observar que en el gráfico, la dispersión de los residuos se mantiene constante a medida que los valores ajustados cambian, lo que indica que la varianza de los residuos es uniforme en todos los niveles de predicción.
bptest(residuos ~ fitted(modelo1))
##
## studentized Breusch-Pagan test
##
## data: residuos ~ fitted(modelo1)
## BP = 71.283, df = 1, p-value < 2.2e-16
Análisis. La prueba de Breusch-Pagan muestra un valorBP de 71.28 y un p-value muy bajo (2.2e-16), lo que significa que el p-value es esencialmente cero. Esto indica que hay evidencia significativa de heterocedasticidad en el modelo. En otras palabras, la varianza de los residuos no es constante a lo largo de todo el rango de valores ajustados, lo que viola el supuesto de homocedasticidad en el modelo de regresión lineal.
qqnorm(residuos1)
qqline(residuos1)
Análisis. Se observa que los puntos se ajustan de manera aproximada a una linea diagonal, lo que nos sugiere que los residuos se distribuyen de manera similar a una distribución normal. Las desviaciones de la línea diagonal son mínimas y no se pueden ver patrones no lineales tan evidentes, por lo cual fortalece la conslución de que los residuos siguen una distribución normal.
solicitud1 <- data.frame(
areaconst = 200,
estrato = "4",
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
precio_solicitud1 <- predict(modelo1, newdata = solicitud1)
cat("El precio predicho de la vivienda estrato 4 es:", precio_solicitud1,"\n")
## El precio predicho de la vivienda estrato 4 es: 328.6259
solicitud1 <- data.frame(
areaconst = 200,
estrato = "5",
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
precio_solicitud1 <- predict(modelo1, newdata = solicitud1)
cat("El precio predicho de la vivienda estrato 5 es:", precio_solicitud1,"\n")
## El precio predicho de la vivienda estrato 5 es: 367.8633
ofertas_potenciales <- df1 %>%
filter(areaconst >= 200,
parqueaderos == 1,
banios <= 2,
habitaciones <= 4,
estrato %in% c(4, 5),
preciom <= 350)
# Mostrar las ofertas potenciales
ofertas_potenciales
## # A tibble: 3 × 13
## id zona tipo barrio piso estrato preciom areaconst parqueaderos banios
## <dbl> <chr> <chr> <chr> <chr> <fct> <dbl> <dbl> <dbl> <dbl>
## 1 3586 Zona N… Casa la me… 01 4 330 240 1 2
## 2 1924 Zona N… Casa vipasa 01 4 320 264 1 2
## 3 1943 Zona N… Casa vipasa 1 5 350 346 1 2
## # ℹ 3 more variables: habitaciones <dbl>, longitud <dbl>, latitud <dbl>
leaflet() %>% addTiles() %>%
setView(lng = -76.51595234451665, lat = 3.436834062816008, zoom = 12) %>%
addCircleMarkers(lng = ofertas_potenciales$longitud,
lat = ofertas_potenciales$latitud,
label = as.character(paste0(ofertas_potenciales$tipo, " estrato:", ofertas_potenciales$estrato," ", "Baños:", ofertas_potenciales$banios," ", "Precio:",ofertas_potenciales$preciom, "'000.000", " ", " Pisos:", ofertas_potenciales$piso)),
stroke = FALSE,
fillOpacity = 0.5,
radius = 4,
color = 'green'
)
Mostrar los primeros 3 registros de la base filtrada
base2 = subset(df, tipo =="Apartamento" & zona=="Zona Sur")
head(base2, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1724 Zona S… 01 5 240 87 1 3 3
## 2 4386 Zona S… 01 5 310 137 2 3 4
## 3 6857 Zona S… 03 3 100 49 0 1 2
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
leaflet() %>% addTiles() %>%
setView(lng = -76.51595234451665, lat = 3.436834062816008, zoom = 12) %>%
addCircleMarkers(lng = base2$longitud,
lat = base2$latitud,
label = as.character(paste0(base2$tipo, " est:", base2$estrato, " Precio:",base2$preciom, "'000.000", " Pisos:", base2$piso)),
stroke = FALSE,
fillOpacity = 0.5,
radius = 4,
color = 'green'
)
Creo mi nueva df con la información solo de la solicitud 2 y la reordeno
df2 = base2
head(df2,10)
## # A tibble: 10 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1724 Zona … 01 5 240 87 1 3 3
## 2 4386 Zona … 01 5 310 137 2 3 4
## 3 6857 Zona … 03 3 100 49 0 1 2
## 4 6412 Zona … 05 6 620 135 2 3 4
## 5 1770 Zona … 06 5 170 56 1 1 2
## 6 1353 Zona … 1 4 220 75 0 2 3
## 7 1391 Zona … 1 4 162 60 0 2 3
## 8 1489 Zona … 1 5 225 65 0 2 3
## 9 1490 Zona … 1 5 250 75 0 2 3
## 10 1492 Zona … 1 5 325 107 0 2 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Realizo el análisis exploratorio de datos
df2 <- df2 %>% select(id, zona, tipo, barrio, piso, estrato, preciom, areaconst, parqueaderos, banios, habitaciones, longitud, latitud)
ggpairs(df2[,7:11], title="Análisis exploratorio de datos")
Observamos una buena correlación entre precio y las variables independientes: areacosnt (0.773), parqueaderos(0.67), banios(0.74)
Convertimos “estrato” en factor, creo el modelo y un resumen de él
df2$estrato <- factor(df2$estrato)
str(df2)
## tibble [2,164 × 13] (S3: tbl_df/tbl/data.frame)
## $ id : num [1:2164] 1724 4386 6857 6412 1770 ...
## $ zona : chr [1:2164] "Zona Sur" "Zona Sur" "Zona Sur" "Zona Sur" ...
## $ tipo : chr [1:2164] "Apartamento" "Apartamento" "Apartamento" "Apartamento" ...
## $ barrio : chr [1:2164] "acopi" "acopi" "acopi" "acopi" ...
## $ piso : chr [1:2164] "01" "01" "03" "05" ...
## $ estrato : Factor w/ 4 levels "3","4","5","6": 3 3 1 4 3 2 2 3 3 3 ...
## $ preciom : num [1:2164] 240 310 100 620 170 220 162 225 250 325 ...
## $ areaconst : num [1:2164] 87 137 49 135 56 75 60 65 75 107 ...
## $ parqueaderos: num [1:2164] 1 2 0 2 1 0 0 0 0 0 ...
## $ banios : num [1:2164] 3 3 1 3 1 2 2 2 2 2 ...
## $ habitaciones: num [1:2164] 3 4 2 4 2 3 3 3 3 3 ...
## $ longitud : num [1:2164] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:2164] 3.37 3.38 3.38 3.34 3.37 ...
modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = df2)
residuos2 <- resid(modelo2)
summary(modelo2)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = df2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1219.38 -38.00 0.26 33.06 873.18
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -18.46671 13.00872 -1.420 0.155879
## areaconst 1.45787 0.05404 26.977 < 2e-16 ***
## estrato4 17.53434 9.38919 1.868 0.061967 .
## estrato5 33.07259 9.68612 3.414 0.000651 ***
## estrato6 179.61965 11.82556 15.189 < 2e-16 ***
## habitaciones -17.75743 3.98827 -4.452 8.92e-06 ***
## parqueaderos 49.04515 3.41023 14.382 < 2e-16 ***
## banios 49.75506 3.61529 13.762 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 96.85 on 2156 degrees of freedom
## Multiple R-squared: 0.788, Adjusted R-squared: 0.7873
## F-statistic: 1145 on 7 and 2156 DF, p-value: < 2.2e-16
Estadísticamente notamos que obtenemos un R2 de 0.788, lo cual indica que nuestro modelo es relativamente bueno para explicar variabilidad en la variable de precio y que la variable “estrato4” es estadísticamente no significativa debido al valor p 0.061 pero al ser una variable categorica, no la eliminamos de nuestro modelo.
Nuestro modelo qieda de la siguiente forma: preciom= -18.46 + 1.45*areaconst + 17.53*estrato4 + 33.07*estrato5 + 179.61*estrato6 - 17.75*habitaciones + 49.04*parqueaderos + 49.75*banios
Validamos supuestos del modelo
plot(modelo2, which = 1)
Como en el caso 1, también se puede apreciar que los puntos de dispersión se distribuyen aparentemente de manera aleatoria alrededor de la línea horizontal en y=0, sin mostrar patrones no lineales evidentes.
plot(modelo2, which = 3)
También se cumple el supuesto de homocedasticidad al observar que en el gráfico, la dispersión de los residuos se mantiene constante a medida que los valores ajustados cambian.
bptest(residuos2 ~ fitted(modelo2))
##
## studentized Breusch-Pagan test
##
## data: residuos2 ~ fitted(modelo2)
## BP = 313.78, df = 1, p-value < 2.2e-16
Presenta un BP de 313.78 y un p-value muy bajo (2.2e-16), lo que significa que el p-value es esencialmente cero. Esto indica que hay evidencia significativa de heterocedasticidad en el modelo.
qqnorm(residuos2)
qqline(residuos2)
Se observa que los puntos se ajustan de manera aproximada a una linea diagonal, lo que nos sugiere que los residuos se distribuyen de manera similar a una distribución normal.
solicitud2 <- data.frame(
areaconst = 300,
estrato = "5",
habitaciones = 5,
parqueaderos = 3,
banios = 3
)
precio_solicitud2 <- predict(modelo2, newdata = solicitud2)
cat("El precio predicho de la vivienda estrato 5 en la zona sur es:", precio_solicitud2,"\n")
## El precio predicho de la vivienda estrato 5 en la zona sur es: 659.5818
solicitud2 <- data.frame(
areaconst = 300,
estrato = "6",
habitaciones = 5,
parqueaderos = 3,
banios = 3
)
precio_solicitud2 <- predict(modelo2, newdata = solicitud2)
cat("El precio predicho de la vivienda estrato 6 en la zona sur es:", precio_solicitud2,"\n")
## El precio predicho de la vivienda estrato 6 en la zona sur es: 806.1289
Ofertas potenciales para la solicitud 2
ofertas_potenciales2 <- df2 %>%
filter(areaconst >= 300,
parqueaderos >= 3,
banios >= 3,
habitaciones <= 5,
estrato %in% c(5, 6),
preciom <= 850)
ofertas_potenciales2
## # A tibble: 1 × 13
## id zona tipo barrio piso estrato preciom areaconst parqueaderos banios
## <dbl> <chr> <chr> <chr> <chr> <fct> <dbl> <dbl> <dbl> <dbl>
## 1 5574 Zona S… Apar… pance 1 6 850 352 4 3
## # ℹ 3 more variables: habitaciones <dbl>, longitud <dbl>, latitud <dbl>
leaflet() %>% addTiles() %>%
setView(lng = -76.51595234451665, lat = 3.436834062816008, zoom = 12) %>%
addCircleMarkers(lng = ofertas_potenciales2$longitud,
lat = ofertas_potenciales2$latitud,
label = as.character(paste0(ofertas_potenciales2$tipo, " estrato:", ofertas_potenciales2$estrato," ", "Baños:", ofertas_potenciales2$banios," ", "Precio:",ofertas_potenciales2$preciom, "'000.000", " ", " Pisos:", ofertas_potenciales2$piso)),
stroke = FALSE,
fillOpacity = 0.5,
radius = 4,
color = 'green'
)