1 ENTENDIMIENTO DEL NEGOCIO

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

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

2 CONTEXTO

2.1 Área de Estudio

Santiago de Cali, la capital del departamento de Valle del Cauca, es la tercera ciudad más grande de Colombia con una población estimada de 2.460.392 habitantes en 2024. Esta metrópoli ha experimentado un notable crecimiento poblacional, constituyendo más del 60% de la población total del departamento. Distribuida en 336 barrios y 22 comunas, Cali es un centro urbano vibrante que ha visto una expansión significativa debido a la migración interna desde áreas rurales hacia zonas urbanas.

La ciudad ofrece una atractiva combinación de oportunidades para el mercado inmobiliario, caracterizado por la constante valorización de propiedades. Esta tendencia se debe a una creciente demanda, respaldada por su clima agradable y estable que facilita un estilo de vida activo y saludable. Además, Cali es un epicentro cultural y de entretenimiento, famoso por sus festivales de salsa, teatros, galerías de arte, y una variada oferta gastronómica. La diversidad del mercado inmobiliario incluye desde lujosas residencias hasta apartamentos vacacionales, lo que ofrece opciones para una amplia gama de compradores y un entorno favorable para la inversión, con propiedades que aumentan su valor de forma constante.

library(sf)
library(leaflet)

ruta_archivo <- "C:/Users/lvasquez/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/Barrios_Cali.gpkg"
#ruta_archivo <- "C:/Users/ASUS/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/Cali_WGS84.gpkg"

file.exists(ruta_archivo)
Cali_WGS84 <- st_read(ruta_archivo) # fuente de datos secundaria (www.cali.gov.co)
Cali_WGS84 <- st_transform(Cali_WGS84, crs = '+proj=longlat +datum=WGS84')

leaflet() %>%
  addTiles() %>%
  setView(lng = -76.53, lat = 3.42, zoom = 11.5) %>%
  addPolygons(data = Cali_WGS84, color = "gray",weight = 2,fillColor = "gray", stroke = 0.1, opacity = 0.9, popup = paste0("Barrio: ",Cali_WGS84$barrio,"<br>","Comuna: ",Cali_WGS84$comuna))

2.2 Fuentes de datos

Datos primarios

Los datos empleados en el análisis de la oferta inmobiliaria en la ciudad de Cali, fue extraída de OLX mediante procedimiento webscraping y contenidos en el paquete MODELOS de R. Este conjunto de datos cuenta con un total de 8327 registros y 15 atributos, de interés para el sector. Esta base de datos fue, transformada empleando técnicas estadísticas de limpieza e imputación y creando nuevas variables que serán útiles para analizar el nicho de mercado, objetivo esencial para la compañía. El proceso EDL puede consultarse a detalle en apartado ANEXOS.

if (!is.element("DT", installed.packages()[,1])){
  install.packages("DT", repos = "http://mirror.fcaglp.unlp.edu.ar/CRAN/")
}
library(DT)

vivienda5 <- read.csv("C:/Users/ASUS/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/vivienda5.csv")

datatable(vivienda5, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T) ) %>% formatStyle(names(vivienda5), backgroundColor = "lightgray") 

Datos secundarios

Como estrategia para la limpieza e imputación de datos, se consultó e incluyó en el proceso, como base de datos adicional, la capa geográfica de división político - administrativa de la ciudad de Cali, emitido por la página de la alcaldía www.cali.gov.co, basada en la información del censo del 2018. Esta capa cuenta con un total de 366 registros y 7 atributos.

datatable(Shape_BCali, filter="top", options = list(pageLength = 5, scrollX=T))%>%
  formatStyle(names(Shape_BCali), backgroundColor = "lightgray")

3 RESULTADOS Y DISCUCIÓN

3.1 Caso 1: Casas al norte de Cali

De acuerdo con la primera opción de compra de vivienda realizada a la empresa C&A, se realizó un filtado de todas las viviendas tipo casa en el sector norte de la ciudad de Cali, que se encontraba en la base de datos de la inmobiliaria. Con base en estos registros, se implementó un modelo de regresion lieneal multiple, con el fin de predecir el precio de una vivienda de este tipo con base a todas sus caracteristicas. El modelo resultante se define a continuación:

\(Precio-estimado [m]=633.985+121.251*Area-construida+18.098*No-Habitaciones+\) \(26.140*No-Banios+20.555*No-Parqueaderos−310.617*Estrato3−220.851*Estrato4−194.451*Estrato5\)

El modelo muestra que el precio base de las casas en esta zona de cali es de 633.958 millones de pesos y que por cada metro cuadrado construido el precio aumenta en 121.251 millones, asi como por cada habitación o número de baños o parqueaderos de acuerdo al coeficiente que acompaña la característica de la vivienda en la ecuación del modelo. Sin embargo, ya que se encuentran casas en el estarto 6 (estrato base con mayor cantidad de inmuebles) estrato 5, estrato 4 y estrato 3, los precios pueden bajar considerablemente en 194.451, 220.851 y 310.617 millones, respectivamente.

Ahora bien teniendo en cuenta la solicitud del cliente y el modelo implementado, se estimó el precio de la casa solicitada, obteniendo un valor de :

# Preparar los datos para la Vivienda 1
datos_vivienda1 <- data.frame(
    Area_construida = 200,
    No_Habitaciones = 4,
    No_Banios = 2,
    No_Parqueaderos = 1,
    Estrato3 = 0,  # No se especifica Estrato 3
    Estrato4 = 1,  # Estrato 4
    Estrato5 = 1,  # No se especifica Estrato 5
    Estrato6 = 0   # No se especifica Estrato 6
)

load("C:/Users/ASUS/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_2/wks.RData")
# Aplicar el modelo para hacer la predicción
prediccion_vivienda1 <- predict(modelo1, datos_vivienda1)

# Mostrar la predicción
cat("El precio estimado de la vivienda en millones es:", prediccion_vivienda1, "\n")
## El precio estimado de la vivienda en millones es: 24614.17

3.1.1 Opciones de compra

Este precio es bastante elevando para el crédito preaprobado de 350 millones. Sin embargo de acuerdo a las ofertas que tiene la inmobiliaria y que estan acorde al presupuesto, se presentan algunas opciones que pueden ser de interés para el cliente. Pero esto implica, contar con menor área construida, buscar en un estrato menor o modificar las comodidades de la vivienda.

library(DT)

# Cargar el dataframe desde un archivo .csv
vivienda_norte <- read.csv("vivienda_norte.csv")
# Filtrar los registros que sean de tipo 'casa' y 'zona norte'
vivienda_opciones1 <- vivienda_norte %>%
  filter(vivienda_norte$Precio_m<= 350 & vivienda_norte$No_Habitaciones == 4 & vivienda_norte$No_Banios ==2 & vivienda_norte$No_Parqueaderos ==1 )

datatable(vivienda_opciones1, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T)) %>% formatStyle(names(vivienda_opciones1), backgroundColor = "lightgray")

3.2 Caso 2: Apartamentos al sur de Cali

Respecto a la segunda opción de compra de vivienda realizada a la empresa C&A, igualmente se filtaron todas las viviendas tipo apartamento en el sector sur de la ciudad de Cali, que se encontraba en la base de datos de la inmobiliaria. Con base en estos registros, se implementó un modelo de regresion lineal multiple, con el fin de predecir el precio de una vivienda de este tipo con base a todas sus características. El modelo resultante se define a continuación:

\(Precio-estimado [m]=425.618+60.786*Area-construida-2.245*No-Habitaciones+\) \(49.568*No-Banios+52.253*No-Parqueaderos−135.939*Estrato3−144.339*Estrato4−125.916*Estrato5\)

El modelo muestra que el precio base de los apartamentos en esta zona de cali es de 425.618 millones de pesos y que por cada metro cuadrado construido el precio aumenta en 60.786 millones, asi como por cada número de baños o parqueaderos de acuerdo al coeficiente que acompaña la característica de la vivienda en la ecuación del modelo. Sin embrago, ya que se ecuentran apartamentos en el estarto 6 (estrato base con mayor cantidad de inmuebles) estrato 5, estrato 4 y estrato 3, los precios pueden bajar considerablemente en 194.451, 220.851 y 310.617 millones, respectivamente en consideración con el estrato base.

Ahora bien teniendo en cuenta la solictud del cliente y el modelo implementado, se estimó el precio del apartamento solicitado, obteniendo un valor de :

# Preparar los datos para la Vivienda 1
datos_vivienda2 <- data.frame(
    Area_construida = 300,
    No_Habitaciones = 5,
    No_Banios = 3,
    No_Parqueaderos = 3,
    Estrato3 = 0,  # No se especifica Estrato 3
    Estrato4 = 0,  # No se especifica Estrato 4
    Estrato5 = 1,  # No se especifica Estrato 5
    Estrato6 = 1   # No se especifica Estrato 6
)

load("C:/Users/ASUS/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_2/wks.RData")
# Aplicar el modelo para hacer la predicción
prediccion_vivienda2 <- predict(modelo2, datos_vivienda2)

# Mostrar la predicción
cat("El precio estimado de la vivienda en millones es:", prediccion_vivienda2, "\n")
## El precio estimado de la vivienda en millones es: 18829.6

3.2.1 Opciones de compra

Este precio es bastante elevando para el crédito preaprobado de 850 millones. Sin embrago de acuerdo a las ofertas que tiene la inmobiliaria y que estan acorde al presupuesto, se presentan algunas opciones que pueden ser de interés para el cliente. Pero esto implica, contar con menor área construida o modificar las comodidades de la vivienda.

library(DT)
vivienda_sur<- read.csv("vivienda_sur.csv")
# Filtrar los registros que sean de tipo 'apartamento' y 'zona sur'
vivienda_opciones2 <- vivienda_sur %>%
  filter(vivienda_sur$Precio_m <= 850 & vivienda_sur$No_Habitaciones == 5  )

