Introducción

El siguiente es un informe para compartir el estudio y análisis sobre la base de datos de ofertas de vivienda (más específicamente apartamentos) en la que estudiaremos que relación hay entre las variables área y precio en pro de presentar información útil para los directivos de la inmobiliaria A&C y de esta forma tomar decisiones sobre el precio de los apartamentos según el área construida.

Objetivos

Los siguientes son objetivos que permitiran hacer un analisis estadistico para las decisiones de los directivos de B&C:

  1. Analizar las variables precio y área construida.
  2. Crear distintos modelos de regresión lineal simple.
  3. Validar la precisión y legitimidad de los modelos.
  4. Entregar información relevante a los directivos.

Métodos

Habrá distintos métodos para satisfacer la finalidad de cada objetivo y serán los siguientes:

1. Analizar las variables precio y área construida.

  • Hacer una limpieza previa para estudiar los datos correctos y que provean información.
  • Hacer gráficos pertinentes para ver la distribución y variabilidad de los datos.
  • Tener los indicadores de ambas variables para ver información importante.
  • Hacer gráficos e indicadores para ver la relación entre ambas variables.

2. Crear distintos modelos de regresión lineal simple.

  • Crear un modelo de regresión de lineal simple con los datos crudos.
  • Crear distintos modelos con variación de cada variable usando logaritmos.

3.Validar la precisión y legitimidad de los modelos.

  • Realizar indicadores como el R2 para cada modelo.
  • Generar gráficos para ver el ajuste de los modelos.
  • Validar los supuestos para cada modelo.

4. Entregar información relevante a los directivos.

  • Comparar el rendimiento de cada modelo.
  • Concluir con respecto a los resultados.

Anexos

Limpieza previa

faltantes <- colSums(is.na(vivienda_faltantes)) %>%
                 as.data.frame() 
faltantes #Visualización de datos faltantes por variable
data1 <- vivienda_faltantes[!is.na(vivienda_faltantes$id), ] #Eliminamos los datos nulos sin información relevante
data1 <- data1[!duplicated(data1$id), ] #Eliminamos los datos que esten repetidos
faltantes <- colSums(is.na(data1)) %>%
                 as.data.frame() 
faltantes #Visualización de datos faltantes por variable despues de la limpieza
valortipos <- unique(data1$tipo)
valortipos #Quiero ver todos los valores que tiene la variable tipo
## [1] "Apartamento" "Casa"        "APARTAMENTO" "casa"        "CASA"       
## [6] "apto"
#Como hay valores que significan lo mismo pero estan escritos de forma distinta los dejaremos todos en un mismo formato
data1$tipo[data1$tipo %in% c("APARTAMENTO", "apto")] <- "Apartamento"  # Reemplazamos las variaciones de "Apartamento"
data1$tipo[data1$tipo %in% c("casa", "CASA")] <- "Casa"  # Reemplazamos las variaciones de "Casa"
valordesp <- unique(data1$tipo)
valordesp #Quiero ver todos los valores que tiene la variable tipo despues del cambio
## [1] "Apartamento" "Casa"
data1 = subset(data1, tipo == "Apartamento")
attach(data1)

Antes de empezar con el análisis realizamos una limpieza previa donde eliminamos datos faltantes que no nos aportan información puesto que tienen datos nulos, además normalizamos la variable tipo para que todas quedaran bajo el mismo formato ya que antes se escribieron de forma distinta. Finalmente hicimos un filtro para escoger solamente los apartamentos para hacer el estudio.

AED precio y área

Precio en millones de pesos

# Obtener el resumen de la variable preciom
summary_preciom <- summary(preciom)
# Calcular la desviación estándar de preciom
sd_preciom <- sd(preciom)

# Crear un data frame con el resumen y la desviación estándar
summary_and_sd_df <- data.frame(
  Estadísticas = c(names(summary_preciom), "Desviación Estándar"),
  Valor = c(summary_preciom, sd_preciom)
)

# Utilizar kable para formatear la tabla
kable(summary_and_sd_df, "html") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  column_spec(1, bold = TRUE)
Estadísticas Valor
Min. Min. 58.0000
1st Qu. 1st Qu. 175.0000
Median Median 279.0000
Mean Mean 366.9918
3rd Qu. 3rd Qu. 430.0000
Max. Max. 1950.0000
Desviación Estándar 289.2272
ggplot(data1, aes(x = preciom)) +
  geom_histogram(binwidth = 30, fill = "skyblue", color = "black") +
  labs(x = "Precio de la vivienda (millones de pesos)", y = "Frecuencia",
       title = "Distribución del Precio de la Vivienda")

