Introducción

A continuación, se presenta un informe detallado sobre el trabajo realizado para una inmobiliaria en respuesta a una solicitud de compra de vivienda en dos zonas distintas de Cali. Este análisis se basa en un conjunto de datos de ofertas inmobiliarias y tiene como objetivo filtrar, analizar y modelar la información para identificar las mejores opciones disponibles según las necesidades del cliente y las condiciones del crédito pre-aprobado.

El informe se estructura en varias secciones. Primero, se realiza un filtro de la base de datos para incluir únicamente las casas ubicadas en la zona norte de la ciudad, presentando un resumen de los primeros registros y una visualización geoespacial de las ofertas disponibles. Luego, se lleva a cabo un análisis exploratorio de datos enfocado en la correlación entre el precio de la vivienda y diversas características como el área construida, el estrato, el número de habitaciones y baños, entre otras. Posteriormente, se estima un modelo de regresión lineal múltiple para evaluar la relación entre estas variables y el precio de la vivienda, interpretando los coeficientes y la calidad del ajuste del modelo. Además, se realiza la validación de supuestos del modelo y se formulan recomendaciones para mejorar su desempeño.

Con base en el modelo identificado, se predicen los precios de viviendas con características específicas de la solicitud y se sugieren opciones viables dentro del presupuesto establecido. Finalmente, este procedimiento se replica para la segunda solicitud, asegurando un análisis completo y detallado para ambas zonas de interés.

Resumen ejecutivo:

Para la base de datos completa de la ciudad de Cali se hallo un total de 5,100 apartamentos y 3,219 casas, lo que indica una mayor oferta de apartamentos en comparación con casas.

Distribución de Viviendas por Estrato

En cuanto a la distribución de viviendas por estrato, se encontró que:

El estrato 5 es el más predominante, seguido del estrato 4.

Distribución por Zonas

Respecto a la distribución por zonas, la mayor concentración de viviendas se encuentra en:

Impacto del Estrato en la Valorización de la Vivienda

Tras evaluar distintos enfoques para preservar la variable estrato en la regresión, dado su impacto en la valorización de la vivienda y su relación con el nivel socioeconómico, se logró identificar su importancia en la determinación del precio. Para la primera solicitud, una vivienda con 200 m² de área construida, 1 parqueadero, 2 baños y 4 habitaciones, ubicada en estratos 4 y 5, se estimaron los siguientes valores:

Para la segunda solictud, un apartamento en la Zona Sur de Cali con 300 m² de área construida, 3 parqueaderos, 3 baños y 5 habitaciones, se realizó un análisis considerando el impacto del estrato en la valorización del inmueble.

Los resultados muestran que, manteniendo las mismas características y variando únicamente el estrato, se obtuvieron los siguientes valores estimados:

Anexo

Analisis exploratorio de la base “Vivienda”

table(vivienda$tipo)
## 
## Apartamento        Casa 
##        5100        3219

Se encuentra que en la ciudad de Cali existen 5100 apartamentos y 3219 casas.

table(vivienda$estrato)
## 
##    3    4    5    6 
## 1453 2129 2750 1987

En relación al estrato se encutra que el estrato 5 es el concepto mayor numero de viviendas, seguido del estrato 4.

table(vivienda$zona)
## 
##  Zona Centro   Zona Norte   Zona Oeste Zona Oriente     Zona Sur 
##          124         1920         1198          351         4726

Por ultimo, la zonas donde se ubican en mayor preponderiancia las viviendas son la zona sur con 4726 viviedas seguido de la zona norte con 1920.

Valores faltantes base completa

apply(X = is.na(vivienda), MARGIN = 2, FUN = sum)
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3         2638            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1605            3            3            3            3            3 
##      latitud 
##            3
Bajo el resultado se identifica datos faltantes para las variables piso y parqueadero de forma sustancial. Por el momento, como se trabajará con las casas de la zona norte, en consecuencia se trabajará la NA más adelante. Ahora procedemos a crear la variable que nos permita solo delimitar viviendas de tipo “Casa” y que la zona solo sea la “Norte”.

Primer punto: Casas - zona norte.

1. Realice un filtro a la base de datos e incluya solo las ofertas de : base1: casas, de la zona norte de la ciudad. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. (Adicional un mapa con los puntos de las bases. Discutir si todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por que?).

variable_1 = subset(vivienda, tipo == "Casa" & zona == "Zona Norte")
variable_1[1: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  4057 Zona N… 02          6     750       445           NA      7            6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Observamos cuantos elementos tenemos para nuestro tipo de vivienda y su zona geografica respectiva.

desagregar= table(variable_1$zona, variable_1$tipo)
head(desagregar)
##             
##              Casa
##   Zona Norte  722

Por lo anterior, se tiene que existen 722 casas en la zona norte.

Mapa 1. Viviendas zona norte.

mapa_norte = leaflet(data = variable_1) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud, 
                   radius = 2,
                   color = "red",
                   fillOpacity = 0.7,
                   stroke = FALSE)
mapa_norte
En relación al mapa anterior, se evidencia que los elementos que hacen referencia a las casas de la zona norte, no estan del todo ubicados en esta zona. Si bien existe una concentración en la zona ya mencionada - en contraste existen algunas cosas que se ubican en el centro y sur de la ciudad.

Valores faltantes base: casas zona norte

apply(X = is.na(variable_1), MARGIN = 2, FUN = sum)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0          372            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          287            0            0            0            0            0 
##      latitud 
##            0

Procedo a mirar las caracteriticas de la variable piso. Para lo cual arroja como resultado que tiene formato de texto.