datatable(vivienda_opciones2, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T) ) %>% formatStyle(names(vivienda_opciones2), backgroundColor = "lightgray") 

4 CONCLUSIONES

  • Los modelos de regresión lineal múltiple han estimado un precio significativamente mayor al crédito preaprobado del cliente para las dos opciones. Esto indica un desajuste considerable entre las expectativas del cliente y las realidades del mercado inmobiliario, lo que podría limitar las opciones de compra disponibles dentro del presupuesto.

  • El área construida se ha identificado como la variable más influyente en la determinación del precio de la vivienda, según el modelo. Esto sugiere que para ajustar el precio a las capacidades financieras del cliente, sería recomendable buscar propiedades con menor área construida o considerar la posibilidad de modificar otras características que influyen en el valor.

  • De acuerdo con las expectativas del cliente y los valores estimados por el modelo, sería más viable adquirir un inmueble en la zona sur de Cali debido a los precios más accesibles. Sin embargo, para que la compra sea viable, sería necesario realizar ajustes en cuanto al tipo de vivienda y algunas comodidades, como reducir el número de habitaciones, baños, o buscar un estrato más bajo. Estas modificaciones permitirían que la opción de compra se alinee mejor con el presupuesto del cliente.

5 RECOMENDACIONES

El modelo de regresión lineal múltiple es una herramienta poderosa para predecir el valor de una variable dependiente. Sin embargo, en este caso, aunque se obtuvo un coeficiente de determinación (R²) superior al 70%, este valor podría estar sesgado debido a la presencia de valores atípicos o al incumplimiento de los supuestos del modelo. Por lo tanto, es fundamental considerar la posibilidad de realizar transformaciones en los datos, seleccionar otras variables más adecuadas o, en su defecto, optar por un modelo de regresión que no sea lineal y se ajuste mejor al conjunto de datos. Estas acciones son esenciales para mejorar la precisión de las predicciones.

Además, es importante contar con una base de datos limpia y con una cantidad suficiente de registros para entrenar y evaluar el modelo de manera adecuada. Esto no solo mejorará el desempeño del modelo, sino que también aumentará la precisión y fiabilidad de las predicciones realizadas.

6 ANEXOS

6.1 Metodología de solución

Una vez entendido el problema, se plantea la siguiente metodología para geneerar una respuesta adecuada basada en herramientas estadisticas. Para este caso se seguirán los pasos que se muestran a continuación.

  1. Primero se realizará un análisis exploratorio de la base de datos completa que permita identificar el estado de los datos y determinar los procedimientos de limpieza e imputación que se requieran de acuerdo con el objetivo.

  2. Se realizará un filtro a la base de datos limpia, incluyendo solo las ofertas de : base1: casas, de la zona norte de la ciudad. Se presentarán los primeros 3 registros de la 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?).

  3. Se realizará 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, usando gráficos interactivos con el paquete plotly para interpretar los resultados.

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

  5. Realizar 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).

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

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

  8. Finalmente se realizará el mismo procedimiento anterior para la segunda solicitud que tiene un crédito pre-aprobado por valor de $850 millones.

6.2 Exploración de datos

La exploración de los datos es una de las etapa más importante en el proceso de análisis de datos, cuyo objetivo es comprender las características principales de un conjunto de datos antes de aplicar modelos estadísticos o técnicas de machine learning. Este análisis incluye la identificación de patrones, detección de anomalías, verificación de suposiciones y la comprensión de la estructura de los datos. La base de datos de bienes raíces empleada para el análisis, se muestra en la siguiente tabla.

library(DT)
library(paqueteMODELOS)

data(vivienda)  # Carga de los datos 
vivienda1=vivienda # Crea una copia de la base de datos

#Renombramiento de variables
colnames(vivienda1)<- c("id","Zona","Piso","Estrato","Precio_m","Area_construida","No_Parqueaderos","No_banios","No_habitaciones","Tipo_inmueble","Barrio","Longitud","Latitud")

vivienda1$Estrato <- as.character(vivienda1$Estrato)

datatable(vivienda1, filter="top", options = list(pageLength = 5, scrollX=T))%>%
  formatStyle(names(vivienda1), backgroundColor = "lightgray")

6.2.1 Análisis univariado

Dentro del análisis exploratorio se encuentra el análisis univariado, el cual se centra en la evaluación de una sola variable a la vez. Este tipo de análisis utiliza estadísticas resumen como la media, mediana, moda, desviación estándar y percentiles para proporcionar una visión general de la distribución de la variable. También se utilizan visualizaciones como histogramas, gráficos de barras y diagramas de caja para representar gráficamente la distribución y detectar posibles outliers.

Identificación de tipos de datos

Identificar el tipo de variable al comenzar un análisis estadístico es importante para determinar el enfoque metodológico adecuado y seleccionar las herramientas estadísticas apropiadas. Las variables se clasifican en cualitativas (categóricas) y cuantitativas (numéricas), y esta clasificación es esencial para aplicar las técnicas estadísticas correctas.

Las variables cualitativas requieren métodos específicos de análisis, como pruebas de frecuencia y proporción, mientras que las variables cuantitativas se abordan mediante técnicas estadísticas como la media, la mediana y la desviación estándar.

En el conjunto de datos de interés, se identifican un total de 8 variables cuantitativas de tipo numérico y 5 variables cualitativas (categóricas) de tipo carácter. De estas últimas, 4 son ordinales y 1 es nominal.

library(DT)

# Filtrar variables numéricas y de tipo carácter
numericas <- sapply(vivienda1, is.numeric)
caracteres <- sapply(vivienda1, is.character)

# Crear data frames para cada tipo
tabla_numericas <- data.frame(
  Variable = names(numericas[numericas]),
  Tipo = sapply(vivienda1[numericas], class)
)

tabla_caracteres <- data.frame(
  Variable = names(caracteres[caracteres]),
  Tipo = sapply(vivienda1[caracteres], class)
)

# Mostrar las tablas 
datatable(tabla_numericas, filter = "top", options = list(pageLength = 8, scrollX = TRUE)) %>%
  formatStyle(columns = 'Variable', backgroundColor = 'lightgray')
datatable(tabla_caracteres, filter = "top", options = list(pageLength = 5, scrollX = TRUE)) %>%
  formatStyle(columns = 'Variable', backgroundColor = 'lightgray')

Distribución de los datos

  • Variables Cuantitativas

A partir del análisis de los siguientes gráficos estadísticos, se observa que las variables continuas, como el área construida y el precio, presentan una distribución sesgada hacia la derecha. Esto indica que la mayoría de los datos se concentran en valores bajos tanto para el área construida como para el precio, una asimetría confirmada por los gráficos cuantil-cuantil, los cuales muestran que los datos no siguen una distribución normal.

Por otro lado, las variables discretas, como el número de habitaciones, muestran una mayor simetría en comparación con las variables número de baños y parqueaderos, que presentan un sesgo hacia la derecha. Este comportamiento se contrasta con los diagramas de cajas y bigotes, los cuales evidencian la presencia de datos atípicos en la mayoría de las variables, especialmente en las variables precio y área construida. Asimismo, se observa que la mayoría de los inmuebles, cuentan con 1 parqueadero, 2 baños y 3 habitaciones.

library(dplyr)
library(ggplot2)
library(gridExtra)
library(grid) 

# Filtrar solo las variables numéricas
vivienda_numericas <- vivienda1 %>% select(where(is.numeric))

# Asegurar el tipo numérico
vivienda_numericas <- vivienda_numericas %>%
  mutate(across(everything(), as.numeric))

# Eliminar la columna id
vivienda_numericas <- vivienda_numericas%>% 
  select(-id)

# Crear una lista para almacenar los histogramas
histogramas <- list()

# Crear un histograma para cada variable numérica
binwidths <- c(0.5, 60, 0.5, 0.5, 0.5, 0.03, 0.03)

for (i in seq_along(vivienda_numericas)) {
  p <- ggplot(vivienda_numericas, aes_string(x = names(vivienda_numericas)[i])) +
    geom_histogram(binwidth = binwidths[i], fill = 'darkgray', color = 'black', alpha = 0.7) +
    labs(x = names(vivienda_numericas)[i],
         y = 'Frecuencia') +
    theme_minimal()+
    theme(axis.title.x = element_text(size = 9),   
    axis.title.y = element_text(size = 9))
   
  #Almacenar histogramas
  histogramas[[i]] <- p
}

# Crear un texto general para el título
titulo_general <- textGrob("Histogramas de Distribucion de Variables", 
                            gp = gpar(fontsize = 14, fontface = "bold"))

# Crear el subplot con grid.arrange y agregar el título general
grid.arrange(
  do.call("arrangeGrob", c(histogramas, ncol = 2)),
  top = titulo_general
)

library(ggplot2)
library(gridExtra)
library(grid) 

# Seleccionar las variables continuas
variables_continuas <- vivienda_numericas %>% select(Precio_m, Area_construida)

# Crear una lista para almacenar los gráficos Q-Q
qq_plots <- list()

# Lista con los nombres de las variables para usar en el loop
var_names <- names(variables_continuas)

# Generar un gráfico Q-Q para cada variable continua
for (i in seq_along(var_names)) {
  var_name <- var_names[i]
  qq_plots[[i]] <- ggplot(variables_continuas, aes(sample = .data[[var_name]])) +
    stat_qq() +
    stat_qq_line(color = "red") +
    labs(x = "Cuantiles Teoricos",
         y = "Cuantiles Muestrales") +
    theme_minimal() +
    theme(
      axis.title.x = element_text(size = 9),
      axis.title.y = element_text(size = 9),
      )
}

# Combinar los gráficos Q-Q en un subplot de 1x2
grid.arrange(
  grobs = qq_plots,
  ncol = 2,
  top = textGrob("Diagrama Q-Q de las Variables Continuas", gp = gpar(fontsize = 14, fontface = "bold"))
)