ggplot(data1, aes(y = preciom)) +
  geom_boxplot(fill = "lightblue", color = "black") +
  labs(y = "Precio de la vivienda (millones de pesos)",
       title = "Boxplot del Precio de la Vivienda")

qqnorm(preciom)
qqline(preciom)

lillie.test(x = preciom )
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  preciom
## D = 0.18453, p-value < 2.2e-16

El resumen estadístico del precio de vivienda muestra que:

-El precio mínimo es 58 millones de pesos.

-El precio máximo es 1999 millones de pesos.

-La mediana del precio es 330 millones de pesos.

-La media del precio es 433.9 millones de pesos.

-La desviación estándar del precio es 328.665 millones de pesos.

Lo anterior muestra que hay una asimetría bastante notable con cola hacia la derecha pues la mayoría de las casas valen menos de 500 millones de pesos sin embargo el precio va hasta casi los 2000 millones, lo que también indica una alta variabilidad como se ve en la desviación estándar y en el boxplot.

También vemos con el grafico de normalidad y la prueba de normalidad Kolmogorov-Smirnov (como el Shapiro-Wilk test pero para muestras de mayor tamaño) que la distribución de los datos no es normal por lo que no podremos aplicar indicadores como el coeficiente de correlación de Pearson.

Área en metros cuadrados

# Resumen de la variable areaconst
summary_areaconst <- summary(areaconst)

# Desviación estándar de areaconst
sd_areaconst <- sd(areaconst)

# Crear un data frame con el resumen y la desviación estándar de areaconst
summary_and_sd_areaconst_df <- data.frame(
  Estadísticas = c(names(summary_areaconst), "Desviación Estándar"),
  Valor = c(summary_areaconst, sd_areaconst)
)

# Utilizar kable para formatear la tabla
kable(summary_and_sd_areaconst_df, "html") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  column_spec(1, bold = TRUE)
Estadísticas Valor
Min. Min. 35.00000
1st Qu. 1st Qu. 68.00000
Median Median 90.00000
Mean Mean 112.79483
3rd Qu. 3rd Qu. 130.00000
Max. Max. 932.00000
Desviación Estándar 69.35951
ggplot(data1, aes(x = areaconst)) +
  geom_histogram(binwidth = 30,fill = "lightgreen", color = "black") +
  labs(x = "Área de la vivienda (metros cuadrados)", y = "Frecuencia",
       title = "Distribución del Área de la Vivienda")

ggplot(data1, aes(y = areaconst)) +
  geom_boxplot(fill = "lightgreen", color = "black") +
  labs(y = "Área de la vivienda (metros cuadrados)",
       title = "Boxplot del Área de la Vivienda")

qqnorm(areaconst)
qqline(areaconst)

lillie.test(x = areaconst )
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  areaconst
## D = 0.17581, p-value < 2.2e-16

El resumen estadístico del área de la vivienda muestra que:

-El área mínima es 30 metros cuadrados.

-El área máxima es 1745 metros cuadrados.

-La mediana del área es 123 metros cuadrados.

-La media del área es 174.9 metros cuadrados.

-La desviación estándar del área es 142.964 metros cuadrados.

Desde ahora se ve que la distribución del área tiene un comportamiento similar a la distribución del precio pues también se ve una asimetría con cola a la derecha y una gran variabilidad puesto que el 50% de las viviendas tienen un área de entre 30 a 123 metros cuadrados, mientras que el resto de las viviendas va de 123 a 1745 metros cuadrados. Al igual que en el precio, podemos ver que esta distribución tampoco es normal.

AED precio vs área

ggplot(data1, aes(x = areaconst, y = preciom)) +
  geom_point(color = "blue", alpha = 0.7) +
  labs(x = "Área de la vivienda (metros cuadrados)", y = "Precio de la vivienda (millones de pesos)",
       title = "Relación entre Precio y Área Construida")

spear = round(cor(preciom, areaconst, method = "spearman"),3)
spear
## [1] 0.89

