Actividad 2 - Caso C&A

1. Problema

Enunciado


Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.


Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.


Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:



Características Vivienda 1 Vivienda 2
Tipo Casa Apartamento
área construida 200 300
parqueaderos 1 3
baños 2 3
habitaciones 4 5
estrato 4 o 5 5 o 6
zona Norte Sur
crédito preaprobado 350 millones 850 millones


Ayude a María a responder la solicitud, mediante técnicas modelación que usted conoce. Ella requiere le envíe un informe ejecutivo donde analice los dos casos y sus recomendaciones (Informe). Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos (Anexos) .




Datos

Los datos de los tres últimos meses se adjuntan en la base que puede obtener con el siguiente código en R

variable descripción
zona ubicación de la vivienda : Zona Centro, Zona Norte,…
piso piso que ocupa la vivienda : primer piso, segundo piso…
estrato estrato socio-económico : 3,4,5,6
preciom precio de la vivienda en millones de pesos
areaconst área construida
parqueaderos número de parqueaderos
banios número de baños
habitaciones número de habitaciones
tipo tipo de vivienda : Casa, Apartamento
barrio barrio de ubicación de la vivienda : 20 de Julio, alamos,..
longitud coordenada geográfica
latitud coordenada geográfica

2. Importación de datos

#install.packages("devtools") # solo la primera vez
#options(repos = c(CRAN = "https://cloud.r-project.org/"))
suppressMessages(suppressWarnings(devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)))
## stringi   (1.8.3  -> 1.8.4  ) [CRAN]
## rlang     (1.1.3  -> 1.1.4  ) [CRAN]
## cli       (3.6.2  -> 3.6.3  ) [CRAN]
## Rcpp      (1.0.12 -> 1.0.13 ) [CRAN]
## digest    (0.6.34 -> 0.6.37 ) [CRAN]
## curl      (5.2.1  -> 5.2.2  ) [CRAN]
## fastmap   (1.1.1  -> 1.2.0  ) [CRAN]
## htmltools (0.5.8  -> 0.5.8.1) [CRAN]
## package 'rlang' successfully unpacked and MD5 sums checked
## package 'cli' successfully unpacked and MD5 sums checked
## package 'Rcpp' successfully unpacked and MD5 sums checked
## package 'digest' successfully unpacked and MD5 sums checked
## package 'curl' successfully unpacked and MD5 sums checked
## package 'fastmap' successfully unpacked and MD5 sums checked
## package 'htmltools' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\Carlos\AppData\Local\Temp\RtmpyU9oAC\downloaded_packages
## ── R CMD build ─────────────────────────────────────────────────────────────────
##       ✔  checking for file 'C:\Users\Carlos\AppData\Local\Temp\RtmpyU9oAC\remotes184601cad5ff0\Centromagis-paqueteMODELOS-78ce06f/DESCRIPTION'
##       ─  preparing 'paqueteMODELOS': (1.9s)
##    checking DESCRIPTION meta-information ...  ✔  checking DESCRIPTION meta-information
##   ─  checking for LF line-endings in source and make files and shell scripts
## ─  checking for empty or unneeded directories
##   ─  building 'paqueteMODELOS_0.1.0.tar.gz'
##      
## 
library(paqueteMODELOS)
data("vivienda")

2.1 Importanción de Librerias

Para realizar la exploración de datos, se procede a importar las librerías que serán utilizadas en el análisis (Previa instalación de las mismas)

library(paqueteMETODOS)
library(dplyr)
library(ggplot2)
library(tidyverse)
library(plotly)
library(cowplot)
library(tidyr)
library(summarytools)
library(PerformanceAnalytics)
library(lmtest)
library(car)

3. Preparación de los datos

El análisis comienza con la preparación del conjunto de datos vivienda2, que es una copia del dataframe original vivienda. Este paso asegura que los datos originales permanezcan intactos mientras se realizan transformaciones y limpiezas en la copia.

#Se crea una base para modificación
vivienda2 <- vivienda
#Realizar el conteo de registros vacios
colSums(is.na(vivienda2))
##           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

