1. Realice un filtro a la base de datos e incluya sólo las ofertas de apartamentos. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta.

Iniciamos analizando los registros de ofertas de apartamentos del conjunto de datos vivienda.

vivienda_apto <- subset(vivienda, tipo=="Apartamento")
kable(head(vivienda_apto, 3), "html") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
1212 Zona Norte 01 5 260 90 1 2 3 Apartamento acopi -76.51350 3.45891
1724 Zona Norte 01 5 240 87 1 3 3 Apartamento acopi -76.51700 3.36971
2326 Zona Norte 01 4 220 52 2 2 3 Apartamento acopi -76.51974 3.42627

Podemos observar la cantidad de registros que solo hacen referencia a ofertas de tipo apartamento.

conteo <- table(vivienda_apto$tipo)
kable(conteo, "html", caption = "Conteo en la variable Tipo") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Conteo en la variable Tipo
Var1 Freq
Apartamento 5100

Seleccionamos únicamente las variables de interés para el análisis: preciom, areaconst, estrato, banios, habitaciones, parqueaderos, zona.

vivienda_apto <- select(vivienda_apto, preciom, areaconst, estrato, banios,
                        habitaciones, parqueaderos, zona)
kable(head(vivienda_apto,3), "html", caption = "Dataframe con columnas de interes") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Dataframe con columnas de interes
preciom areaconst estrato banios habitaciones parqueaderos zona
260 90 5 2 3 1 Zona Norte
240 87 5 3 3 1 Zona Norte
220 52 4 2 3 2 Zona Norte

Validamos los registros que tienen datos faltantes.

faltantes <- colSums(is.na(vivienda_apto)) %>%
                 as.data.frame() 
kable(faltantes, "html", caption = "Conteo de faltantes") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Conteo de faltantes
.
preciom 0
areaconst 0
estrato 0
banios 0
habitaciones 0
parqueaderos 869
zona 0

Se eliminan los 869 registros que tienen datos faltantes en la variable parqueaderos. También se eliminan registros que tienen datos atípicos en el número de baños y en el número de habitaciones.

vivienda_apto <- subset(vivienda_apto, !is.na(parqueaderos))
vivienda_apto <- subset(vivienda_apto, banios!=0)
vivienda_apto <- subset(vivienda_apto, banios!=8)
vivienda_apto <- subset(vivienda_apto, habitaciones!=0)
vivienda_apto <- subset(vivienda_apto, habitaciones!=7)
vivienda_apto <- subset(vivienda_apto, habitaciones!=9)
kable(head(vivienda_apto,5), "html", caption = "Dataframe para analisis") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Dataframe para analisis
preciom areaconst estrato banios habitaciones parqueaderos zona
260 90 5 2 3 1 Zona Norte
240 87 5 3 3 1 Zona Norte
220 52 4 2 3 2 Zona Norte
310 137 5 3 4 2 Zona Norte
520 98 6 2 2 2 Zona Norte

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

Enfocándonos en las variables precio y área construida, podríamos ver las dispersiones segmentadas por zonas y detallar las características de cada punto.

plot_ly(data = vivienda_apto, 
        x = ~areaconst, 
        y = ~preciom, 
        type = 'scatter', 
        mode = 'markers',
        color = ~zona,
        text = ~paste("Estrato:", estrato, "<br>Banios:", banios,
                      "<br>Habitaciones:", habitaciones)) %>%
layout(title = "Relacion entre el Precio de la vivienda y Area Construida",
       xaxis = list(title = "Area construida"),
       yaxis = list(title = "Precio (millones)"))

En el gráfico se puede observar un comportamiento natural de que a mayor área construida mayor precio de venta, lo que significa una relación casi lineal entre estas dos variables. También se pueden ver datos atípicos de viviendas con precios muy bajos para áreas construidas muy grandes.

En este caso realizaremos la observación usando un boxplot.

plot_ly(data = vivienda_apto, 
        x = ~as.factor(estrato), 
        y = ~preciom, 
        type = 'box', 
        color = ~as.factor(estrato)) %>%