Viendo el grafico de dispersión vemos que en su mayoría vemos un patrón entre el precio y el área construida. Se ve que en general entre mayor sea el área, mayor será el precio, es decir es una relación lineal positiva. Lo anterior lo podemos confirmar haciendo el coeficiente de correlación de Spearman (ya que ninguna variable se distribuye de forma normal) y nos da 0.89 aproximadamente, esto quiere decir que hay una relación lineal positiva fuerte.

Estimación modelo de regresión lineal simple

# Estimación del modelo de regresión lineal
lm_model = lm(preciom ~ areaconst, data=data1)
# Gráfico de dispersión de las variables transformadas
plot(areaconst, preciom, main = "Precio vs Área Construida", xlab = "Área Construida", ylab = "Precio")

# Añadir la línea de regresión
abline(lm_model, col = "red")

# Añadir etiquetas para la línea de regresión
legend("topleft", legend = "Recta de Regresión", col = "red", lty = 1, cex = 0.8)

lm_model
## 
## Call:
## lm(formula = preciom ~ areaconst, data = data1)
## 
## Coefficients:
## (Intercept)    areaconst  
##     -22.797        3.456

Según este modelo el precio aumenta en 3.456 millones de peso por metro cuadrado aproximadamente.

Intervalo de confianza 95% B1

# Resumen del modelo
summary(lm_model)
## 
## Call:
## lm(formula = preciom ~ areaconst, data = data1)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2898.94   -60.12   -14.99    44.79  1020.85 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -22.79656    4.32840  -5.267 1.45e-07 ***
## areaconst     3.45573    0.03269 105.714  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 161.9 on 5097 degrees of freedom
## Multiple R-squared:  0.6868, Adjusted R-squared:  0.6867 
## F-statistic: 1.118e+04 on 1 and 5097 DF,  p-value: < 2.2e-16
confint(lm_model)
##                  2.5 %     97.5 %
## (Intercept) -31.282086 -14.311041
## areaconst     3.391643   3.519814

Tanto B0 como B1 son significativos puesto que se rechaza la hipótesis de que sean igual a 0 ya que el valor-p de la hipótesis t en cada caso es cero, además el intervalo de confianza de B1 no incluye el 0. Vemos que con una confianza del 95% B1 se encuentra entre 3.39 y 3.52 millones de pesos que es lo que según este modelo sube el precio por cada metro cuadrado.

Cálculo e interpretación de indicador de bondad R**2

# Obtener el R^2 del modelo
r_squared <- summary(lm_model)$r.squared

r_squared
## [1] 0.6867725

Como el indicador esta más cercano a 1 asumimos que el área construida explica en un 68.68% la variabilidad en el precio de la vivienda, es decir que el modelo puede que tenga un buen ajuste.

Predicciones (110m2)

# Precio estimado utilizando el modelo de regresión lineal
predict(lm_model, data.frame(areaconst=110), interval = "confidence", level = 0.95)
##        fit      lwr      upr
## 1 357.3336 352.8855 361.7817
# Filtrar las viviendas con área construida igual a 110 metros cuadrados
data_area_110 <- subset(data1, areaconst == 110)

# Valor mínimo del precio de las viviendas con área construida igual a 110 metros cuadrados
min(data_area_110$preciom)
## [1] 175
# Valor máximo del precio de las viviendas con área construida igual a 110 metros cuadrados
max(data_area_110$preciom)
## [1] 550
# Calcular el precio promedio de las viviendas con área construida igual a 110 metros cuadrados
average_price_area_110 <- mean(data_area_110$preciom)

average_price_area_110
## [1] 335.6556

El precio promedio estimado para un apartamento de 110 metros cuadrados según el modelo es de 357.3 millones de pesos, en cambio el promedio de precio para los apartamentos de 110 metros cuadrados es de 335.7 millones de pesos. Una vivienda de 200 millones por un apartamento de estas características podría ser atractivo ya que el precio máximo es de 550 millones y el mínimo de 175 entonces habría posibilidad de que fuera atractivo, realmente todo depende de otras variables como el estrato y la zona que también pueden influir en el precio.

Validación de supuestos

Normalidad

# Prueba de Lilliefors para los residuos
lillie.test(lm_model$residuals)
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  lm_model$residuals
## D = 0.14644, p-value < 2.2e-16
# Histograma de los residuos
hist(lm_model$residuals, main = "Histograma de los Residuos", xlab = "Residuos")

# Gráfico de qqplot de los residuos
qqnorm(lm_model$residuals)
qqline(lm_model$residuals)