El resultado muestra que algunas columnas tienen valores faltantes significativos. Por ejemplo, la columna piso tiene 2,638 valores faltantes, y parqueaderos tiene 1,605 valores faltantes. Otros campos como zona, estrato, preciom, y otros también tienen pocos registros faltantes, en su mayoría 3 o menos.

En lugar de imputar los valores faltantes utilizando la moda o la media, lo que podría alterar la variabilidad de la muestra y afectar los resultados de futuros análisis, se decide eliminar los registros con valores faltantes en base a una columna de referencia con el fin de eliminar el registro general. Esto busca preservar la integridad de los datos para análisis posteriores.

#Se eliminan los registros de una fila completa conforme a una columna en específico.
#Se toma de referencia la columna Id.

#Se cuenta la cantidad de nulos de la base
sapply(vivienda2,function(x) sum(is.na(x)))
##           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
vivienda2 = vivienda2[!is.na(vivienda2$id),]

Después de eliminar los registros con valores faltantes en la columna id, se vuelve a realizar un conteo de valores faltantes por columna.

# Validar los datos faltantes de la base por columna
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2635            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1602            0            0            0            0            0 
##      latitud 
##            0

El resultado muestra que las columnas id, zona, estrato, preciom, areaconst, banios, habitaciones, tipo, barrio, longitud, y latitud ya no tienen valores faltantes. Sin embargo, aún hay valores faltantes en las columnas piso (2,635 registros faltantes) y parqueaderos (1,602 registros faltantes).

3.1 Imputación de datos

Se determina la distribución porcentual de los valores perdidos respecto del total de datos para cada columna, esto proporciona una visión clara de la cantidad de datos faltantes, lo que es clave para decidir cómo manejar estas ausencias.

porcentajeMiss <- function(x) {sum(is.na(x)) / length(x)*100}
# por columna
apply(vivienda2, 2, porcentajeMiss)
##           id         zona         piso      estrato      preciom    areaconst 
##      0.00000      0.00000     31.67448      0.00000      0.00000      0.00000 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##     19.25712      0.00000      0.00000      0.00000      0.00000      0.00000 
##      latitud 
##      0.00000

A continuación, se verifica si existen duplicados en la columna id. Los duplicados pueden causar problemas en el análisis posterior, por lo que es importante identificarlos y manejarlos adecuadamente.

#Contar duplicados:

duplicados = duplicated(vivienda2$id)
sum(duplicados)
## [1] 0

3.2 Imputación de datos a la variable parqueaderos

Dado que la columna parqueaderos tiene un 19.26% de valores faltantes, se decide imputar estos valores utilizando la media de parqueaderos dentro de grupos definidos por las combinaciones de las columnas zona, tipo, y estrato.

# Se crea tabla resumen con el cálculo de la media por grupo (zona, tipo, estrato), se excluyen valores vacíos
mediatable = vivienda2 %>%
  group_by(zona, tipo, estrato) %>%
  summarise_at(vars(parqueaderos), list(media = ~round(mean(., na.rm = TRUE))))

# Se crea una columna nueva clave (En tabla resumen) que permita hacer un left_join para los vacíos.
mediatable$clave = paste(mediatable$zona, mediatable$tipo, mediatable$estrato)

# Se crea una columna nueva clave (En dataframe de vivienda2) que permita hacer un left_join para los vacíos.
vivienda2$clave = paste(vivienda2$zona, vivienda2$tipo, vivienda2$estrato)

# Se realiza cruce con la función left_join()
vivienda2 = vivienda2 %>%
  left_join(mediatable, by = "clave") %>%
  mutate(
    parqueaderos = ifelse(is.na(parqueaderos), media, parqueaderos),
    media = NULL
  )

# Elimina columnas redundantes después del left_join
vivienda2 = vivienda2 %>%
  select(-zona.y, -tipo.y, -estrato.y)

# Definir como dataframe (si es necesario)
vivienda2 <- as.data.frame(vivienda2)