summary(variable_1$piso)
##    Length     Class      Mode 
##       722 character character

Por lo anterior, como la variable contiene números en formato de texto los proceso a cambiar a numeros. por consiguiente procedo a mirar las estadisticas de esa variable.

Cambio de formato texto a numerico.

variable_1$piso = as.numeric(variable_1$piso)
summary(variable_1$piso)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   1.000   2.000   2.000   1.994   2.000   7.000     372

Miramos ahora la distribución de la variable para poder determinar la imputación.

Si el p-valor es mayor a 0.05, no se rechaza la hipótesis nula, indicando que los datos podrían no seguir una distribución normal.
Si el p-valor es menor a 0.05, se rechaza la hipótesis nula, concluyendo que los datos siguen una distribución normal.

En este momento se consideraba reemplazar con la mediana, siendo este una medida para distribuciones no normales. Empero, en cuanto hicimos imputación con knn dejamos este a un lado (la primera opcion), sin embargo se deja esto como parte de evidencia del proceso.

shapiro.test(variable_1$piso)
## 
##  Shapiro-Wilk normality test
## 
## data:  variable_1$piso
## W = 0.7956, p-value < 2.2e-16

Tenemos un p-valor obtenido es < 2.2e-16, se cumple la condición para rechazar la hipótesis nula. Por lo tanto, se concluye que la variable piso no sigue una distribución normal. Así pues, cumpliendo el criterio de una no normalidad = mediana. Se procede a utilizar una tabla de resumen con la mediana del área construida en relación al número de pisos para poder hacer imputación.

Relación del area mediana por estrato

tabla_resumen = aggregate(variable_1$areaconst, 
                           by = list(Piso = variable_1$piso), 
                           FUN = median, na.rm = TRUE)
colnames(tabla_resumen)[2] = "Mediana_AreaConstruida"
print(tabla_resumen)
##   Piso Mediana_AreaConstruida
## 1    1                  246.5
## 2    2                  254.0
## 3    3                  200.0
## 4    4                  177.5
## 5    7                  470.0
#variable_1$piso = ifelse(
#  is.na(variable_1$piso) & variable_1$areaconst <= 177.5, 4,
#  ifelse(is.na(variable_1$piso) & variable_1$areaconst > 177.5 & variable_1$areaconst <= 200.0, 3,
#  ifelse(is.na(variable_1$piso) & variable_1$areaconst > 200.0 & variable_1$areaconst <= 246.5, 1,
#  ifelse(is.na(variable_1$piso) & variable_1$areaconst > 246.5 & variable_1$areaconst <= 254.0, 2,
#  ifelse(is.na(variable_1$piso) & variable_1$areaconst > 254.0 & variable_1$areaconst <= 470.0, 7,
#  ifelse(is.na(variable_1$piso) & variable_1$areaconst > 470.0, 7,
#         variable_1$piso))))))
#cat("Valores NA restantes en 'piso':", sum(is.na(variable_1$piso)), "\n")
#table(variable_1$piso)

KNN como metodo de imputación.

imputacion_piso = variable_1[, c("piso", "areaconst")]
# Aplicar la imputación KNN (k = 5 es un valor común, pero se puede ajustar)
variable_1_imputado = kNN(imputacion_piso, 
                           variable = "piso",    # Variable a imputar
                           k = 5,               # Número de vecinos
                           imp_var = FALSE)     # No agregar columnas auxiliares

variable_1$piso = variable_1_imputado$piso # Reemplazar la columna imputada en el dataset original
cat("Valores NA restantes en 'piso':", sum(is.na(variable_1$piso)), "\n") # Mirar si quedaron datos faltantes
## Valores NA restantes en 'piso': 0
table(variable_1$piso)
## 
##   1   2   3   4   7 
## 135 494  86   6   1

El proceso de imputación fue correcto.

Graficamos

ggplot(variable_1, aes(x = factor(piso))) +
  geom_bar(fill = "red", color = "black") +
  labs(title = "Distribucion de Pisos Tras la Imputacion KNN",
       x = "Piso",
       y = "Frecuencia") +
  theme_minimal()

ggplot(variable_1, aes(x = areaconst, y = factor(piso), color = factor(piso))) +
  geom_jitter(alpha = 0.7, size = 2) +
  labs(title = "Relacion entre Area Construida y Piso Imputado",
       x = "Area Construida",
       y = "Piso") +
  theme_minimal()

Valores faltantes base: casas zona norte - parqueaderos.

apply(X = is.na(variable_1), MARGIN = 2, FUN = sum)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          287            0            0            0            0            0 
##      latitud 
##            0

Bajo el anterior resultado, se constata que la variable con Na solo es parqueaderos, para lo cual imputamos de nuevo.

imputacion_parqueadero = variable_1[, c("parqueaderos", "areaconst")]
variable_2_imputado = kNN(imputacion_parqueadero, 
                           variable = "parqueaderos",    
                           k = 5,  
                           imp_var = FALSE)  

variable_1$parqueaderos = variable_2_imputado$parqueaderos 
cat("Valores NA restantes en 'parqueaderos':", sum(is.na(variable_1$parqueaderos)), "\n")
## Valores NA restantes en 'parqueaderos': 0
table(variable_1$parqueaderos)
## 
##   1   2   3   4   5   6   7   8   9  10 
## 296 268  80  51  11   8   5   1   1   1

El proceso de imputación fue correcto.

Segundo punto: Analsis exploratorio (Casas).

2. Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa norte) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