Según la prueba de normalidad y los gráficos no parece que los errores tengan una distribución normal, por lo que hay que mejorar el ajuste.

Homoscedasticidad

lmtest::bptest(lm_model)
## 
##  studentized Breusch-Pagan test
## 
## data:  lm_model
## BP = 1001.7, df = 1, p-value < 2.2e-16

En la prueba el valor P no supera el nivel de significancia (0.05) por lo que se concluye que los residuos no tienen una varianza constante sino que va incrementando.

Linealidad

# Obtener los residuos
residuos <- resid(lm_model)

# Gráfico de dispersión de los residuos vs valores ajustados
plot(fitted(lm_model), residuos, main = "Residuos vs Valores Ajustados", xlab = "Valores Ajustados", ylab = "Residuos")

# Añadir una línea horizontal en y = 0
abline(h = 0, col = "red")

La relación entre las variables y los errores no es lineal pues vemos que los residuos no tienen una distribución aleatoria sino que siguen un patrón en el que hay mas varianza entre mas se avanza.

No autocorrelación

lmtest::dwtest(lm_model)
## 
##  Durbin-Watson test
## 
## data:  lm_model
## DW = 1.6644, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

El valor p es menor a 0.05 por lo que la autocorrelación es significativa y distinta de 0.

Outliers

# Gráfico de dispersión de los residuos
plot(lm_model$residuals, main = "Gráfico de Dispersión de los Residuos", ylab = "Residuos")

# Boxplot de los residuos
boxplot(lm_model$residuals, main = "Boxplot de los Residuos")

Con estos gráficos y los hechos para cada variable, vemos que hay bastante valores atípicos en ambas variables y en los residuos que pueden generar sesgos en los estimadores de los coeficientes.

Transformaciones y otros modelos

Modelo 1

modelo1=lm(preciom ~ log(areaconst), data=data1)
# Gráfico de dispersión de las variables transformadas
plot(log(areaconst), preciom, main = "Precio vs Log(Área Construida)", xlab = "Log(Área Construida)", ylab = "Precio")

# Añadir la línea de regresión
abline(modelo1, col = "red")

# Añadir etiquetas para la línea de regresión
legend("topleft", legend = "Recta de Regresión", col = "red", lty = 1, cex = 0.8)

summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ log(areaconst), data = data1)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1201.43   -62.55     3.08    53.61  1042.76 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    -1954.709     21.253  -91.97   <2e-16 ***
## log(areaconst)   505.335      4.601  109.83   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 157.6 on 5097 degrees of freedom
## Multiple R-squared:  0.703,  Adjusted R-squared:  0.7029 
## F-statistic: 1.206e+04 on 1 and 5097 DF,  p-value: < 2.2e-16

Se ve mejor que el modelo inicial sin embargo sigue teniendo mayor error a medida que avanza el logaritmo del área construida.

Supuestos

Normalidad
# Prueba de Lilliefors para los residuos
lillie.test(modelo1$residuals)
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  modelo1$residuals
## D = 0.13735, p-value < 2.2e-16
# Histograma de los residuos
hist(modelo1$residuals, main = "Histograma de los Residuos", xlab = "Residuos")

# Gráfico de qqplot de los residuos
qqnorm(modelo1$residuals)
qqline(modelo1$residuals)

Según la prueba de normalidad y los gráficos no parece que los errores tengan una distribución normal, por lo que hay que mejorar el ajuste.

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

En la prueba el valor P no supera el nivel de significancia (0.05) por lo que se concluye que los residuos no tienen una varianza constante sino que va incrementando.

Linealidad
# Obtener los residuos
residuos <- resid(modelo1)

# Gráfico de dispersión de los residuos vs valores ajustados
plot(fitted(modelo1), residuos, main = "Residuos vs Valores Ajustados", xlab = "Valores Ajustados", ylab = "Residuos")

# Añadir una línea horizontal en y = 0
abline(h = 0, col = "red")

La relación entre las variables y los errores no es lineal pues vemos que los residuos no tienen una distribución aleatoria sino que siguen un patrón en el que hay mas varianza entre mas se avanza.

No autocorrelación
lmtest::dwtest(modelo1)
## 
##  Durbin-Watson test
## 
## data:  modelo1
## DW = 1.6741, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

El valor p es menor a 0.05 por lo que la autocorrelación es significativa y distinta de 0.