# Crear una lista para almacenar los gráficos de boxplot
boxplots <- list()

# Lista con los nombres de las variables numéricas para usar en el loop
var_names <- names(vivienda_numericas)

# Generar un gráfico de boxplot para cada variable numérica
for (i in seq_along(var_names)) {
  var_name <- var_names[i]
  boxplots[[i]] <- ggplot(vivienda_numericas, aes(x = .data[[var_name]], y ="" )) +
    geom_boxplot(fill = "darkgray", color = "black") +
    labs(x = var_name,
         y = "") +
    theme_minimal() +
    theme(
      axis.title.x = element_text(size = 9),
      axis.title.y = element_text(size = 9),
      
    )
}

# Combinar los gráficos de boxplot en un subplot de 4x2
grid.arrange(
  grobs = boxplots,
  ncol = 2,
  nrow = 4,
  top = textGrob("Boxplots de las Variables Numericas", gp = gpar(fontsize = 14, fontface = "bold"))
)

  • Variables Cualitativas

El gráfico de barras permite identificar la categoría más frecuente en cada una de las variables analizadas. En este caso, se observa que las tres variables presentan datos faltantes, siendo la variable “Piso” la que tiene mayor afectación. Además, la mayoría de los registros están localizados en el sur de Cali, son de tipo apartamento y, dentro de las unidades residenciales, se encuentran ubicados entre el 1 y el 5 piso, siendo el segundo piso el más frecuente. Esta información es relevante para realizar la imputación de los datos faltantes, especialmente cuando se combina con las relaciones que puedan existir con otras variables.

# Filtrar solo las variables cualitativas
vivienda_char <- vivienda1 %>% select(where(is.character))

# Convertir las variables cualitativas a factores
vivienda_char <- vivienda_char %>% mutate(across(everything(), as.factor))

# Eliminar la columna Barrio
vivienda_char <- vivienda_char%>% 
  select(-Barrio)

# Crear una lista para almacenar los gráficos
plots <- list()

# Crear diagramas de barras para cada variable cualitativa

binwidths <- c(0.5, 1, 0.2)
for (col in names(vivienda_char)) {
  p <- ggplot(vivienda_char, aes_string(x = col)) +
    geom_bar(binwidth = binwidths[i],fill = "darkgray", color = "black") +
    labs(title = col, x = '', y = 'Frecuencia') +
    theme(axis.title.x = element_text(size = 9),  # Tamaño del título del eje x
      axis.title.y = element_text(size = 9),  # Tamaño del título del eje y
      axis.text.x = element_text(size = 9, angle = 45, hjust = 1),  # Tamaño del texto del eje x
      axis.text.y = element_text(size = 9),  # Tamaño del texto del eje y
      plot.title = element_text(size = 11, face = "bold", hjust = 0.5) )
  
  plots[[col]] <- p
}

# Ajustar la cantidad de gráficos a 2x2
grid.arrange(
  grobs = plots, 
  nrow = 2, 
  ncol = 2, 
  top = textGrob("Diagrama de barras variables categoricas", 
                 gp = gpar(fontsize = 14, fontface = "bold"))
)

6.2.2 Análisis bivariado

  • Variables Cuantitativas

El análisis de correlación permite inferir que las relaciones más fuertes se observan entre el precio de los inmuebles y las variables, área construida, número de parqueaderos y número de baños. Esta es una relación directa muy común en el mercado inmobiliario.

library(GGally)

# Eliminar la columna lon y lat
corr_matrix <- vivienda_numericas%>% 
  select(-Longitud,-Latitud)


# Crear el pair plot
ggpairs(corr_matrix%>% select(where(is.numeric)))

  • Variables Cualitativas

El gráfico de barras apiladas muestra la relación entre las variables categóricas, permitiendo observar que en todas las zonas de Cali, la mayoría de los inmuebles de tipo casa tienen hasta 3 pisos, excepto en la zona oriente, donde pueden llegar a tener hasta 4 pisos. Por otro lado, los apartamentos constituyen la mayoría de los inmuebles construidos a partir del quinto piso, un comportamiento que se mantiene en todas las zonas de Cali, salvo en la zona oriente, donde en el piso 10 se cataloga un inmueble como casa. Esto podría ser un error de clasificación o un inmueble con un área construida bastante grande, semejante a una casa. Adicionalmente, se aprecia la existencia de datos faltantes y que la mayoría de inmuebles en estrato 3 son de tipo casa, mientras que las viviendas de estarto 5 en su gran mayoría son de tipo apartamento.

# Gráfico de barras apiladas con facet grid
ggplot(vivienda_char, aes(x = vivienda_char$Zona, fill = vivienda_char$Tipo_inmueble)) +
  geom_bar(position = "fill") +
  facet_wrap(~ vivienda_char$Piso) +
  labs(x = 'Zona', y = 'Proporcion', fill = 'Tipo_inmueble', title = 'Grafico de relacion variables categoricas')+
  theme(
    plot.title = element_text(size = 14,face = "bold", hjust = 0.5),     
    axis.text.x = element_text(size = 8, angle = 50, hjust = 1)  
  )

# Gráfico de barras apiladas con facet grid
ggplot(vivienda_char, aes(x = vivienda_char$Zona, fill = vivienda_char$Tipo_inmueble)) +
  geom_bar(position = "fill") +
  facet_wrap(~ vivienda_char$Estrato) +
  labs(x = 'Zona', y = 'Proporcion', fill = 'Tipo_inmueble', title = 'Grafico de relacion variables categoricas')+
  theme(
    plot.title = element_text(size = 14,face = "bold", hjust = 0.5),     
    axis.text.x = element_text(size = 8, angle = 50, hjust = 1)  
  )

6.2.3 Analisis de datos faltantes

A partir de la información suministrada por el siguiente gráfico, se puede establecer que las variables que presentan datos faltantes son: Piso, Número de Parqueaderos, Longitud, Latitud y Barrio, siendo las dos primeras las que contienen la mayor cantidad de datos faltantes con 1909 y 876, correspondientes al 22.9% y 10.5% del total de los datos, respectivamente. Además, se puede identificar que, de este grupo de datos faltantes, 726 registros presentan ausencia tanto en la variable Piso como en la variable Número de Parqueaderos, y 3 registros tienen datos faltantes en las cinco variables al mismo tiempo.

library(naniar)

# Visualizar los datos faltantes 
gg_miss_upset(vivienda1)

6.3 Limpieza y transformación de los datos

Limpieza de registros sin datos en todas las variables

Se identificaron los registros con ausencia de datos en la mayoría de las variables. Dado que estos registros no aportan información relevante para el análisis es necesario eliminarlos, según sea su proporción dentro del conjunto de datos. En total, se encontraron solo 3 registros con esta característica, lo que equivale a una pérdida mínima.

# Calcular el número de NA en cada fila
na_cont <- apply(vivienda1, 1, function(row) sum(is.na(row)))

# Calcular el umbral (más de la mitad de las columnas)
umbral <- ncol(vivienda1) / 2

# Identificar las filas con NA en más de la mitad de las columnas
fil_na_mayoria <- which(na_cont > umbral)

# Mostrar las posiciones de las filas
mensaje <- paste("Las filas con la mayoria de datos con NA son:", paste(fil_na_mayoria, collapse = ", ") )
print(mensaje)
## [1] "Las filas con la mayoria de datos con NA son: 8320, 8321, 8322"
# Eliminar las filas con NA en más de la mitad de las columnas
vivienda2 <- vivienda1[-fil_na_mayoria, ]

Limpieza variable Barrio y Zona