layout(title = "Precio de la vivienda por estrato",
       xaxis = list(title = "Estrato"),
       yaxis = list(title = "Precio (millones)"))

A partir de la media del precio de la vivienda para cada estrato se puede validar que a medida en que aumenta el estrato también tiende a aumentar el precio de la vivienda. Para el estrato 3 la media es 128.5 millones, para el estrato 4 la media es 210 millones, para el estrato 5 la media es 300 millones y para el estrato 6 la media es 610 millones. Esto significa que existe cierto grado de relación positiva entre estas dos variables. Es importante tener en cuenta que hay muchos valores atípicos para cada estrato.

Se realiza el análisis usando un boxplot.

plot_ly(data = vivienda_apto, 
        x = ~as.factor(banios), 
        y = ~preciom, 
        type = 'box', 
        color = ~as.factor(banios)) %>%
layout(title = "Precio de la vivienda por numero de banios",
       xaxis = list(title = "Numero de banios"),
       yaxis = list(title = "Precio (millones)"))

De igual forma es bastante evidente la relación positiva entre el precio de un inmueble y el número de baños, pues también se puede considerar el aumento en la media del precio para cada cantidad analizada, concluyendo que a mayor cantidad de baños mayor es el precio de la vivienda.

Se realiza el análisis usando un boxplot.

plot_ly(data = vivienda_apto, 
        x = ~as.factor(habitaciones), 
        y = ~preciom, 
        type = 'box', 
        color = ~as.factor(habitaciones)) %>%
layout(title = "Precio de la vivienda por numero de habitaciones",
       xaxis = list(title = "Numero de habitaciones"),
       yaxis = list(title = "Precio (millones)"))

En este caso la relación entre el número de habitaciones y el precio de la vivienda no es muy directa, principalmente porque para las cantidades 1, 2 y 3 la media en el precio es muy similar. Puede notarse una tendencia un poco creciente a partir de 4 habitaciones.

Se realiza el análisis usando un boxplot.

plot_ly(data = vivienda_apto, 
        x = ~as.factor(zona), 
        y = ~preciom, 
        type = 'box', 
        color = ~as.factor(zona)) %>%
layout(title = "Precio de la vivienda por zonas",
       xaxis = list(title = "Zonas"),
       yaxis = list(title = "Precio (millones)"))

Para la distribución de precios de las viviendas por zonas se ve claramente que la zona oeste tiene los apartamentos más costosos, la zona sur y la zona norte presenta valores similares en la media de los precios, en precios mínimos y en el precio máximo, así como en los valores atípicos. La zona oriente y la zona centro presentan las viviendas con menores precios. También se puede tener en cuenta que la mayoría de los apartamentos se encuentran en la zona sur, en la zona oeste y en la zona norte

conteo_zonas <- table(vivienda_apto$zona)
kable(conteo_zonas, "html", caption = "Conteo en la variable Zona") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Conteo en la variable Zona
Var1 Freq
Zona Centro 10
Zona Norte 847
Zona Oeste 962
Zona Oriente 21
Zona Sur 2374
cor_matrix <- cor(select(vivienda_apto, preciom, areaconst, estrato, banios, habitaciones, parqueaderos))
cor_text <- round(cor_matrix, 3)
cor_text <- as.character(cor_text)
plot_ly(
  x = colnames(cor_matrix),
  y = rownames(cor_matrix),
  z = cor_matrix,
  type = "heatmap",
  text = cor_text,
  colorscale = "Viridis"
) %>% 
add_annotations(
    x = rep(colnames(cor_matrix), each = nrow(cor_matrix)),
    y = rep(rownames(cor_matrix), times = ncol(cor_matrix)),
    text = cor_text,
    showarrow = FALSE,
    font = list(color = "white", size = 12)
  )

En la matriz de correlación se observa que todas las variables presentan correlacion positiva con la variable precio. El área construida tiene una correlación lineal muy importante con la variable precio, y las variables cantidad de baños y cantidad de parqueaderos son muy significativas. El estrato también está relacionado linealmente con el precio, como lo vimos anteriormente, pero por el contrario la variable cantidad de habitaciones casi no tiene relación con el precio de las viviendas.

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 sí son estadísticamente significativos. Las interpretaciones deben estar 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).