Outliers
# Gráfico de dispersión de los residuos
plot(modelo1$residuals, main = "Gráfico de Dispersión de los Residuos", ylab = "Residuos")

# Boxplot de los residuos
boxplot(modelo1$residuals, main = "Boxplot de los Residuos")

ggplot(data1, aes(y = log(areaconst))) +
  geom_boxplot(fill = "lightgreen", color = "black") +
  labs(y = "log área construida (Metros cuadrados)",
       title = "Boxplot del log de área construida")

Con estos gráficos y los hechos para cada variable, vemos que hay bastante valores atípicos en ambas variables y en los residuos que pueden generar sesgos en los estimadores de los coeficientes.

Modelo 2

modelo2=lm(log(preciom) ~ areaconst, data=data1)
# Gráfico de dispersión de las variables transformadas
plot(areaconst, log(preciom), main = "Log(Precio) vs Área Construida", xlab = "Área Construida", ylab = "Log(Precio)")

# Añadir la línea de regresión
abline(modelo2, col = "red")

# Añadir etiquetas para la línea de regresión
legend("topleft", legend = "Recta de Regresión", col = "red", lty = 1, cex = 0.8)

summary(modelo2)
## 
## Call:
## lm(formula = log(preciom) ~ areaconst, data = data1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -6.0372 -0.2579  0.0358  0.2858  1.1744 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 4.840e+00  1.082e-02   447.5   <2e-16 ***
## areaconst   7.401e-03  8.168e-05    90.6   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4045 on 5097 degrees of freedom
## Multiple R-squared:  0.6169, Adjusted R-squared:  0.6169 
## F-statistic:  8209 on 1 and 5097 DF,  p-value: < 2.2e-16

No parece ser un modelo adecuado, pues la línea no se ajusta bien a la mayoría de los datos.

Supuestos

Normalidad
# Prueba de Lilliefors para los residuos
lillie.test(modelo2$residuals)
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  modelo2$residuals
## D = 0.039726, p-value < 2.2e-16
# Histograma de los residuos
hist(modelo2$residuals, main = "Histograma de los Residuos", xlab = "Residuos")

# Gráfico de qqplot de los residuos
qqnorm(modelo2$residuals)
qqline(modelo2$residuals)

Según la prueba de normalidad y los gráficos no parece que los errores tengan una distribución normal, por lo que hay que mejorar el ajuste.

Homoscedasticidad
lmtest::bptest(modelo2)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo2
## BP = 594.41, df = 1, p-value < 2.2e-16

En ambas pruebas el valor P no supera el nivel de significancia (0.05) por lo que se concluye que los residuos no tienen una varianza constante sino que va incrementando.

Linealidad
# Obtener los residuos
residuos <- resid(modelo2)

# Gráfico de dispersión de los residuos vs valores ajustados
plot(fitted(modelo2), residuos, main = "Residuos vs Valores Ajustados", xlab = "Valores Ajustados", ylab = "Residuos")

# Añadir una línea horizontal en y = 0
abline(h = 0, col = "red")

La relación entre las variables y los errores no es lineal pues vemos que los residuos no tienen una distribución aleatoria sino que siguen un patrón en el que hay mas varianza entre mas se avanza.

No autocorrelación
lmtest::dwtest(modelo2)
## 
##  Durbin-Watson test
## 
## data:  modelo2
## DW = 1.5008, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

El valor p es menor a 0.05 por lo que la autocorrelación es significativa y distinta de 0.

Outliers
# Gráfico de dispersión de los residuos
plot(modelo2$residuals, main = "Gráfico de Dispersión de los Residuos", ylab = "Residuos")

# Boxplot de los residuos
boxplot(modelo2$residuals, main = "Boxplot de los Residuos")

ggplot(data1, aes(y = log(preciom))) +
  geom_boxplot(fill = "lightblue", color = "black") +
  labs(y = "log precio (millones de pesos)",
       title = "Boxplot del log de precio")

Con estos gráficos y los hechos para cada variable, vemos que hay bastante valores atípicos en ambas variables y en los residuos que pueden generar sesgos en los estimadores de los coeficientes. A pesar de que haya datos atípicos mejoraron los de el logaritmo del precio en comparación al precio normal.

Modelo 3

modelo3=lm(log(preciom) ~ log(areaconst), data=data1)
# Gráfico de dispersión de las variables transformadas
plot(log(areaconst), log(preciom), main = "Log(Precio) vs Log(Área Construida)", xlab = "Log(Área Construida)", ylab = "Log(Precio)")