Para corregir las variable Barrios, se utilizó una base de datos secundaria que contenía información geográfica oficial de los barrios como referencia. Esta capa geográfica sirvió como patrón principal para la corrección de los nombres de los barrios en la base de datos original. En primer lugar, se estandarizaron los nombres de los barrios en ambas bases de datos, eliminando acentos, tildes y convirtiendo todo a mayúsculas. Luego, se compararon los nombres de los barrios en ambas bases de datos y se identificaron aquellos que coincidían. Los barrios que no coincidían se corrigieron manualmente, utilizando la información de la capa geográfica como guía. Este proceso también se validó utilizando google maps para identificar cualquier discrepancia entre los nombres de los barrios en las dos fuentes de datos. Es importante destacar que la base de datos de la alcaldía no estaba actualizada, por lo que los barrios que no se encontraron en el patrón se asignaron como NA y fueron eliminados junto con los que no hacen parte del municipio.Asimismo, para reclasificar las zonas, se consultó un mapa de Cali (https://www.cali.gov.co/planeacion/publicaciones/169423/zonas_geograficas_idesc/) donde se muestra la distribución de las comunas en las diefrentes zonas de Cali, asi las cosas, teniendo en cuenta la información de las comunas incluidas dentro de la capa geográfica de la Alcaldia, se logró reclasificar los registros de acuerdo a la zona adecuada.

## Reading layer `Barrios' from data source 
##   `C:\Users\ASUS\OneDrive - PUJ Cali\2024_2\Metodos_estadisticos_para_toma_decisiones\Unidad_2\Cali_WGS84.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 335 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -76.59078 ymin: 3.331802 xmax: -76.46124 ymax: 3.501895
## Geodetic CRS:  WGS 84 (CRS84)

Imputación de datos faltantes

Teniendo en cuenta que el porcentaje de datos faltantes es bastante representativo, eliminarlos podría resultar en la pérdida de información valiosa. Por lo tanto, se optó por realizar una agrupación por las variables con mayor relación tanto con la variable piso como zona. En este caso, se filtraron los datos por zona, estrato y tipo de inmueble y se calculó el valor más frecuente para el caso de la variable piso (variable categórica) y la mediana en el caso de la variable numero de parqueaderos (variable numérica discreta) para realizar la imputación.

vivienda4<-vivienda3
vivienda4$Piso<-as.numeric(vivienda4$Piso)
vivienda4$No_Parqueaderos<-as.numeric(vivienda4$No_Parqueaderos)

vivienda4$Piso_imputado<-vivienda4$Piso # copia de piso
vivienda4$Parquea_imputado<-vivienda4$No_Parqueaderos # copia de piso


vivienda4 <- vivienda4 %>%
  mutate(
    Posicion_Piso_NA = ifelse(is.na(Piso), row_number(), NA),
    Posicion_Parqueaderos_NA = ifelse(is.na(No_Parqueaderos), row_number(), NA)
  )

# Función para calcular la moda
calcular_moda <- function(x) {
  tbl <- table(x)
  moda <- as.numeric(names(tbl)[tbl == max(tbl)])
  return(moda)
}

#Lista de zonas, estratos y tipos de inmueble
zonas <- unique(vivienda4$Zona_limpia)
estratos <- unique(vivienda4$Estrato)
tipos_inmueble <- unique(vivienda4$Tipo_inmueble)

# Crear listas para almacenar los subconjuntos y los resultados
subconjuntos <- list()
resultados <- list()

# Iterar a través de las combinaciones de zona, estrato y tipo de inmueble
for (zona in zonas) {
  for (estrato in estratos) {
    for (tipo in tipos_inmueble) {
      
      
      # Crear el subconjunto
      subconjunto <- subset(vivienda4, Zona_limpia == zona & Estrato == estrato & Tipo_inmueble == tipo)
      
      # Guardar el subconjunto
      subconjuntos[[paste(zona, estrato, tipo, sep = "_")]] <- subconjunto
      
      # Calcular la moda para Piso
      moda_piso <- calcular_moda(subconjunto$Piso)
      
      # Calcular la mediana de Parqueaderos y redondear a un solo dígito
      mediana_parqueaderos <- median(subconjunto$No_Parqueaderos, na.rm = TRUE)
      mediana_parqueaderos_redondeada <- round(mediana_parqueaderos, 1)
      
    
      valores_no_na_piso <- na.omit(subconjunto$Posicion_Piso_NA) #extrae las posiciones de este subconjunto donde piso tiene NA del conjunto original 
      valores_no_na_parq<- na.omit(subconjunto$Posicion_Parqueaderos_NA) #extrae las posiciones de este subconjunto donde piso tiene NA del conjunto original 
      
      # Asignar moda a los NA en Piso
      vivienda4$Piso_imputado[valores_no_na_piso] <- moda_piso
      vivienda4$Piso<-as.factor(vivienda4$Piso_imputado)
      
      # Asignar mediana los NA en Parqueaderos
      vivienda4$Parquea_imputado[valores_no_na_parq] <-  mediana_parqueaderos_redondeada
      
      # Guardar el resultado
      resultados[[paste(zona, estrato, tipo, sep = "_")]] <- list(
        Moda_Piso = moda_piso,
        Mediana_Parqueaderos_Redondeada = mediana_parqueaderos_redondeada
      )
    }
  }
}

Limpieza de valores cero en la variable baños y habitaciones

Dado que durante el análisis exploratorio se identificaron registros de inmuebles con valores de cero en las variables No_habitaciones y No_baños, se decidió imputar estos datos. Estos valores no tenían una relación lógica con las otras variables registradas, por lo que se empleó un método similar al utilizado para el manejo de datos faltantes, utilizando la mediana para reemplazar los valores en cero. Finalmente, en la tabla y gráfica siguientes se presenta la base de datos definitiva, confirmando que no existen datos faltantes que puedan afectar el análisis.

library(naniar)

vivienda4$No_banios<-as.numeric(vivienda4$No_banios)
vivienda4$No_habitaciones<-as.numeric(vivienda4$No_habitaciones)

vivienda4$Banios_limpio<-vivienda4$No_banios # copia 
vivienda4$Habita_limpio<-vivienda4$No_habitaciones # copia 

vivienda4 <- vivienda4 %>%
  mutate(
    Posicion_Banios_Cero = ifelse(No_banios == 0, row_number(), NA),
    Posicion_Habitaciones_Cero = ifelse(No_habitaciones == 0, row_number(), NA)
  )

#Lista de  estratos y tipos de inmueble
estratos <- unique(vivienda4$Estrato)
tipos_inmueble <- unique(vivienda4$Tipo_inmueble)

# Crear listas para almacenar los subconjuntos y los resultados
subconjuntos <- list()
resultados <- list()

# Iterar a través de las combinaciones de zona, estrato y tipo de inmueble
for (estrato in estratos) {
  for (tipo in tipos_inmueble) {
      
      
      # Crear el subconjunto
      subconjunto <- subset(vivienda4, Estrato == estrato & Tipo_inmueble == tipo)
      
      # Guardar el subconjunto
      subconjuntos[[paste(estrato, tipo, sep = "_")]] <- subconjunto
      
      # Calcular la mediana baños
      mediana_banios <- median(subconjunto$No_banios, na.rm = TRUE)
      mediana_banios <- round( mediana_banios, 1)
      
      # Calcular la mediana habitaciones
      mediana_habita <- median(subconjunto$No_habitaciones, na.rm = TRUE)
      mediana_habita <- round( mediana_habita, 1)
      
    
      valores_no_na_banio <- na.omit(subconjunto$Posicion_Banios_Cero) 
      valores_no_na_habita <- na.omit(subconjunto$Posicion_Habitaciones_Cero) 
      
      # Asignar mediana a los NA en baño
      vivienda4$Banios_limpio[valores_no_na_banio] <- mediana_banios
      
      # Asignar mediana los NA en habitación
      vivienda4$Habita_limpio[ valores_no_na_habita] <- mediana_habita
      
      # Guardar el resultado
      resultados[[paste(estrato, tipo, sep = "_")]] <- list(
        Mediana_Banio = mediana_banios,
        Mediana_Habitacion = mediana_habita
      )
  }
}

vivienda5<-vivienda4 %>% select(-Zona, -Piso, -No_banios,-No_habitaciones, -No_Parqueaderos, -Barrio, -Posicion_Piso_NA, -Posicion_Parqueaderos_NA, -Posicion_Banios_Cero, -Posicion_Habitaciones_Cero)
colnames(vivienda5)<- c("Id","Estrato","Precio_m","Area_construida","Tipo_inmueble","Longitud","Latitud","Barrio","Comuna","Zona","Piso","No_Parqueaderos", "No_Banios", "No_Habitaciones")

#write.csv(vivienda5, "C:/Users/lvasquez/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/vivienda5.csv", row.names = FALSE)

datatable(vivienda5, filter="top", options = list(pageLength = 5, scrollX=T))%>%
formatStyle(names(vivienda5), backgroundColor = "lightgray")
# Visualizar los datos faltantes
gg_miss_var(vivienda5)

Verificación distribución de datos

Como se puede observar, despues del proceso de limpieza e imputación de los datos, la distribución de las variables tanto cuantitativas como cualitativas no se vio alterada de forma drástica, ya que presentan los mismos sesgos y frecuenicas dominantes para el caso de las cualitativas.

library(grid)
library(gridExtra)

# Filtrar solo las variables numéricas
vivienda_numericas <- vivienda5 %>% select(where(is.numeric))

# Asegurar el tipo numérico
vivienda_numericas <- vivienda_numericas %>%
  mutate(across(everything(), as.numeric))

# Eliminar la columna id
vivienda_numericas <- vivienda_numericas%>% 
  select(-Id,-Piso)

# Crear una lista para almacenar los histogramas
histogramas <- list()

# Crear un histograma para cada variable numérica
binwidths <- c(0.5, 60, 0.03, 0.03, 0.5, 0.5, 0.5)

for (i in seq_along(vivienda_numericas)) {
  p <- ggplot(vivienda_numericas, aes_string(x = names(vivienda_numericas)[i])) +
    geom_histogram(binwidth = binwidths[i], fill = 'darkgray', color = 'black', alpha = 0.7) +
    labs(x = names(vivienda_numericas)[i],
         y = 'Frecuencia') +
    theme_minimal()+
    theme(axis.title.x = element_text(size = 9),   
    axis.title.y = element_text(size = 9))
   
  #Almacenar histogramas
  histogramas[[i]] <- p
}

# Crear un texto general para el título
titulo_general <- textGrob("Histogramas de Distribucion de Variables", 
                            gp = gpar(fontsize = 14, fontface = "bold"))

# Crear el subplot con grid.arrange y agregar el título general
grid.arrange(
  do.call("arrangeGrob", c(histogramas, ncol = 2)),
  top = titulo_general
)

vivienda5$Piso <- as.character(vivienda5$Piso)
vivienda5$Barrio <- as.character(vivienda5$Barrio)
# Filtrar solo las variables cualitativas
vivienda_char <- vivienda5 %>% select(where(is.character))

# Convertir las variables cualitativas a factores
vivienda_char <- vivienda_char %>% mutate(across(everything(), as.factor))

# Eliminar la columna Barrio
vivienda_char <- vivienda_char%>% 
  select(-Barrio,-Comuna)

# Crear una lista para almacenar los gráficos
plots <- list()

# Crear diagramas de barras para cada variable cualitativa

binwidths <- c(0.5, 1, 0.2)
for (col in names(vivienda_char)) {
  p <- ggplot(vivienda_char, aes_string(x = col)) +
    geom_bar(binwidth = binwidths[i],fill = "darkgray", color = "black") +
    labs(title = col, x = '', y = 'Frecuencia') +
    theme(axis.title.x = element_text(size = 9),  # Tamaño del título del eje x
      axis.title.y = element_text(size = 9),  # Tamaño del título del eje y
      axis.text.x = element_text(size = 9, angle = 45, hjust = 1),  # Tamaño del texto del eje x
      axis.text.y = element_text(size = 9),  # Tamaño del texto del eje y
      plot.title = element_text(size = 11, face = "bold", hjust = 0.5) )
  
  plots[[col]] <- p
}

# Ajustar la cantidad de gráficos a 2x2
grid.arrange(
  grobs = plots, 
  nrow = 2, 
  ncol = 2, 
  top = textGrob("Diagrama de barras variables categoricas", 
                 gp = gpar(fontsize = 14, fontface = "bold"))
)

6.4 Análisis caso 1

Filtrado del conjunto de datos

Teniendo en cuenta la solicitud realizada a la inmobiliaria, se filtraron unicamente las viviendas de tipo casa que se encuentran en la zona norte de la ciudad, encontrando un total de 660 posibles oportunidades para cumplir con el requerimiento, los cuales se muestran en la siguiente tabla:

# Filtrar los registros que sean de tipo 'casa' y 'zona norte'
vivienda_filtrada1 <- vivienda5 %>%
  filter(Tipo_inmueble == "Casa" & Zona == "Zona Norte")

# Tabla de dataframe filtrado
datatable(vivienda_filtrada1, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T) ) %>% formatStyle(names(vivienda_filtrada1), backgroundColor = "lightgray") 

