UNIVERSIDAD PONTIFICIA JAVERIANA CALI



Maestría en Ciencia de Datos



Actividad 2, Caso C&A: análisis, modelación y recomendación de viviendas




Presentado por:
Ana Isabel Giraldo Alzate

Dainer Orlando Cruz Chate




Modelos Estadísticos para la Toma de Decisiones




2026


1 Resumen ejecutivo

Este informe analiza dos solicitudes de vivienda recibidas por C&A: una casa en la zona norte y un apartamento en la zona sur de Cali. El proceso integra descripción inicial, calidad de datos, imputación KNN condicionada, limpieza, comparación antes-después, mapas, correlaciones, regresión lineal múltiple, validación de supuestos, predicciones y selección de ofertas compatibles con los presupuestos aprobados.

2 Contexto

María fundó C&A después de adquirir experiencia inmobiliaria en Cali y Bogotá. En un escenario de desaceleración de las ventas, una compañía internacional solicitó apoyo para ubicar a dos empleados con sus familias. La vivienda 1 debe ser una casa en el norte, de 200 m², un parqueadero, dos baños, cuatro habitaciones, estrato 4 o 5 y presupuesto máximo de $350 millones. La vivienda 2 debe ser un apartamento en el sur, de 300 m², tres parqueaderos, tres baños, cinco habitaciones, estrato 5 o 6 y presupuesto máximo de $850 millones.

3 Introducción

La valoración inmobiliaria puede abordarse mediante modelos hedónicos, en los que el precio se explica por atributos físicos, socioeconómicos y de localización [1]. En Colombia, el análisis de precios debe considerar la dinámica del crédito y la heterogeneidad territorial [2, 3]. La regresión múltiple permite cuantificar asociaciones parciales, pero requiere validar linealidad, normalidad de residuos, homocedasticidad, independencia y multicolinealidad [4, 5]. Los intervalos de predicción comunican la incertidumbre de una vivienda individual [6]. El trabajo se desarrolla de forma reproducible en R Markdown [7], con gráficos plotly [8], mapas leaflet [9] y datos de paqueteMODELOS [10].

4 Objetivos

4.1 Objetivo general

Estimar y validar modelos de regresión múltiple que permitan predecir precios y recomendar ofertas compatibles con las características y presupuestos de las dos solicitudes.

4.2 Objetivos específicos

  1. Describir la base antes y después de su limpieza.
  2. Evaluar completitud, unicidad, validez y plausibilidad geográfica.
  3. Aplicar KNN solo cuando existan faltantes en variables de modelación.
  4. Explorar asociaciones entre precio y atributos de la vivienda.
  5. Estimar y validar un modelo para cada segmento.
  6. Predecir precios, contrastarlos con el crédito y recomendar cinco ofertas por caso.

5 Metodología

6 Carga y diccionario

data("vivienda", package="paqueteMODELOS")
req <- c("zona","estrato","preciom","areaconst","parqueaderos","banios","habitaciones","tipo","barrio","longitud","latitud")
stopifnot(all(req %in% names(vivienda)))
vivienda0 <- vivienda |>
  mutate(zona=trimws(as.character(zona)), tipo=trimws(as.character(tipo)), barrio=trimws(as.character(barrio)),
         across(c(estrato,preciom,areaconst,parqueaderos,banios,habitaciones,longitud,latitud),as.numeric))
cat("Base original:", nrow(vivienda0), "filas y", ncol(vivienda0), "columnas.")
## Base original: 8322 filas y 13 columnas.
dic <- tibble::tribble(~Variable,~Descripción,~Tipo,~Unidad,
"zona","Sector de ubicación","Categórica nominal","Categoría","piso","Piso ocupado","Ordinal","Piso",
"estrato","Estrato socioeconómico","Ordinal","1 a 6","preciom","Precio ofertado","Continua","Millones COP",
"areaconst","Área construida","Continua","m²","parqueaderos","Número de parqueaderos","Discreta","Conteo",
"banios","Número de baños","Discreta","Conteo","habitaciones","Número de habitaciones","Discreta","Conteo",
"tipo","Tipo de vivienda","Nominal","Casa/Apartamento","barrio","Barrio","Nominal","Nombre",
"longitud","Coordenada longitudinal","Continua","Grados","latitud","Coordenada latitudinal","Continua","Grados")
tabla(dic,"Diccionario de datos")
Diccionario de datos
Variable Descripción Tipo Unidad
zona Sector de ubicación Categórica nominal Categoría
piso Piso ocupado Ordinal Piso
estrato Estrato socioeconómico Ordinal 1 a 6
preciom Precio ofertado Continua Millones COP
areaconst Área construida Continua
parqueaderos Número de parqueaderos Discreta Conteo
banios Número de baños Discreta Conteo
habitaciones Número de habitaciones Discreta Conteo
tipo Tipo de vivienda Nominal Casa/Apartamento
barrio Barrio Nominal Nombre
longitud Coordenada longitudinal Continua Grados
latitud Coordenada latitudinal Continua Grados

7 Análisis descriptivo antes de la limpieza