Procedemos a mirar la relación entre precio y area:

grafica_area_precio = plot_ly(data = variable_1, x = ~areaconst, y = ~preciom, color = ~zona,
                            type = "scatter", mode = "markers", marker = list(size = 10),
                            text = ~paste("Zona:", zona, "<br>Precio:", preciom, "<br>Area Construida:", areaconst)) %>%
                    layout(title = "Correlacion entre Precio y Área Construida",
                           xaxis = list(title = "Area Construida"),
                           yaxis = list(title = "Precio"))
grafica_area_precio
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

Bajo lo anterior, podemos aceverar que existe una correlación del 73% para afirmar que a medida que el area construida de la vivienda de la zona norte aumenta, asi mimso lo hace el precio.

Correlaciones

Zona_casa = subset(variable_1, tipo == "Casa")
variables_interes = Zona_casa[, c("preciom", "areaconst", "estrato", "banios", "habitaciones", "parqueaderos")]
correlation_matrix = cor(variables_interes)
corrplot(correlation_matrix, method = "color", addCoef.col = "black", number.cex = 0.7)

#correlation_matrix

La matriz de correlación muestra la relación lineal entre las variables del modelo, con valores entre -1 y 1. Se observa una fuerte correlación positiva entre el precio (preciom) y el área construida (areaconst) con un valor de 0.73, indicando que a mayor área, mayor es el precio. El estrato (estrato) también presenta una correlación significativa con el precio (0.61), seguido por el número de baños (banios) con 0.52 y los parqueaderos (parqueaderos) con 0.45. Las habitaciones (habitaciones) tienen una correlación baja (0.32) con el precio. En cuanto a las relaciones entre predictores, se destaca la correlación moderada entre baños y habitaciones (0.57) y entre área construida y estrato (0.46). No se identifican correlaciones extremadamente altas entre las variables predictoras, lo que puede que indique una baja probabilidad de multicolinealidad.

Tercer punto: Regresión Lineal (Casas)

3. Estime 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 ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

Regresión lineal sin tratar la variable estrato

modelo1 = lm(preciom ~ areaconst + habitaciones + parqueaderos + banios + estrato, data = variable_1)
summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + habitaciones + parqueaderos + 
##     banios + estrato, data = variable_1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -897.27  -79.61  -14.84   43.05 1088.01 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -236.6023    29.4534  -8.033 3.91e-15 ***
## areaconst       0.7878     0.0443  17.783  < 2e-16 ***
## habitaciones    0.7455     4.0932   0.182  0.85553    
## parqueaderos   20.0147     5.4955   3.642  0.00029 ***
## banios         23.6731     5.3524   4.423 1.13e-05 ***
## estrato        82.4298     7.2011  11.447  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 157.7 on 716 degrees of freedom
## Multiple R-squared:  0.6571, Adjusted R-squared:  0.6547 
## F-statistic: 274.4 on 5 and 716 DF,  p-value: < 2.2e-16

Cuarto punto: Validaciones -Regresión (Casas).

4. Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

Para validar supuestos de un modelo lineal:

  • Linealidad.
  • Normalidad de residuos.
  • Homocedasticidad.
  • Ausencia de valores atípicos influyentes.
par(mfrow=c(2,2))
plot(modelo1)

Hipótesis del Test de Anderson-Darling:

Si el p-valor es mayor que el nivel de significancia del 5%, no se rechaza la hipótesis nula, indicando que los residuos siguen una distribución normal.

Si el p-valor es menor que el nivel de significancia, se rechaza la hipótesis nula, lo que sugiere que los residuos no son normales.

ad.test(modelo1$residuals)
## 
##  Anderson-Darling normality test
## 
## data:  modelo1$residuals
## A = 25.5, p-value < 2.2e-16

Dado que el p-valor es menor que un nivel de significancia, se rechaza la hipótesis nula de que los residuos siguen una distribución normal.

Hipótesis del shapiro.test:

Si el p-valor es mayor que el nivel de significancia del 5%, no se rechaza la hipótesis nula, indicando que los residuos son normales.

Si el p-valor es menor que el nivel de significancia, se rechaza la hipótesis nula, lo que sugiere que los residuos no son normales.

shapiro.test(resid(modelo1))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelo1)
## W = 0.8385, p-value < 2.2e-16

El p-valor es menor que el nivel de significancia por tanto se rechaza la hipótesis nula, que establece que los residuos tienen una distribución normal. Es decir, igual que el Anderson-Darling los residuos del modelo no son normales.

Hipótesis del Test de Breusch-Pagan:

Si el p-valor es mayor que el nivel de significancia (por ejemplo, 0.05), no se rechaza la hipótesis nula, indicando homocedasticidad.

Si el p-valor es menor que el nivel de significancia, se rechaza la hipótesis nula, lo que sugiere heterocedasticidad en los residuos.

lmtest::bptest(modelo1)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo1
## BP = 129.23, df = 5, p-value < 2.2e-16

Hipótesis del Test de Goldfeld-Quandt:

Si el p-valor es mayor que un nivel de significancia del 5%, NO se rechaza H₀, lo que indica que no hay evidencia de heterocedasticidad.

Si el p-valor es menor que el nivel de significancia del 5%, SE rechaza H₀, lo que sugiere que hay evidencia de heterocedasticidad.

lmtest::gqtest(modelo1)
## 
##  Goldfeld-Quandt test
## 
## data:  modelo1
## GQ = 1.1028, df1 = 355, df2 = 355, p-value = 0.1785
## alternative hypothesis: variance increases from segment 1 to 2

