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.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.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
# ============================
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 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.
# ============================
# 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.
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.
# ============================
# 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.
# ============================
# 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.
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² | 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:
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.
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.
# ============================
# 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.
# ============================
# 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.
# ============================
# 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.
# ============================
# 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:
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.
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.
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.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.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
# ============================
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.
# ============================
# 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
# ============================
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.
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 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 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.
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.
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.
# ============================
# 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.
# ============================
# 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.
# ============================
# 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.
# ============================
# 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.
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.
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.
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.