tipo_var <- function(x) if(is.numeric(x)) "Cuantitativa" else "Categórica"
res_antes <- tibble(Variable=names(vivienda0), Tipo=vapply(vivienda0,tipo_var,character(1)),
  Observaciones=vapply(vivienda0,function(x)sum(!is.na(x)),numeric(1)),
  Faltantes=vapply(vivienda0,function(x)sum(is.na(x)),numeric(1))) |>
  mutate(Pct_faltantes=round(100*Faltantes/nrow(vivienda0),2))
tabla(res_antes,"Variables antes de la limpieza")
Variables antes de la limpieza
Variable Tipo Observaciones Faltantes Pct_faltantes
id Cuantitativa 8319 3 0.04
zona Categórica 8319 3 0.04
piso Categórica 5684 2638 31.70
estrato Cuantitativa 8319 3 0.04
preciom Cuantitativa 8320 2 0.02
areaconst Cuantitativa 8319 3 0.04
parqueaderos Cuantitativa 6717 1605 19.29
banios Cuantitativa 8319 3 0.04
habitaciones Cuantitativa 8319 3 0.04
tipo Categórica 8319 3 0.04
barrio Categórica 8319 3 0.04
longitud Cuantitativa 8319 3 0.04
latitud Cuantitativa 8319 3 0.04
vars_num <- intersect(c("preciom","areaconst","estrato","parqueaderos","banios","habitaciones"),names(vivienda0))
describir <- function(d){bind_rows(lapply(vars_num,function(v){x=d[[v]];tibble(Variable=v,N=sum(!is.na(x)),Media=mean(x,na.rm=TRUE),Mediana=median(x,na.rm=TRUE),DE=sd(x,na.rm=TRUE),Min=min(x,na.rm=TRUE),Q1=quantile(x,.25,na.rm=TRUE),Q3=quantile(x,.75,na.rm=TRUE),Max=max(x,na.rm=TRUE))}))}
tabla(describir(vivienda0),"Estadísticos antes de la limpieza")
Estadísticos antes de la limpieza
Variable N Media Mediana DE Min Q1 Q3 Max
preciom 8320 433.89 330 328.65 58 220 540 1999
areaconst 8319 174.93 123 142.96 30 80 229 1745
estrato 8319 4.63 5 1.03 3 4 5 6
parqueaderos 6717 1.84 2 1.12 1 1 2 10
banios 8319 3.11 3 1.43 0 2 4 10
habitaciones 8319 3.61 3 1.46 0 3 4 10
g <- lapply(seq_along(vars_num),function(i){v<-vars_num[i];p<-ggplot(vivienda0,aes(x=.data[[v]],text=paste(v,.data[[v]])))+geom_histogram(bins=30,fill=pastel[i],color="white",na.rm=TRUE)+theme_minimal()+labs(title=paste("Distribución inicial de",v),x=v,y="Frecuencia");ggplotly(p,tooltip="text")});htmltools::tagList(g)
for(v in intersect(c("zona","tipo","piso"),names(vivienda0))){
 f<-vivienda0|>mutate(Categoria=fct_explicit_na(factor(.data[[v]]),na_level="Sin dato"))|>count(Categoria,sort=TRUE)|>mutate(Porcentaje=round(100*n/sum(n),2))
 print(tabla(f,paste("Frecuencias de",v)))
 p<-ggplot(f,aes(reorder(Categoria,n),n,text=paste0("n: ",n,"<br>",Porcentaje,"%")))+geom_col(fill=pastel[4])+coord_flip()+theme_minimal()+labs(title=paste("Frecuencias de",v),x=NULL,y="Frecuencia");print(ggplotly(p,tooltip="text"))
}
## <table class="table table-striped table-hover table-responsive" style="width: auto !important; margin-left: auto; margin-right: auto;">
## <caption>Frecuencias de zona</caption>
##  <thead>
##   <tr>
##    <th style="text-align:left;"> Categoria </th>
##    <th style="text-align:right;"> n </th>
##    <th style="text-align:right;"> Porcentaje </th>
##   </tr>
##  </thead>
## <tbody>
##   <tr>
##    <td style="text-align:left;"> Zona Sur </td>
##    <td style="text-align:right;"> 4726 </td>
##    <td style="text-align:right;"> 56.79 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Zona Norte </td>
##    <td style="text-align:right;"> 1920 </td>
##    <td style="text-align:right;"> 23.07 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Zona Oeste </td>
##    <td style="text-align:right;"> 1198 </td>
##    <td style="text-align:right;"> 14.40 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Zona Oriente </td>
##    <td style="text-align:right;"> 351 </td>
##    <td style="text-align:right;"> 4.22 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Zona Centro </td>
##    <td style="text-align:right;"> 124 </td>
##    <td style="text-align:right;"> 1.49 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Sin dato </td>
##    <td style="text-align:right;"> 3 </td>
##    <td style="text-align:right;"> 0.04 </td>
##   </tr>
## </tbody>
## </table><table class="table table-striped table-hover table-responsive" style="width: auto !important; margin-left: auto; margin-right: auto;">
## <caption>Frecuencias de tipo</caption>
##  <thead>
##   <tr>
##    <th style="text-align:left;"> Categoria </th>
##    <th style="text-align:right;"> n </th>
##    <th style="text-align:right;"> Porcentaje </th>
##   </tr>
##  </thead>
## <tbody>
##   <tr>
##    <td style="text-align:left;"> Apartamento </td>
##    <td style="text-align:right;"> 5100 </td>
##    <td style="text-align:right;"> 61.28 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Casa </td>
##    <td style="text-align:right;"> 3219 </td>
##    <td style="text-align:right;"> 38.68 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> Sin dato </td>
##    <td style="text-align:right;"> 3 </td>
##    <td style="text-align:right;"> 0.04 </td>
##   </tr>
## </tbody>
## </table><table class="table table-striped table-hover table-responsive" style="width: auto !important; margin-left: auto; margin-right: auto;">
## <caption>Frecuencias de piso</caption>
##  <thead>
##   <tr>
##    <th style="text-align:left;"> Categoria </th>
##    <th style="text-align:right;"> n </th>
##    <th style="text-align:right;"> Porcentaje </th>
##   </tr>
##  </thead>
## <tbody>
##   <tr>
##    <td style="text-align:left;"> Sin dato </td>
##    <td style="text-align:right;"> 2638 </td>
##    <td style="text-align:right;"> 31.70 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 02 </td>
##    <td style="text-align:right;"> 1450 </td>
##    <td style="text-align:right;"> 17.42 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 03 </td>
##    <td style="text-align:right;"> 1097 </td>
##    <td style="text-align:right;"> 13.18 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 01 </td>
##    <td style="text-align:right;"> 860 </td>
##    <td style="text-align:right;"> 10.33 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 04 </td>
##    <td style="text-align:right;"> 607 </td>
##    <td style="text-align:right;"> 7.29 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 05 </td>
##    <td style="text-align:right;"> 567 </td>
##    <td style="text-align:right;"> 6.81 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 06 </td>
##    <td style="text-align:right;"> 245 </td>
##    <td style="text-align:right;"> 2.94 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 08 </td>
##    <td style="text-align:right;"> 211 </td>
##    <td style="text-align:right;"> 2.54 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 07 </td>
##    <td style="text-align:right;"> 204 </td>
##    <td style="text-align:right;"> 2.45 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 09 </td>
##    <td style="text-align:right;"> 146 </td>
##    <td style="text-align:right;"> 1.75 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 10 </td>
##    <td style="text-align:right;"> 130 </td>
##    <td style="text-align:right;"> 1.56 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 11 </td>
##    <td style="text-align:right;"> 84 </td>
##    <td style="text-align:right;"> 1.01 </td>
##   </tr>
##   <tr>
##    <td style="text-align:left;"> 12 </td>
##    <td style="text-align:right;"> 83 </td>
##    <td style="text-align:right;"> 1.00 </td>
##   </tr>
## </tbody>
## </table>

