Carga de librerías

# ============================
# CARGA DE LIBRERÍAS
# ============================

# Manipulación y visualización
library(tidyverse)
library(ggplot2)
library(plotly)
library(dplyr)
library(corrplot)
library(leaflet)

# Tablas
library(gt)
library(broom)

# Modelado y validación
library(caret)
library(car)
library(lmtest)

# Métricas
library(Metrics)

# Carga de base de datos
library(paqueteMODELOS)

# Paleta personal para el informe
pal <- c("#222222", "#6B2EBF", "#F7B26A", "#76A9DC", "#6DC8A4")

Base de Datos

# ============================
# CARGA DE LA BASE DE DATOS
# ============================

# Instalar devtools si no está disponible
if (!requireNamespace("devtools", quietly = TRUE)) {
  install.packages("devtools")
}

# Instalar paqueteMODELOS desde GitHub si no está disponible
if (!requireNamespace("paqueteMODELOS", quietly = TRUE)) {
  devtools::install_github("centro-magis/paqueteMODELOS")
}

# Cargar paquete y base de datos
library(paqueteMODELOS)
data("vivienda")

# Verificación rápida
dim(vivienda)
## [1] 8322   13
glimpse(vivienda)
## Rows: 8,322
## Columns: 13
## $ id           <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
## $ zona         <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
## $ piso         <chr> NA, NA, NA, "02", "01", "01", "01", "01", "02", "02", "02…
## $ estrato      <dbl> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
## $ preciom      <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
## $ areaconst    <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
## $ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, NA, 3, 2, 2, 1, 4, 2, 2, 2,…
## $ banios       <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
## $ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
## $ tipo         <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
## $ barrio       <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
## $ longitud     <dbl> -76.51168, -76.51237, -76.51537, -76.54000, -76.51350, -7…
## $ latitud      <dbl> 3.43382, 3.43369, 3.43566, 3.43500, 3.45891, 3.36971, 3.4…

1 PREDICCIÓN PRECIO VIVIENDA 1 (CASA)

1.1 Filtro de la Base de Datos

# ============================
# 1.1 FILTRO BASE DE DATOS - VIVIENDA 1
# ============================

vivienda1_base <- vivienda %>%
  filter(
    tipo == "Casa",
    zona == "Zona Norte",
    estrato %in% c(4, 5)
  )

# Verificación del filtro
data.frame(
  Registros = nrow(vivienda1_base),
  Variables = ncol(vivienda1_base)
) %>%
  gt() %>%
  tab_header(
    title = "Tabla 1. Dimensión de la base filtrada para la Vivienda 1"
  )
Tabla 1. Dimensión de la base filtrada para la Vivienda 1
Registros Variables
432 13

Se realizó un filtración de la base de datos original conservando únicamente viviendas comparables con las características requeridas por la empresa dentro de la solicitud para la vivienda 1: casas, ubicadas en la Zona Norte y pertenecientes a los estratos 4 o 5.

El resultado se guarda en un nuevo data frame denominado vivienda1_base, que será la base de trabajo del Caso 1. La Tabla 1 resume cuántos registros comparables quedaron disponibles para modelar.

Este procedimiento permite construir el modelo sobre observaciones homogéneas y pertinentes para ese caso en particular, lo que ayuda a mejorar la validez de la predicción del precio y resulta siendo fundamental pues evita comparar la vivienda objetivo con otros inmuebles de distinta naturaleza, lo cual podría sesgar la estimación del modelo que se construirá.

# ============================
# PRIMEROS REGISTROS BASE 1
# ============================

vivienda1_base %>%
  slice(1:3) %>%
  gt() %>%
  tab_header(
    title = "Tabla 2. Primeros tres registros de la base filtrada (Base 1)"
  )
Tabla 2. Primeros tres registros de la base filtrada (Base 1)
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
1209 Zona Norte 02 5 320 150 2 4 6 Casa acopi -76.51341 3.47968
1592 Zona Norte 02 5 780 380 2 3 3 Casa acopi -76.51674 3.48721
4460 Zona Norte 02 4 625 355 3 5 5 Casa acopi -76.53179 3.40590

La Tabla 2 muestra los tres primeros registros de la base de datos ya filtrada, correspondiente a viviendas tipo casa ubicadas en la zona norte de la ciudad y pertenecientes a los estratos definidos en el caso de estudio (4 o 5). Esta visualización permite verificar la correcta estructura de los datos que se utilizarán para el análisis posterior.

# ============================
# VERIFICACIÓN DEL FILTRO
# ============================

vivienda1_base %>%
  count(tipo, zona) %>%
  gt() %>%
  tab_header(
    title = "Tabla 3. Verificación del filtro aplicado a la base de datos"
  )
Tabla 3. Verificación del filtro aplicado a la base de datos
tipo zona n
Casa Zona Norte 432

Esta Tabla 3 recién presentada confirma que la base filtrada contiene únicamente viviendas tipo casa ubicadas en la zona norte, lo que evidencia que el proceso de filtrado se realizó correctamente y que las observaciones incluidas corresponden al segmento de mercado definido para el análisis.

# ============================
# MAPA DE LAS VIVIENDAS
# ============================

leaflet(vivienda1_base) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 5,
    color = "#6B2EBF",
    stroke = FALSE,
    fillOpacity = 0.7,
    popup = ~paste(
      "<b>Precio:</b>", preciom, "millones<br>",
      "<b>Área:</b>", areaconst, "m²<br>",
      "<b>Estrato:</b>", estrato
    )
  )

Figura 1. Localización geográfica de viviendas (Base 1).

La Figura 1 ilustra la distribución geográfica de las viviendas incluidas en la base filtrada. Se observa que los puntos presentan una concentración espacial coherente con la ubicación correspondiente a la zona norte de la ciudad. No obstante, también es cierto que algunos puntos se situán ligeramente fuera del área esperada debido a imprecisiones en la georreferenciación de los datos, errores en el registro de coordenadas o a que los límites entre zonas urbanas no siempre son estrictamente definidos en términos geográficos. Este comportamiento es habitual en bases de datos inmobiliarias y no necesariamente implica inconsistencias en el proceso de filtrado.

1.2 Análisis Exploratorio de datos

1.2.1 Estadísticos Descriptivos

# ============================
# 1.2.1 RESUMEN ESTADÍSTICO
# ============================

tabla_resumen <- vivienda1_base %>%
  select(preciom, areaconst, habitaciones, banios, parqueaderos) %>%
  summarise(
    across(
      everything(),
      list(
        Min = ~min(., na.rm = TRUE),
        Q1 = ~quantile(., 0.25, na.rm = TRUE),
        Mediana = ~median(., na.rm = TRUE),
        Media = ~mean(., na.rm = TRUE),
        SD = ~sd(., na.rm = TRUE),
        Q3 = ~quantile(., 0.75, na.rm = TRUE),
        RIQ = ~IQR(., na.rm = TRUE),
        Max = ~max(., na.rm = TRUE)
      )
    )
  ) %>%
  pivot_longer(
    everything(),
    names_to = c("Variable", "Estadistico"),
    names_sep = "_"
  ) %>%
  pivot_wider(
    names_from = Estadistico,
    values_from = value
  ) %>%
  mutate(
    Variable = dplyr::recode(
      Variable,
      preciom = "Precio",
      areaconst = "Área construida",
      habitaciones = "Habitaciones",
      banios = "Baños",
      parqueaderos = "Parqueaderos"
    )
  )