modelo1 <- lm(preciom  ~ areaconst + as.factor(estrato) 
              + habitaciones + parqueaderos + banios, data=vivienda_apto)
summary(modelo1)

Call:
lm(formula = preciom ~ areaconst + as.factor(estrato) + habitaciones + 
    parqueaderos + banios, data = vivienda_apto)

Residuals:
     Min       1Q   Median       3Q      Max 
-1717.57   -53.79     1.01    46.46   975.63 

Coefficients:
                     Estimate Std. Error t value             Pr(>|t|)    
(Intercept)         -50.89668   12.58957  -4.043            0.0000538 ***
areaconst             2.06448    0.04785  43.148 < 0.0000000000000002 ***
as.factor(estrato)4  22.40280    8.81151   2.542                0.011 *  
as.factor(estrato)5  38.22519    8.66813   4.410            0.0000106 ***
as.factor(estrato)6 162.96423   10.17599  16.015 < 0.0000000000000002 ***
habitaciones        -43.70730    3.87466 -11.280 < 0.0000000000000002 ***
parqueaderos         81.68291    4.20303  19.434 < 0.0000000000000002 ***
banios               51.52874    3.37370  15.274 < 0.0000000000000002 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 132.3 on 4206 degrees of freedom
Multiple R-squared:  0.8009,    Adjusted R-squared:  0.8006 
F-statistic:  2418 on 7 and 4206 DF,  p-value: < 0.00000000000000022

Intercepto (-50.896): Representa el valor estimado del precio de una vivienda cuando las variables independientes son cero. Contextualmente, en este caso no tiene sentido que las variables predictoras sean iguales a cero.

Área construída (2.064): Indica que por cada incremento por unidad de metros cuadrados el precio de la vivienda aumentará en 2.064 millones de pesos, mientras las demás variables se mantengan constantes. Resulta lógico interpretar que en promedio el precio de los apartamentos tendrán ese crecimiento constante dependiente del tamaño del inmueble.

Estrato: En la variable estrato se aplicó One-Hot Encoder, lo que generó una variable para cada valor de estrato: estrato_4, estrato_5 y estrato_6. Los valores en estas variables sólo podrán ser cero o uno, lo que indica que el estrato 4 tiene un peso de 22.4 millones en el valor de la vivienda, el estrato 5 tiene un peso de 38.22 millones en el precio de la vivienda y el estrato 6 presenta un peso mucho mayor de 162.96 millones de pesos en el valor de la vivienda.

Número de habitaciones (-43.707): Indica que por cada aumento por unidad en la cantidad de habitaciones el precio de la vivienda disminuirá en 43.707 millones de pesos, mientras las demás variables se mantengan constantes. Esto puede indicar que los datos muestran tendencias de disminución del precio cuando hay muchas habitaciones.

Número de parqueaderos (81.682): Indica que por cada aumento por unidad en la cantidad de parqueaderos el precio de la vivienda aumentará en 81.682 millones de pesos, mientras las demás variables se mantengan constantes. Esto tiene mucho sentido en el contexto del mercado inmobiliario debido a lo apetecidos que son los parqueaderos en una ciudad.

Número de baños (51.528): Indica que por cada aumento por unidad en la cantidad de baños el precio de la vivienda aumentará en 51.528 millones de pesos, mientras las demás variables se mantengan constantes. También es un factor común ante los deseos de los compradores de tener un inmueble con mayor cantidad de baños.

Según el resúmen del modelo se puede observar que todos los coeficientes son estadísticamente significativos ya que presentan un valor de \(p\) aproximadamente igual a cero, lo que permite rechazar la hipótesis nula de que cada coeficiente puede ser igual a cero.

El \(R^2\) tiene un valor de 0.8, lo que significa que el modelo puede explicar el \(80\%\) de la variabilidad del precio de las viviendas. Este valor es importante y puede indicar que el modelo es robusto y presenta un buen ajuste.