8 Calidad, imputación KNN y limpieza

coord_mala <- with(vivienda0,!is.na(longitud)&!is.na(latitud)&!(between(longitud,-77,-76)&between(latitud,3,4)))
cal_antes <- tibble(Indicador=c("Registros","Duplicados","Celdas faltantes","Precios no positivos","Áreas no positivas","Estratos inválidos","Coordenadas no plausibles"),Antes=c(nrow(vivienda0),sum(duplicated(vivienda0)),sum(is.na(vivienda0)),sum(vivienda0$preciom<=0,na.rm=TRUE),sum(vivienda0$areaconst<=0,na.rm=TRUE),sum(!vivienda0$estrato%in%1:6,na.rm=TRUE),sum(coord_mala)))
tabla(cal_antes,"Calidad antes de la limpieza")
Calidad antes de la limpieza
Indicador Antes
Registros 8322
Duplicados 1
Celdas faltantes 4275
Precios no positivos 0
Áreas no positivas 0
Estratos inválidos 3
Coordenadas no plausibles 0
naniar::gg_miss_var(vivienda0,show_pct=TRUE)+theme_minimal()+labs(title="Faltantes antes de la limpieza")

La imputación se restringe a predictores numéricos del modelo. No se imputan zona, tipo, barrio ni coordenadas, pues ello podría crear categorías o ubicaciones artificiales.

vars_modelo<-c("preciom","areaconst","estrato","parqueaderos","banios","habitaciones")
n_imp<-sum(is.na(vivienda0[vars_modelo]))
if(n_imp>0){set.seed(2026);vivienda_imp<-VIM::kNN(vivienda0,variable=vars_modelo,k=5,imp_var=FALSE);cat("Se imputaron",n_imp,"celdas mediante KNN, k=5.")}else{vivienda_imp<-vivienda0;cat("No fue necesario aplicar KNN.")}
## Se imputaron 1619 celdas mediante KNN, k=5.
vivienda_limpia<-vivienda_imp|>distinct()|>filter(!is.na(zona),!is.na(tipo),preciom>0,areaconst>0,estrato%in%1:6,parqueaderos>=0,banios>0,habitaciones>0)
coord_mala2<-with(vivienda_limpia,!is.na(longitud)&!is.na(latitud)&!(between(longitud,-77,-76)&between(latitud,3,4)))
cal_despues<-tibble(Indicador=cal_antes$Indicador,Despues=c(nrow(vivienda_limpia),sum(duplicated(vivienda_limpia)),sum(is.na(vivienda_limpia)),sum(vivienda_limpia$preciom<=0),sum(vivienda_limpia$areaconst<=0),sum(!vivienda_limpia$estrato%in%1:6),sum(coord_mala2)))
comparacion_calidad<-left_join(cal_antes,cal_despues,by="Indicador")|>mutate(Cambio=Despues-Antes)
tabla(comparacion_calidad,"Calidad antes y después de la limpieza")
Calidad antes y después de la limpieza
Indicador Antes Despues Cambio
Registros 8322 8243 -79
Duplicados 1 0 -1
Celdas faltantes 4275 2593 -1682
Precios no positivos 0 0 0
Áreas no positivas 0 0 0
Estratos inválidos 3 0 -3
Coordenadas no plausibles 0 0 0