# Restaurar nombres originales, retirando los duplicados con .x o .y
vivienda2 = vivienda2 %>%
  rename_with(~ gsub("\\.x|\\.y", "", .x), contains(".x") | contains(".y"))

Después de la imputación, se valida el estado de los valores faltantes en el dataframe. Esto asegura que las imputaciones se realizaron correctamente y que los valores faltantes han sido manejados adecuadamente.

# Validar los datos faltantes de la base por columna
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2635            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            3            0            0            0            0            0 
##      latitud        clave 
##            0            0

Los valores faltantes en la columna parqueaderos se han reducido a solo 3 observaciones. Sin embargo, estos 3 valores faltantes persisten porque no cumplen con las condiciones de las combinaciones de zona, tipo, y estrato para las cuales se calcularon las medias.

Dado que estas 3 observaciones faltantes en parqueaderos no se pueden imputar, se decide eliminarlas del dataframe para asegurar la integridad del análisis.

# Se identifican 3 observaciones faltantes que no cumplen con la condición por las 3 variables cruzadas anteriormente, se deciden retirar.

vivienda2 = vivienda2[!is.na(vivienda2$parqueaderos),]
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2633            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud        clave 
##            0            0

3.2 Imputación variable piso

Para el caso de la variable piso se realiza inicialmente la conversión de esta columna a un formato numérico a un carácter. Posteriormente se realiza el mismo ejercicio de imputación realizado para la variable parqueadero utilizando la media de piso dentro de grupos definidos por las combinaciones de las columnas zona, tipo, y estrato.

# Convertir la columna `piso` a numérico
vivienda2$piso <- as.numeric(as.character(vivienda2$piso))

# Se crea tabla resumen con el cálculo de la media por grupo (zona, tipo, estrato), excluyendo valores vacíos
mediatablep = vivienda2 %>%
  group_by(zona, tipo, estrato) %>%
  summarise(media = round(mean(piso, na.rm = TRUE)), .groups = "drop")

# Se crea una columna nueva clave (En tabla resumen) que permita hacer un left_join para los vacíos.
mediatablep$clave = paste(mediatablep$zona, mediatablep$tipo, mediatablep$estrato)

# Se crea la columna clave en el dataframe original
vivienda2$clave = paste(vivienda2$zona, vivienda2$tipo, vivienda2$estrato)

# Se realiza cruce con la función left_join()
vivienda2 = vivienda2 %>%
  left_join(mediatablep, by = "clave") %>%
  mutate(
    piso = ifelse(is.na(piso), media, piso)
  ) %>%
  select(-media, -clave, -zona.y, -tipo.y, -estrato.y) 

# Elimina columnas innecesarias

# Definir como dataframe (si es necesario)
vivienda2 <- as.data.frame(vivienda2)


# Restaurar nombres originales, retirando los duplicados con .x o .y
vivienda2 = vivienda2 %>%
  rename_with(~ gsub("\\.x|\\.y", "", .x), contains(".x") | contains(".y"))

Después de realizar la imputación, se verifica la presencia de valores faltantes en el dataframe para asegurarse de que el proceso de imputación se ha realizado correctamente.

# Validar los datos faltantes de la base por columna
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            4            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud 
##            0
# Se identifican 4 observaciones de valores faltantes que no cumplen con la condición por las 3 variables cruzadas anteriormente, se deciden retirar.