Hipótesis del Test de Durbin-Watson:

El estadístico de Durbin-Watson (DW) varía entre 0 y 4:

  • DW ≈ 2: No hay autocorrelación.
  • DW < 2: Hay autocorrelación positiva.
  • DW > 2: Hay autocorrelación negativa.
lmtest::dwtest(modelo1)
## 
##  Durbin-Watson test
## 
## data:  modelo1
## DW = 1.6398, p-value = 5.095e-07
## alternative hypothesis: true autocorrelation is greater than 0

Índice de inflación de la varianza (VIF)

  • VIF = 1: No hay correlación entre la variable predictora y las demás variables.
  • 1 < VIF < 5: La correlación es moderada, generalmente aceptable.
  • VIF ≥ 5: Hay una alta correlación, se sugiere revisar las variables.
  • VIF ≥ 10: Fuerte multicolinealidad, puede ser problemático, se recomienda eliminar o transformar alguna variable.
vif(modelo1)
##    areaconst habitaciones parqueaderos       banios      estrato 
##     1.589877     1.622511     1.312713     1.928622     1.452233

Quinto paso: Regresión - Ofertas (Casas).

5. Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra que la empresa tiene crédito pre-aprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.

Estamos buscando una casa en la zona norte, con un area de 200mt2 que tenga 1 parqueadero, 2 baños y 4 habitaciones ademas de que este en un estrato de 4 y 5.

solicitud1 = data.frame(
  areaconst = 200,
  parqueaderos = 1,
  estrato = c(4, 5),
  banios = 2,
  habitaciones = 4
)
print(solicitud1)
##   areaconst parqueaderos estrato banios habitaciones
## 1       200            1       4      2            4
## 2       200            1       5      2            4

Bajo el anterior resultado encontramos productos adecuados a la solicitud, empero estos no tiene

Sexto paso: Regresión - Predicciones (Casas)

6. Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.

predict(modelo1,solicitud1)
##        1        2 
## 321.0180 403.4478

Tercer punto: Regresión con One-Hot Encoding (Casas)

Como tenemos una variable ordinal, para manejar usamos el one-hot encoding para convertirlas en variables dummy.

library(fastDummies)
variable_1 = dummy_cols(variable_1, 
                         select_columns = "estrato", 
                         remove_first_dummy = FALSE, 
                         remove_selected_columns = FALSE)
colnames(variable_1) = sub("^estrato_", "estrato.", colnames(variable_1))
head(variable_1)
## # A tibble: 6 × 17
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1209 Zona N…     2       5     320       150            2      4            6
## 2  1592 Zona N…     2       5     780       380            2      3            3
## 3  4057 Zona N…     2       6     750       445            2      7            6
## 4  4460 Zona N…     2       4     625       355            3      5            5
## 5  6081 Zona N…     2       5     750       237            2      6            6
## 6  7824 Zona N…     2       4     600       160            1      4            5
## # ℹ 8 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   estrato.3 <int>, estrato.4 <int>, estrato.5 <int>, estrato.6 <int>

Regresión con One-Hot Encoding para estrato (Casas).

modelohot = lm(preciom ~ areaconst + habitaciones + parqueaderos + banios +
              estrato.3 + estrato.4 + estrato.5 + estrato.6, 
              data = variable_1)
summary(modelohot)
## 
## Call:
## lm(formula = preciom ~ areaconst + habitaciones + parqueaderos + 
##     banios + estrato.3 + estrato.4 + estrato.5 + estrato.6, data = variable_1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -905.96  -73.54  -14.87   46.56 1081.06 
## 
## Coefficients: (1 not defined because of singularities)
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   339.33007   30.78775  11.022  < 2e-16 ***
## areaconst       0.78765    0.04365  18.043  < 2e-16 ***
## habitaciones    1.27377    4.06436   0.313 0.754069    
## parqueaderos   20.92767    5.41954   3.862 0.000123 ***
## banios         22.60229    5.35561   4.220 2.75e-05 ***
## estrato.3    -321.66433   26.30550 -12.228  < 2e-16 ***
## estrato.4    -240.95403   25.01771  -9.631  < 2e-16 ***
## estrato.5    -190.42701   23.34232  -8.158 1.53e-15 ***
## estrato.6            NA         NA      NA       NA    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 155.4 on 714 degrees of freedom
## Multiple R-squared:  0.6681, Adjusted R-squared:  0.6648 
## F-statistic: 205.3 on 7 and 714 DF,  p-value: < 2.2e-16