9 Análisis descriptivo después de la limpieza

res_despues<-tibble(Variable=names(vivienda_limpia),Tipo=vapply(vivienda_limpia,tipo_var,character(1)),Observaciones=vapply(vivienda_limpia,function(x)sum(!is.na(x)),numeric(1)),Faltantes=vapply(vivienda_limpia,function(x)sum(is.na(x)),numeric(1)))|>mutate(Pct_faltantes=round(100*Faltantes/nrow(vivienda_limpia),2))
tabla(res_despues,"Variables después de la limpieza")
Variables después de la limpieza
Variable Tipo Observaciones Faltantes Pct_faltantes
id Cuantitativa 8243 0 0.00
zona Categórica 8243 0 0.00
piso Categórica 5650 2593 31.46
estrato Cuantitativa 8243 0 0.00
preciom Cuantitativa 8243 0 0.00
areaconst Cuantitativa 8243 0 0.00
parqueaderos Cuantitativa 8243 0 0.00
banios Cuantitativa 8243 0 0.00
habitaciones Cuantitativa 8243 0 0.00
tipo Categórica 8243 0 0.00
barrio Categórica 8243 0 0.00
longitud Cuantitativa 8243 0 0.00
latitud Cuantitativa 8243 0 0.00
tabla(describir(vivienda_limpia),"Estadísticos después de la limpieza")
Estadísticos después de la limpieza
Variable N Media Mediana DE Min Q1 Q3 Max
preciom 8243 433.27 330 328.93 58 220 540 1999
areaconst 8243 174.04 122 142.32 30 80 227 1745
estrato 8243 4.64 5 1.03 3 4 5 6
parqueaderos 8243 1.73 1 1.06 1 1 2 10
banios 8243 3.13 3 1.41 1 2 4 10
habitaciones 8243 3.63 3 1.43 1 3 4 10
comp<-bind_rows(vivienda0|>select(all_of(vars_modelo))|>mutate(Estado="Antes"),vivienda_limpia|>select(all_of(vars_modelo))|>mutate(Estado="Después"))|>pivot_longer(-Estado,names_to="Variable",values_to="Valor")
p<-ggplot(comp,aes(Valor,fill=Estado))+geom_density(alpha=.45,na.rm=TRUE)+facet_wrap(~Variable,scales="free",ncol=2)+scale_fill_manual(values=c("Antes"=pastel[2],"Después"=pastel[3]))+theme_minimal()+labs(title="Distribuciones antes y después",x=NULL);ggplotly(p)

10 Funciones analíticas