vivienda2 = vivienda2[!is.na(vivienda2$piso),]
colSums(is.na(vivienda2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud 
##            0

4. Pasos requeridos para la obtención de los resultados

4.1 Punto 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.

# Filtrar solo los apartamentos
aptos <- subset(vivienda2, tipo == "Apartamento")

# Mostrar los primeros 3 registros
head(aptos, 3)
# Tabla descriptiva
summary(aptos)
##        id           zona                piso          estrato     
##  Min.   :   3   Length:5096        Min.   : 1.00   Min.   :3.000  
##  1st Qu.:2181   Class :character   1st Qu.: 3.00   1st Qu.:4.000  
##  Median :4158   Mode  :character   Median : 4.00   Median :5.000  
##  Mean   :4285                      Mean   : 4.65   Mean   :4.727  
##  3rd Qu.:6556                      3rd Qu.: 5.00   3rd Qu.:6.000  
##  Max.   :8317                      Max.   :12.00   Max.   :6.000  
##     preciom         areaconst      parqueaderos        banios     
##  Min.   :  58.0   Min.   : 35.0   Min.   : 1.000   Min.   :0.000  
##  1st Qu.: 175.0   1st Qu.: 68.0   1st Qu.: 1.000   1st Qu.:2.000  
##  Median : 279.0   Median : 90.0   Median : 1.000   Median :2.000  
##  Mean   : 366.8   Mean   :112.8   Mean   : 1.485   Mean   :2.617  
##  3rd Qu.: 430.0   3rd Qu.:130.0   3rd Qu.: 2.000   3rd Qu.:3.000  
##  Max.   :1950.0   Max.   :932.0   Max.   :10.000   Max.   :8.000  
##   habitaciones       tipo              barrio             longitud     
##  Min.   :0.000   Length:5096        Length:5096        Min.   :-76.59  
##  1st Qu.:3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median :3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   :2.971                                         Mean   :-76.53  
##  3rd Qu.:3.000                                         3rd Qu.:-76.52  
##  Max.   :9.000                                         Max.   :-76.46  
##     latitud     
##  Min.   :3.334  
##  1st Qu.:3.380  
##  Median :3.419  
##  Mean   :3.419  
##  3rd Qu.:3.453  
##  Max.   :3.498
#Comprobación del filtro solicitado
unique(aptos$tipo)
## [1] "Apartamento"

4.2 Punto 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, 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.

# Calculamos las medidas descriptivas de las variables
summarytools::descr(aptos[,4:9])
## Descriptive Statistics  
## aptos  
## N: 5096  
## 
##                     areaconst    banios   estrato   habitaciones   parqueaderos   preciom
## ----------------- ----------- --------- --------- -------------- -------------- ---------
##              Mean      112.76      2.62      4.73           2.97           1.49    366.80
##           Std.Dev       69.37      1.07      0.98           0.68           0.71    288.99
##               Min       35.00      0.00      3.00           0.00           1.00     58.00
##                Q1       68.00      2.00      4.00           3.00           1.00    175.00
##            Median       90.00      2.00      5.00           3.00           1.00    279.00
##                Q3      130.00      3.00      6.00           3.00           2.00    430.00
##               Max      932.00      8.00      6.00           9.00          10.00   1950.00
##               MAD       41.51      1.48      1.48           0.00           0.00    176.43
##               IQR       62.00      1.00      2.00           0.00           1.00    255.00
##                CV        0.62      0.41      0.21           0.23           0.48      0.79
##          Skewness        2.61      0.91     -0.24           0.06           2.14      2.16
##       SE.Skewness        0.03      0.03      0.03           0.03           0.03      0.03
##          Kurtosis       11.18      0.72     -0.97           3.82          10.94      5.44
##           N.Valid     5096.00   5096.00   5096.00        5096.00        5096.00   5096.00
##         Pct.Valid      100.00    100.00    100.00         100.00         100.00    100.00
#Se crea matriz descriptiva de las variables
chart.Correlation(aptos[,4:9], histogram = TRUE, method = "pearson")

A partir de la matriz generada se destaca correlaciones significativas y positivas entre el precio y variables como área construida, estrato, parqueaderos y baños, lo que permite afirmar que estas variables son buenos predictores del precio. Las habitaciones parecen tener una relación más débil.

# Calcular la matriz de correlación
correlaciones <- cor(aptos[,c('preciom','areaconst','estrato','banios','habitaciones')])

# Crear mapa de calor
heatmap_correlaciones <- plot_ly(
  z = correlaciones,
  x = colnames(correlaciones),
  y = colnames(correlaciones),
  type = "heatmap",
  colorscale = "Viridis",
  showscale = TRUE
) %>%
layout(
  title = "Matriz de Correlación con Valores",
  xaxis = list(title = "Variables"),
  yaxis = list(title = "Variables")
)

# Añadir los valores numéricos
for (i in 1:nrow(correlaciones)) {
  for (j in 1:ncol(correlaciones)) {
    heatmap_correlaciones <- heatmap_correlaciones %>%
      add_annotations(
        x = colnames(correlaciones)[j],
        y = colnames(correlaciones)[i],
        text = round(correlaciones[i, j], 2),
        showarrow = FALSE,
        font = list(color = 'black', size = 14)  # Aumentar el tamaño del texto
      )
  }
}

heatmap_correlaciones
# Gráfico de dispersión: Precio vs. Área Construida
scatter_precio_area <- plot_ly(
  data = aptos, 
  x = ~areaconst, 
  y = ~preciom, 
  text = ~zona, 
  mode = "markers", 
  type = "scatter", 
  marker = list(size = 10),  # Tamaño de los puntos más grande
  name = "Precio vs. Área Construida"
)
scatter_precio_area
# Gráfico de dispersión: Precio vs. Estrato
scatter_precio_estrato <- plot_ly(
  data = aptos, 
  x = ~estrato, 
  y = ~preciom, 
  text = ~zona, 
  mode = "markers", 
  type = "box", 
  marker = list(size = 10),  # Tamaño de los puntos más grande
  name = "Precio vs. Estrato"
)
scatter_precio_estrato
# Gráfico de dispersión: Precio vs. Número de Baños
scatter_precio_banios <- plot_ly(
  data = aptos, 
  x = ~banios, 
  y = ~preciom, 
  text = ~zona, 
  mode = "markers", 
  type = "box", 
  marker = list(size = 10),  # Tamaño de los puntos más grande
  name = "Precio vs. Número de Baños"
)
scatter_precio_banios
# Gráfico de dispersión: Precio vs. Número de Habitaciones
scatter_precio_habitaciones <- plot_ly(
  data = aptos, 
  x = ~habitaciones, 
  y = ~preciom, 
  text = ~zona, 
  mode = "markers", 
  type = "box", 
  marker = list(size = 10),  # Tamaño de los puntos más grande
  name = "Precio vs. Número de Habitaciones"
)
scatter_precio_habitaciones
# Combinar todos los gráficos en un solo layout
subplot_graficos <- subplot(
  scatter_precio_area, scatter_precio_estrato, scatter_precio_banios, scatter_precio_habitaciones,
  nrows = 2, margin = 0.05
) %>%
  layout(
    title = "Visualización de Precios vs otras variables",
    showlegend = TRUE,  # Mostrar la leyenda
    legend = list(orientation = "h", y = -0.2),  # Colocar la leyenda en la parte inferior
    height = 450,  # Aumentar la altura del gráfico para que sea más grande
    width = 750   # Aumentar el ancho del gráfico para que sea más grande
  )

# Mostrar la visualización combinada
subplot_graficos

*Precio vs. Área Construida: Muestra una clara correlación positiva, lo que indica que a mayor área construida, mayor es el precio de la vivienda. Hay algunas propiedades con área grande y precio más bajo, lo que podría ser de valor profundizar.

*Precio vs. Estrato: Las viviendas en estratos más altos tienen precios significativamente mayores.

*Precio vs. Número de Baños: Los precios tienden a aumentar con el número de baños.

*Precio vs. Número de Habitaciones: No hay una relación lineal clara entre el número de habitaciones y el precio; el precio varía dentro de un rango más amplio en función de las habitaciones.

4.3 Punto 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).

modelo = lm(preciom ~ areaconst + estrato + parqueaderos + banios + habitaciones, data = aptos)
summary(modelo)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios + 
##     habitaciones, data = aptos)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1670.64   -50.20     1.73    44.23  1016.74 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -264.94915   12.65816  -20.93   <2e-16 ***
## areaconst       1.97487    0.04269   46.26   <2e-16 ***
## estrato        51.29788    2.54399   20.16   <2e-16 ***
## parqueaderos   96.48033    3.79164   25.45   <2e-16 ***
## banios         49.02637    2.94388   16.65   <2e-16 ***
## habitaciones  -35.34753    3.20607  -11.03   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 130.4 on 5090 degrees of freedom
## Multiple R-squared:  0.7967, Adjusted R-squared:  0.7965 
## F-statistic:  3988 on 5 and 5090 DF,  p-value: < 2.2e-16
  1. Área Construida (areaconst): El coeficiente de 1.97 indica que por cada metro cuadrado adicional de área construida, el precio de la vivienda aumenta en 1.97 millones de pesos, manteniendo constantes las demás variables. Este coeficiente es altamente significativo (p < 2e-16), lo que sugiere que el área construida es un factor importante en la determinación del precio. Esto es lógico, ya que mayor espacio generalmente implica un precio más alto.

  2. Estrato (estrato): Con un coeficiente de 51.30, cada aumento en el estrato se asocia con un incremento de 51.30 millones de pesos en el precio. Dado que este coeficiente también es muy significativo (p < 2e-16), refleja que viviendas en áreas de estrato más alto tienden a ser más costosas, lo cual es coherente con las expectativas.

  3. Parqueaderos (parqueaderos): El coeficiente de 96.48 sugiere que cada parqueadero adicional aumenta el precio en 96.48 millones de pesos. Este efecto es significativo (p < 2e-16) y lógico, ya que la disponibilidad de estacionamiento es un factor valioso en el mercado inmobiliario.

  4. Baños (banios): Cada baño adicional incrementa el precio en 49.03 millones de pesos. Con una alta significancia (p < 2e-16), este resultado es lógico, ya que más baños generalmente aumentan el valor percibido de la propiedad.

  5. Habitaciones (habitaciones): El coeficiente es negativo (-35.35), lo que sugiere que agregar una habitación reduce el precio en 35.35 millones de pesos. Aunque este coeficiente es significativo (p < 2e-16), es un resultado que pareciera incorrecto. Podría indicar que, en este conjunto de datos, las viviendas con más habitaciones tienden a ser más pequeñas en otras dimensiones importantes (como área construida), o que hay una relación no lineal entre el número de habitaciones y el precio.

R² (0.7967): El valor de R² indica que el modelo explica el 79.67% de la variabilidad en los precios de las viviendas. Esto sugiere que el modelo tiene un buen ajuste, ya que captura la mayoría de las variaciones en el precio. Sin embargo, hay un 20.33% de la variabilidad que no es explicada por el modelo, lo que implica que hay otros factores no incluidos en el modelo que podrían influir en los precios.

Mejoras Potenciales del Modelo:

*Incluir variables adicionales: Se podria considerar incluir otras variables relevantes que influyan significativamente sobre la variable precio, como la antigüedad de la propiedad, la proximidad a servicios de transporte, o el estado general de la propiedad.

*Explorar relaciones no lineales: Dado que el número de habitaciones tiene un coeficiente negativo inesperado, se podría explorar relaciones no lineales entre las variables predictoras y el precio.

*Interacción entre variables: Adicionalmente se podria considerar si existen interacciones entre variables (Ej. entre el área construida y número de habitaciones) para capturar las dinámicas complejas que influyen en el precio.

4.4 Punto 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).