Varias cosas aqui, el hecho de que el estrato.6 esté marcado como NA indica colinealidad, lo que podría distorsionar las interpretaciones de los coeficiente puesto que el coeficiente de los estratos es negativo, lo que parece contrario a la intuición (normalmente, se espera que el precio aumente con el estrato). Asi pues comprobamos la muticolenalidad con el siguiente resultado (esta en #, puesto que al compilar en Rmarkdown ponia limitantes):

# vif(modelohot) = Error en vif.default(modelohot): there are aliased coefficients in the model

Tercer paso: Regresión con One-Hot Encoding para estrato -Ajustado (Casas).

Regresión lineal con One-Hot Encoding para estrato -Ajustado (Casas).

modelohot_ajustado = lm(preciom ~ areaconst + habitaciones + parqueaderos + banios 
               + estrato.4 + estrato.5 + estrato.6, 
              data = variable_1)
summary(modelohot_ajustado)
## 
## Call:
## lm(formula = preciom ~ areaconst + habitaciones + parqueaderos + 
##     banios + estrato.4 + estrato.5 + estrato.6, data = variable_1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -905.96  -73.54  -14.87   46.56 1081.06 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   17.66574   17.73965   0.996 0.319668    
## areaconst      0.78765    0.04365  18.043  < 2e-16 ***
## habitaciones   1.27377    4.06436   0.313 0.754069    
## parqueaderos  20.92767    5.41954   3.862 0.000123 ***
## banios        22.60229    5.35561   4.220 2.75e-05 ***
## estrato.4     80.71030   17.16526   4.702 3.09e-06 ***
## estrato.5    131.23732   16.12267   8.140 1.76e-15 ***
## estrato.6    321.66433   26.30550  12.228  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 155.4 on 714 degrees of freedom
## Multiple R-squared:  0.6681, Adjusted R-squared:  0.6648 
## F-statistic: 205.3 on 7 and 714 DF,  p-value: < 2.2e-16

El modelo muestra que eliminar estrato.3 como referencia mejora la interpretación de los coeficientes. Ahora, los coeficientes de los estratos reflejan cuánto más costoso es un estrato en comparación con el estrato 3, lo que tiene más sentido lógico. Además, el R² del modelo sigue siendo 66.8%, indicando que el modelo sigue explicando una cantidad significativa de la variabilidad en los precios de la vivienda sin perder capacidad predictiva.

Índice de inflación de la varianza (VIF)

vif(modelohot_ajustado)
##    areaconst habitaciones parqueaderos       banios    estrato.4    estrato.5 
##     1.590497     1.648099     1.315237     1.989326     1.526901     1.822809 
##    estrato.6 
##     1.456475

No hay colinealidad significativa porque todas las variables tienen VIF < 5.

Cuarto punto: Validaciones con One-Hot Encoding para estrato - Ajustado (Casas).

Para validar supuestos de un modelo lineal:

par(mfrow=c(2,2))
plot(modelohot_ajustado)

Hipótesis del Test de Anderson-Darling:

ad.test(modelohot_ajustado$residuals)
## 
##  Anderson-Darling normality test
## 
## data:  modelohot_ajustado$residuals
## A = 26.859, p-value < 2.2e-16

Hipótesis del shapiro.test:

shapiro.test(resid(modelohot_ajustado))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelohot_ajustado)
## W = 0.83131, p-value < 2.2e-16

Hipótesis del Test de Breusch-Pagan:

lmtest::bptest(modelohot_ajustado)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelohot_ajustado
## BP = 131.38, df = 7, p-value < 2.2e-16

Hipótesis del Test de Goldfeld-Quandt:

lmtest::gqtest(modelohot_ajustado)
## 
##  Goldfeld-Quandt test
## 
## data:  modelohot_ajustado
## GQ = 1.1729, df1 = 353, df2 = 353, p-value = 0.0673
## alternative hypothesis: variance increases from segment 1 to 2

Hipótesis del Test de Durbin-Watson:

lmtest::dwtest(modelohot_ajustado)
## 
##  Durbin-Watson test
## 
## data:  modelohot_ajustado
## DW = 1.7117, p-value = 3.966e-05
## alternative hypothesis: true autocorrelation is greater than 0
  • Los residuos no son normales.
  • Hay evidencia de heterocedasticidad (según Breusch-Pagan).
  • Hay autocorrelación positiva en los residuos (según Durbin-Watson).

Quinto paso: One-Hot Encoding Ajustado - Ofertas (Casas).

Estamos buscando una casa en la zona norte, con un area de 200mt2 que tenga 1 parqueadero, 2 baños y 4 habitaciones ademas de que este en un estrato de 4 y 5.

library(fastDummies)
solicitudhot = data.frame(
  areaconst = 200,
  parqueaderos = 1,
  estrato = c(4, 5),
  banios = 2,
  habitaciones = 4
)
solicitudhot = dummy_cols(solicitudhot, select_columns = "estrato", 
                   remove_first_dummy = FALSE, 
                   remove_selected_columns = FALSE)
print(solicitudhot)
##   areaconst parqueaderos estrato banios habitaciones estrato_4 estrato_5
## 1       200            1       4      2            4         1         0
## 2       200            1       5      2            4         0         1

Sexto paso: One-Hot Encoding Ajustado - Predicción (Casas).

colnames(solicitudhot) = sub("estrato_", "estrato.", colnames(solicitudhot))
dummies_necesarias = c("estrato.3", "estrato.4", "estrato.5", "estrato.6")
for (col in dummies_necesarias) {
  if (!col %in% colnames(solicitudhot)) {
    solicitudhot[[col]] = 0
  }
}
solicitudhot = solicitudhot[, c("areaconst", "parqueaderos", "banios", "habitaciones", 
                 "estrato.3", "estrato.4", "estrato.5", "estrato.6")]
predicciones = predict(modelohot_ajustado, solicitudhot) #Predicciones
print(predicciones)
##        1        2 
## 327.1338 377.6608

Bajo los anteriores resultados la casa 1 con las mismas caracteristicas a expceion del estrato, se encutra que la primera tiene un valor de 327 millones y la segunda de 377 millones.

Primer punto: Departamento - zona sur.

variable_2 = subset(vivienda, tipo == "Apartamento" & zona == "Zona Sur")
variable_2[1:3,]
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  5098 Zona S… 05          4     290        96            1      2            3
## 2   698 Zona S… 02          3      78        40            1      1            2
## 3  8199 Zona S… <NA>        6     875       194            2      5            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
desagregar2= table(variable_2$zona, variable_2$tipo)
head(desagregar2)
##           
##            Apartamento
##   Zona Sur        2787