filtrar<-function(d,t,z)d|>filter(grepl(paste0("^",t,"$"),tipo,ignore.case=TRUE),grepl(z,zona,ignore.case=TRUE))|>mutate(estrato_f=factor(estrato))
mapa<-function(d,titulo){g<-d|>filter(between(longitud,-77,-76),between(latitud,3,4));leaflet(g)|>addProviderTiles(providers$CartoDB.Positron)|>addCircleMarkers(~longitud,~latitud,radius=4,color="#7CB9E8",fillOpacity=.75,popup=~paste0("<b>",tipo,"</b><br>",barrio,"<br>$",preciom," millones"))|>addControl(titulo,"topright")}
cor_tab<-function(d)bind_rows(lapply(vars_modelo[-1],function(v){z<-cor.test(d[[v]],d$preciom,method="spearman",exact=FALSE);tibble(Variable=v,Rho=unname(z$estimate),p_valor=z$p.value,Decision=ifelse(z$p.value<.05,"Significativa","No significativa"))}))
eda<-function(d,titulo){htmltools::tagList(lapply(seq_along(vars_modelo[-1]),function(i){v<-vars_modelo[-1][i];p<-ggplot(d,aes(.data[[v]],preciom,text=paste0("Barrio: ",barrio,"<br>Precio: ",preciom)))+geom_point(color=pastel[i],alpha=.7)+geom_smooth(method="lm",color="#355C7D",fill=pastel[5])+theme_minimal()+labs(title=paste(titulo,"Precio vs",v),y="Millones COP");ggplotly(p,tooltip="text")}))}
ajustar<-function(d)lm(preciom~areaconst+estrato_f+habitaciones+parqueaderos+banios,data=d)
diagnosticar<-function(m){r<-residuals(m);bp<-bptest(m);dw<-dwtest(m);vf<-car::vif(m);vm<-if(is.matrix(vf))max(vf[,"GVIF"]^(1/(2*vf[,"Df"])))else max(vf);pn<-if(length(r)<=5000)shapiro.test(r)$p.value else nortest::ad.test(r)$p.value;tibble(Supuesto=c("Normalidad","Homocedasticidad","Independencia","Multicolinealidad"),Prueba=c(ifelse(length(r)<=5000,"Shapiro-Wilk","Anderson-Darling"),"Breusch-Pagan","Durbin-Watson","VIF/GVIF"),p_valor=c(pn,bp$p.value,dw$p.value,NA),Resultado=c(ifelse(pn>=.05,"No se rechaza H0","Se rechaza H0"),ifelse(bp$p.value>=.05,"Varianza constante","Heterocedasticidad"),ifelse(dw$p.value>=.05,"Sin evidencia de autocorrelación","Posible autocorrelación"),ifelse(vm<5,"Aceptable","Revisar colinealidad")))}
graf_diag<-function(m){a<-augment(m);p1<-ggplot(a,aes(.fitted,.std.resid))+geom_point(color=pastel[1])+geom_hline(yintercept=0)+geom_smooth(se=FALSE)+theme_minimal()+labs(title="Residuos vs ajustados");p2<-ggplot(a,aes(sample=.std.resid))+stat_qq(color=pastel[4])+stat_qq_line()+theme_minimal()+labs(title="Gráfico Q-Q");p1+p2}
predecir<-function(m,d,area,est,hab,parq,ban){n<-data.frame(areaconst=area,estrato_f=factor(as.character(est),levels=levels(d$estrato_f)),habitaciones=hab,parqueaderos=parq,banios=ban);as.data.frame(predict(m,n,interval="prediction"))|>mutate(Estrato=est,.before=1)}
ofertas<-function(d,pres,area,ests,hab,parq,ban,n=5){x<-d|>filter(preciom<=pres,estrato%in%ests,between(longitud,-77,-76),between(latitud,3,4));x|>mutate(distancia=sqrt(((areaconst-area)/pmax(sd(areaconst),1))^2+((habitaciones-hab)/pmax(sd(habitaciones),1))^2+((parqueaderos-parq)/pmax(sd(parqueaderos),1))^2+((banios-ban)/pmax(sd(banios),1))^2+((estrato-mean(ests))/pmax(sd(estrato),1))^2))|>arrange(distancia)|>slice_head(n=n)|>mutate(Prioridad=row_number())}
mapa_ofertas<-function(x,titulo)leaflet(x)|>addProviderTiles(providers$CartoDB.Positron)|>addAwesomeMarkers(~longitud,~latitud,popup=~paste0("<b>Prioridad ",Prioridad,"</b><br>",barrio,"<br>$",preciom," millones<br>",areaconst," m²"))|>addControl(titulo,"topright")

11 Caso 1: casa en el norte

base1<-filtrar(vivienda_limpia,"Casa","Norte");stopifnot(nrow(base1)>=10)
tabla(head(base1,3),"Primeros tres registros de base1");tabla(base1|>count(tipo,zona),"Comprobación del filtro");mapa(base1,"Casas del norte")
Primeros tres registros de base1
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud estrato_f
1209 Zona Norte 02 5 320 150 2 4 6 Casa acopi -76.51 3.48 5
1592 Zona Norte 02 5 780 380 2 3 3 Casa acopi -76.52 3.49 5
4057 Zona Norte 02 6 750 445 1 7 6 Casa acopi -76.53 3.39 6
Comprobación del filtro
tipo zona n
Casa Zona Norte 700

Los puntos fuera del sector visualmente esperado pueden originarse en errores de geocodificación, coordenadas aproximadas o diferencias entre zonificación comercial y administrativa. Una confirmación rigurosa requiere polígonos oficiales.

eda(base1,"Caso 1:");tabla(cor_tab(base1),"Spearman: caso 1",4)
Spearman: caso 1
Variable Rho p_valor Decision
areaconst 0.8196 0 Significativa
estrato 0.7166 0 Significativa
parqueaderos 0.5973 0 Significativa
banios 0.6447 0 Significativa
habitaciones 0.4351 0 Significativa

Se eligió Spearman porque estrato es ordinal; habitaciones, baños y parqueaderos son discretas; y precio/área pueden ser asimétricos y contener valores extremos. Spearman examina relaciones monótonas mediante rangos sin exigir normalidad ni linealidad estricta, a diferencia de Pearson.