# Validación de supuestos

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

Los gráficos generados permiten evaluar el cumplimiento de los supuestos del modelo.

*Residuos vs valores ajustados: Nos permite identificar si existe algún patrón en los residuos. Idealmente, los residuos deberían estar distribuidos aleatoriamente alrededor de 0. Para este caso se observa un patrón que indica posibles problemas de no linealidad.

*Q-Q Plot: Los residuos deberían alinearse a la diagonal, para este caso se evidencia que las desviaciones de los residuos no siguen una distribución normal, especialmente en los extremos.

*Scale-Location (Scale-Location): El gráfico debería ser horizontal; pero en este caso, una tendencia ascendente indica heterocedasticidad ya que los residuos se dispersan a medida que aumentan los valores ajustados.

# Prueba de homocedasticidad
bptest(modelo)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo
## BP = 1438.7, df = 5, p-value < 2.2e-16

Al aplicar la prueba de Breusch-Pagan (bptest) la cual se utiliza para verificar la homocedasticidad (si los residuos tienen varianza constante). Los resultados que muestran lo siguiente:

*BP = 1438.7: Este es el estadístico de la prueba Breusch-Pagan.

*p-value < 2.2e-16: Un valor p extremadamente bajo como el calculado indica que rechazamos la hipótesis nula de homocedasticidad, lo que significa que existe heterocedasticidad (la varianza de los residuos no es constante).