tabla_resumen %>%
  gt() %>%
  tab_header(
    title = "Tabla 4. Estadísticos descriptivos de las variables numéricas"
  ) %>%
  cols_label(
    Variable = "Variable",
    Min = "Mínimo",
    Q1 = "Q1",
    Mediana = "Mediana",
    Media = "Media",
    SD = "Desv. Est.",
    Q3 = "Q3",
    RIQ = "RIQ",
    Max = "Máximo"
  ) %>%
  fmt_number(
    columns = -Variable,
    decimals = 2
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 4. Estadísticos descriptivos de las variables numéricas
Variable Mínimo Q1 Mediana Media Desv. Est. Q3 RIQ Máximo
Precio 125.00 350.00 450.00 508.25 242.11 600.00 250.00 1,940.00
Área construida 45.00 200.00 280.00 301.67 156.47 360.00 160.00 1,188.00
Habitaciones 0.00 4.00 4.00 4.59 1.72 5.00 1.00 10.00
Baños 0.00 3.00 4.00 3.90 1.36 5.00 2.00 10.00
Parqueaderos 1.00 1.00 2.00 2.31 1.38 3.00 2.00 9.00

Dentro de la Tabla 4 se presenta un resumen descriptivo de las variables numéricas relevantes para el análisis del precio de la vivienda. Estas estadísticas permiten identificar la escala de las variables, así como los rangos típicos de área construida, número de habitaciones, baños y parqueaderos en el segmento de casas ubicadas en la zona norte y pertenecientes a estratos 4 y 5.

1.2.2 Datos Faltantes

# ============================
# 1.2.2 DATOS FALTANTES
# ============================

na_vivienda1 <- vivienda1_base %>%
  summarise(
    across(
      everything(),
      ~sum(is.na(.))
    )
  ) %>%
  pivot_longer(
    everything(),
    names_to = "Variable",
    values_to = "NA"
  )

na_vivienda1 %>%
  gt() %>%
  tab_header(
    title = "Tabla 5. Cantidad de datos faltantes por variable"
  )
Tabla 5. Cantidad de datos faltantes por variable
Variable NA
id 0
zona 0
piso 203
estrato 0
preciom 0
areaconst 0
parqueaderos 106
banios 0
habitaciones 0
tipo 0
barrio 0
longitud 0
latitud 0

En la Tabla 5 se puede identificar que la mayoría de las variables no presentan valores ausentes, lo que indica una buena calidad de la información disponible. Sin embargo, las variables piso y parqueaderos registran 203 y 106 valores faltantes, respectivamente, por lo que requieren tratamiento previo.

1.2.3 Datos atípicos

# ============================
# 1.2.3 OUTLIERS
# ============================

vivienda1_base %>%
  select(preciom, areaconst, habitaciones, banios, parqueaderos) %>%
  pivot_longer(
    cols = everything(),
    names_to = "Variable",
    values_to = "Valor"
  ) %>%
  mutate(
    Variable = factor(
      Variable,
      levels = c("preciom", "areaconst", "habitaciones", "banios", "parqueaderos"),
      labels = c("Precio", "Área construida", "Habitaciones", "Baños", "Parqueaderos")
    )
  ) %>%
  ggplot(aes(x = "", y = Valor)) +
  geom_boxplot(fill = "#6B2EBF", alpha = 0.7, width = 0.4) +
  facet_wrap(~ Variable, scales = "free_y", ncol = 3) +
  labs(
    x = NULL,
    y = "Valor"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text = element_text(face = "bold")
  )

Figura 2. Identificación de valores atípicos en variables numéricas.

La Figura 2 muestra la presencia de valores atípicos en todas las variables numéricas, siendo más notorios en precio y área construida, donde se observan varios valores extremos superiores que indican propiedades considerablemente más costosas o de mayor tamaño que el promedio. Las variables habitaciones, baños y parqueaderos presentan menor dispersión, aunque también registran algunos outliers. En conjunto, estos resultados sugieren una distribución heterogénea del mercado inmobiliario, con ciertas observaciones que se alejan del comportamiento típico y que podrían influir en análisis posteriores como la correlación o la regresión.

1.2.4 Imputación de Datos Faltantes

# ============================
# IMPUTACIÓN PARQUEADEROS
# ============================

vivienda1_base <- vivienda1_base %>%
  mutate(
    parqueaderos = ifelse(is.na(parqueaderos), 0, parqueaderos)
  )

# ============================
# VERIFICACIÓN DE IMPUTACIÓN
# ============================

verificacion_na <- vivienda1_base %>%
  summarise(
    piso = sum(is.na(piso)),
    parqueaderos = sum(is.na(parqueaderos))
  ) %>%
  pivot_longer(
    everything(),
    names_to = "Variable",
    values_to = "faltantes"
  )

verificacion_na %>%
  gt() %>%
  tab_header(
    title = "Tabla 6. Verificación de datos faltantes tras imputación"
  ) %>%
  cols_label(
    Variable = "Variable",
    faltantes = "Datos faltantes (NA)"
  )
Tabla 6. Verificación de datos faltantes tras imputación
Variable Datos faltantes (NA)
piso 203
parqueaderos 0

Durante la inspección de datos faltantes descritos en la Tabla 5 se identificó la presencia de valores NA en la variable parqueaderos así como en la variable pisos.

En el contexto del mercado inmobiliario, la ausencia de registro en la variable parqueaderos suele asociarse a inmuebles que no disponen de parqueadero. Por esta razón, los valores faltantes fueron imputados con el valor 0, permitiendo conservar las observaciones disponibles sin introducir distorsiones en la interpretación de la variable.

Por otra parte, aunque la variable piso presenta valores faltantes, esta no se considera relevante para el modelo de predicción del precio en este caso de estudio. Dado que el análisis se centrará en variables como área construida, estrato, número de baños, número de habitaciones y zona, las cuales representan los atributos estructurales y socioeconómicos más influyentes en la valoración del inmueble, la variable piso será excluida del análisis sin afectar la estimación del modelo.

La Tabla 6 evidencia la permanencia de los valores faltantes en la variable pisos, así como la ausencia de faltantes en la variable parqueaderos, tras la imputación.

1.2.5 Tratamiento de Atípicos

La Figura 2 permite identificar la presencia de valores atípicos en varias de las variables numéricas analizadas, particularmente en el precio y el área construida. Estos valores extremos corresponden a observaciones que se sitúan fuera del rango definido por el criterio intercuartílico (1.5 × RIQ), lo cual también puede inferirse a partir de los estadísticos descriptivos presentados en la Tabla 4.

No obstante, en el contexto del mercado inmobiliario, estos valores extremos no necesariamente representan errores de registro, sino propiedades con características superiores dentro del mercado, como viviendas de mayor tamaño o valor. Por esta razón, los datos atípicos se conservarán para el análisis, permitiendo que el modelo capture la heterogeneidad real del mercado y evitando introducir sesgos derivados de la eliminación arbitraria de observaciones.

1.2.6 Correlación entre variables numéricas

# ============================
# MATRIZ DE CORRELACIÓN INTERACTIVA
# ============================

vars_corr <- vivienda1_base %>%
  select(preciom, areaconst, estrato, banios, habitaciones)

matriz_corr <- cor(vars_corr, use = "complete.obs")

plot_ly(
  x = colnames(matriz_corr),
  y = rownames(matriz_corr),
  z = matriz_corr,
  type = "heatmap",
  colors = colorRamp(c("#6B2EBF", "#FFFFFF", "#F7B26A")),
  zmin = -1,
  zmax = 1,
  text = round(matriz_corr, 2),
  texttemplate = "%{text}",
  textfont = list(color = "black", size = 12),
  hovertemplate = paste(
    "<b>Variable X:</b> %{x}<br>",
    "<b>Variable Y:</b> %{y}<br>",
    "<b>Correlación:</b> %{z:.2f}<extra></extra>"
  ),
  colorbar = list(title = "r")
) %>%
  layout(
    xaxis = list(
      title = "",
      tickangle = -45
    ),
    yaxis = list(
      title = "",
      autorange = "reversed"
    )
  )

Figura 3. Matriz de correlación interactiva entre variables numéricas para vivienda 1 (Casa).

La matriz de correlación permite evaluar la intensidad de la relación lineal entre el precio de la vivienda y las variables estructurales consideradas en el análisis. Dentro de la Figura 3 se puede identificar que el área construida presenta una de las asociaciones más fuertes con el precio (0.7), lo cual es consistente con la lógica del mercado inmobiliario, donde el tamaño del inmueble constituye uno de los principales determinantes de su valor.

De igual forma, variables como el número de baños y el número de habitaciones muestran correlaciones positivas con el precio (0.37), lo que sugiere que viviendas con mayor capacidad habitacional tienden a presentar valores de mercado superiores.

Aunque la variable estrato corresponde a una escala ordinal, se incluye dentro del análisis de correlación debido a que representa un gradiente socioeconómico que suele presentar relación creciente con el valor de las viviendas; es por esto que en estudios inmobiliarios es habitual tratar esta variable como numérica para analizar su asociación con el precio del inmueble. En ese sentido el estrato socioeconómico también presenta una relación positiva con el precio, aunque más débil (0.22), reflejando diferencias estructurales en la valorización del mercado según el nivel socioeconómico del sector.

1.2.7 Distribución del precio de las casas en zona norte

# ============================
# PRECIO SEGÚN ZONA
# ============================

p_zona <- ggplot(vivienda1_base,
                 aes(x = zona, y = preciom)) +
  geom_boxplot(fill = "#6B2EBF") +
  labs(
    x = "Zona",
    y = "Precio"
  ) +
  theme_minimal()

ggplotly(p_zona)

Figura 4. Distribución del precio de las casas en zona norte.

El boxplot de la Figura 4 permite analizar la distribución del precio de las viviendas específicamente para zona norte, facilitando la comparación de sus niveles y dispersión. En el caso mostrado para Zona Norte, la mediana del precio se sitúa alrededor de 450 millones, mientras que el 50 % central de las observaciones se concentra aproximadamente entre 350 y 600 millones. Asimismo, se identifican numerosos valores atípicos superiores, algunos cercanos a 2000 millones, lo que evidencia la presencia de propiedades de alto valor y sugiere cierta heterogeneidad en el mercado inmobiliario dentro de esta zona.

Nota: Dado que el análisis se restringe únicamente a viviendas ubicadas en la zona norte, la variable zona no presenta variabilidad dentro del subconjunto analizado, por lo que no es posible realizar comparaciones entre zonas en este caso.

1.3 Modelo de Regresión Lineal Múltiple

Con el fin de explicar el comportamiento del precio de las viviendas del subconjunto analizado, se estimará un Modelo de Regresión Lineal Múltiple, tomando como variable respuesta el precio y como variables explicativas el área construida, estrato, número de habitaciones, número de parqueaderos y número de baños. Estas variables corresponden a los atributos solicitados en el enunciado parala actividad y representan características estructurales y socioeconómicas relevantes para la valoración del inmueble.

# ============================
# 1.3 ESTIMACIÓN DEL MODELO
# ============================

modelo_vivienda1 <- lm(
  preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
  data = vivienda1_base
)

# Tabla de coeficientes
tabla_coef <- tidy(modelo_vivienda1) %>%
  mutate(
    term = dplyr::recode(
      term,
      `(Intercept)` = "Intercepto",
      areaconst = "Área construida",
      estrato = "Estrato",
      habitaciones = "Habitaciones",
      parqueaderos = "Parqueaderos",
      banios = "Baños"
    )
  )

tabla_coef %>%
  gt() %>%
  tab_header(
    title = "Tabla 7. Coeficientes estimados del modelo de regresión lineal múltiple"
  ) %>%
  cols_label(
    term = "Variable",
    estimate = "Coeficiente",
    std.error = "Error estándar",
    statistic = "t",
    p.value = "Valor p"
  ) %>%
  fmt_number(
    columns = c(estimate, std.error, statistic, p.value),
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 7. Coeficientes estimados del modelo de regresión lineal múltiple
Variable Coeficiente Error estándar t Valor p
Intercepto −123.9851 80.9906 −1.5309 0.1265
Área construida 0.9491 0.0567 16.7508 0.0000
Estrato 43.9725 17.1911 2.5579 0.0109
Habitaciones 13.3024 5.9079 2.2516 0.0249
Parqueaderos −0.7712 5.5599 −0.1387 0.8897
Baños 21.2006 7.3507 2.8841 0.0041
resumen_modelo <- glance(modelo_vivienda1) %>%
  select(r.squared, adj.r.squared, sigma, statistic, p.value) %>%
  rename(
    `R²` = r.squared,
    `R² ajustado` = adj.r.squared,
    `Error estándar residual` = sigma,
    `Estadístico F` = statistic,
    `Valor p del modelo` = p.value
  )

resumen_modelo %>%
  gt() %>%
  tab_header(
    title = "Tabla 8. Indicadores globales de ajuste del modelo"
  ) %>%
  fmt_number(
    columns = everything(),
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 8. Indicadores globales de ajuste del modelo
R² ajustado Error estándar residual Estadístico F Valor p del modelo
0.5259 0.5204 167.6722 94.5196 0.0000

Las Tablas 7 y 8 presentan los resultados de la estimación del modelo de regresión lineal múltiple para explicar el precio de la vivienda.

Dentro de la Tabla 7 los coeficientes estimados muestran que variables como área construida, estrato, número de habitaciones y número de baños presentan efectos positivos y estadísticamente significativos sobre el precio:

  • El coeficiente del área construida (0.949) indica que, manteniendo constantes las demás variables, cada metro cuadrado adicional incrementa el precio de la vivienda en aproximadamente 0.95 millones de pesos.
  • Un aumento de un nivel en el estrato socioeconómico incrementa el precio en aproximadamente 44 millones de pesos (Coeficiente de 43.97).
  • Cada habitación adicional aumenta el precio en cerca de 13 millones (Coeficiente de 13.3).
  • Cada baño adicional aumenta el precio en aproximadamente 21 millones de pesos (21.2).

Estos resultados son consistentes con la lógica del mercado inmobiliario, ya que viviendas más grandes, ubicadas en estratos más altos y con mayor número de espacios tienden a alcanzar precios más elevados. En contraste, la variable parqueaderos no resulta estadísticamente significativa, lo que sugiere que dentro de este modelo su efecto sobre el precio no es concluyente.

Ahora, a nivel global, la Tabla 8 muestra que el modelo presenta un R² de 0.5259 (R² ajustado de 0.5204), lo que implica que aproximadamente el 52 % de la variabilidad del precio de la vivienda es explicada por las variables incluidas. Asimismo, el estadístico F es significativo (p < 0.001), lo que confirma que el modelo en su conjunto posee capacidad explicativa para el precio de las viviendas.

Opción de Mejora: Aunque el ajuste es moderado, podría mejorarse incorporando variables adicionales relacionadas con características más detalladas de ubicación, calidad de construcción o antigüedad del inmueble, por ejemplo.

1.4 Validación de supuestos

Con el fin de evaluar la validez del modelo estimado, se realizará la verificación de los supuestos clásicos de la regresión lineal. En particular, se analizaron los supuestos de linealidad y homocedasticidad, normalidad de los residuos, independencia de los errores y ausencia de multicolinealidad entre las variables explicativas.

1.4.1 Linealidad y homocedasticidad (residuos vs valores ajustados)

# ============================
# SUPUESTO 1: LINEALIDAD Y HOMOCEDASTICIDAD
# ============================

residuos <- resid(modelo_vivienda1)
valores_ajustados <- fitted(modelo_vivienda1)

data.frame(valores_ajustados, residuos) %>%
  ggplot(aes(x = valores_ajustados, y = residuos)) +
  geom_point(color = "#6B2EBF", alpha = 0.6) +
  geom_hline(yintercept = 0, color = "#222222") +
  labs(
    x = "Valores ajustados",
    y = "Residuos"
  ) +
  theme_minimal()

Figura 5. Residuos vs valores ajustados.

La Figura 5 presenta el gráfico de residuos frente a los valores ajustados del modelo de regresión. En general, los residuos se distribuyen alrededor de la línea horizontal en cero sin mostrar un patrón sistemático claro, lo que sugiere que el supuesto de linealidad es razonablemente adecuado. No obstante, se observa una mayor dispersión de los residuos para valores ajustados más altos, lo que podría indicar cierta heterocedasticidad en el modelo. Asimismo, la presencia de algunos residuos extremos demuestra, como ya ha sido anotado, la existencia de observaciones atípicas que pueden influir en el ajuste del modelo.

1.4.2 Normalidad de los residuos

# ============================
# SUPUESTO 2: NORMALIDAD DE LOS RESIDUOS
# ============================

data.frame(residuos) %>%
  ggplot(aes(sample = residuos)) +
  stat_qq(color = "#6B2EBF") +
  stat_qq_line(color = "#222222") +
  labs(
    x = "Cuantiles teóricos normales",
    y = "Cuantiles muestrales de los residuos"
  ) +
  theme_minimal()

Figura 6. Residuos vs valores ajustados.

La Figura 6 presenta el QQ-plot de los residuos del modelo de regresión, el cual permite evaluar el supuesto de normalidad.

En general, los puntos se alinean de manera cercana a la recta teórica en la parte central de la distribución, lo que sugiere un comportamiento aproximadamente normal de los residuos. No obstante, se observan desviaciones en los extremos, particularmente en la cola superior, lo que indica - como se ha recalcado varias veces - la presencia de algunos valores atípicos o colas más pesadas que las esperadas bajo normalidad estricta. No obstante, se puede decir que el supuesto de normalidad se cumple de forma razonable, aunque con ligeras desviaciones en los extremos. Pequeñas desviaciones en los extremos son comunes en datos reales y no necesariamente invalidan el modelo.

1.4.3 Independencia de los residuos

# ============================
# SUPUESTO 3: INDEPENDENCIA DE LOS ERRORES
# ============================

library(lmtest)

dw <- dwtest(modelo_vivienda1)

tabla_dw <- data.frame(
  Estadistico = unname(dw$statistic),
  p_valor = dw$p.value
)

tabla_dw %>%
  gt() %>%
  tab_header(
    title = "Tabla 9. Prueba de Durbin-Watson para independencia de los errores"
  ) %>%
  cols_label(
    Estadistico = "Estadístico DW",
    p_valor = "p-valor"
  ) %>%
  fmt_number(
    columns = everything(),
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 9. Prueba de Durbin-Watson para independencia de los errores
Estadístico DW p-valor
1.7630 0.0059

La Tabla 9 presenta los resultados de la prueba de Durbin-Watson, utilizada para evaluar la independencia de los residuos del modelo. El estadístico obtenido es DW = 1.763, valor cercano a 2, lo que sugiere que no existe una autocorrelación fuerte entre los errores. No obstante, el p-valor indica cierta evidencia de autocorrelación positiva. En estos contextos de análisis de datos inmobiliarios —donde las observaciones corresponden a viviendas distintas— este resultado suele tener un impacto limitado sobre la interpretación general del modelo. En caso de requerirse un análisis más riguroso, podrían explorarse modelos que incorporen efectos espaciales o variables adicionales de localización.

1.4.4 Multicolinealidad entre predictores

# ============================
# SUPUESTO 4: MULTICOLINEALIDAD
# ============================

tabla_vif <- data.frame(
  Variable = names(car::vif(modelo_vivienda1)),
  VIF = as.numeric(car::vif(modelo_vivienda1))
) %>%
  mutate(
    Variable = dplyr::recode(
      Variable,
      areaconst = "Área construida",
      estrato = "Estrato",
      habitaciones = "Habitaciones",
      parqueaderos = "Parqueaderos",
      banios = "Baños"
    ),
    Diagnóstico = case_when(
      VIF < 5 ~ "Sin evidencia de multicolinealidad",
      VIF >= 5 & VIF < 10 ~ "Multicolinealidad moderada",
      VIF >= 10 ~ "Multicolinealidad severa"
    )
  )

tabla_vif %>%
  gt() %>%
  tab_header(
    title = "Tabla 10. Factor de inflación de la varianza (VIF) de los predictores"
  ) %>%
  cols_label(
    Variable = "Variable",
    VIF = "VIF",
    Diagnóstico = "Diagnóstico"
  ) %>%
  fmt_number(
    columns = VIF,
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 10. Factor de inflación de la varianza (VIF) de los predictores
Variable VIF Diagnóstico
Área construida 1.2050 Sin evidencia de multicolinealidad
Estrato 1.0617 Sin evidencia de multicolinealidad
Habitaciones 1.5845 Sin evidencia de multicolinealidad
Parqueaderos 1.1479 Sin evidencia de multicolinealidad
Baños 1.5351 Sin evidencia de multicolinealidad

La Tabla 10 muestra resultados del factor de inflación de la varianza (VIF) que indican que todas las variables explicativas presentan valores cercanos a 1 y muy por debajo del umbral crítico de 5. Esto sugiere que no existe evidencia de multicolinealidad significativa entre los predictores incluidos en el modelo. En consecuencia, las estimaciones de los coeficientes pueden considerarse estables y confiables desde el punto de vista de la colinealidad entre variables explicativas.

Finalmente, esta evaluación de los supuestos del modelo indica que:

  • La linealidad se cumple de forma razonable, aunque se observa cierta heterocedasticidad leve en valores ajustados altos y algunos residuos extremos.
  • El QQ-plot sugiere una aproximación aceptable a la normalidad, con desviaciones moderadas en las colas.
  • La prueba de Durbin-Watson (DW = 1.763) no evidencia una autocorrelación fuerte entre los errores.
  • Los valores VIF cercanos a 1 confirman la ausencia de multicolinealidad relevante entre los predictores.

De este modo, y en en general, el modelo cumple de manera razonable con los supuestos de la regresión lineal, por lo que sus resultados pueden considerarse interpretables.

No obstante, en aras del mejor rendimiento del modelo se podrían explorar transformación de variables, métodos robustos frente a heterocedasticidad o la incorporación de variables adicionales relacionadas con la localización y características del inmueble.

1.5 Predicción del precio de la vivienda

Con el fin de estimar el precio esperado de la vivienda solicitada por la empresa, se utiliza el modelo de regresión lineal múltiple estimado previamente. La vivienda corresponde a una casa ubicada en la zona norte, con 200 m² de área construida, 4 habitaciones, 2 baños y 1 parqueadero. Dado que el estrato indicado puede corresponder a estrato 4 o 5**, se realizan predicciones para ambos escenarios con el fin de evaluar el rango posible del precio estimado por el modelo.

# ============================
# 1.5 PREDICCIÓN DEL PRECIO
# ============================

vivienda_pred <- data.frame(
  areaconst = 200,
  estrato = c(4, 5),
  habitaciones = 4,
  parqueaderos = 1,
  banios = 2
)

predicciones <- predict(modelo_vivienda1, newdata = vivienda_pred)

tabla_prediccion <- data.frame(
  Estrato = vivienda_pred$estrato,
  precio_estimado = predicciones
)

tabla_prediccion %>%
  gt() %>%
  tab_header(
    title = "Tabla 11. Predicción del precio de la vivienda 1"
  ) %>%
  cols_label(
    Estrato = "Estrato",
    precio_estimado = "Precio estimado (millones)"
  ) %>%
  fmt_number(
    columns = precio_estimado,
    decimals = 2
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 11. Predicción del precio de la vivienda 1
Estrato Precio estimado (millones)
4 336.57
5 380.54

La Tabla 11 presenta la predicción del precio de una vivienda con características constantes de área construida, número de habitaciones, parqueaderos y baños, variando únicamente el estrato socioeconómico. Los resultados muestran que una vivienda ubicada en estrato 4 tendría un precio estimado de aproximadamente 336.57 millones, mientras que en estrato 5 el valor esperado aumenta a 380.54 millones. Esta diferencia refleja el efecto positivo del estrato sobre el precio de la vivienda, consistente con lo estimado en el modelo de regresión.

1.6 Ofertas.

Con base en las predicciones obtenidas mediante el modelo de regresión, se identificarán ofertas potenciales de viviendas comparables que podrían responder a la solicitud realizada por la empresa. Para ello, se considerarán viviendas ubicadas en la zona norte, con características similares en área construida, número de habitaciones, baños y parqueaderos.

Adicionalmente, se tendrá en cuenta que la empresa dispone de un crédito preaprobado máximo de 350 millones de pesos, por lo que únicamente se seleccionarán inmuebles cuyo precio estimado o registrado se encuentre dentro de este rango.

# ============================
# IDENTIFICACIÓN DE OFERTAS POTENCIALES
# ============================

ofertas_potenciales <- vivienda1_base %>%
  filter(
    areaconst >= 150 & areaconst <= 250,
    habitaciones >= 3 & habitaciones <= 5,
    banios >= 2,
    parqueaderos >= 1,
    preciom <= 350
  ) %>%
  select(preciom, areaconst, habitaciones, banios, parqueaderos, latitud, longitud) %>%
  head(5)

ofertas_potenciales
## # A tibble: 5 × 7
##   preciom areaconst habitaciones banios parqueaderos latitud longitud
##     <dbl>     <dbl>        <dbl>  <dbl>        <dbl>   <dbl>    <dbl>
## 1     350       200            4      3            3    3.49    -76.5
## 2     245       165            3      2            2    3.49    -76.5
## 3     335       202            5      4            1    3.48    -76.5
## 4     335       220            3      3            1    3.49    -76.5
## 5     340       250            4      4            2    3.46    -76.5
ofertas_potenciales %>%
  gt() %>%
  tab_header(
    title = "Tabla 12. Ofertas potenciales de viviendas comparables"
  ) %>%
  cols_label(
    preciom = "Precio (millones)",
    areaconst = "Área construida",
    habitaciones = "Habitaciones",
    banios = "Baños",
    parqueaderos = "Parqueaderos",
    latitud = "Latitud",
    longitud = "Longitud"
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 12. Ofertas potenciales de viviendas comparables
Precio (millones) Área construida Habitaciones Baños Parqueaderos Latitud Longitud
350 200 4 3 3 3.48503 -76.53010
245 165 3 2 2 3.48532 -76.53042
335 202 5 4 1 3.48399 -76.53044
335 220 3 3 1 3.48810 -76.53088
340 250 4 4 2 3.46500 -76.53300

La Tabla 12 presenta un conjunto de ofertas potenciales de viviendas comparables que cumplen con características similares a la vivienda analizada en términos de área construida, número de habitaciones, baños y parqueaderos, además de ubicarse dentro del rango de precio considerado.

Los resultados muestran propiedades con precios entre 245 y 350 millones de pesos, lo que permite identificar alternativas de mercado dentro del presupuesto establecido.

Las coordenadas geográficas fueron incluidas para facilitar la posterior visualización espacial de estas opciones y su comparación con la vivienda de referencia.

# ============================
# MAPA DE OFERTAS
# ============================

library(leaflet)

leaflet(ofertas_potenciales) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 6,
    color = "#6B2EBF",
    popup = ~paste("Precio:", preciom, "millones")
  )

Figura 7. Mapa de Ofertas Vivienda 1.

El mapa anterior de la Figura 7 presenta la localización de las cinco ofertas potenciales de viviendas comparables ubicadas en la zona norte de la ciudad.

Estas viviendas presentan características estructurales similares a la solicitud de la empresa y se encuentran dentro del límite del crédito preaprobado de 350 millones de pesos, por lo que representan alternativas viables para la negociación. En consecuencia, estas propiedades pueden servir como referencia de mercado para formular una oferta competitiva por la vivienda analizada, permitiendo a la empresa comparar ubicación, características físicas y nivel de precios dentro del mismo sector urbano.

2 PREDICCIÓN PRECIO VIVIENDA 2 (APARTAMENTO)

2.1 Filtro de la Base de Datos

# ============================
# 2.1 FILTRO BASE DE DATOS - VIVIENDA 2
# ============================

vivienda2_base <- vivienda %>%
  filter(
    tipo == "Apartamento",
    zona == "Zona Sur",
    estrato %in% c(5, 6)
  )

# Verificación del filtro
data.frame(
  Registros = nrow(vivienda2_base),
  Variables = ncol(vivienda2_base)
) %>%
  gt() %>%
  tab_header(
    title = "Tabla 13. Dimensión de la base filtrada para la Vivienda 2"
  )
Tabla 13. Dimensión de la base filtrada para la Vivienda 2
Registros Variables
1495 13

Para analizar el segundo caso, se realizó un filtro de la base de datos conservando únicamente apartamentos ubicados en la zona sur y pertenecientes a los estratos 5 o 6, de acuerdo con las características definidas. La Tabla 13 resume la dimensión de la base resultante, la cual constituye el subconjunto de observaciones comparables que se utilizará en el análisis posterior.

# ============================
# PRIMEROS REGISTROS BASE 2
# ============================

vivienda2_base %>%
  slice(1:3) %>%
  gt() %>%
  tab_header(
    title = "Tabla 14. Primeros tres registros de la base filtrada (Base 2)"
  )
Tabla 14. Primeros tres registros de la base filtrada (Base 2)
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
8199 Zona Sur NA 6 875 194 2 5 3 Apartamento aguacatal -76.557 3.459
8102 Zona Sur 05 5 344 107 2 2 3 Apartamento altos de guadalupe -76.555 3.410
7073 Zona Sur 02 6 910 182 2 4 3 Apartamento arboleda -76.547 3.449

La Tabla 14 presenta los tres primeros registros de la base filtrada correspondiente a apartamentos ubicados en la zona sur. Esta visualización permite verificar la estructura de los datos y confirmar que las observaciones seleccionadas corresponden al segmento de mercado relevante para el análisis de la segunda vivienda.

# ============================
# VERIFICACIÓN DEL FILTRO BASE 2
# ============================

vivienda2_base %>%
  count(tipo, zona, estrato) %>%
  gt() %>%
  tab_header(
    title = "Tabla 15. Verificación del filtro aplicado a la base de datos (Base 2)"
  )
Tabla 15. Verificación del filtro aplicado a la base de datos (Base 2)
tipo zona estrato n
Apartamento Zona Sur 5 1033
Apartamento Zona Sur 6 462

La Tabla 15 confirma que la base filtrada contiene únicamente apartamentos ubicados en la zona sur y pertenecientes a los estratos 5 o 6, lo que evidencia que el proceso de selección se realizó correctamente y que las observaciones incluidas corresponden al mercado relevante para el análisis de la segunda vivienda.

# ============================
# MAPA DE LAS VIVIENDAS BASE 2
# ============================

leaflet(vivienda2_base) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 4,
    color = "#6B2EBF",
    stroke = FALSE,
    fillOpacity = 0.7
  )

Figura 8. Localización geográfica de apartamentos en zona sur.

La anterior Figura 8 muestra la distribución geográfica de los apartamentos incluidos en la base filtrada. En general, los puntos se concentran en sectores coherentes con la zona sur de la ciudad, lo que sugiere consistencia entre la clasificación espacial de la variable zona y las coordenadas geográficas registradas. No obstante, algunos puntos pueden aparecer entre otras zonas, situación que puede explicarse por imprecisiones en la georreferenciación o por la delimitación administrativa de ciertos sectores urbanos.

2.2 Análisis Exploratorio de datos

2.2.1 Estadísticos Descriptivos

# ============================
# 2.2.1 RESUMEN ESTADÍSTICO
# ============================

tabla_resumen2 <- vivienda2_base %>%
  select(preciom, areaconst, habitaciones, banios, parqueaderos) %>%
  summarise(
    across(
      everything(),
      list(
        Min = ~min(., na.rm = TRUE),
        Q1 = ~quantile(., 0.25, na.rm = TRUE),
        Mediana = ~median(., na.rm = TRUE),
        Media = ~mean(., na.rm = TRUE),
        SD = ~sd(., na.rm = TRUE),
        Q3 = ~quantile(., 0.75, na.rm = TRUE),
        RIQ = ~IQR(., na.rm = TRUE),
        Max = ~max(., na.rm = TRUE)
      )
    )
  ) %>%
  pivot_longer(
    everything(),
    names_to = c("Variable", "Estadistico"),
    names_sep = "_"
  ) %>%
  pivot_wider(
    names_from = Estadistico,
    values_from = value
  ) %>%
  mutate(
    Variable = dplyr::recode(
      Variable,
      preciom = "Precio",
      areaconst = "Área construida",
      habitaciones = "Habitaciones",
      banios = "Baños",
      parqueaderos = "Parqueaderos"
    )
  )

tabla_resumen2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 16. Estadísticos descriptivos de las variables numéricas (Base 2)"
  ) %>%
  cols_label(
    Variable = "Variable",
    Min = "Mínimo",
    Q1 = "Q1",
    Mediana = "Mediana",
    Media = "Media",
    SD = "Desv. Est.",
    Q3 = "Q3",
    RIQ = "RIQ",
    Max = "Máximo"
  ) %>%
  fmt_number(
    columns = -Variable,
    decimals = 2
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 16. Estadísticos descriptivos de las variables numéricas (Base 2)
Variable Mínimo Q1 Mediana Media Desv. Est. Q3 RIQ Máximo
Precio 93.00 250.00 310.00 386.66 216.54 450.00 200.00 1,750.00
Área construida 43.00 84.00 102.00 117.05 60.61 133.00 49.00 932.00
Habitaciones 0.00 3.00 3.00 3.09 0.63 3.00 0.00 6.00
Baños 0.00 2.00 3.00 2.87 1.00 3.00 1.00 8.00
Parqueaderos 1.00 1.00 2.00 1.61 0.72 2.00 1.00 10.00

La Tabla 16 presenta los estadísticos descriptivos de las variables numéricas de la segunda base de datos para el segundo caso de estudio.

Se observa que el precio de las viviendas presenta una alta variabilidad, con valores que oscilan entre 93 y 1,750 millones, lo que refleja una amplia heterogeneidad en el mercado inmobiliario analizado. En contraste, variables como habitaciones, baños y parqueaderos muestran menor dispersión, concentrándose alrededor de sus valores centrales. Asimismo, el área construida presenta una media de 117 m², lo que sugiere que la mayoría de las viviendas analizadas corresponden a inmuebles de tamaño medio.

2.2.2 Datos Faltantes

# ============================
# 2.2.2 DATOS FALTANTES
# ============================

na_vivienda2 <- vivienda2_base %>%
  summarise(
    across(
      everything(),
      ~sum(is.na(.))
    )
  ) %>%
  pivot_longer(
    everything(),
    names_to = "Variable",
    values_to = "NA"
  )

na_vivienda2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 17. Cantidad de datos faltantes por variable (Base 2)"
  )
Tabla 17. Cantidad de datos faltantes por variable (Base 2)
Variable NA
id 0
zona 0
piso 338
estrato 0
preciom 0
areaconst 0
parqueaderos 56
banios 0
habitaciones 0
tipo 0
barrio 0
longitud 0
latitud 0

La Tabla 17 muestra la cantidad de datos faltantes en la segunda base de datos analizada. Se observa que la mayoría de las variables no presentan valores ausentes, lo que indica una adecuada calidad de la información. No obstante, las variables piso y parqueaderos registran 338 y 56 datos faltantes, respectivamente, por lo que probablemente requieran tratamiento previo al modelado.

2.2.3 Datos atípicos

# ============================
# BOXPLOTS DE VARIABLES
# ============================

vivienda2_base %>%
  select(preciom, areaconst, habitaciones, banios, parqueaderos) %>%
  pivot_longer(
    cols = everything(),
    names_to = "Variable",
    values_to = "Valor"
  ) %>%
  mutate(
    Variable = dplyr::recode(
      Variable,
      preciom = "Precio",
      areaconst = "Área construida",
      habitaciones = "Habitaciones",
      banios = "Baños",
      parqueaderos = "Parqueaderos"
    )
  ) %>%
  ggplot(aes(x = "", y = Valor)) +
  geom_boxplot(fill = "#6B2EBF", alpha = 0.6) +
  facet_wrap(~Variable, scales = "free") +
  labs(
    x = "",
    y = "Valor"
  ) +
  theme_minimal()

Figura 9. Distribución y detección de valores atípicos en variables estructurales de los apartamentos.

La Figura 9 muestra la distribución de las principales variables estructurales de los apartamentos y permite identificar la presencia de valores atípicos.

Se observa que el precio y el área construida presentan la mayor dispersión, con varios valores extremos superiores que reflejan propiedades de mayor tamaño o valor dentro del mercado. En contraste, variables como habitaciones, baños y parqueaderos presentan una distribución más concentrada alrededor de sus valores centrales, lo que sugiere menor variabilidad en estas características. No obstante, también se identifican algunos valores atípicos en estas variables, particularmente en parqueaderos y baños.

Estos resultados permiten evidenciar cierta heterogeneidad en las características físicas de los apartamentos analizados.

2.2.4 Imputación de datos faltantes

# ============================
# 2.2.4 IMPUTACIÓN DE DATOS FALTANTES
# ============================

vivienda2_base <- vivienda2_base %>%
  mutate(
    parqueaderos = ifelse(is.na(parqueaderos), 0, parqueaderos)
  )

# ============================
# VERIFICACIÓN DE IMPUTACIÓN
# ============================

verificacion_na2 <- vivienda2_base %>%
  summarise(
    piso = sum(is.na(piso)),
    parqueaderos = sum(is.na(parqueaderos))
  ) %>%
  pivot_longer(
    everything(),
    names_to = "Variable",
    values_to = "faltantes"
  )

verificacion_na2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 20. Verificación de datos faltantes tras imputación (Base 2)"
  ) %>%
  cols_label(
    Variable = "Variable",
    faltantes = "Datos faltantes (NA)"
  )
Tabla 20. Verificación de datos faltantes tras imputación (Base 2)
Variable Datos faltantes (NA)
piso 338
parqueaderos 0

La Tabla 20 muestra la verificación de datos faltantes después del proceso de imputación aplicado a la base de datos. Se observa que la variable parqueaderos ya no presenta valores ausentes, lo que confirma que la imputación fue realizada correctamente.

Por su parte, la variable piso mantiene 338 datos faltantes, ya que no fue imputada; aunque podría resultar útil en el caso de apartamentos, esta variable no hace parte de las características solicitadas por la empresa para el análisis, por lo que no se estimó pertinente imputarla.

2.2.5 Tratamiento de datos atípicos

La Figura 9 evidencia la presencia de valores atípicos en algunas de las variables numéricas consideradas en el análisis, particularmente en el precio y en el área construida. Estas observaciones se identifican mediante el criterio del rango intercuartílico (1.5 × RIQ) y su existencia también puede inferirse a partir de la dispersión observada en los estadísticos descriptivos presentados en la Tabla 16.

No obstante, en el contexto del mercado de apartamentos, la presencia de valores extremos suele reflejar la heterogeneidad propia del mercado inmobiliario, donde existen inmuebles con características significativamente superiores en términos de tamaño, localización o calidad constructiva. Por esta razón, estos valores no se interpretan necesariamente como errores de registro.

En consecuencia, los valores atípicos se mantendrán dentro del conjunto de datos, permitiendo que el análisis capture de manera más realista la variabilidad presente en los precios de los apartamentos y evitando introducir posibles sesgos derivados de la eliminación arbitraria de observaciones.

2.2.6 Correlación entre variables numéricas

# ============================
# 2.2.6 MATRIZ DE CORRELACIÓN
# ============================

cor_base2 <- vivienda2_base %>%
  select(preciom, areaconst, estrato, habitaciones, parqueaderos, banios)

cor_matrix2 <- cor(cor_base2, use = "complete.obs")

plot_ly(
  x = colnames(cor_matrix2),
  y = colnames(cor_matrix2),
  z = cor_matrix2,
  type = "heatmap",
  colors = colorRamp(c("#6B2EBF", "white", "#F7B26A")),
  text = round(cor_matrix2, 2),
  texttemplate = "%{text}",
  textfont = list(color = "black", size = 12),
  hovertemplate = paste(
    "<b>Variable X:</b> %{x}<br>",
    "<b>Variable Y:</b> %{y}<br>",
    "<b>Correlación:</b> %{z:.2f}<extra></extra>"
  )
) %>%
  layout(
    xaxis = list(title = ""),
    yaxis = list(title = "", autorange = "reversed")
  )

Figura 10. Matriz de correlación interactiva entre variables numéricas para vivienda 2 (Apartamento).

La Figura 10 ilustra la matriz de correlación entre las principales variables numéricas de las viviendas tipo apartamento. Se puede observar que el precio presenta una correlación positiva moderada con el área construida (0.70), el número de baños (0.67), los parqueaderos (0.66) y el estrato también muestra una relación positiva con el precio (0.64), , indicando que viviendas ubicadas en estratos más altos tienden a presentar mayores precios.Todo lo anterior sugiere que estas características influyen de manera importante en el valor del inmueble.

En contraste, la variable habitaciones presenta una correlación más débil con el precio (0.28), lo que sugiere un menor peso relativo en la determinación del valor de los apartamentos analizados.

2.2.7 Distribución del precio de los apartamentos en zona sur

# ============================
# 2.2.7 DISTRIBUCIÓN DEL PRECIO EN ZONA SUR
# ============================

ggplot(vivienda2_base, aes(x = zona, y = preciom)) +
  geom_boxplot(fill = "#6B2EBF", alpha = 0.8) +
  labs(
    x = "Zona",
    y = "Precio"
  ) +
  theme_minimal()

Figura 11. Distribución del precio de los apartamentos en zona sur

El boxplot de la Figura 11 permite describir la distribución del precio de los apartamentos dentro del subconjunto correspondiente a la zona sur.

La mediana del precio se ubica en un nivel intermedio del rango observado, mientras que el 50 % central de las observaciones se concentra en una franja relativamente acotada, lo que refleja cierta estabilidad en los valores típicos del mercado. No obstante, se identifican varios valores atípicos superiores, lo que evidencia la presencia de apartamentos con precios considerablemente más altos y sugiere heterogeneidad en este segmento inmobiliario.

Nota: Dado que el análisis se restringe únicamente a apartamentos ubicados en la zona sur, la variable zona no presenta variabilidad dentro del subconjunto analizado, por lo que no es posible realizar comparaciones entre zonas en este caso.

2.3 Modelo de Regresión Lineal Múltiple

Con el fin de explicar el precio de los apartamentos en función de sus características estructurales, se estimará un modelo de regresión lineal múltiple utilizando como variables explicativas el área construida, el estrato socioeconómico, el número de habitaciones, el número de parqueaderos y el número de baños. Estas variables son las solicitadas explícitamente por la empresa como factores definitorios dentro del mercado inmobiliario para la selección d ela segunda vivienda: Apartamento.

# ============================
# 2.3.1 ESTIMACIÓN DEL MODELO
# ============================

modelo_vivienda2 <- lm(
  preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
  data = vivienda2_base
)

summary(modelo_vivienda2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivienda2_base)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1076.80   -54.33     0.27    42.53   891.00 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -719.43416   39.65687 -18.141  < 2e-16 ***
## areaconst       1.30175    0.06286  20.708  < 2e-16 ***
## estrato       146.24212    7.49639  19.508  < 2e-16 ***
## habitaciones  -17.55046    5.52224  -3.178  0.00151 ** 
## parqueaderos   62.91206    4.80322  13.098  < 2e-16 ***
## banios         46.84640    4.37753  10.702  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 111.8 on 1489 degrees of freedom
## Multiple R-squared:  0.7342, Adjusted R-squared:  0.7333 
## F-statistic: 822.6 on 5 and 1489 DF,  p-value: < 2.2e-16
# ============================
# COEFICIENTES DEL MODELO
# ============================

tabla_coef2 <- broom::tidy(modelo_vivienda2)

tabla_coef2 %>%
  mutate(
    term = dplyr::recode(
      term,
      `(Intercept)` = "Intercepto",
      areaconst = "Área construida",
      estrato = "Estrato",
      habitaciones = "Habitaciones",
      parqueaderos = "Parqueaderos",
      banios = "Baños"
    )
  ) %>%
  gt() %>%
  tab_header(
    title = "Tabla 21. Coeficientes estimados del modelo de regresión lineal múltiple"
  ) %>%
  cols_label(
    term = "Variable",
    estimate = "Coeficiente",
    std.error = "Error estándar",
    statistic = "t",
    p.value = "Valor p"
  ) %>%
  fmt_number(
    columns = c(estimate, std.error, statistic, p.value),
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 21. Coeficientes estimados del modelo de regresión lineal múltiple
Variable Coeficiente Error estándar t Valor p
Intercepto −719.4342 39.6569 −18.1415 0.0000
Área construida 1.3017 0.0629 20.7084 0.0000
Estrato 146.2421 7.4964 19.5083 0.0000
Habitaciones −17.5505 5.5222 −3.1781 0.0015
Parqueaderos 62.9121 4.8032 13.0979 0.0000
Baños 46.8464 4.3775 10.7015 0.0000

En la Tabla 21 se presentan los coeficientes estimados del modelo de regresión lineal múltiple para explicar el precio de los apartamentos.

Los resultados muestran que el área construida, el estrato, los parqueaderos y los baños tienen un efecto positivo y estadísticamente significativo sobre el precio, indicando que viviendas con mayor tamaño, ubicadas en estratos más altos y con mayores comodidades tienden a tener precios más elevados.

En particular, el estrato presenta uno de los mayores impactos marginales sobre el valor del inmueble. Pero por la otra mano, en la variable habitaciones se observa un coeficiente negativo, lo que sugiere que, manteniendo constantes las demás variables, un mayor número de habitaciones podría asociarse con una menor valoración marginal del precio.

Pero, todos los predictores resultan estadísticamente significativos, lo que respalda la capacidad explicativa del modelo estimado.

# ============================
# INDICADORES DEL MODELO
# ============================

glance_modelo2 <- broom::glance(modelo_vivienda2)

tabla_glance2 <- data.frame(
  `R²` = glance_modelo2$r.squared,
  `R² ajustado` = glance_modelo2$adj.r.squared,
  `Error estándar residual` = glance_modelo2$sigma,
  `Estadístico F` = glance_modelo2$statistic,
  `Valor p del modelo` = glance_modelo2$p.value
)

tabla_glance2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 22. Indicadores globales de ajuste del modelo"
  ) %>%
  fmt_number(
    columns = everything(),
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 22. Indicadores globales de ajuste del modelo
R. R..ajustado Error.estándar.residual Estadístico.F Valor.p.del.modelo
0.7342 0.7333 111.8261 822.5943 0.0000

La Tabla 22 presenta los indicadores globales de ajuste del modelo de regresión estimado. El coeficiente de determinación (R² = 0.7342) indica que aproximadamente el 73 % de la variabilidad del precio de los apartamentos es explicada por las variables incluidas en el modelo. El R² ajustado (0.7333) es muy cercano al R², lo que sugiere que las variables incorporadas aportan información relevante sin introducir sobreajuste.

Asimismo, el estadístico F presenta un valor elevado y altamente significativo (p < 0.001), lo que confirma que el modelo, en su conjunto, posee una capacidad explicativa estadísticamente significativa.

2.4 Validación de supuestos

Con el fin de evaluar la validez del modelo estimado, se realizará la verificación de los supuestos clásicos de la regresión lineal. En particular, se analizan los supuestos de linealidad y homocedasticidad, normalidad de los residuos, independencia de los errores y ausencia de multicolinealidad entre las variables explicativas.

2.4.1 Linealidad y homocedasticidad (residuos vs valores ajustados)

# ============================
# SUPUESTO 1: LINEALIDAD Y HOMOCEDASTICIDAD
# ============================

residuos2 <- resid(modelo_vivienda2)
valores_ajustados2 <- fitted(modelo_vivienda2)

data.frame(valores_ajustados2, residuos2) %>%
  ggplot(aes(x = valores_ajustados2, y = residuos2)) +
  geom_point(color = "#6B2EBF", alpha = 0.6) +
  geom_hline(yintercept = 0, color = "#222222") +
  labs(
    x = "Valores ajustados",
    y = "Residuos"
  ) +
  theme_minimal()

Figura 12. Residuos vs valores ajustados.

La anterior Figura 12 presenta el gráfico de residuos frente a los valores ajustados del modelo.

En general, los residuos se distribuyen alrededor de la línea horizontal en cero sin mostrar un patrón sistemático claro, lo que sugiere que el supuesto de linealidad es razonablemente adecuado. No obstante, puede observarse cierta dispersión en algunos rangos de valores ajustados, lo que podría indicar leves variaciones en la varianza de los errores.

2.4.2 Normalidad de los residuos

# ============================
# SUPUESTO 2: NORMALIDAD DE LOS RESIDUOS
# ============================

data.frame(residuos2) %>%
  ggplot(aes(sample = residuos2)) +
  stat_qq(color = "#6B2EBF") +
  stat_qq_line(color = "#222222") +
  labs(
    x = "Cuantiles teóricos normales",
    y = "Cuantiles muestrales de los residuos"
  ) +
  theme_minimal()

Figura 13. QQ-plot de los residuos del modelo.

La Figura 13 ilustra el QQ-plot de los residuos del modelo y en términos generales, los puntos se alinean de forma cercana a la recta teórica en la zona central de la distribución, lo que sugiere un comportamiento aproximadamente normal. No obstante, se observan pequeñas desviaciones en los extremos, lo cual puede asociarse a la presencia de valores los valores atípicos o colas ligeramente más pesadas que las esperadas bajo normalidad estricta.

2.4.3 Independencia de los residuos

# ============================
# SUPUESTO 3: INDEPENDENCIA DE LOS ERRORES
# ============================

library(lmtest)

dw2 <- dwtest(modelo_vivienda2)

tabla_dw2 <- data.frame(
  Estadistico = unname(dw2$statistic),
  p_valor = dw2$p.value
)

tabla_dw2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 23. Prueba de Durbin-Watson para independencia de los errores"
  ) %>%
  cols_label(
    Estadistico = "Estadístico DW",
    p_valor = "p-valor"
  ) %>%
  fmt_number(
    columns = everything(),
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 23. Prueba de Durbin-Watson para independencia de los errores
Estadístico DW p-valor
1.7230 0.0000

La Tabla 23 presenta los resultados de la prueba de Durbin-Watson, utilizada para evaluar la independencia de los residuos del modelo. Un valor del estadístico cercano a 2 sugiere ausencia de autocorrelación significativa entre los errores. Este resultado (1.72) indica que el supuesto de independencia se cumple de manera razonable para el modelo estimado.

2.4.4 Multicolinealidad entre predictores

# ============================
# SUPUESTO 4: MULTICOLINEALIDAD
# ============================

tabla_vif2 <- data.frame(
  Variable = names(car::vif(modelo_vivienda2)),
  VIF = as.numeric(car::vif(modelo_vivienda2))
) %>%
  mutate(
    Variable = dplyr::recode(
      Variable,
      areaconst = "Área construida",
      estrato = "Estrato",
      habitaciones = "Habitaciones",
      parqueaderos = "Parqueaderos",
      banios = "Baños"
    ),
    Diagnostico = case_when(
      VIF < 5 ~ "Sin evidencia de multicolinealidad",
      VIF >= 5 & VIF < 10 ~ "Multicolinealidad moderada",
      VIF >= 10 ~ "Multicolinealidad severa"
    )
  )

tabla_vif2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 24. Factor de inflación de la varianza (VIF) de los predictores"
  ) %>%
  cols_label(
    Variable = "Variable",
    VIF = "VIF",
    Diagnostico = "Diagnóstico"
  ) %>%
  fmt_number(
    columns = VIF,
    decimals = 4
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 24. Factor de inflación de la varianza (VIF) de los predictores
Variable VIF Diagnóstico
Área construida 1.7341 Sin evidencia de multicolinealidad
Estrato 1.4346 Sin evidencia de multicolinealidad
Habitaciones 1.4671 Sin evidencia de multicolinealidad
Parqueaderos 1.6396 Sin evidencia de multicolinealidad
Baños 2.2879 Sin evidencia de multicolinealidad

La Tabla 24 muestra los valores del factor de inflación de la varianza (VIF) para cada una de las variables explicativas. En general, valores de VIF inferiores a 5 indican ausencia de multicolinealidad significativa entre los predictores. Este resultado sugiere que las variables incluidas en el modelo no presentan relaciones lineales fuertes entre sí y que las estimaciones de los coeficientes pueden considerarse estables.

2.5 Predicción del precio de la vivienda

Una vez estimado y validado el modelo de regresión lineal múltiple, se utiliza para realizar la predicción del precio esperado de la vivienda 2, correspondiente a un apartamento en la zona sur con las características definidas por la empresa. Dado que el estrato puede ser 5 o 6, se calculan dos escenarios de predicción con el fin de obtener un rango razonable del valor esperado del inmueble.

# ============================
# II.2.5 PREDICCIÓN DEL PRECIO
# ============================

vivienda2_pred <- data.frame(
  areaconst = 300,
  estrato = c(5, 6),
  habitaciones = 5,
  parqueaderos = 3,
  banios = 3
)

predicciones2 <- predict(modelo_vivienda2, newdata = vivienda2_pred)

tabla_prediccion2 <- data.frame(
  Estrato = vivienda2_pred$estrato,
  precio_estimado = predicciones2
)

tabla_prediccion2 %>%
  gt() %>%
  tab_header(
    title = "Tabla 24. Predicción del precio de la vivienda 2"
  ) %>%
  cols_label(
    Estrato = "Estrato",
    precio_estimado = "Precio estimado (millones)"
  ) %>%
  fmt_number(
    columns = precio_estimado,
    decimals = 2
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 24. Predicción del precio de la vivienda 2
Estrato Precio estimado (millones)
5 643.82
6 790.07

La Tabla 24 presenta las predicciones del precio de la vivienda 2 obtenidas a partir del modelo de regresión lineal múltiple estimado para apartamentos en la zona sur.

De acuerdo con las características especificadas, el modelo sugiere un rango de precios asociado al estrato considerado. En general, se espera que un apartamento con 300 m² de área construida, 5 habitaciones, 3 baños y 3 parqueaderos presente un valor relativamente alto dentro del mercado analizado, siendo coherente que el precio estimado aumente al pasar de estrato 5 a estrato 6, dada la mayor valorización asociada a sectores socioeconómicos superiores, tal como lo muestra la tabla.

2.6 Ofertas.

Con base en las predicciones obtenidas del modelo y considerando el crédito preaprobado de hasta 850 millones de pesos, se identificarán ofertas potenciales de apartamentos que podrían ajustarse a las características de la vivienda solicitada.

Para ello se seleccionan inmuebles ubicados en la zona sur, con características comparables y con precios dentro del rango de financiación disponible.

# ============================
# 2.6 OFERTAS POTENCIALES
# ============================

ofertas_vivienda2 <- vivienda2_base %>%
  filter(preciom <= 850) %>%
  arrange(desc(preciom)) %>%
  slice(1:5)

ofertas_vivienda2 %>%
  select(preciom, areaconst, habitaciones, banios, parqueaderos, estrato, barrio) %>%
  gt() %>%
  tab_header(
    title = "Tabla 25. Ofertas potenciales de apartamentos (≤ 850 millones)"
  ) %>%
  cols_label(
    preciom = "Precio",
    areaconst = "Área construida",
    habitaciones = "Habitaciones",
    banios = "Baños",
    parqueaderos = "Parqueaderos",
    estrato = "Estrato",
    barrio = "Barrio"
  ) %>%
  fmt_number(
    columns = preciom,
    decimals = 0
  ) %>%
  tab_options(
    heading.align = "center",
    table.font.size = 12
  )
Tabla 25. Ofertas potenciales de apartamentos (≤ 850 millones)
Precio Área construida Habitaciones Baños Parqueaderos Estrato Barrio
850 187.0 4 5 3 6 ciudad jardín
850 168.0 4 5 3 6 ciudad jardín
850 192.0 3 5 2 6 ciudad jardín
850 191.8 4 5 2 6 ciudad jardín
850 186.0 4 5 3 6 ciudad jardín

La Tabla 25 presenta un conjunto de ofertas potenciales de apartamentos que cumplen con el criterio de precio máximo establecido de 850 millones de pesos. Las viviendas identificadas muestran características relativamente homogéneas, con áreas construidas entre aproximadamente 168 y 192 m², 3 a 4 habitaciones, 5 baños y 2 a 3 parqueaderos, ubicadas en estrato 6. Asimismo, todas las ofertas se localizan en el barrio Ciudad Jardín, lo que sugiere una concentración de propiedades comparables dentro de esta zona.

# ============================
# MAPA DE OFERTAS POTENCIALES
# ============================

leaflet(ofertas_vivienda2) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 6,
    color = "#6B2EBF",
    stroke = FALSE,
    fillOpacity = 0.8,
    popup = ~paste(
      "<b>Precio:</b>", preciom, "millones<br>",
      "<b>Área:</b>", areaconst, "m²<br>",
      "<b>Habitaciones:</b>", habitaciones
    )
  )

Figura 14. Ofertas potenciales de apartamentos en zona sur.

La Figura 14 muestra la localización geográfica de cinco ofertas potenciales de apartamentos que cumplen con el criterio de precio máximo asociado al crédito preaprobado de 850 millones de pesos. Estas propiedades se ubican dentro de la zona sur, lo cual las hace comparables con la vivienda solicitada. En general, los inmuebles seleccionados presentan características estructurales similares en términos de área construida y número de habitaciones, lo que los convierte en alternativas viables dentro del mercado analizado. Estas ofertas pueden servir como referencia para la negociación y evaluación de opciones por parte de la empresa interesada en adquirir la vivienda.

3 CONCLUSIÓN

El análisis desarrollado permitió estimar modelos de regresión lineal múltiple para explicar el precio de viviendas a partir de características estructurales como área construida, estrato, número de habitaciones, número de baños y parqueaderos.

Los resultados ayudaron a evidenciar que el tamaño del inmueble y sus atributos constructivos tienen una relación positiva con el precio, lo cual es consistente con la lógica del mercado inmobiliario.

Las predicciones obtenidas permitieron aproximar el valor esperado de las viviendas solicitadas y, a partir de estas estimaciones, identificar ofertas comparables dentro del rango de financiamiento disponible.

Este ejercicio muestra cómo el uso de modelos estadísticos y análisis exploratorio de datos puede apoyar procesos de valoración inmobiliaria y toma de decisiones en el mercado de vivienda y no sólo eso sino también cómo la ciencia de datos puede apoyar la toma de decisiones tan cotidianas como la selección de una vivienda.