# Añadir la línea de regresión
abline(modelo3, col = "red")

# Añadir etiquetas para la línea de regresión
legend("topleft", legend = "Recta de Regresión", col = "red", lty = 1, cex = 0.8)

summary(modelo3)
## 
## Call:
## lm(formula = log(preciom) ~ log(areaconst), data = data1)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2.70538 -0.17417  0.03435  0.20750  1.31363 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    0.178375   0.042106   4.236 2.31e-05 ***
## log(areaconst) 1.196335   0.009115 131.247  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.3123 on 5097 degrees of freedom
## Multiple R-squared:  0.7717, Adjusted R-squared:  0.7716 
## F-statistic: 1.723e+04 on 1 and 5097 DF,  p-value: < 2.2e-16

Este es el modelo que se ve mejor y se ajusta en mayor medida a los datos. #### Supuestos

Normalidad
# Prueba de Lilliefors para los residuos
lillie.test(modelo3$residuals)
## 
##  Lilliefors (Kolmogorov-Smirnov) normality test
## 
## data:  modelo3$residuals
## D = 0.057323, p-value < 2.2e-16
# Histograma de los residuos
hist(modelo3$residuals, main = "Histograma de los Residuos", xlab = "Residuos")

# Gráfico de qqplot de los residuos
qqnorm(modelo3$residuals)
qqline(modelo3$residuals)

Según la prueba de normalidad y los gráficos no parece que los errores tengan una distribución normal, por lo que hay que mejorar el ajuste.

Homoscedasticidad
lmtest::bptest(modelo3)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo3
## BP = 273.15, df = 1, p-value < 2.2e-16

En ambas pruebas el valor P no supera el nivel de significancia (0.05) por lo que se concluye que los residuos no tienen una varianza constante sino que va incrementando.

Linealidad
# Obtener los residuos
residuos <- resid(modelo3)

# Gráfico de dispersión de los residuos vs valores ajustados
plot(fitted(modelo3), residuos, main = "Residuos vs Valores Ajustados", xlab = "Valores Ajustados", ylab = "Residuos")

# Añadir una línea horizontal en y = 0
abline(h = 0, col = "red")

La relación entre las variables y los errores no es lineal pues vemos que los residuos no tienen una distribución aleatoria sino que siguen un patrón en el que hay mas varianza entre mas se avanza, sin embargo es la mejor comparada con los anteriores modelos.

No autocorrelación
lmtest::dwtest(modelo3)
## 
##  Durbin-Watson test
## 
## data:  modelo3
## DW = 1.5855, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

El valor p es menor a 0.05 por lo que la autocorrelación es significativa y distinta de 0.

Outliers
# Gráfico de dispersión de los residuos
plot(modelo3$residuals, main = "Gráfico de Dispersión de los Residuos", ylab = "Residuos")

# Boxplot de los residuos
boxplot(modelo3$residuals, main = "Boxplot de los Residuos")

ggplot(data1, aes(y = log(preciom))) +
  geom_boxplot(fill = "lightblue", color = "black") +
  labs(y = "log precio (millones de pesos)",
       title = "Boxplot del log de precio")

ggplot(data1, aes(y = log(areaconst))) +
  geom_boxplot(fill = "lightgreen", color = "black") +
  labs(y = "log área construida (Metros cuadrados)",
       title = "Boxplot del log de área construida")

Con estos gráficos y los hechos para cada variable, vemos que hay bastante valores atípicos en ambas variables y en los residuos que pueden generar sesgos en los estimadores de los coeficientes.

Comparación

stargazer(lm_model,modelo1, modelo2, modelo3, type="text", df=FALSE)
## 
## ==========================================================================
##                                      Dependent variable:                  
##                     ------------------------------------------------------
##                               preciom                  log(preciom)       
##                          (1)           (2)          (3)           (4)     
## --------------------------------------------------------------------------
## areaconst             3.456***                    0.007***                
##                        (0.033)                    (0.0001)                
##                                                                           
## log(areaconst)                     505.335***                  1.196***   
##                                      (4.601)                    (0.009)   
##                                                                           
## Constant             -22.797***   -1,954.709***   4.840***     0.178***   
##                        (4.328)      (21.253)      (0.011)       (0.042)   
##                                                                           
## --------------------------------------------------------------------------
## Observations            5,099         5,099        5,099         5,099    
## R2                      0.687         0.703        0.617         0.772    
## Adjusted R2             0.687         0.703        0.617         0.772    
## Residual Std. Error    161.887       157.642       0.405         0.312    
## F Statistic         11,175.520*** 12,063.700*** 8,208.982*** 17,225.730***
## ==========================================================================
## Note:                                          *p<0.1; **p<0.05; ***p<0.01