Observamos cuantos elementos tenemos para nuestro tipo de vivienda y su zona geografica respectiva. Por lo anterior, se tiene que existen 28787 departamentos en la zona sur

Mapa 1. Departamentos zona sur

mapa_sur = leaflet(data = variable_2) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud, 
                   radius = 2,
                   color = "blue",
                   fillOpacity = 0.7,
                   stroke = FALSE)
mapa_sur

En relación al mapa anterior, se evidencia que los elementos que hacen referencia a los departamentos de la zona sur, no estan del todo ubicados en esta zona. Si bien existe una concentración en la zona ya mencionada - en contraste existen algunas cosas que se ubican en el centro y norte de la ciudad.

Valores faltantes base: departamentos zona sur

apply(X = is.na(variable_2), MARGIN = 2, FUN = sum)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0          622            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          406            0            0            0            0            0 
##      latitud 
##            0

Cambio de formato texto a numerico.

variable_2$piso = as.numeric(variable_2$piso)

KNN como metodo de imputación de la variable piso.

imputacion_piso2 = variable_2[, c("piso", "areaconst")]
variable_1_imputado2 = kNN(imputacion_piso2, 
                           variable = "piso",    
                           k = 5,        
                           imp_var = FALSE)     

variable_2$piso = variable_1_imputado2$piso 
cat("Valores NA restantes en 'piso':", sum(is.na(variable_2$piso)), "\n") 
## Valores NA restantes en 'piso': 0
table(variable_1$piso)
## 
##   1   2   3   4   7 
## 135 494  86   6   1

El proceso de imputación fue el correcto en cuanto tenemos cero valores faltantes.

Graficamos la distribucioón de los pisos.

ggplot(variable_2, aes(x = factor(piso))) +
  geom_bar(fill = "blue", color = "black") +
  labs(title = "Distribucion de Pisos Tras la Imputacion KNN",
       x = "Piso",
       y = "Frecuencia") +
  theme_minimal()

Del anterior grafico podemos afirmar que la mayor cantidad de viviendas se encuentran en departamentos de 1 a 5 pisos.

ggplot(variable_2, aes(x = areaconst, y = factor(piso), color = factor(piso))) +
  geom_jitter(alpha = 0.7, size = 2) +
  labs(title = "Relacion entre Area Construida y Piso Imputado",
       x = "Area Construida",
       y = "Piso") +
  theme_minimal()

Valores faltantes base: departamentos zona sur - parqueaderos.

apply(X = is.na(variable_2), MARGIN = 2, FUN = sum)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          406            0            0            0            0            0 
##      latitud 
##            0

KNN como metodo de imputación de la variable parqueadero.

imputacion_parqueadero2 = variable_2[, c("parqueaderos", "areaconst")]
variable_2_imputado2 = kNN(imputacion_parqueadero2, 
                           variable = "parqueaderos",    
                           k = 5,  
                           imp_var = FALSE)  

variable_2$parqueaderos = variable_2_imputado2$parqueaderos 
cat("Valores NA restantes en 'parqueaderos':", sum(is.na(variable_2$parqueaderos)), "\n")
## Valores NA restantes en 'parqueaderos': 0
table(variable_2$parqueaderos)
## 
##    1    2    3    4   10 
## 1930  741   83   31    2

El proceso de imputación fue el correcto en cuanto tenemos cero valores faltantes.

Segundo punto: Analsis exploratorio - Departamentos zona sur.

2. Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa norte) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

grafica_area_precio2 = plot_ly(data = variable_2, x = ~areaconst, y = ~preciom, color = ~zona,
                            type = "scatter", mode = "markers", marker = list(size = 10),
                            text = ~paste("Zona:", zona, "<br>Precio:", preciom, "<br>Area Construida:", areaconst)) %>%
                    layout(title = "Correlación entre Precio y Área Construida",
                           xaxis = list(title = "Area Construida"),
                           yaxis = list(title = "Precio"))
grafica_area_precio2
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

Correlaciones

Zona_apartamento = subset(variable_1, tipo == "Casa")
variables_interes = Zona_apartamento[, c("preciom", "areaconst", "estrato", "banios", "habitaciones", "parqueaderos")]
correlation_matrix = cor(variables_interes)
corrplot(correlation_matrix, method = "number", addCoef.col = "red", number.cex = 0.7)

#correlation_matrix
El análisis de la correlación entre las variables del modelo sugiere que elprecio de la vivienda está fuertemente influenciado por el área construida y el estrato, con coeficientes de correlación de 0.73 y 0.61, respectivamente. Esto indica que a mayor área construida y estrato, el precio tiende a aumentar, lo cual es coherente con la lógica del mercado inmobiliario. Por otro lado, variables como baños y parqueaderos también muestran una relación positiva con el precio (0.52 y 0.45, respectivamente), aunque con menor impacto en comparación con areaconst y estrato. Finalmente, el número de habitaciones presenta una correlación más baja con el precio (0.32), lo que indica que su efecto sobre el valor de la vivienda es menos significativo en comparación con las demás variables.

Convertimos la estrato en factor.

library(fastDummies)
variable_2 = dummy_cols(variable_2, 
                         select_columns = "estrato", 
                         remove_first_dummy = FALSE, 
                         remove_selected_columns = FALSE)