Se graficaron los registros seleccionados en un mapa, sin embrago se puede observar que existen puntos por fuera de la zona norte de cali, lo cual nos hace pensar que la asignación de las coordenadas puede estar errada, asumiendo que las zonas estan bien clasificadas. Esto se podría tratar haciendo un limpieza exhaustiva de las coordendas, extrayendo el centroide de cada poligono de la base de datos oficial y asignarle esas coordenadas a los resgistros que posee la inmobiliaria. Sin embrago en este caso no se abordará, lo que se hizo fue seleccionar la latitud más al sur y la longitud más al oeste que estuviera por dentro de la zona norte y con ello filtrar solo los registros que queban por encima de esta coordenada, obteniendo los registros que se muestran en el segundo mapa.

library(sf)
library(leaflet)

#ruta_archivo <- "C:/Users/lvasquez/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/Barrios_Cali.gpkg"
ruta_archivo <- "C:/Users/ASUS/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/Cali_WGS84.gpkg"

file.exists(ruta_archivo)
## [1] TRUE
Cali_WGS84 <- st_read(ruta_archivo) # fuente de datos secundaria (www.cali.gov.co)
## Multiple layers are present in data source C:\Users\ASUS\OneDrive - PUJ Cali\2024_2\Metodos_estadisticos_para_toma_decisiones\Unidad_1\Cali_WGS84.gpkg, reading layer `Barrios'.
## Use `st_layers' to list all layer names and their type in a data source.
## Set the `layer' argument in `st_read' to read a particular layer.
## Reading layer `Barrios' from data source 
##   `C:\Users\ASUS\OneDrive - PUJ Cali\2024_2\Metodos_estadisticos_para_toma_decisiones\Unidad_1\Cali_WGS84.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 335 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -76.59078 ymin: 3.331802 xmax: -76.46124 ymax: 3.501895
## Geodetic CRS:  WGS 84 (CRS84)
Cali_WGS84 <- st_transform(Cali_WGS84, crs = '+proj=longlat +datum=WGS84')


leaflet() %>%
  addTiles() %>%
  setView(lng = -76.53, lat = 3.42, zoom = 11.5) %>%
  addPolygons(data = Cali_WGS84, color = "gray",weight = 2,fillColor = "gray", stroke = 0.1, opacity = 0.9, popup = paste0("Barrio: ",Cali_WGS84$barrio,"<br>","Comuna: ",Cali_WGS84$comuna) ) %>%
  addCircleMarkers(data=vivienda_filtrada1,lng=vivienda_filtrada1$Longitud, lat = vivienda_filtrada1$Latitud, fillColor = "red",radius = 2,color = "red",popup = paste0("Barrio: ",vivienda_filtrada1$Barrio,"<br>","Comuna: ",vivienda_filtrada1$Comuna))
# Filtrar los registros que sean de tipo 'casa' y 'zona norte'
vivienda_norte <- vivienda_filtrada1 %>%
  filter(Latitud >= 3.46355 & Longitud >=-76.54139)

# Guardar el dataframe en un archivo .csv
write.csv(vivienda_norte, file = "vivienda_norte.csv", row.names = FALSE)

leaflet() %>%
  addTiles() %>%
  setView(lng = -76.53, lat = 3.42, zoom = 11.5) %>%
  addPolygons(data = Cali_WGS84, color = "gray",weight = 2,fillColor = "gray", stroke = 0.1, opacity = 0.9, popup = paste0("Barrio: ",Cali_WGS84$barrio,"<br>","Comuna: ",Cali_WGS84$comuna) ) %>%
  addCircleMarkers(data=vivienda_norte,lng=vivienda_norte $Longitud, lat = vivienda_norte $Latitud, fillColor = "red",radius = 2,color = "red",popup = paste0("Barrio: ",vivienda_norte $Barrio,"<br>","Comuna: ",vivienda_norte $Comuna))

6.4.1 Análisis de correlación

De acuerdo a lo observado en la matriz de correlación, se ve que el precion de una casa en la zona norte de cali, esta principlmente modulado por el área construida y por las comodidades que el inmueble tenga en cuanto a cantidad de baños, parqueaderos y el número de habitaciones. Adicionalmente se observa que el precio y el area construida no tienen una relación lineal pues su gráfico de dispersión presenta una forma de cono, por otro lado, la relación con las demas variables, tampoco tiene una forma definida, son graficos bastante dispersos, lo que quiere decir que no necesariamente cuando alguna de estas variables aumenta, lo hará en la misma proporción el precio de una casa en esta zona de Cali.

library(GGally)

# Filtrar solo las variables numéricas
vivienda_numericas <- vivienda_norte %>% select(where(is.numeric))

# Asegurar el tipo numérico
vivienda_numericas <- vivienda_numericas %>%
  mutate(across(everything(), as.numeric))

# Eliminar la columna lon y lat
corr_matrix <- vivienda_numericas%>% 
  select(-Longitud,-Latitud,-Id)


# Crear el pair plot
ggpairs(corr_matrix%>% select(where(is.numeric)))

Por otro lado, los precios más altos en las casas de la zona norte de Cali están asociados al estrato 5 y 6, con casos puntuales quizas asociados a mayor área construidas o mayores comodidades. Sin embrago, hay gran cantidad de casas de estrato 6 que presentan precios que oscilan entre los 520 y 900 millones de pesos aproximadamente. En general en esta zona se encuentran oferta en estrato 3, 4, 5 y 6 que pueden ser atractivas para los compradores.

# Graficar usando la muestra
ggplot(vivienda_norte, aes(x = Estrato, y = Precio_m, fill = Zona)) +
  geom_boxplot() +
  labs(title = "Box Plot de Precios por Estrato y Zona",
       x = "Estrato",
       y = "Precio") +
  theme(plot.title = element_text(size = 14, face = "bold", hjust = 1),     
        axis.text.x = element_text(size = 6, angle = 45, hjust = 1)) +  
  theme_minimal()

6.4.2 Estimación del modelo y validación

En el análisis del modelo de regresión lineal ajustado para predecir el precio de las viviendas (Precio_m), los coeficientes estimados proporcionan una visión de los factores que influyen en el valor de la propiedad. El coeficiente del intercepto, que se estima en 633.985 millones, representa el valor base de la vivienda cuando todas las demás variables son cero. La variable Area_construida muestra un coeficiente significativo de 121.251, indicando una fuerte relación positiva con el precio; es decir, un incremento en el área construida está asociado con un aumento sustancial en el precio. En contraste, los coeficientes para No_Habitaciones (18.098), No_Banios (26.140) y No_Parqueaderos (20.555) no son estadísticamente significativos, sugiriendo que, aunque estas variables tienen algún impacto, su influencia en el precio no es tan robusta o consistente. Los coeficientes negativos y significativos para las variables dummy de estrato (Estrato3, Estrato4, Estrato5) revelan diferencias significativas en el precio en comparación con el estrato base, con disminuciones promedio de 310.617, 220.851 y 194.451 millones, respectivamente. Por su parte, el coeficiente de determinación R2 de 0.7256 sugiere que el modelo explica aproximadamente el 72.56% de la variabilidad en el precio de las viviendas, reflejando un ajuste moderado del modelo a los datos observados.

# Cargar las librerías necesarias
library(DT)
library(dplyr)       # Para manipulación de datos
library(car)         # Para el cálculo de VIF
library(ggplot2)     # Para visualización de gráficos

# Establecer una semilla para reproducibilidad
set.seed(123)

# Definir el número de iteraciones para la validación cruzada simple
num_iterations <- 100

# Vector para almacenar el modelo ajustado en la última iteración
modelo_final <- NULL

# Vector para almacenar el MSE de cada iteración
cv_MSE <- rep(NA, num_iterations)

train_size <- floor(0.7 * nrow(vivienda_norte))  # 70% del número total de filas