# Multicolinealidad
vif(modelo)
##    areaconst      estrato parqueaderos       banios habitaciones 
##     2.628911     1.854024     2.169228     2.966416     1.408790

Al evaluar la multicolinealidad se busca medir si hay correlación alta entre las variables independientes en un modelo de regresión.

Los valores de VIF entre 1 y 5 indican baja o moderada multicolinealidad, lo cual es aceptable. No parece haber un problema grave de multicolinealidad en este modelo, ya que todos los VIF están por debajo de 5.

# Calcular la media de los residuos
mean(resid(modelo))
## [1] -2.897097e-15

En regresión lineal, uno de los supuestos es que los residuos tengan una media de cero. En este caso, el valor -2.897097e-15 es muy cercano a cero, lo que podria confirmar que el modelo cumple con este supuesto, por lo que es un buen indicador de ajuste.

# Prueba de Durbin-Watson para independencia de los errores
library(lmtest)
dwtest(modelo)
## 
##  Durbin-Watson test
## 
## data:  modelo
## DW = 1.6353, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

El resultado de la prueba de Durbin-Watson para evaluar la autocorrelación de los residuos genera los siguientes resultados:

*DW = 1.6353: Este valor indica un nivel intermedio de autocorrelación. Un valor cercano a 2 sugiere independencia de los residuos (sin autocorrelación).