m1<-ajustar(base1);tabla(tidy(m1,conf.int=TRUE)|>mutate(Significativo=ifelse(p.value<.05,"Sí","No")),"Coeficientes: caso 1",4);aj1<-glance(m1);tabla(aj1|>select(r.squared,adj.r.squared,sigma,statistic,p.value,nobs),"Ajuste: caso 1",4);tabla(diagnosticar(m1),"Supuestos: caso 1",4);graf_diag(m1)
Coeficientes: caso 1
term estimate std.error statistic p.value conf.low conf.high Significativo
(Intercept) 8.6917 18.7678 0.4631 0.6434 -28.1569 45.5403 No
areaconst 0.7561 0.0457 16.5335 0.0000 0.6664 0.8459
estrato_f4 65.4790 17.5917 3.7222 0.0002 30.9395 100.0185
estrato_f5 126.8010 16.7560 7.5675 0.0000 93.9023 159.6997
estrato_f6 311.0661 27.2823 11.4017 0.0000 257.5000 364.6322
habitaciones 3.3904 4.7157 0.7190 0.4724 -5.8684 12.6492 No
parqueaderos 21.9668 5.5669 3.9460 0.0001 11.0369 32.8968
banios 25.6966 5.9108 4.3474 0.0000 14.0914 37.3019
Ajuste: caso 1
r.squared adj.r.squared sigma statistic p.value nobs
0.6707 0.6674 154.4191 201.39 0 700
Supuestos: caso 1
Supuesto Prueba p_valor Resultado
Normalidad Shapiro-Wilk 0 Se rechaza H0
Homocedasticidad Breusch-Pagan 0 Heterocedasticidad
Independencia Durbin-Watson 0 Posible autocorrelación
Multicolinealidad VIF/GVIF NA Aceptable

Para cada coeficiente, H0 establece que beta es cero y H1 que es diferente de cero. Un coeficiente positivo de área es económicamente lógico porque una mayor superficie suele elevar el valor. Signos positivos en estrato, baños o parqueaderos también son plausibles por ubicación y comodidad. Un signo contrario o falta de significancia debe discutirse considerando colinealidad, segmentación y variables omitidas. En normalidad H0 indica residuos normales; en Breusch-Pagan, varianza constante; en Durbin-Watson, ausencia de autocorrelación. El VIF no es una prueba de hipótesis formal y se interpreta como diagnóstico, con alerta práctica desde 5.

pred1<-bind_rows(predecir(m1,base1,200,4,4,1,2),predecir(m1,base1,200,5,4,1,2));tabla(pred1,"Predicción vivienda 1, millones COP")
Predicción vivienda 1, millones COP
Estrato fit lwr upr
1…1 4 312.32 7.56 617.09
1…2 5 373.64 69.12 678.17
of1<-ofertas(base1,350,200,c(4,5),4,1,2);tabla(of1|>select(Prioridad,barrio,preciom,areaconst,estrato,habitaciones,parqueaderos,banios,distancia),"Cinco ofertas vivienda 1");mapa_ofertas(of1,"Ofertas vivienda 1")
Cinco ofertas vivienda 1
Prioridad barrio preciom areaconst estrato habitaciones parqueaderos banios distancia
1 ciudad los álamos 215 143.85 4 4 1 2 0.94
2 acopi 275 190.00 4 3 1 2 1.08
3 la merced 350 216.00 5 4 2 2 1.14
4 los andes 280 180.00 4 4 1 3 1.15
5 la merced 330 240.00 4 3 1 2 1.21

La predicción se contrasta con $350 millones. Si el estimado o buena parte del intervalo supera ese valor, existe riesgo presupuestal y conviene negociar o flexibilizar atributos. Las cinco ofertas no se eligen solo por precio: pertenecen al segmento solicitado, respetan el crédito y minimizan conjuntamente diferencias estandarizadas de área, estrato, habitaciones, baños y parqueaderos.

12 Caso 2: apartamento en el sur

base2<-filtrar(vivienda_limpia,"Apartamento","Sur");stopifnot(nrow(base2)>=10)
tabla(head(base2,3),"Primeros tres registros de base2");tabla(base2|>count(tipo,zona),"Comprobación del filtro");mapa(base2,"Apartamentos del sur");eda(base2,"Caso 2:");tabla(cor_tab(base2),"Spearman: caso 2",4)
Primeros tres registros de base2
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud estrato_f
5098 Zona Sur 05 4 290 96 1 2 3 Apartamento acopi -76.53 3.45 4
698 Zona Sur 02 3 78 40 1 1 2 Apartamento aguablanca -76.50 3.40 3
8199 Zona Sur NA 6 875 194 2 5 3 Apartamento aguacatal -76.56 3.46 6
Comprobación del filtro
tipo zona n
Apartamento Zona Sur 2777
Spearman: caso 2
Variable Rho p_valor Decision
areaconst 0.8659 0 Significativa
estrato 0.7517 0 Significativa
parqueaderos 0.6808 0 Significativa
banios 0.7055 0 Significativa
habitaciones 0.3920 0 Significativa
m2<-ajustar(base2);tabla(tidy(m2,conf.int=TRUE)|>mutate(Significativo=ifelse(p.value<.05,"Sí","No")),"Coeficientes: caso 2",4);aj2<-glance(m2);tabla(aj2|>select(r.squared,adj.r.squared,sigma,statistic,p.value,nobs),"Ajuste: caso 2",4);tabla(diagnosticar(m2),"Supuestos: caso 2",4);graf_diag(m2)
Coeficientes: caso 2
term estimate std.error statistic p.value conf.low conf.high Significativo
(Intercept) -42.4131 10.3493 -4.0982 0.0000 -62.7061 -22.1200
areaconst 1.3007 0.0475 27.3883 0.0000 1.2076 1.3938
estrato_f4 28.2584 6.8813 4.1065 0.0000 14.7653 41.7514
estrato_f5 53.5087 7.1052 7.5309 0.0000 39.5766 67.4408
estrato_f6 204.7992 8.9655 22.8431 0.0000 187.2195 222.3789
habitaciones -12.8016 3.2910 -3.8899 0.0001 -19.2547 -6.3486
parqueaderos 63.9413 3.7606 17.0028 0.0000 56.5674 71.3152
banios 39.7720 2.9650 13.4138 0.0000 33.9582 45.5859
Ajuste: caso 2
r.squared adj.r.squared sigma statistic p.value nobs
0.7902 0.7896 87.5483 1489.457 0 2777
Supuestos: caso 2
Supuesto Prueba p_valor Resultado
Normalidad Shapiro-Wilk 0 Se rechaza H0
Homocedasticidad Breusch-Pagan 0 Heterocedasticidad
Independencia Durbin-Watson 0 Posible autocorrelación
Multicolinealidad VIF/GVIF NA Aceptable