En ningún modelo se cumplieron los supuestos, esto dado a que es difícil predecir solo con una variable y un modelo tan sencillo, sin embargo el modelo en el que tanto el área como el precio se expresaban como logaritmos tuvo el mejor ajuste de todos, esto se puede ver en las gráficas y también con el R2 que es el mas alto de todos y nos indica que el modelo explica en un 77.2% la variabilidad del logaritmo del precio dado el logaritmo del área construida.

Resultados

Luego de hacer el modelo y sus ajustes vemos que realmente ninguno cumplió con algún supuesto, además tenemos datos muy variables y con bastantes datos atípicos a pesar de haber hecho el filtro de solo usar apartamentos. De igual forma hubieron modelos mejores que otros y con buenos resultados para un modelo tan simple y con tan pocas variables.

Conclusiones

A pesar de que se ve cierto patrón entre el área y el precio a simple vista, no es uno que se pueda explicar correctamente de forma lineal. De entre todas las aproximaciones que se hicieron la mejor fue usando el logaritmo de ambas variables como se ve en el gráfico en el que comparamos los distintos modelos, además este modelo también tiene el R2 mas alto que nos indica que el logaritmo del área indica en un 77.2% la variabilidad del logaritmo del precio. A pesar de que fue el mejor modelo, no cumplió con ninguno de los supuestos que legitiman a un modelo confiable por lo que no es realmente útil mas es bueno para lo simple que es.

# Dividir el área de trazado en una cuadrícula de 2x2
par(mfrow = c(2, 2))

# Gráfico 1: Precio vs Área Construida
plot(areaconst, preciom, main = "Precio vs Área Construida", xlab = "Área Construida", ylab = "Precio")
abline(lm_model, col = "red")
legend("topleft", legend = "Recta de Regresión", col = "red", lty = 1, cex = 0.8)

# Gráfico 2: Precio vs Log(Área Construida)
plot(log(areaconst), preciom, main = "Precio vs Log(Área Construida)", xlab = "Log(Área Construida)", ylab = "Precio")
abline(modelo1, col = "red")

# Gráfico 3: Log(Precio) vs Área Construida
plot(areaconst, log(preciom), main = "Log(Precio) vs Área Construida", xlab = "Área Construida", ylab = "Log(Precio)")
abline(modelo2, col = "red")

# Gráfico 4: Log(Precio) vs Log(Área Construida)
plot(log(areaconst), log(preciom), main = "Log(Precio) vs Log(Área Construida)", xlab = "Log(Área Construida)", ylab = "Log(Precio)")
abline(modelo3, col = "red")

Con todo esto en cuenta se ofrecen cuatro soluciones:

  1. Crear modelos lineales con ajustes para cada tipo de vivienda y para cada zona. Es posible que la alta variabilidad de los datos y valores atípicos vengan del origen distinto de los datos por lo que, si se crearan distintos filtros como por zona, tipo e incluso estrato (si se ve necesario) se podría construir un modelo lineal simple que represente mejor la relación entre estas 2 variables para cada subconjunto.
  2. Crear modelos lineales más complejos. Se podrían usar modelos usando más variables, es decir modelos de regresión múltiple, esto tendría en cuenta la variabilidad de las zonas, tipos y estratos dentro del mismo modelo lo que podría generar resultados más confiables y un modelo más preciso.
  3. Usar otros tipos de modelos más complejos. Utilizando modelos más complejos como por ejemplo los de aprendizaje automático podríamos tener una regresión más precisa con los filtros e hiperparametros correspondientes.
  4. Hacer una limpieza exhaustiva previa de los datos antes de realizar el modelo. Si se podan los datos atípicos y se estandarizan mas los datos se podría crear un modelo que encaje mejor, sin embargo esto reduciría un poco el alcance del modelo y se limitaría a los datos cercanos al promedio.

En conclusión, este tipo de modelos puede resultar útil mas no infalible, puede servir de guía para establecer precios y estrategias más todavía carece de la minuciosidad necesaria para ser confiable por sí solo.