Una opción para intentar mejorar el ajuste del modelo puede ser la realización de transformaciones en las variables predictoras o en la variable dependiente. El modelo estudiado es un modelo Lin-Lin, por lo que las transformaciones pueden ser modelo Log-Lin, modelo Lin-Log y modelo Log-Log. También se puede realizar un análisis exhaustivo de los datos para identificar con mayor detalle datos atípicos, evaluar multicolinealidad, agregar otra variable que pueda ser significativa o realizar algún preprocesamiento en los datos para disminuir ruido.

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(modelo1)

qqnorm(resid(modelo1))
qqline(resid(modelo1))

hist(resid(modelo1))

shapiro.test(resid(modelo1))

    Shapiro-Wilk normality test

data:  resid(modelo1)
W = 0.84441, p-value < 0.00000000000000022

Las gráficas Normal Q-Q Plot y el histograma indican gráficamente la violación del supuesto de que los errores deben ser una variable aleatoria con distribución normal, y la prueba de Shapiro-Wilk lo confirma con un \(p-value<0.05\), indicando que se rechaza la hipótesis nula de que los residuales siguen una distribución normal. El no cumplimiento de este supuesto implica sesgo en los intervalos de confianza y pruebas de hipótesis no confiables, que podrían repercutir en un resultado erróneo en una predicción. Para corregir el no cumplimiento del supuesto de normalidad de los errores se podría realizar una transformación logarítmica o de raíz cuadrada en la variable dependiente precio de la vivienda.

plot(modelo1, which = 1)

gqtest(modelo1)

    Goldfeld-Quandt test

data:  modelo1
GQ = 1.5523, df1 = 2099, df2 = 2099, p-value < 0.00000000000000022
alternative hypothesis: variance increases from segment 1 to 2

La prueba Goldfeld-Quandt presenta un \(p-value<0.05\), indicando que se rechaza la hipótesis nula de que la varianza de los residuos es constante. La violación del supuesto de homocedasticidad puede llevar a inferencias estadistica poco confiables. Una transformación logarítmica o de raíz cuadrada sobre la variable dependiente (preciom) podría ayudar a corregir la varianza no constante en los residuos

dwtest(modelo1)

    Durbin-Watson test

data:  modelo1
DW = 1.7195, p-value < 0.00000000000000022
alternative hypothesis: true autocorrelation is greater than 0

La prueba Durbin-Watson presenta un \(p-value<0.05\), lo cual indica que se debe rechazar la hipótesis nula de independencia entre los errores, que a su vez significa que hay evidencia de autocorrelación entre los residuos del modelo. La autocorrelación en los residuos afecta directamente el error estándar en el cálculo de los coeficientes de regresión. Para corregir la violación de este supuesto se podría usar términos de desfase en las variables independeientes.

avPlots(modelo1)

En la gráfica anterior podemos observar mucha varianza en cada una de las variables independiente del modelo, lo que sugiere que no hay una relación lineal precisa. Esto implica que el modelo no es capaz de relacionar adecuadamente las variables predictoras con la variable independiente, lo que conlleva a subajuste o sobreajuste. Para agregar linealidad al modelo se puede realizar alguna transformación logarítmica o cuadrática en algunas de las variables independientes.

5. Realice una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba. Estime el modelo con la muestra del 70%. Muestre los resultados

set.seed(1)
split <- sample.split(vivienda_apto$preciom, SplitRatio=0.7)
train_data <- subset(vivienda_apto, split==TRUE)
test_data <- subset(vivienda_apto, split==FALSE)
modelo2 <- lm(preciom ~ areaconst + as.factor(estrato) 
              + habitaciones + parqueaderos + banios, data = train_data)
summary(modelo2)

Call:
lm(formula = preciom ~ areaconst + as.factor(estrato) + habitaciones + 
    parqueaderos + banios, data = train_data)

Residuals:
     Min       1Q   Median       3Q      Max 
-1786.24   -53.38     1.02    45.99   965.05 

Coefficients:
                     Estimate Std. Error t value             Pr(>|t|)    