*p-value < 2.2e-16: Este valor es extremadamente bajo, lo que sugiere que se debe rechazar la hipótesis nula de que no hay autocorrelación. Esto indica presencia de autocorrelación positiva en los errores.

Recomendaciones para mejorar el modelo:

*Se podria realizar una transformación de variables aplicando Logaritmos o raíces cuadradas en las variables dependientes o independientes para corregir la heterocedasticidad.

*Otra opción podria ser considerar modelos no lineales, explorando otros modelos más flexibles como árboles de decisión o random forest si los supuestos lineales no son suficientes.

4.5 Punto 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(2)
trainIndex <- sample(1:nrow(aptos), 0.7 * nrow(aptos))
trainData <- aptos[trainIndex, ]
testData <- aptos[-trainIndex, ]

modelo_ent <- lm(preciom ~ areaconst + estrato + parqueaderos + banios + habitaciones, data = trainData)
summary(modelo_ent)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios + 
##     habitaciones, data = trainData)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1203.25   -50.25     1.54    44.13  1003.40 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -272.41343   15.18384 -17.941   <2e-16 ***
## areaconst       2.09759    0.05349  39.216   <2e-16 ***
## estrato        51.85184    3.02639  17.133   <2e-16 ***
## parqueaderos   86.18148    4.37859  19.682   <2e-16 ***
## banios         48.77335    3.49566  13.953   <2e-16 ***
## habitaciones  -33.18172    3.85777  -8.601   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 130.9 on 3561 degrees of freedom
## Multiple R-squared:  0.799,  Adjusted R-squared:  0.7987 
## F-statistic:  2830 on 5 and 3561 DF,  p-value: < 2.2e-16

Frente a los residuos, se identifica que para la distribución de residuos la mediana es cercana a 0, lo cual es ideal, pero hay un rango considerable desde -1203.25 a 1003.40, lo que sugiere que pueden haber algunos errores en las predicciones.

En cuanto a los coeficientes, se dan las siguientes interpretaciones:

*Área construida (2.10): Cada aumento de un metro cuadrado en el área construida aumenta el precio en 2.10 millones de pesos.

*Estrato (51.85): Cada aumento en el estrato incrementa el precio en 51.85 millones de pesos.

*Parqueaderos (86.18): Cada parqueadero adicional incrementa el precio en 86.18 millones de pesos.

*Baños (48.77): Cada baño adicional aumenta el precio en 48.77 millones de pesos.

*Habitaciones (-33.18): Contrario a lo que se espera, cada habitación adicional reduce el precio en 33.18 millones de pesos, lo que podría inferir un posible efecto de colinealidad o una relación más compleja entre las demás variables.

R² (0.799): El R² indica que aproximadamente el 79.9% de la variabilidad en el precio es explicada por las variables independientes, lo que indica un buen ajuste del modelo.

Realizando una comparación de los modelo ejecutados (Conjunto completo vs Conjunto de entrenamiento) se destaca que aunque se cuenta con resultados similares, los resultados del modelo con el conjunto completo son ligeramente más altos en magnitud absoluta.

En cuando al R² ambos modelos cuentas con un valor cercano al 0.80, lo que indica que ambos tienen un ajuste similar.

Por último el tamaño de los residuos del modelo para el conjunto de datos completo presenta residuos más grandes, lo que puede indicar mayor variabilidad.

4.6 Punto 6

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

# Se generan predicciones utilizando el modelo entrenado para predecir el precio de las propiedades en el conjunto de prueba.
predicciones <- predict(modelo_ent, newdata = testData, type = "response")
db_pred <- data.frame(
  Valores_Reales = testData$preciom, 
  Valores_Predichos = predicciones
)


# Graficar la comparación entre valores reales y predichos
plot(db_pred$Valores_Reales, db_pred$Valores_Predichos,
     xlab = "Valores Reales", ylab = "Valores Predichos",
     main = "Comparación entre Valores Reales y Predichos")
abline(0, 1, col = "red")

A partir del gráfico generado, se muestra que existe una relación positiva entre los valores reales y los predichos, lo que indica que el modelo es capaz de predecir de manera razonable los precios de las propiedades.

Sin embargo, hay una dispersión considerable de los puntos alrededor de la línea roja de referencia. Esto sugiere que aunque el modelo ajusta bien los datos en general, hay algunos casos donde la predicción es menos precisa, particularmente en valores más altos.

4.7 Punto 7

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

# calcula el error cuadrático medio (MSE)
mse <- mean((testData$preciom - predicciones)^2)
mse
## [1] 16837.86

El MSE mide el promedio de los errores al cuadrado entre los valores predichos y los reales. Cuanto más bajo sea el MSE, mejor es el ajuste del modelo. Este valor sugiere que en promedio, los errores al cuadrado son 16837.86, sin embargo, este valor es difícil de interpretar directamente porque está en unidades cuadradas, por eso se calcula el MSE.

# Se calcula la raiz cuadrada del MSE
rmse <- sqrt(mean((testData$preciom - predicciones)^2))
rmse
## [1] 129.7608

El RMSE es la raíz cuadrada del MSE. Representa el tamaño promedio de los errores de predicción en la misma escala que la variable dependiente (precio). Un RMSE de 129.76 indica un error promedio de 129.76 millones de pesos. Comparando estos valores con la media del precio (preciom) de 366.8 y su rango máximo de 1950, el RMSE es moderado, lo que indica que los errores de predicción son razonablemente bajos en relación con el rango de precios.

# Cálculo error absoluto medio (MAE)
mae <- mean(abs(testData$preciom - predicciones))
mae
## [1] 75.72597

Mide el error promedio en las predicciones. Este valor nos indica que, en promedio, el modelo de regresión predice los precios de las propiedades con un error de 75.73 millones de pesos.

# Calcular R^2
r2 <- cor(testData$preciom, predicciones)^2
r2
## [1] 0.7895114

El valor del R² indica que el 79% de la variabilidad del precio de las propiedades está explicada por las variables predictoras. Este se podria considerar un buen nivel de ajuste.