pred2<-bind_rows(predecir(m2,base2,300,5,5,3,3),predecir(m2,base2,300,6,5,3,3));tabla(pred2,"Predicción vivienda 2, millones COP")
Predicción vivienda 2, millones COP
Estrato fit lwr upr
1…1 5 648.44 475.58 821.30
1…2 6 799.73 626.84 972.62
of2<-ofertas(base2,850,300,c(5,6),5,3,3);tabla(of2|>select(Prioridad,barrio,preciom,areaconst,estrato,habitaciones,parqueaderos,banios,distancia),"Cinco ofertas vivienda 2");mapa_ofertas(of2,"Ofertas vivienda 2")
Cinco ofertas vivienda 2
Prioridad barrio preciom areaconst estrato habitaciones parqueaderos banios distancia
1 capri 350 270.00 5 4 3 3 1.26
2 San Fernando 350 258.00 5 5 2 4 1.71
3 el ingenio 700 250.00 6 5 2 4 1.79
4 cuarto de legua 410 295.55 5 4 2 4 1.80
5 cuarto de legua 520 320.00 5 4 2 4 1.84

La predicción se compara con $850 millones bajo el mismo criterio. Las alternativas priorizadas equilibran cercanía al perfil, ubicación y viabilidad financiera. La revisión final debe incluir visita, tradición, gravámenes, administración, antigüedad, estado físico y avalúo.

13 Comparación de modelos

comparacion<-bind_rows(tibble(Caso="Casa norte",N=aj1$nobs,R2=aj1$r.squared,R2_ajustado=aj1$adj.r.squared,RMSE=aj1$sigma,AIC=aj1$AIC,Presupuesto=350),tibble(Caso="Apartamento sur",N=aj2$nobs,R2=aj2$r.squared,R2_ajustado=aj2$adj.r.squared,RMSE=aj2$sigma,AIC=aj2$AIC,Presupuesto=850));tabla(comparacion,"Comparación de modelos")
Comparación de modelos
Caso N R2 R2_ajustado RMSE AIC Presupuesto
Casa norte 700 0.67 0.67 154.42 9052.01 350
Apartamento sur 2777 0.79 0.79 87.55 32729.32 850

14 Conclusiones

El presente análisis logró cumplir satisfactoriamente el objetivo general de desarrollar y validar modelos estadísticos que permitieran estimar el valor comercial de viviendas a partir de sus principales características físicas y socioeconómicas, proporcionando una herramienta objetiva para apoyar los procesos de búsqueda, evaluación y comercialización de inmuebles. A través de una rigurosa depuración de la base de datos, el análisis exploratorio de la información y la construcción de modelos de regresión múltiple diferenciados por segmento de mercado, fue posible identificar los factores que más influyen en la formación de precios y generar predicciones útiles para orientar decisiones inmobiliarias fundamentadas en evidencia.

Los resultados evidencian que el área construida constituye el principal determinante del precio en ambos segmentos analizados, seguida por variables como el estrato socioeconómico, el número de baños y la cantidad de parqueaderos. Estas características explican una proporción considerable de la variabilidad observada en el mercado, lo que confirma que los precios de los inmuebles responden, en gran medida, a atributos observables y cuantificables. Para el caso de las viviendas ubicadas en la zona norte, el modelo alcanzó una capacidad explicativa cercana al 67%, permitiendo establecer que una vivienda con las características requeridas por el cliente presenta un valor esperado aproximado de $312 millones en estrato 4 y de $374 millones en estrato 5. Estos resultados indican que el presupuesto disponible de $350 millones resulta compatible con inmuebles de estrato 4 y se encuentra en el límite para viviendas de estrato 5, situación que exige procesos de negociación más activos o una flexibilización de algunos atributos deseados.

Por su parte, el modelo desarrollado para apartamentos en la zona sur mostró un desempeño superior, explicando cerca del 79% de la variación observada en los precios y presentando un menor error de predicción. Las estimaciones indican que un apartamento con las características definidas por el cliente tendría un valor esperado cercano a los $648 millones en estrato 5 y de aproximadamente $800 millones en estrato 6. Bajo este escenario, el presupuesto de $850 millones permite acceder a una oferta amplia y competitiva en estrato 5 y a una parte importante del mercado de estrato 6, aunque con mayores riesgos de sobrecostos en este último segmento.