(Intercept)         -56.09536   14.99910  -3.740             0.000188 ***
areaconst             2.14348    0.05696  37.629 < 0.0000000000000002 ***
as.factor(estrato)4  25.54393   10.35631   2.467             0.013700 *  
as.factor(estrato)5  42.52222   10.15313   4.188            0.0000289 ***
as.factor(estrato)6 169.23115   11.95698  14.153 < 0.0000000000000002 ***
habitaciones        -41.95271    4.65900  -9.005 < 0.0000000000000002 ***
parqueaderos         76.52107    4.92637  15.533 < 0.0000000000000002 ***
banios               50.14270    3.96264  12.654 < 0.0000000000000002 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 132.2 on 2980 degrees of freedom
Multiple R-squared:  0.8049,    Adjusted R-squared:  0.8045 
F-statistic:  1757 on 7 and 2980 DF,  p-value: < 0.00000000000000022

El modelo entrenado con el \(70\%\) de los datos tiene un resultado similar al modelo entrenado con el total de los datos. Los residuos se distribuyen en rangos similares (min-max) y los valores de los coeficientes son casi idénticos, perdiendo significancia estadística únicamente en la variable dummy para el estrato 4. Este nuevo modelo también presenta un \(R^2\) igual a 0.80, indicando que puede explicar el \(80\%\) de la varianza del precio.

6. Realice predicciones con el modelo anterior usando los datos de prueba (30%)

predicciones <- predict(modelo2, newdata=test_data)
resultados <- data.frame(preciom = test_data$preciom, prediccion = predicciones)
kable(head(resultados), "html", caption = "DF de puebas con predicciones") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
DF de puebas con predicciones
preciom prediccion
310 415.74320
820 1030.50928
450 469.57377
170 149.22017
78 72.40224
750 724.57376

7. Calcule el error cuadrático medio, el error absoluto medio y el R2, interprete

# Error cuadrático medio
mse <- mse(test_data$preciom, predicciones)
# Error absoluto medio
mae <- mae(test_data$preciom, predicciones)
# R2 Score
media_precio <- mean(test_data$preciom)
SSR <- sum((test_data$preciom - predicciones)^2)
SST <- sum((test_data$preciom - media_precio)^2)
r2_score <- 1 - (SSR/SST)
metricas <- data.frame(
  Modelo = "modelo2",
  ECM = mse,
  EAM = mae,
  R2 = r2_score
)
kable(metricas, "html", caption = "Metricas de evaluación") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Metricas de evaluación
Modelo ECM EAM R2
modelo2 17607.83 81.29649 0.7894513

Esta medida se puede interpretar como la diferencia cuadrática entre el valor predicho y el valor real esperado en promedio. En la evaluación del modelo con el conjunto de datos de pruebas obtenemos un valor de ECM igual a 17607.83, lo que significa que el modelo tiene un desviación aproximada de 17607.83 millones de pesos, con la escala elevada al cuadrado, por lo cual puede ser poco intuitivo para validar la exactitud de modelo. El ECM penaliza principalmente los errores grandes, por lo que un menor valor de ECM significa un mejor modelo.

El EAM es el promedio de todos los errores absolutos entre el valor real y el valor predicho, y que sirve para evaluar la precisión del modelo de regresión lineal. En la evaluación del modelo con el conjunto de datos de pruebas tenemos un EAM igual a 81.296, que indica en promedio que las predicciones se desvían 81.296 millones de pesos frente a los valores reales. Esta es una medida de más fácil interpretación dentro del contexto del problema, y se puede asumir que es un valor relativamente bajo en consideración a los valores de los precios de viviendas que se manejan en el conjunto de datos.

El coeficiente de determinación \(R^2\) con un valor de 0.789 indica que el ajuste del modelo de regresión lineal múltiple puede explicar el \(78.9\%\) de la variabilidad en el precio del inmueble a partir de la variabilidad de las variables predictoras área construída, estrato, número de habitaciones, número de parqueaderos y número de baños. El \(21.1\%\) restante puede depender de algunas variables no tenidas en cuenta en el modelo. En el contexto del mercado inmobiliario, el valor de \(R^2\) presentado por el modelo en la evaluación de los datos de prueba es significativo y puede sugerir que el modelo es robusto y eficiente en la predicción del precio de las viviendas.