colnames(variable_2) = sub("^estrato_", "estrato.", colnames(variable_2))
head(variable_2)
## # A tibble: 6 × 17
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  5098 Zona S…     5       4     290        96            1      2            3
## 2   698 Zona S…     2       3      78        40            1      1            2
## 3  8199 Zona S…     4       6     875       194            2      5            3
## 4  1241 Zona S…     3       3     135       117            2      2            3
## 5  5370 Zona S…     4       3     135        78            1      1            3
## 6  6975 Zona S…     6       4     220        75            1      2            3
## # ℹ 8 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   estrato.3 <int>, estrato.4 <int>, estrato.5 <int>, estrato.6 <int>

Tercer paso: Regresión con One-Hot Encoding para estrato - Ajustado (Departamentos).

modelohot2 = lm(preciom ~ areaconst + habitaciones + parqueaderos + banios 
               + estrato.4 + estrato.5 + estrato.6, 
              data = variable_2)
summary(modelohot2)
## 
## Call:
## lm(formula = preciom ~ areaconst + habitaciones + parqueaderos + 
##     banios + estrato.4 + estrato.5 + estrato.6, data = variable_2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1068.53   -35.30    -1.53    35.24   899.89 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -42.75112   10.13781  -4.217 2.55e-05 ***
## areaconst      1.29897    0.04646  27.962  < 2e-16 ***
## habitaciones -12.13429    3.16646  -3.832  0.00013 ***
## parqueaderos  61.71619    3.54214  17.423  < 2e-16 ***
## banios        39.39832    2.84552  13.846  < 2e-16 ***
## estrato.4     29.46584    6.86920   4.290 1.85e-05 ***
## estrato.5     56.13105    7.09131   7.915 3.53e-15 ***
## estrato.6    209.82000    8.89017  23.601  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 87.67 on 2779 degrees of freedom
## Multiple R-squared:  0.7911, Adjusted R-squared:  0.7905 
## F-statistic:  1503 on 7 and 2779 DF,  p-value: < 2.2e-16
  • El modelo es altamente explicativo (R² = 79.11%), lo que indica que el 79.1% de la variabilidad en el precio se explica por las variables incluidas.

  • Todos los coeficientes son estadísticamente significativos (p < 0.05), lo que confirma que cada variable tiene un impacto real en el precio de la vivienda.

  • El área construida y los parqueaderos son de los factores más determinantes, mientras que las habitaciones tienen un efecto negativo inesperado. Este resultado sugiere que, en el contexto del mercado analizado, el número de habitaciones no es un factor clave en la determinación del precio y que otros atributos, como la distribución del espacio o la calidad de la construcción, podrían ser más relevantes.

  • Los estratos más altos aumentan significativamente el precio, lo cual es lógico, ya que las viviendas en estratos más altos tienden a estar ubicadas en zonas con mejor infraestructura y mayor demanda.

Cuarto punto: Validaciones con One-Hot Encoding para estrato - Ajustado (Departamentos).

4. Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

par(mfrow=c(2,2))
plot(modelohot2)

Hipótesis del Test de Anderson-Darling:

ad.test(modelohot2$residuals)
## 
##  Anderson-Darling normality test
## 
## data:  modelohot2$residuals
## A = 92.193, p-value < 2.2e-16

Hipótesis del shapiro.test:

shapiro.test(resid(modelohot2))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelohot2)
## W = 0.77557, p-value < 2.2e-16

Hipótesis del Test de Breusch-Pagan:

lmtest::bptest(modelohot2)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelohot2
## BP = 795.02, df = 7, p-value < 2.2e-16

Hipótesis del Test de Goldfeld-Quandt:

lmtest::gqtest(modelohot2)
## 
##  Goldfeld-Quandt test
## 
## data:  modelohot2
## GQ = 0.9008, df1 = 1386, df2 = 1385, p-value = 0.974
## alternative hypothesis: variance increases from segment 1 to 2

Los resultados del análisis de supuestos muestran que el modelo presenta problemas de normalidad, heterocedasticidad y autocorrelación en los residuos. Las pruebas de Anderson-Darling y Shapiro-Wilk indican que los residuos no siguen una distribución normal, lo que podría afectar la validez de los intervalos de confianza y pruebas de hipótesis. Además, la prueba de Breusch-Pagan sugiere la presencia de heterocedasticidad, lo que indica que la varianza de los errores no es constante. Sin embargo, la prueba de Goldfeld-Quandt no confirma este resultado con certeza. Por otro lado, la prueba de Durbin-Watson evidencia autocorrelación positiva en los residuos, lo que sugiere una posible dependencia en los datos. Dado que estos problemas pueden afectar la validez y eficiencia de los estimadores, se recomienda considerar transformaciones en la variable dependiente (logaritmo o Box-Cox) para mejorar la normalidad, así como el uso de errores estándar robustos para corregir la heterocedasticidad y posibles métodos que mitiguen la autocorrelación en los residuos, como modelos autorregresivos o errores estándar corregidos por autocorrelación.

Hipótesis del Test de Durbin-Watson:

lmtest::dwtest(modelohot2)
## 
##  Durbin-Watson test
## 
## data:  modelohot2
## DW = 1.7102, p-value = 7.278e-15
## alternative hypothesis: true autocorrelation is greater than 0

Quinto paso: One-Hot Encoding Ajustado - Ofertas (Departamentos).

Estamos buscando un Apartamento en la zona sur, con un area de 300mt2 que tenga 3 parqueadero, 3 baños y 5 habitaciones ademas de que este en un estrato de 5 y 6.