El ejercicio de priorización de ofertas permitió identificar alternativas reales que, aun cuando no reproducen exactamente todas las características requeridas por los clientes, representan opciones viables desde la perspectiva económica y funcional. Este hallazgo confirma una realidad frecuente del mercado inmobiliario: la vivienda perfecta rara vez existe en la oferta disponible, por lo que las decisiones de compra suelen sustentarse en la capacidad de equilibrar atributos, ubicación y presupuesto.

De manera general, el estudio demuestra que los modelos predictivos constituyen una herramienta valiosa para fundamentar estrategias de comercialización, optimizar los procesos de asesoría y reducir la incertidumbre en la valoración de inmuebles. Sin embargo, también evidencia que las proyecciones deben interpretarse como referencias técnicas y no como avalúos definitivos, dado que factores como el estado físico del inmueble, su antigüedad, las condiciones jurídicas, la seguridad del sector, las características urbanísticas y la dinámica coyuntural del mercado pueden influir significativamente en el valor final de negociación.

En consecuencia, los resultados obtenidos permiten concluir que la combinación entre análisis estadístico, conocimiento del mercado y validación comercial constituye la estrategia más robusta para respaldar decisiones inmobiliarias informadas, eficientes y alineadas con las necesidades reales de compradores y vendedores.

15 Recomendaciones estratégicas para la comercialización de viviendas:

  • Alcance: Los precios corresponden a ofertas, no necesariamente a transacciones cerradas. Por tanto, el modelo orienta negociación y comparación, pero no reemplaza el avalúo profesional.

  • Adoptar una comercialización basada en valor y no únicamente en precio: Los resultados muestran que variables como área, estrato, baños y parqueaderos impactan directamente la valoración de los inmuebles. Por ello, las estrategias comerciales deben enfocarse en comunicar claramente estos atributos diferenciales al cliente y no limitarse a competir por precio. Acción recomendada: construir fichas comerciales que resalten los atributos que más valor generan según el modelo estadístico.

  • Utilizar los modelos predictivos como soporte para negociaciones: Las predicciones obtenidas permiten establecer rangos razonables de negociación y detectar inmuebles sobrevalorados o subvalorados dentro del mercado. Acción recomendada: incorporar las estimaciones del modelo en las reuniones de asesoría para sustentar técnicamente las ofertas y contraofertas.

  • Priorizar inmuebles con mayor ajuste entre presupuesto y perfil del cliente: La investigación evidenció que el mayor obstáculo en muchos procesos no es el presupuesto sino encontrar inmuebles que cumplan simultáneamente todas las características deseadas. Acción recomendada: desarrollar un sistema de puntuación que mida el nivel de coincidencia entre los requerimientos del cliente y cada inmueble disponible, permitiendo priorizar las alternativas con mayor probabilidad de cierre.

  • Fortalecer la comercialización del segmento de estrato 5: Los resultados muestran que este segmento ofrece el mejor equilibrio entre precio, disponibilidad de oferta y compatibilidad con los presupuestos analizados. Acción recomendada: concentrar campañas de captación y promoción en zonas con alta presencia de inmuebles de estrato 5, donde existe una mayor probabilidad de concretar negocios.

  • Implementar estrategias de negociación diferenciadas para estrato 6: Aunque el mercado de estrato 6 ofrece mayor valorización y rentabilidad potencial, también presenta mayores riesgos de exceder los presupuestos de los compradores. Acción recomendada: diseñar procesos de negociación anticipada con propietarios para identificar márgenes reales de descuento antes de presentar los inmuebles a los clientes.

  • Incorporar variables de comportamiento comercial en futuros modelos: Actualmente, la predicción se fundamenta principalmente en características físicas y socioeconómicas del inmueble.

  • Acción recomendada: registrar y analizar variables como:

    • Tiempo promedio de venta.
    • Número de visitas recibidas.
    • Cantidad de contraofertas.
    • Precio inicial versus precio de cierre.
    • Motivo de descarte de los inmuebles.
    • Nivel de demanda por sector.

Esto permitirá construir modelos predictivos más precisos y alineados con el comportamiento real del mercado.

  • La principal oportunidad identificada para C&A consiste en evolucionar desde una comercialización basada en criterios subjetivos hacia una comercialización soportada en analítica inmobiliaria, donde cada recomendación al cliente esté respaldada por evidencia cuantitativa, análisis de mercado y criterios objetivos de valoración. Este enfoque no solo incrementará la confianza de los compradores, sino que también mejorará la eficiencia comercial, reducirá los tiempos de cierre y aumentará la probabilidad de concretar negocios exitosos y sostenibles.

Referencias

[1]
[2]
[3]
«Análisis de la cartera y del mercado inmobiliario en Colombia», Banco de la República, 2025.
[4]
«Introduction to Linear Regression Analysis», Wiley, 2021.
[5]
«An R Companion to Applied Regression», SAGE, 2019.
[6]
«An Introduction to Statistical Learning», Springer, 2021.
[7]
«R Markdown: The Definitive Guide», Chapman; Hall/CRC, 2018.
[8]
[9]
[10]
«paqueteMODELOS», 2026.