# Realizar validación cruzada simple en un bucle
for (i in 1:num_iterations) {
    
    # Dividir los datos en entrenamiento (70%) y prueba (30%)
    train_index <- sample(seq_len(nrow(vivienda_norte)), size = train_size)
    
    train_data <- vivienda_norte[train_index, ]
    test_data <- vivienda_norte[-train_index, ]
    
    # Convertir la variable categórica a variable dummy para el conjunto de entrenamiento
    dummy_estrato_train <- model.matrix(~ Estrato - 1, data = train_data)
    train_data2 <- cbind(train_data, dummy_estrato_train)
    
    # Eliminar las columnas no usadas en el modelo en el conjunto de entrenamiento
    train_data2 <- train_data2 %>%
                   select(-Id, -Piso, -Estrato, -Tipo_inmueble, -Longitud, -Latitud,
                          -Barrio, -Comuna, -Zona)
    
    # Normalizar las variables numéricas del conjunto de entrenamiento
    numeric_vars_train <- sapply(train_data2, is.numeric)
    numeric_vars_train['Precio_m'] <- FALSE  # No normalizar la variable objetivo
    numeric_vars_train['Estrato3'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_train['Estrato4'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_train['Estrato5'] <- FALSE  # No normalizar la variable dummy
    #numeric_vars_train['Estrato6'] <- FALSE  # No normalizar la variable dummy
    train_data2[numeric_vars_train] <- scale(train_data2[numeric_vars_train])
    
    # Convertir la variable categórica a variable dummy para el conjunto de prueba
    dummy_estrato_test <- model.matrix(~ Estrato - 1, data = test_data)
    test_data2 <- cbind(test_data, dummy_estrato_test)
    
    # Eliminar las columnas no usadas en el modelo en el conjunto de prueba
    test_data2 <- test_data2 %>%
                  select(-Id, -Piso, -Estrato, -Tipo_inmueble, -Longitud, -Latitud,
                         -Barrio, -Comuna, -Zona)
    
    # Normalizar las variables numéricas del conjunto de prueba
    numeric_vars_test <- sapply(test_data2, is.numeric)
    numeric_vars_test['Precio_m'] <- FALSE  # No normalizar la variable objetivo
    numeric_vars_test['Estrato3'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_test['Estrato4'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_test['Estrato5'] <- FALSE  # No normalizar la variable dummy
    #numeric_vars_test['Estrato6'] <- FALSE  # No normalizar la variable dummy
    
    test_data2[numeric_vars_test] <- scale(test_data2[numeric_vars_test])
    
    # Entrenar el modelo en el conjunto de entrenamiento
    modelo1 <- lm(Precio_m ~ Area_construida + No_Habitaciones + No_Banios + No_Parqueaderos +
                   Estrato3 + Estrato4 + Estrato5  , data = train_data2)
    
    # Guardar el modelo ajustado en la última iteración
    modelo_final1 <- modelo1 # Guardar el modelo ajustado en la última iteración
    
    # Predecir en el conjunto de prueba
    predicciones <- predict(modelo1, newdata = test_data2)
    
    # Calcular el MSE y almacenarlo
    cv_MSE[i] <- mean((test_data2$Precio_m - predicciones)^2)
}

# Mostrar el promedio del MSE a través de las iteraciones
cat("Error Cuadrático Medio promedio a través de las iteraciones:", mean(cv_MSE), "\n")
## Error Cuadrático Medio promedio a través de las iteraciones: 18266.61
# Mostrar el resumen del modelo final
if (!is.null(modelo_final1)) {
    resumen_modelo <- summary(modelo_final1)
    print(resumen_modelo)
    
    # Extraer y mostrar el coeficiente de ajuste R^2
    r2 <- resumen_modelo$adj.r.squared
    cat("Coeficiente de determinación ajustado R^2:", r2, "\n")
}
## 
## Call:
## lm(formula = Precio_m ~ Area_construida + No_Habitaciones + No_Banios + 
##     No_Parqueaderos + Estrato3 + Estrato4 + Estrato5, data = train_data2)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -652.64  -57.40   -7.30   39.64  745.94 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      633.985     38.781  16.348  < 2e-16 ***
## Area_construida  121.251     10.480  11.569  < 2e-16 ***
## No_Habitaciones   18.098     10.097   1.793   0.0741 .  
## No_Banios         26.140     10.773   2.427   0.0159 *  
## No_Parqueaderos   20.555      8.282   2.482   0.0136 *  
## Estrato3        -310.617     43.024  -7.220 4.63e-12 ***
## Estrato4        -220.851     41.955  -5.264 2.75e-07 ***
## Estrato5        -194.451     39.468  -4.927 1.41e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 124.1 on 289 degrees of freedom
## Multiple R-squared:  0.7321, Adjusted R-squared:  0.7256 
## F-statistic: 112.8 on 7 and 289 DF,  p-value: < 2.2e-16
## 
## Coeficiente de determinación ajustado R^2: 0.7255726
# Visualizar la distribución del MSE
p1 <- ggplot(data = data.frame(cv_MSE = cv_MSE), aes(x = 1, y = cv_MSE)) +
      geom_boxplot(outlier.shape = NA) +
      geom_jitter(colour = c("#034A94"), width = 0.1) +
      coord_flip() +
      labs(title = "Distribución del error de validación simple") +
      theme_bw() +
      theme(axis.title.x = element_blank())
print(p1)

A partir del gráfico de la distribución de los errores de validación simple, específicamente el error cuadrático medio (MSE), del modelo de regresión utilizado para predecir el precio de una casa a partir de variables como el área construida, el número de habitaciones, baños, parqueaderos, y diferentes estratos socioeconómicos. La mediana del MSE, sugiere que el modelo tiene un error típico bastante consistente, indicando que las predicciones del precio de las casas se desvían de los valores reales de manera controlada. La existencia de pocos valores atípicos en la distribución de errores sugiere que el modelo podria presentar algunas inconsistencias en la predicción para la mayoría de las casas, reflejado en la dispersión de los puntos alrededor de la mediana, indicando que existe cierta variabilidad en la precisión de las predicciones, lo que podría estar relacionado con características específicas de las casas no capturadas completamente por el modelo. Estos resultados sugieren que, si bien el modelo es razonablemente preciso en términos generales, podría beneficiarse de ajustes adicionales o de la inclusión de más variables predictoras que capturen mejor las diferencias en los precios de las casas, para reducir aún más el error de predicción y mejorar la confiabilidad del modelo en diversas condiciones de mercado.

6.4.3 Validación de supuestos

Los supuestos en el modelo de regresión lineal multiple son esenciales para garantizar la validez y precisión de las inferencias y predicciones derivadas del modelo. Estos supuestos aseguran que las estimaciones de los coeficientes sean válidas, lo que permite una interpretación adecuada de su significado en el contexto del problema.

  • Linealidad

La relación entre las variables independientes y la variable dependiente debe ser lineal. Esto significa que la variable dependiente es una combinación lineal de las variables independientes. Como se observa en la siguiente gráfica de los residuos del modelo vs los valores ajustados, estos están distribuidos alrededor de la línea horizontal (cero), sin embargo intentan agruparse un poco en forma de cono, lo que sugiere que la relación entre las variables independientes y la dependiente probablemente no es lineal.

# Graficar residuos vs. valores ajustados
plot(modelo1$fitted.values, modelo1$residuals,
     main = "Residuos vs. Valores Ajustados",
     xlab = "Valores Ajustados",
     ylab = "Residuos")
abline(h = 0, col = "red")

  • Independencia de los Errores

Esta independencia implica que los errores no están correlacionados entre sí. En otras palabras, el error asociado con una observación no está relacionado con el error de otra observación. El análisis de la independencia de los errores en un modelo de regresión lineal es fundamental para garantizar la validez de las inferencias estadísticas. En el gráfico de dispersión de los residuos, se observa que están distribuidos de manera casi homogénea tanto por encima como por debajo de la línea de cero. Esta distribución uniforme sugiere que los errores no presentan patrones sistemáticos a lo largo del rango de valores ajustados, lo que es un indicio positivo de independencia.

  • Homoscedasticidad

En la gráfica se observa la distribución de los datos estimados frente a los residuos en la gráfica de dispersión, la cual mostró una distribución alrededor de cero con una cierta forma cónica, por lo que se procedió a realizar una prueba de hipótesis para confirmar la homocedasticidad en el modelo de regresión. Los resultados obtenidos del test de Breusch-Pagan arrojaron un valor \(p\) de 3.9315e-18. Esta cifra indica que existe suficiente evidencia para rechazar la hipótesis nula de homocedasticidad, lo que confirma que la varianza de los errores no se mantiene constante a lo largo de todos los niveles de las variables independientes.

## 
##  studentized Breusch-Pagan test
## 
## data:  modelo1
## BP = 97.278, df = 7, p-value < 2.2e-16
  • Multicolinealidad

Como se observó en el análisis de correlación, las variables independientes muestran correlaciones moderadas entre sí, así como con la variable objetivo. Los valores de correlación varían entre 0.4 y 0.7, lo que indica una relación significativa, pero no sugiere una multicolinealidad severa. Esta magnitud de correlación sugiere que, aunque las variables están relacionadas, no hay evidencia inmediata de multicolinealidad extremadamente alta que pueda comprometer la estabilidad del modelo.

  • Normalidad de errores

Los errores del modelo (las diferencias entre los valores observados y los valores predichos) están normalmente distribuidos. Esto implica que los errores siguen una distribución normal con media cero y varianza constante.

El análisis de los residuos del modelo de regresión lineal multiple, muestra que los puntos se distribuyen de manera relativamente uniforme alrededor de la línea de referencia en el gráfico Q-Q (quantile-quantile). Este comportamiento indica que los residuos siguen aproximadamente una distribución normal, lo que es una condición deseable para la validez de las inferencias y predicciones del modelo. Sin embargo, la prueba de hipótesis de normalidad proporciona resultados inconsistentes con esta observación. El test de Kolmogorov-Smirnov muestra un estadístico D de 0.1366 con un valor \(p\) de 3.0608e-5. Estos resultados proporcionan evidencia suficiente para rechazar la hipótesis nula de normalidad de los residuos.

Teniendo en cuenta que no se cumplen los supuestos del modelo de regresion, se podrían aplicar transformaciones logarítmicas, raíz cuadrada o Box-Cox a la variable objetivo para estabilizar la varianza y aproximar una distribución normal. Además, se podrían incluir variables relevantes y la posible exclusión de variables irrelevantes o redundantes. La creación de nuevas variables derivadas o interacciones entre variables puede ayudar a capturar relaciones no lineales. También se debe realizar una inspección más detallada de los outliers y valores influyentes, ya que estos pueden afectar significativamente la normalidad y la heterocedasticidad de los residuos. Estos ajustes en la base de datos no solo mejorarán la calidad del modelo, sino que también contribuirán a una interpretación más robusta y confiable de los resultados.

6.5 Análisis caso 2

Filtrado del conjunto de datos

Teniendo en cuenta la segunda solicitud , se filtraron unicamente las viviendas de tipo apartamento que se encuentran en la zona sur de la ciudad, encontrando un total de 2198 posibles oportunidades para cumplir con el requerimiento, los cuales se muestran en la siguiente tabla:

# Filtrar los registros que sean de tipo 'apartamentos' y 'zona sur'
vivienda_filtrada2 <- vivienda5 %>%
  filter(Tipo_inmueble == "Apartamento" & Zona == "Zona Sur")

# Tabla de dataframe filtrado
datatable(vivienda_filtrada2, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T) ) %>% formatStyle(names(vivienda_filtrada2), backgroundColor = "lightgray") 

Se graficaron los registros seleccionados en un mapa, sin embrago se puede observar que existen puntos por fuera de la zona sur de cali, lo cual nos hace pensar que la asignación de las coordenadas puede estar errada, asumiendo que las zonas estan bien clasificadas. Esto se podría tratar haciendo un limpieza exhaustiva de las coordendas, extrayendo el centroide de cada poligono de la base de datos oficial y asignarle esas coordenadas a los resgistros que posee la inmobiliaria. Sin embrago en este caso no se abordará, lo que se hizo fue seleccionar un rango de longitud y latitud para filtrar solo los registros que queban contenidos en estas coordenadas, obteniendo los registros que se muestran en el segundo mapa.

library(sf)
library(leaflet)

#ruta_archivo <- "C:/Users/lvasquez/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/Barrios_Cali.gpkg"
ruta_archivo <- "C:/Users/ASUS/OneDrive - PUJ Cali/2024_2/Metodos_estadisticos_para_toma_decisiones/Unidad_1/Cali_WGS84.gpkg"

file.exists(ruta_archivo)
## [1] TRUE
Cali_WGS84 <- st_read(ruta_archivo) # fuente de datos secundaria (www.cali.gov.co)
## Multiple layers are present in data source C:\Users\ASUS\OneDrive - PUJ Cali\2024_2\Metodos_estadisticos_para_toma_decisiones\Unidad_1\Cali_WGS84.gpkg, reading layer `Barrios'.
## Use `st_layers' to list all layer names and their type in a data source.
## Set the `layer' argument in `st_read' to read a particular layer.
## Reading layer `Barrios' from data source 
##   `C:\Users\ASUS\OneDrive - PUJ Cali\2024_2\Metodos_estadisticos_para_toma_decisiones\Unidad_1\Cali_WGS84.gpkg' 
##   using driver `GPKG'
## Simple feature collection with 335 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -76.59078 ymin: 3.331802 xmax: -76.46124 ymax: 3.501895
## Geodetic CRS:  WGS 84 (CRS84)
Cali_WGS84 <- st_transform(Cali_WGS84, crs = '+proj=longlat +datum=WGS84')


leaflet() %>%
  addTiles() %>%
  setView(lng = -76.53, lat = 3.42, zoom = 11.5) %>%
  addPolygons(data = Cali_WGS84, color = "gray",weight = 2,fillColor = "gray", stroke = 0.1, opacity = 0.9, popup = paste0("Barrio: ",Cali_WGS84$barrio,"<br>","Comuna: ",Cali_WGS84$comuna) ) %>%
  addCircleMarkers(data=vivienda_filtrada2,lng=vivienda_filtrada2$Longitud, lat = vivienda_filtrada2$Latitud, fillColor = "red",radius = 2,color = "red",popup = paste0("Barrio: ",vivienda_filtrada2$Barrio,"<br>","Comuna: ",vivienda_filtrada2$Comuna))
# Filtrar los registros que sean de tipo 'apartamento' y 'zona sur'
vivienda_sur <- vivienda_filtrada2 %>%
  filter(Latitud <= 3.4100  & Longitud >-76.54859 & Longitud < (-76.5000) )

# Guardar el dataframe en un archivo .csv
write.csv(vivienda_sur, file = "vivienda_sur.csv", row.names = FALSE)

leaflet() %>%
  addTiles() %>%
  setView(lng = -76.53, lat = 3.42, zoom = 11.5) %>%
  addPolygons(data = Cali_WGS84, color = "gray",weight = 2,fillColor = "gray", stroke = 0.1, opacity = 0.9, popup = paste0("Barrio: ",Cali_WGS84$barrio,"<br>","Comuna: ",Cali_WGS84$comuna) ) %>%
  addCircleMarkers(data=vivienda_sur,lng=vivienda_sur$Longitud, lat = vivienda_sur$Latitud, fillColor = "red",radius = 2,color = "red",popup = paste0("Barrio: ",vivienda_sur$Barrio,"<br>","Comuna: ",vivienda_sur$Comuna))

6.5.1 Análisis de correlación

De acuerdo a lo observado en la matriz de correlación, se ve que el precio de un apartamento en la zona sur de cali, esta principlmente modulado por el área construida, la cantidad de baños, el número de parqueaderos y en menor grado el número de habitaciones. Adicionalmente se observa que el precio y el area construida tienen una relación un poco más lineal de acuerdo con el gráfico de dispersión. Por otro lado, la relación con las demas variables, tampoco tiene una forma definida, son graficos bastante dispersos, lo que quiere decir que no necesariamente cuando alguna de estas variables aumenta, lo hará en la misma proporción el precio de un apartamento en esta zona de Cali.

library(GGally)

# Filtrar solo las variables numéricas
vivienda_numericas2 <- vivienda_sur %>% select(where(is.numeric))

# Asegurar el tipo numérico
vivienda_numericas2 <- vivienda_numericas2 %>%
  mutate(across(everything(), as.numeric))

# Eliminar la columna lon y lat
corr_matrix <- vivienda_numericas2%>% 
  select(-Longitud,-Latitud,-Id)


# Crear el pair plot
ggpairs(corr_matrix%>% select(where(is.numeric)))

Por otro lado, los precios más altos en los apartamentos de la zona sur de Cali están asociados efectivamente al estrato 6, donde se concentran la mayoria de los inmuebles con precios que oscilan entre los 400 y 700 millones de pesos aproximadamente. En general en esta zona se encuentran oferta en estrato 3, 4, 5 y 6 que pueden ser atractivas para los compradores.

# Graficar usando la muestra
ggplot(vivienda_sur, aes(x = Estrato, y = Precio_m, fill = Zona)) +
  geom_boxplot() +
  labs(title = "Box Plot de Precios por Estrato y Zona",
       x = "Estrato",
       y = "Precio") +
  theme(plot.title = element_text(size = 14, face = "bold", hjust = 1),     
        axis.text.x = element_text(size = 6, angle = 45, hjust = 1)) +  
  theme_minimal()

6.5.2 Estimación del modelo y validación

El coeficiente del intercepto en este caso, que se estima en 425.618 millones, representa el valor base de la vivienda tipo apartamento cuando todas las demás variables son cero. Las variables Area_construida (60.786), No_Banios (49.568) y No_Parqueaderos (52.253) muestran coeficientes significativos, indicando una relación positiva con el precio; es decir, un incremento en una de estas está asociado con un aumento en el precio. En contraste, el coeficientes para No_Habitaciones (-2.245), no es estadísticamente significativos, sugiriendo que, aunque esta variable tienen algún impacto, su influencia en el precio no es tan robusta o consistente. Los coeficientes negativos y significativos para las variables dummy de estrato (Estrato3, Estrato4, Estrato5) muestran diferencias significativas en el precio en comparación con el estrato base, con disminuciones promedio de 135.939, 144.339 y 125.916 millones, respectivamente. Por su parte, el coeficiente de determinación R2 de 0.7909 sugiere que el modelo explica aproximadamente el 79.09% de la variabilidad en el precio de los apartamentos, reflejando un ajuste sólido del modelo a los datos observados.

# Cargar las librerías necesarias
library(DT)
library(dplyr)       # Para manipulación de datos
library(car)         # Para el cálculo de VIF
library(ggplot2)     # Para visualización de gráficos

# Establecer una semilla para reproducibilidad
set.seed(234)

# Definir el número de iteraciones para la validación cruzada simple
num_iterations <- 100

# Vector para almacenar el modelo ajustado en la última iteración
modelo_final <- NULL

# Vector para almacenar el MSE de cada iteración
cv_MSE <- rep(NA, num_iterations)

train_size <- floor(0.7 * nrow(vivienda_sur))

# Realizar validación cruzada simple en un bucle
for (i in 1:num_iterations) {
    
    # Dividir los datos en entrenamiento (70%) y prueba (30%)
    train_index <- sample(seq_len(nrow(vivienda_sur)), size = train_size)
    
    train_data <- vivienda_sur[train_index, ]
    test_data <- vivienda_sur[-train_index, ]
    
    # Convertir la variable categórica a variable dummy para el conjunto de entrenamiento
    dummy_estrato_train <- model.matrix(~ Estrato - 1, data = train_data)
    train_data2 <- cbind(train_data, dummy_estrato_train)
    
    # Eliminar las columnas no usadas en el modelo en el conjunto de entrenamiento
    train_data2 <- train_data2 %>%
                   select(-Id, -Piso, -Estrato, -Tipo_inmueble, -Longitud, -Latitud,
                          -Barrio, -Comuna, -Zona)
    
    # Normalizar las variables numéricas del conjunto de entrenamiento
    numeric_vars_train <- sapply(train_data2, is.numeric)
    numeric_vars_train['Precio_m'] <- FALSE  # No normalizar la variable objetivo
    numeric_vars_train['Estrato3'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_train['Estrato4'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_train['Estrato5'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_train['Estrato6'] <- FALSE  # No normalizar la variable dummy
    train_data2[numeric_vars_train] <- scale(train_data2[numeric_vars_train])
    
    # Convertir la variable categórica a variable dummy para el conjunto de prueba
    dummy_estrato_test <- model.matrix(~ Estrato - 1, data = test_data)
    test_data2 <- cbind(test_data, dummy_estrato_test)
    
    # Eliminar las columnas no usadas en el modelo en el conjunto de prueba
    test_data2 <- test_data2 %>%
                  select(-Id, -Piso, -Estrato, -Tipo_inmueble, -Longitud, -Latitud,
                         -Barrio, -Comuna, -Zona)
    
    # Normalizar las variables numéricas del conjunto de prueba
    numeric_vars_test <- sapply(test_data2, is.numeric)
    numeric_vars_test['Precio_m'] <- FALSE  # No normalizar la variable objetivo
    numeric_vars_test['Estrato3'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_test['Estrato4'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_test['Estrato5'] <- FALSE  # No normalizar la variable dummy
    numeric_vars_test['Estrato6'] <- FALSE  # No normalizar la variable dummy
    
    test_data2[numeric_vars_test] <- scale(test_data2[numeric_vars_test])
    
    # Entrenar el modelo en el conjunto de entrenamiento
    modelo2 <- lm(Precio_m ~ Area_construida + No_Habitaciones + No_Banios + No_Parqueaderos +
                     Estrato3+Estrato4 + Estrato5  , data = train_data2)
    
    # Guardar el modelo ajustado en la última iteración
    modelo_final <- modelo2 # Guardar el modelo ajustado en la última iteración
    
    # Predecir en el conjunto de prueba
    predicciones <- predict(modelo2, newdata = test_data2)
    
    # Calcular el MSE y almacenarlo
    cv_MSE[i] <- mean((test_data2$Precio_m - predicciones)^2)
}

# Mostrar el promedio del MSE a través de las iteraciones
cat("Error Cuadrático Medio promedio a través de las iteraciones:", mean(cv_MSE), "\n")
## Error Cuadrático Medio promedio a través de las iteraciones: 8961.841
# Mostrar el resumen del modelo final
if (!is.null(modelo_final)) {
    resumen_modelo <- summary(modelo_final)
    print(resumen_modelo)
    
    # Extraer y mostrar el coeficiente de ajuste R^2
    r2 <- resumen_modelo$adj.r.squared
    cat("Coeficiente de determinación ajustado R^2:", r2, "\n")
}
## 
## Call:
## lm(formula = Precio_m ~ Area_construida + No_Habitaciones + No_Banios + 
##     No_Parqueaderos + Estrato3 + Estrato4 + Estrato5, data = train_data2)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -908.16  -37.54   -4.49   36.26  912.19 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      425.618      7.076  60.153  < 2e-16 ***
## Area_construida   60.786      3.601  16.882  < 2e-16 ***
## No_Habitaciones   -2.245      3.108  -0.722     0.47    
## No_Banios         49.568      4.208  11.780  < 2e-16 ***
## No_Parqueaderos   52.253      3.582  14.589  < 2e-16 ***
## Estrato3        -135.939     20.313  -6.692 3.25e-11 ***
## Estrato4        -144.339      9.466 -15.249  < 2e-16 ***
## Estrato5        -125.916      8.310 -15.152  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 92.54 on 1307 degrees of freedom
## Multiple R-squared:  0.792,  Adjusted R-squared:  0.7909 
## F-statistic: 711.1 on 7 and 1307 DF,  p-value: < 2.2e-16
## 
## Coeficiente de determinación ajustado R^2: 0.7909321
# Visualizar la distribución del MSE
p1 <- ggplot(data = data.frame(cv_MSE = cv_MSE), aes(x = 1, y = cv_MSE)) +
      geom_boxplot(outlier.shape = NA) +
      geom_jitter(colour = c("#034A94"), width = 0.1) +
      coord_flip() +
      labs(title = "Distribución del error de validación simple") +
      theme_bw() +
      theme(axis.title.x = element_blank())
print(p1)

A partir del gráfico de la distribución de los errores de validación simple, específicamente el error cuadrático medio (MSE), del modelo de regresión utilizado para predecir el precio de un apartamento a partir de variables como el área construida, el número de habitaciones, baños, parqueaderos, y diferentes estratos socioeconómicos. La mediana del MSE, sugiere que el modelo tiene un error típico bastante consistente, indicando que las predicciones del precio de los apartamentos se desvían de los valores reales de manera controlada. La existencia de valores atípicos moderados en la distribución de errores sugiere que el modelo no tiene problemas graves en la predicción para la mayoría de los apartamentos, lo que podría interpretarse como una buena capacidad de generalización. Sin embargo, la dispersión de los puntos alrededor de la mediana indica que existe cierta variabilidad en la precisión de las predicciones, lo que podría estar relacionado con características específicas de los apartamentos no capturadas completamente por el modelo. Estos resultados sugieren que, si bien el modelo es razonablemente preciso en términos generales, podría beneficiarse de ajustes adicionales o de la inclusión de más variables predictoras que capturen mejor las diferencias en los precios de las casas, para reducir aún más el error de predicción y mejorar la confiabilidad del modelo en diversas condiciones de mercado.

6.5.3 Validación de supuestos

Los supuestos en el modelo de regresión lineal multiple son esenciales para garantizar la validez y precisión de las inferencias y predicciones derivadas del modelo. Estos supuestos aseguran que las estimaciones de los coeficientes sean válidas, lo que permite una interpretación adecuada de su significado en el contexto del problema.

  • Linealidad

La relación entre las variables independientes y la variable dependiente debe ser lineal. Esto significa que la variable dependiente es una combinación lineal de las variables independientes. Como se observa en la siguiente gráfica de los residuos del modelo vs los valores ajustados, estos están distribuidos alrededor de la línea horizontal (cero), sin embargo intentan agruparse en clusters y luego se esparce en forma de cono, lo que sugiere que la relación entre las variables independientes y la dependiente probablemente no es lineal.

# Graficar residuos vs. valores ajustados
plot(modelo2$fitted.values, modelo2$residuals,
     main = "Residuos vs. Valores Ajustados",
     xlab = "Valores Ajustados",
     ylab = "Residuos")
abline(h = 0, col = "red")

  • Independencia de los Errores

Esta independencia implica que los errores no están correlacionados entre sí. En otras palabras, el error asociado con una observación no está relacionado con el error de otra observación. El análisis de la independencia de los errores en un modelo de regresión lineal es fundamental para garantizar la validez de las inferencias estadísticas. En el gráfico de dispersión de los residuos, se observa que están distribuidos de manera casi homogénea tanto por encima como por debajo de la línea de cero. Esta distribución uniforme sugiere que los errores no presentan patrones sistemáticos a lo largo del rango de valores ajustados, lo que es un indicio positivo de independencia.

  • Homoscedasticidad

En la gráfica se observa la distribución de los datos estimados frente a los residuos en la gráfica de dispersión, la cual mostró una distribución alrededor de cero con una cierta forma cónica y clusters, por lo que se procedió a realizar una prueba de hipótesis para confirmar la homocedasticidad en el modelo de regresión. Los resultados obtenidos del test de Breusch-Pagan arrojaron un valor \(p\) de 2.5132. Esta cifra indica que existe suficiente evidencia para rechazar la hipótesis nula de homocedasticidad, lo que confirma que la varianza de los errores no se mantiene constante a lo largo de todos los niveles de las variables independientes.

## 
##  studentized Breusch-Pagan test
## 
## data:  modelo2
## BP = 455.81, df = 7, p-value < 2.2e-16
  • Multicolinealidad

Como se observó en el análisis de correlación, las variables independientes muestran correlaciones moderadas entre sí, así como con la variable objetivo. Los valores de correlación varían entre 0.4 y 0.7, lo que indica una relación significativa, pero no sugiere una multicolinealidad severa. Esta magnitud de correlación sugiere que, aunque las variables están relacionadas, no hay evidencia inmediata de multicolinealidad extremadamente alta que pueda comprometer la estabilidad del modelo.

  • Normalidad de errores

Los errores del modelo (las diferencias entre los valores observados y los valores predichos) están normalmente distribuidos. Esto implica que los errores siguen una distribución normal con media cero y varianza constante.

El análisis de los residuos del modelo de regresión lineal multiple, muestra que los puntos se distribuyen de manera bastante uniforme alrededor de la línea de referencia en el gráfico Q-Q (quantile-quantile). Este comportamiento indica que los residuos siguen aproximadamente una distribución normal, lo que es una condición deseable para la validez de las inferencias y predicciones del modelo. Sin embrago, la prueba de hipótesis de normalidad proporciona resultados inconsistentes con esta observación. El test de Kolmogorov-Smirnov muestra un estadístico D de 0.1262 con un valor \(p\) de 0.0. Estos resultados proporcionan evidencia suficiente para rechazar la hipótesis nula de normalidad de los residuos.

Al igual que el caso anterior, no se cumplen los supuestos del modelo de regresion. Se podrían aplicar transformaciones logarítmicas, raíz cuadrada o Box-Cox a la variable objetivo para estabilizar la varianza y aproximar una distribución normal. Además, se podrían incluir variables relevantes y la posible exclusión de variables irrelevantes o redundantes. La creación de nuevas variables derivadas o interacciones entre variables puede ayudar a capturar relaciones no lineales. También se debe realizar una inspección más detallada de los outliers y valores influyentes, ya que estos pueden afectar significativamente la normalidad y la heterocedasticidad de los residuos. Estos ajustes en la base de datos no solo mejorarán la calidad del modelo, sino que también contribuirán a una interpretación más robusta y confiable de los resultados.