library(fastDummies)
solicitudhot2 = data.frame(
  areaconst = 300,
  parqueaderos = 3,
  estrato = c(5, 6),
  banios = 3,
  habitaciones = 5
)
solicitudhot2 = dummy_cols(solicitudhot2, select_columns = "estrato", 
                   remove_first_dummy = FALSE, 
                   remove_selected_columns = FALSE)
print(solicitudhot2)
##   areaconst parqueaderos estrato banios habitaciones estrato_5 estrato_6
## 1       300            3       5      3            5         1         0
## 2       300            3       6      3            5         0         1

Sexto paso: One-Hot Encoding Ajustado - Predicción (Departamentos)

colnames(solicitudhot2) = sub("estrato_", "estrato.", colnames(solicitudhot2))
dummies_necesarias2 = c("estrato.3", "estrato.4", "estrato.5", "estrato.6")
for (col in dummies_necesarias) {
  if (!col %in% colnames(solicitudhot2)) {
    solicitudhot2[[col]] = 0
  }
}
solicitudhot2 = solicitudhot2[, c("areaconst", "parqueaderos", "banios", "habitaciones", 
                 "estrato.3", "estrato.4", "estrato.5", "estrato.6")]
predicciones2 = predict(modelohot2, solicitudhot2)
print(predicciones2)
##        1        2 
## 645.7426 799.4315

Bajo los anteriores resultados el departamento 1 con las mismas caracteristicas a exepcion del estrato, se encuentra que la primera tiene un valor de 645 millones y la segunda de 799 millones.

Conclusiones

El objetivo de estas conclusiones es relatar el proceso metodológico empleado en el análisis de la información. En primer lugar, se realizó un análisis exploratorio de la base de datos vivienda. A partir de este análisis, se creó una nueva variable denominada variable_1, con el propósito de delimitar las casas ubicadas en la zona norte de la ciudad. Como resultado, se identificaron 722 casas en dicha zona. Sin embargo, se evidenció que algunos registros clasificados como parte de la zona norte estaban en realidad ubicados en otras zonas, como el centro y el sur de la ciudad. A pesar de que la mayor concentración de viviendas sí correspondía a la zona norte, esta discrepancia resaltó la necesidad de una revisión más detallada de la variable de ubicación.

Posteriormente, en el proceso de limpieza de datos, se detectaron valores faltantes en variables como piso y parqueaderos. Además, se identificó que la variable piso contenía números almacenados en formato de texto, lo que requirió su transformación a valores numéricos. Para la imputación de los valores faltantes, inicialmente se consideró la metodología clásica basada en la evaluación de normalidad, utilizando la media o mediana según los resultados. No obstante, se optó por aplicar la técnica de imputación KNN, utilizando la relación entre piso y área construida. Esta metodología ofreció resultados coherentes, por lo que se replicó para imputar los valores faltantes en la variable parqueadero.

Una vez finalizado el proceso de imputación, se analizaron las correlaciones entre las variables. Se observó una fuerte correlación positiva (0.73) entre el precio de la vivienda (preciom) y el área construida (areaconst), indicando que, a mayor área, mayor es el precio de la vivienda.

Posteriormente, se llevó a cabo la regresión lineal múltiple. En una primera instancia, se omitió la variable estrato, dado su carácter ordinal. Sin embargo, al incluirla en el modelo sin ningún tipo de tratamiento, se obtuvieron estimaciones para una vivienda con 200 m² de área construida, 1 parqueadero, 2 baños y 4 habitaciones, ubicada en los estratos 4 y 5, con precios de 321 millones y 403 millones de pesos, respectivamente.

Para mejorar la interpretación del modelo, la variable estrato se transformó mediante el enfoque de one-hot encoding, convirtiéndola en variables dummy. No obstante, los resultados indicaban la presencia de colinealidad, lo que podía distorsionar la interpretación de los coeficientes. En particular, el coeficiente de los estratos era negativo, lo que contradecía la intuición económica, ya que se espera que el precio aumente con el estrato. Identificando el problema, se realizó un ajuste en la referencia del estrato, eliminando el estrato 3 como categoría base. Con este ajuste, los coeficientes pasaron a reflejar de manera más lógica el aumento del precio con respecto al estrato 3.

El modelo ajustado presentó un R² de 66.8%, indicando que sigue explicando una cantidad significativa de la variabilidad en los precios de las viviendas sin pérdida de capacidad predictiva. Además, ninguna variable presentó problemas de multicolinealidad. No obstante, se identificaron ciertas deficiencias en los supuestos del modelo, tales como:

  • Los residuos no siguen una distribución normal.
  • Presencia de heterocedasticidad (según la prueba de Breusch-Pagan).
  • Autocorrelación positiva en los residuos (según la prueba de Durbin-Watson).

A pesar de estos problemas, el modelo permitió obtener estimaciones coherentes y útiles para la toma de decisiones.

Resultados de las Predicciones

Primera solicitud

Para una vivienda con 200 m² de área construida, 1 parqueadero, 2 baños y 4 habitaciones, ubicada en estratos 4 y 5, se estimaron los siguientes valores:

  • Estrato 4: 327 millones de pesos
  • Estrato 5: 377 millones de pesos

Segunda solicitud

Para un apartamento en la Zona Sur de Cali con 300 m² de área construida, 3 parqueaderos, 3 baños y 5 habitaciones, se obtuvieron los siguientes valores estimados:

  • Estrato 5: 645 millones de pesos
  • Estrato 6: 799 millones de pesos

Estos resultados confirman que el estrato es una variable clave en la determinación del precio de las viviendas, ya que, a igualdad de condiciones estructurales, los precios varían significativamente entre los distintos estratos socioeconómicos.