Setup

Analisis de datos

str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   id = col_double(),
##   ..   zona = col_character(),
##   ..   piso = col_character(),
##   ..   estrato = col_double(),
##   ..   preciom = col_double(),
##   ..   areaconst = col_double(),
##   ..   parqueaderos = col_double(),
##   ..   banios = col_double(),
##   ..   habitaciones = col_double(),
##   ..   tipo = col_character(),
##   ..   barrio = col_character(),
##   ..   longitud = col_double(),
##   ..   latitud = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

El piso deberia ser una variable numerica por lo que se realiza la conversion

db = vivienda
db$piso = as.numeric(db$piso)
head(db)
## # A tibble: 6 × 13
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1147 Zona O…    NA       3     250        70            1      3            6
## 2  1169 Zona O…    NA       3     320       120            1      2            3
## 3  1350 Zona O…    NA       3     350       220            2      2            4
## 4  5992 Zona S…     2       4     400       280            3      5            3
## 5  1212 Zona N…     1       5     260        90            1      2            3
## 6  1724 Zona N…     1       5     240        87            1      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Se consultan valores unicos de variables categoricas para identificar correcciones necesarias

columnas_categoricas = db %>%  select_if(is.character)
valores_unicos = sapply(columnas_categoricas, unique)
print(valores_unicos)
## $zona
## [1] "Zona Oriente" "Zona Sur"     "Zona Norte"   "Zona Oeste"   "Zona Centro" 
## [6] NA            
## 
## $tipo
## [1] "Casa"        "Apartamento" NA           
## 
## $barrio
##   [1] "20 de julio"                   "3 de julio"                   
##   [3] "acopi"                         "agua blanca"                  
##   [5] "aguablanca"                    "aguacatal"                    
##   [7] "alameda"                       "alameda del río"              
##   [9] "alameda del rio"               "alamos"                       
##  [11] "alborada"                      "alcazares"                    
##  [13] "alférez real"                 "alferez real"                 
##  [15] "alfonso lópez"                 "alfonso lópez i"              
##  [17] "alfonso lopez"                 "alto jordán"                  
##  [19] "altos de guadalupe"            "altos de menga"               
##  [21] "altos de santa"                "antonio nariño"               
##  [23] "aranjuez"                      "arboleda"                     
##  [25] "arboleda campestre candelaria" "arboledas"                    
##  [27] "atanasio girardot"             "autopista sur"                
##  [29] "bajo aguacatal"                "barranquilla"                 
##  [31] "barrio 7de agosto"             "barrio el recuerdo"           
##  [33] "barrio eucarístico"            "barrio obrero"                
##  [35] "barrio tranquilo y"            "base aérea"                  
##  [37] "belalcazar"                    "Belalcazar"                   
##  [39] "belisario caicedo"             "bella suiza"                  
##  [41] "bella suiza alta"              "bellavista"                   
##  [43] "benjamín herrera"              "berlin"                       
##  [45] "bloques del limonar"           "bochalema"                    
##  [47] "bolivariano"                   "bosques de alboleda"          
##  [49] "bosques del limonar"           "boyacá"                       
##  [51] "bretaña"                       "brisas de guadalupe"          
##  [53] "brisas de los"                 "Brisas De Los"                
##  [55] "brisas del guabito"            "brisas del limonar"           
##  [57] "Bueno Madrid"                  "buenos aires"                 
##  [59] "cañasgordas"                   "cañaveralejo"                 
##  [61] "cañaverales"                   "cañaverales los samanes"      
##  [63] "caldas"                        "Cali"                         
##  [65] "cali bella"                    "cali canto"                   
##  [67] "calibella"                     "calicanto"                    
##  [69] "calicanto viii"                "calima"                       
##  [71] "calimio norte"                 "calipso"                      
##  [73] "cambulos"                      "camino real"                  
##  [75] "Camino Real"                   "campestre"                    
##  [77] "caney"                         "caney especial"               
##  [79] "capri"                         "cascajal"                     
##  [81] "cataya real"                   "ceibas"                       
##  [83] "centelsa"                      "centenario"                   
##  [85] "Centenario"                    "centro"                       
##  [87] "cerro cristales"               "cerros de guadalupe"          
##  [89] "champagnat"                    "chapinero"                    
##  [91] "chiminangos"                   "Chiminangos"                  
##  [93] "chiminangos 1 etapa"           "chiminangos 2 etapa"          
##  [95] "chipichape"                    "ciudad 2000"                  
##  [97] "Ciudad 2000"                   "ciudad antejardin"            
##  [99] "ciudad bochalema"              "ciudad córdoba"               
## [101] "ciudad córdoba reservado"      "ciudad capri"                 
## [103] "ciudad cordoba"                "ciudad country"               
## [105] "ciudad del campo"              "ciudad jardín"                
## [107] "Ciudad Jardín"                 "ciudad jardin"                
## [109] "ciudad jardin pance"           "ciudad los álamos"            
## [111] "ciudad los alamos"             "ciudad meléndez"             
## [113] "ciudad melendez"               "ciudad modelo"                
## [115] "ciudad pacifica"               "Ciudad Pacifica"              
## [117] "ciudad real"                   "ciudad talanga"               
## [119] "ciudad universitaria"          "ciudadela comfandi"           
## [121] "ciudadela del río"             "ciudadela melendez"           
## [123] "ciudadela paso ancho"          "ciudadela pasoancho"          
## [125] "colinas de menga"              "colinas del bosque"           
## [127] "colinas del sur"               "colon"                        
## [129] "colseguros"                    "colseguros andes"             
## [131] "Colseguros Andes"              "comfenalco"                   
## [133] "compartir"                     "conjunto gibraltar"           
## [135] "cristóbal colón"               "cristales"                    
## [137] "cristobal colón"               "cuarto de legua"              
## [139] "departamental"                 "ed benjamin herrera"          
## [141] "el bosque"                     "El Bosque"                    
## [143] "el caney"                      "El Caney"                     
## [145] "el castillo"                   "el cedro"                     
## [147] "el diamante"                   "el dorado"                    
## [149] "el gran limonar"               "el guabal"                    
## [151] "el guabito"                    "el ingenio"                   
## [153] "El Ingenio"                    "el ingenio 3"                 
## [155] "el ingenio i"                  "el ingenio ii"                
## [157] "el ingenio iii"                "el jardín"                    
## [159] "el jordán"                     "el lido"                      
## [161] "el limonar"                    "el nacional"                  
## [163] "el paraíso"                    "el peñon"                     
## [165] "el prado"                      "el refugio"                   
## [167] "el rodeo"                      "el sena"                      
## [169] "el trébol"                    "el troncal"                   
## [171] "el vallado"                    "eucarístico"                  
## [173] "evaristo garcía"               "farrallones de pance"         
## [175] "fenalco kennedy"               "fepicol"                      
## [177] "flora"                         "flora industrial"             
## [179] "floralia"                      "fonaviemcali"                 
## [181] "francisco eladio ramirez"      "fuentes de la"                
## [183] "gaitan"                        "gran limonar"                 
## [185] "granada"                       "guadalupe"                    
## [187] "guadalupe alto"                "guaduales"                    
## [189] "guayaquil"                     "hacienda alferez real"        
## [191] "ingenio"                       "ingenio i"                    
## [193] "ingenio ii"                    "jamundi"                      
## [195] "jamundi alfaguara"             "jorge eliecer gaitán"         
## [197] "jorge isaacs"                  "jose manuel marroquín"        
## [199] "juanamb√∫"                     "juanambu"                     
## [201] "junín"                         "junin"                        
## [203] "la alborada"                   "la alianza"                   
## [205] "la arboleda"                   "la base"                      
## [207] "la buitrera"                   "la campiña"                   
## [209] "la cascada"                    "la ceibas"                    
## [211] "la esmeralda"                  "la flora"                     
## [213] "La Flora"                      "la floresta"                  
## [215] "la fortaleza"                  "la gran colombia"             
## [217] "la hacienda"                   "La Hacienda"                  
## [219] "la independencia"              "la libertad"                  
## [221] "la luisa"                      "la merced"                    
## [223] "la morada"                     "la nueva base"                
## [225] "la playa"                      "la portada al"                
## [227] "la primavera"                  "la reforma"                   
## [229] "la rivera"                     "la rivera i"                  
## [231] "la rivera ii"                  "la riverita"                  
## [233] "la riviera"                    "la selva"                     
## [235] "la villa del"                  "laflora"                      
## [237] "lares de comfenalco"           "las acacias"                  
## [239] "las américas"                 "las camelias"                 
## [241] "las ceibas"                    "las delicias"                 
## [243] "las granjas"                   "las quintas de"               
## [245] "las vegas"                     "las vegas de"                 
## [247] "libertadores"                  "los alamos"                   
## [249] "los alcázares"                 "los alcazares"                
## [251] "los andes"                     "los cámbulos"                 
## [253] "los cambulos"                  "los cristales"                
## [255] "los cristales club"            "los farallones"               
## [257] "los guaduales"                 "Los Guaduales"                
## [259] "los guayacanes"                "los jockeys"                  
## [261] "los libertadores"              "los parques barranquilla"     
## [263] "los robles"                    "lourdes"                      
## [265] "mamellan"                      "manzanares"                   
## [267] "mariano ramos"                 "marroquín iii"                
## [269] "mayapan las vegas"             "meléndez"                    
## [271] "melendez"                      "menga"                        
## [273] "metropolitano del norte"       "miradol del aguacatal"        
## [275] "miraflores"                    "Miraflores"                   
## [277] "morichal de comfandi"          "multicentro"                  
## [279] "municipal"                     "nápoles"                      
## [281] "napoles"                       "normandía"                    
## [283] "normandía west point"          "normandia"                    
## [285] "norte"                         "norte la flora"               
## [287] "nueva base"                    "nueva floresta"               
## [289] "nueva tequendama"              "oasis de comfandi"            
## [291] "oasis de pasoancho"            "occidente"                    
## [293] "pacará"                        "pacara"                       
## [295] "palmas del ingenio"            "pampa linda"                  
## [297] "pampalinda"                    "panamericano"                 
## [299] "pance"                         "Pance"                        
## [301] "parcelaciones pance"           "parque residencial el"        
## [303] "paseo de los"                  "paso del comercio"            
## [305] "pasoancho"                     "poblado campestre"            
## [307] "ponce"                         "popular"                      
## [309] "portada de comfandi"           "portales de comfandi"         
## [311] "porvenir"                      "prados de oriente"            
## [313] "prados del limonar"            "Prados Del Limonar"           
## [315] "prados del norte"              "Prados Del Norte"             
## [317] "prados del sur"                "primavera"                    
## [319] "primero de mayo"               "primitivo crespo"             
## [321] "puente del comercio"           "puente palma"                 
## [323] "quintas de don"                "Quintas De Don"               
## [325] "quintas de salomia"            "rafael uribe uribe"           
## [327] "refugio"                       "rep√∫blica de israel"         
## [329] "rincón de salomia"             "rincon de la"                 
## [331] "riveras del valle"             "rozo la torre"                
## [333] "saavedra galindo"              "salomia"                      
## [335] "samanes"                       "samanes de guadalupe"         
## [337] "sameco"                        "san antonio"                  
## [339] "san bosco"                     "san carlos"                   
## [341] "san cayetano"                  "san fernando"                 
## [343] "San Fernando"                  "san fernando nuevo"           
## [345] "san fernando viejo"            "san joaquín"                  
## [347] "san joaquin"                   "san juan bosco"               
## [349] "san judas"                     "san judas tadeo"              
## [351] "san luís"                      "san luis"                     
## [353] "san nicolás"                   "san nicolas"                  
## [355] "san pedro"                     "san vicente"                  
## [357] "santa"                         "santa anita"                  
## [359] "Santa Anita"                   "santa anita sur"              
## [361] "santa bárbara"                 "santa elena"                  
## [363] "santa fe"                      "santa helena de"              
## [365] "santa isabel"                  "Santa Isabel"                 
## [367] "santa mónica"                  "santa mónica alta"            
## [369] "santa mónica popular"          "santa mónica residencial"     
## [371] "santa monica"                  "Santa Monica"                 
## [373] "santa monica norte"            "santa monica popular"         
## [375] "santa monica residencial"      "santa rita"                   
## [377] "santa rosa"                    "santa teresita"               
## [379] "Santa Teresita"                "Santafe"                      
## [381] "santander"                     "santo domingo"                
## [383] "Santo Domingo"                 "sector aguacatal"             
## [385] "sector cañaveralejo guadalupe" "seminario"                    
## [387] "sierras de normandía"          "siete de agosto"              
## [389] "simón bolivar"                 "tejares cristales"            
## [391] "tejares de san"                "templete"                     
## [393] "tequendama"                    "tequendema"                   
## [395] "terrón colorado"               "torres de comfandi"           
## [397] "unión de vivienda"             "unicentro cali"               
## [399] "urbanización barranquilla"     "urbanización boyacá"          
## [401] "urbanización colseguros"       "urbanización la flora"        
## [403] "urbanización la merced"        "urbanización la nueva"        
## [405] "urbanización las cascadas"     "urbanización nueva granada"   
## [407] "urbanización pacara"           "urbanización río lili"        
## [409] "urbanización san joaquin"      "urbanización tequendama"      
## [411] "urbanizacion el saman"         "urbanizacion gratamira"       
## [413] "urbanizacion lili"             "valle de lili"                
## [415] "valle del lili"                "Valle Del Lili"               
## [417] "valle grande"                  "versalles"                    
## [419] "villa colombia"                "villa de veracruz"            
## [421] "villa del lago"                "villa del parque"             
## [423] "villa del prado"               "Villa Del Prado"              
## [425] "villa del sol"                 "villa del sur"                
## [427] "Villas De Veracruz"            "villas de veracruz"           
## [429] "vipasa"                        "zona centro"                  
## [431] "zona norte"                    "zona norte los"               
## [433] "zona oeste"                    "zona oriente"                 
## [435] "zona residencial"              "zona sur"                     
## [437] NA

Se puede observar que no hay valores que necesiten ajusten en los atributos zona o tipo, se observan algunos campos en donde es necesario realizar correcciones en el atributo barrio (“alf√©rez real” vs “alferez real”, “mel√©ndez” vs “melendez” o “miraflores” vs “Miraflores”), por lo que se reemplazaran los caracteres no legibles y se convertira todo a minusculas

Limpieza del df

# cantidad de valores unicos para el barrio
cat("Valores unicos iniciales para barrio:", length(unique(db$barrio))) 
## Valores unicos iniciales para barrio: 437
db = db[order(db$id),] # Ordenar por columna id
df = subset(db, select = -c(longitud, latitud)) #elimina columnas innecesarias
df = mutate_if(df, is.character, tolower) #convierte a minusculas
df[df == "  "] = "" #elimina espacios dobles del dataframe
df$barrio = gsub("é", "e", df$barrio) #reemplazar errores por acentos
df$barrio = gsub("√∫", "u", df$barrio) #reemplazar errores por acentos
df$barrio = stri_trans_general(str = df$barrio, id = "Latin-ASCII") #elimina acentos
# cantidad de valores unicos para el barrio
cat("Valores unicos finales para barrio:", length(unique(df$barrio))) 
## Valores unicos finales para barrio: 386

Analisis de las variables

tbl_precio_tipo = table(df$tipo, df$preciom)
boxplot(df$preciom ~ df$tipo, data =df, ylab = "tipo", xlab = "preciom", horizontal = T, main ="Precio Vs tipo")

boxplot(df$areaconst ~ df$tipo, data =df, ylab = "tipo", xlab = "area", horizontal = T, main = "Area vs Tipo")

plot(df$preciom ~ df$areaconst, data=df, ylab = "preciom", xlab = "area",main = "Area vs Precio")

skim(df)
Data summary
Name df
Number of rows 8322
Number of columns 11
_______________________
Column type frequency:
character 3
numeric 8
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
zona 3 1 8 12 0 5 0
tipo 3 1 4 11 0 2 0
barrio 3 1 4 29 0 385 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
id 3 1.00 4160.00 2401.63 1 2080.5 4160 6239.5 8319 ▇▇▇▇▇
piso 2638 0.68 3.77 2.61 1 2.0 3 5.0 12 ▇▃▁▁▁
estrato 3 1.00 4.63 1.03 3 4.0 5 5.0 6 ▅▆▁▇▆
preciom 2 1.00 433.89 328.65 58 220.0 330 540.0 1999 ▇▂▁▁▁
areaconst 3 1.00 174.93 142.96 30 80.0 123 229.0 1745 ▇▁▁▁▁
parqueaderos 1605 0.81 1.84 1.12 1 1.0 2 2.0 10 ▇▁▁▁▁
banios 3 1.00 3.11 1.43 0 2.0 3 4.0 10 ▇▇▃▁▁
habitaciones 3 1.00 3.61 1.46 0 3.0 3 4.0 10 ▂▇▂▁▁
missmap(df,main = "Datos Faltantes",y.labels = ' ', y.at = 1 )
## Warning: Unknown or uninitialised column: `arguments`.
## Unknown or uninitialised column: `arguments`.

Observaciones

  • Hay 8322 registros en total
  • Se puede ver que el precio de las casas es mucho mas alto que el de los apartamentos, lo que indirectamente soporta la leve relacion entre el precio y el area; generalmente las casas son mas grandes que los apartamentos (observado en la grafica de area vs tipo), lo que indica que a mayor area, mayor precio
  • Hay registros en blanco en todas las columnas, pero dos variables en particular tienen un numero alto de n_missing
    • La columna piso tiene 2638 valores en blanco y la columna parqueaderos tiene 1605

#Preprocesamiento de datos

Imputacion de NAs

Para el caso particular de la columna piso y parqueadero, se podria interpretar la ausencia como valores que no aplican, por ejemplo, NA para el piso indicaria que es una vivienda de un solo piso, o NA para parqueadero puede indicar que no cuenta con parqueadero, por lo que seria un 0, pero teniendo en cuenta que es un numero considerable de registros, se proceden a eliminar para evitar introducir sesgos a los datos.

df = na.omit(df)
skim(df)
Data summary
Name df
Number of rows 4808
Number of columns 11
_______________________
Column type frequency:
character 3
numeric 8
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
zona 0 1 8 12 0 5 0
tipo 0 1 4 11 0 2 0
barrio 0 1 4 29 0 244 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
id 0 1 4426.92 2306.71 1 2478.75 4473.5 6413.25 8316 ▅▇▇▇▇
piso 0 1 3.89 2.67 1 2.00 3.0 5.00 12 ▇▃▁▁▁
estrato 0 1 4.84 0.93 3 4.00 5.0 6.00 6 ▂▅▁▇▆
preciom 0 1 457.19 325.62 58 244.50 350.0 560.00 1999 ▇▃▁▁▁
areaconst 0 1 174.76 138.29 40 85.00 123.0 225.00 1500 ▇▁▁▁▁
parqueaderos 0 1 1.82 1.10 1 1.00 2.0 2.00 10 ▇▁▁▁▁
banios 0 1 3.22 1.35 0 2.00 3.0 4.00 10 ▇▇▃▁▁
habitaciones 0 1 3.56 1.33 0 3.00 3.0 4.00 10 ▁▇▁▁▁

Como se puede observar, ya no quedan n_missing

Codificacion de variables

Se realizara la codificacion de variables categoricas para poder realizar analisis de correlacion e implmentacion de modelos de ser requeridas

#Convertir variables categoricas a factores
df$zona_num <- as.numeric(as.factor(df$zona))
df$tipo_num <- as.numeric(as.factor(df$tipo))
df$barrio_num <- as.numeric(as.factor(df$barrio))
skim(df)
Data summary
Name df
Number of rows 4808
Number of columns 14
_______________________
Column type frequency:
character 3
numeric 11
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
zona 0 1 8 12 0 5 0
tipo 0 1 4 11 0 2 0
barrio 0 1 4 29 0 244 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
id 0 1 4426.92 2306.71 1 2478.75 4473.5 6413.25 8316 ▅▇▇▇▇
piso 0 1 3.89 2.67 1 2.00 3.0 5.00 12 ▇▃▁▁▁
estrato 0 1 4.84 0.93 3 4.00 5.0 6.00 6 ▂▅▁▇▆
preciom 0 1 457.19 325.62 58 244.50 350.0 560.00 1999 ▇▃▁▁▁
areaconst 0 1 174.76 138.29 40 85.00 123.0 225.00 1500 ▇▁▁▁▁
parqueaderos 0 1 1.82 1.10 1 1.00 2.0 2.00 10 ▇▁▁▁▁
banios 0 1 3.22 1.35 0 2.00 3.0 4.00 10 ▇▇▃▁▁
habitaciones 0 1 3.56 1.33 0 3.00 3.0 4.00 10 ▁▇▁▁▁
zona_num 0 1 4.08 1.26 1 3.00 5.0 5.00 5 ▁▂▂▁▇
tipo_num 0 1 1.34 0.47 1 1.00 1.0 2.00 2 ▇▁▁▁▅
barrio_num 0 1 136.04 69.49 1 73.00 139.0 205.00 244 ▂▇▅▆▇

Correlacion de variables

Teniendo en cuenta que tenemos variables categoricas (aunque ya las hayamos codificado a factores numericos) y variables numericas, usaremos una correlacion de Spearman

cormat = round(cor(df %>% select(-zona, -tipo, -barrio, -id), method = "spearman"),2)
melted_cormat = melt(cormat)

  # Get upper triangle of the correlation matrix
  get_upper_tri <- function(cormat){
    cormat[lower.tri(cormat)]<- NA
    return(cormat)
  }

  reorder_cormat <- function(cormat){
  # Use correlation between variables as distance
  dd <- as.dist((1-cormat)/2)
  hc <- hclust(dd)
  cormat <-cormat[hc$order, hc$order]
  }

# Reorder the correlation matrix
cormat <- reorder_cormat(cormat)
upper_tri <- get_upper_tri(cormat)
# Melt the correlation matrix
melted_cormat <- melt(upper_tri, na.rm = TRUE)
# Create a ggheatmap
ggheatmap <- ggplot(melted_cormat, aes(Var2, Var1, fill = value))+
 geom_tile(color = "white")+
 scale_fill_gradient2(low = "blue", high = "red", mid = "white", 
   midpoint = 0, limit = c(-1,1), space = "Lab", 
    name="Spearman\nCorrelation") +
  theme_minimal()+ # minimal theme
 theme(axis.text.x = element_text(angle = 45, vjust = 1, 
    size = 12, hjust = 1))+
 coord_fixed()
ggheatmap + 
geom_text(aes(Var2, Var1, label = value), color = "black", size = 3) +
theme(
  axis.title.x = element_blank(),
  axis.title.y = element_blank(),
  panel.grid.major = element_blank(),
  panel.border = element_blank(),
  panel.background = element_blank(),
  axis.ticks = element_blank(),
  legend.justification = c(1, 0),
  legend.position = c(0.6, 0.7),
  legend.direction = "horizontal")+
  guides(fill = guide_colorbar(barwidth = 7, barheight = 1,
                title.position = "top", title.hjust = 0.5))

Se puede observar una fuerte correlacion entre el area construida y varias variables como habitaciones (66%), tipo (63%), banios (79%) y preciom (83%) lo que nos dice que el area construida puede ser un indicador del precio y otras caracteristicas de la vivienda.

Modelos

PCA

Para poder realizar Principal Component Analysis (PCA), hay unas consideraciones que se deben tener en cuenta antes de iniciar.

Seleccion de variables

En el paso de limpieza del df eliminamos columnas no relevantes para el dataset, pero para PCA en particular, debemos eliminar algunas columnas extra. Esto se hace basados en el hecho de que PCA se realiza con variables cuantitativas, a pesar de que convertimos nuestras variables categoricas a numericas a traves de la funcion factor, es necesario tener en cuenta la naturaleza ordinal del label encoding, por lo que se remueven las variables categoricas.

Adicionalmente se remueve la variable piso debido a la poca correlacion que muestra en el heatmap

df_pca = df %>% select(-barrio_num, -zona_num, -tipo_num, -barrio, -zona, -tipo, -piso, -id)
str(df_pca)
## tibble [4,808 × 6] (S3: tbl_df/tbl/data.frame)
##  $ estrato     : num [1:4808] 6 4 6 6 6 5 4 6 5 5 ...
##  $ preciom     : num [1:4808] 880 1200 1300 513 870 310 240 690 220 200 ...
##  $ areaconst   : num [1:4808] 237 800 600 160 490 82.5 80 150 92 71 ...
##  $ parqueaderos: num [1:4808] 2 3 4 2 3 1 1 2 2 1 ...
##  $ banios      : num [1:4808] 5 6 7 4 6 2 2 5 3 2 ...
##  $ habitaciones: num [1:4808] 4 7 5 4 5 3 3 4 3 3 ...
##  - attr(*, "na.action")= 'omit' Named int [1:3514] 3 4 12 13 15 16 17 21 22 29 ...
##   ..- attr(*, "names")= chr [1:3514] "3" "4" "12" "13" ...

Preprocesamiento de datos

  • Se debe validar que no hayan valores faltantes en las variables
    • Este punto se soluciono en la Imputacion de NAs
md.pattern(df_pca, rotate.names = TRUE)

  • Las variables deben ser numericas
    • Este punto se soluciono en la seleccion de variables
str(df_pca)
## tibble [4,808 × 6] (S3: tbl_df/tbl/data.frame)
##  $ estrato     : num [1:4808] 6 4 6 6 6 5 4 6 5 5 ...
##  $ preciom     : num [1:4808] 880 1200 1300 513 870 310 240 690 220 200 ...
##  $ areaconst   : num [1:4808] 237 800 600 160 490 82.5 80 150 92 71 ...
##  $ parqueaderos: num [1:4808] 2 3 4 2 3 1 1 2 2 1 ...
##  $ banios      : num [1:4808] 5 6 7 4 6 2 2 5 3 2 ...
##  $ habitaciones: num [1:4808] 4 7 5 4 5 3 3 4 3 3 ...
##  - attr(*, "na.action")= 'omit' Named int [1:3514] 3 4 12 13 15 16 17 21 22 29 ...
##   ..- attr(*, "names")= chr [1:3514] "3" "4" "12" "13" ...
  • Los datos deben estar normalizados
    • Esto no se habia realizado anteriormente, por lo que se aplicara ahora mismo
df_scaled = scale(df_pca)
head(round(df_scaled,2))
##      estrato preciom areaconst parqueaderos banios habitaciones
## [1,]    1.25    1.30      0.45         0.17   1.32         0.33
## [2,]   -0.90    2.28      4.52         1.08   2.06         2.58
## [3,]    1.25    2.59      3.07         1.99   2.80         1.08
## [4,]    1.25    0.17     -0.11         0.17   0.58         0.33
## [5,]    1.25    1.27      2.28         1.08   2.06         1.08
## [6,]    0.17   -0.45     -0.67        -0.74  -0.90        -0.42

Estimacion PCA

res_pca = prcomp(df_pca, scale. = TRUE)
summary(res_pca)
## Importance of components:
##                           PC1    PC2     PC3     PC4     PC5     PC6
## Standard deviation     1.8866 1.0884 0.69530 0.58528 0.49647 0.42886
## Proportion of Variance 0.5932 0.1974 0.08057 0.05709 0.04108 0.03065
## Cumulative Proportion  0.5932 0.7906 0.87117 0.92827 0.96935 1.00000
fviz_eig(res_pca, addlabels = TRUE)

cumulative_variance <- (cumsum(res_pca$sdev^2 / sum(res_pca$sdev^2))*100)

# Plot cumulative variance
plot(cumulative_variance, type = "b", xlab = "Componentes Principales", ylab = "Variabilidad acumulada (%)", ylim = c(0, 100), main = "Variabilidad acumulada")
text(seq_along(cumulative_variance), cumulative_variance, labels = sprintf("%.2f%%", cumulative_variance), pos = 3, cex = 0.7)

Analisis de resultados

En este caso, el primer compenente principal explica el 59.32% de la variabilidad contenida en la base de datos, observando la variabilidad explicada por los componentes, nos damos cuenta que para poder explicar al menos un 80% de la variabilidad, necesitamos conservar 2 componentes principales, que equivale a un 79%

res_pca$rotation[, 1:2]
##                     PC1        PC2
## estrato      -0.2946513 -0.6488133
## preciom      -0.4676270 -0.2332802
## areaconst    -0.4495212  0.2109552
## parqueaderos -0.4287458 -0.1463697
## banios       -0.4639679  0.1419484
## habitaciones -0.3055275  0.6622281
fviz_pca_var(res_pca,
col.var = "contrib", # Color by contributions to the PC
gradient.cols = c("red","blue"),
repel = TRUE,     # Avoid text overlapping
axes = c(1,2)
)

El componente 1, esta asociado principalment con las variables area construida y baños.

El componente 2 se asocia con el numero de habitaciones.

Analisis de Conglomerados

Al igual que con el PCA, necesitamos validar ciertos requerimientos antes de realizar el analisis de conglomerados.

Seleccion de variables

Para poder utilizar distancias euclidianas, y clustering jerarquico, necesitamos garantizar que los valores a usar sean de caracter numerico, por lo que eliminaremos las variables categoricas, adicionalmente conservaremos solo las variables relevantes para el analisis

df_clus = df %>% select(-barrio_num, -zona_num, -tipo_num, -barrio, -zona, -tipo, -piso, -id)
str(df_clus)
## tibble [4,808 × 6] (S3: tbl_df/tbl/data.frame)
##  $ estrato     : num [1:4808] 6 4 6 6 6 5 4 6 5 5 ...
##  $ preciom     : num [1:4808] 880 1200 1300 513 870 310 240 690 220 200 ...
##  $ areaconst   : num [1:4808] 237 800 600 160 490 82.5 80 150 92 71 ...
##  $ parqueaderos: num [1:4808] 2 3 4 2 3 1 1 2 2 1 ...
##  $ banios      : num [1:4808] 5 6 7 4 6 2 2 5 3 2 ...
##  $ habitaciones: num [1:4808] 4 7 5 4 5 3 3 4 3 3 ...
##  - attr(*, "na.action")= 'omit' Named int [1:3514] 3 4 12 13 15 16 17 21 22 29 ...
##   ..- attr(*, "names")= chr [1:3514] "3" "4" "12" "13" ...

Preprocesamiento de datos

  • Se debe validar que no hayan valores faltantes en las variables
    • Este punto se soluciono en la Imputacion de NAs
md.pattern(df_clus, rotate.names = TRUE)

  • Las variables deben ser numericas
    • Este punto se soluciono en la seleccion de variables
str(df_clus)
## tibble [4,808 × 6] (S3: tbl_df/tbl/data.frame)
##  $ estrato     : num [1:4808] 6 4 6 6 6 5 4 6 5 5 ...
##  $ preciom     : num [1:4808] 880 1200 1300 513 870 310 240 690 220 200 ...
##  $ areaconst   : num [1:4808] 237 800 600 160 490 82.5 80 150 92 71 ...
##  $ parqueaderos: num [1:4808] 2 3 4 2 3 1 1 2 2 1 ...
##  $ banios      : num [1:4808] 5 6 7 4 6 2 2 5 3 2 ...
##  $ habitaciones: num [1:4808] 4 7 5 4 5 3 3 4 3 3 ...
##  - attr(*, "na.action")= 'omit' Named int [1:3514] 3 4 12 13 15 16 17 21 22 29 ...
##   ..- attr(*, "names")= chr [1:3514] "3" "4" "12" "13" ...
  • Los datos deben estar normalizados
    • Esto no se habia realizado anteriormente, por lo que se aplicara ahora mismo
head(df_clus)
## # A tibble: 6 × 6
##   estrato preciom areaconst parqueaderos banios habitaciones
##     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1       6     880     237              2      5            4
## 2       4    1200     800              3      6            7
## 3       6    1300     600              4      7            5
## 4       6     513     160              2      4            4
## 5       6     870     490              3      6            5
## 6       5     310      82.5            1      2            3
df_clus_og = df_clus  # Copia del dataset para graficacion en la etapa de analisis
df_clus = scale(df_clus)
df_clus = as.data.frame(df_clus)
head(df_clus)
##      estrato    preciom  areaconst parqueaderos     banios habitaciones
## 1  1.2544421  1.2984933  0.4500474    0.1682046  1.3178809    0.3272519
## 2 -0.9046263  2.2812429  4.5210796    1.0779092  2.0579382    2.5814453
## 3  1.2544421  2.5883521  3.0748870    1.9876139  2.7979956    1.0786497
## 4  1.2544421  0.1714025 -0.1067367    0.1682046  0.5778235    0.3272519
## 5  1.2544421  1.2677824  2.2794811    1.0779092  2.0579382    1.0786497
## 6  0.1749079 -0.4520293 -0.6671364   -0.7415001 -0.9022913   -0.4241459

Clustering

Numero de clusters

Inicialmente se calculan las distancias para los valores estandarizados. Debido a la cantidad de registros, un dendograma no resulta muy util, puesto que seria imposible de leer, por esto, se utilizara el metodo del codo y el indice de Silhouette promedio para determinar el numero optimo de clusters

dis_df_clus = dist(df_clus, method = "euclidean")

# Metodo del codo
fviz_nbclust(df_clus, # data  
             hcut, # clustering algorithm 
             iter.max = 200, # the maximum number of iterations allowed.
             method = "wss") # elbow method

El metodo del codo solo muestra una caida abrupta del primer a segundo cluster, pero de ahi en adelante es relativamente continue, por lo que queda como un ejercicio de interpretacion abierta si se deberia incluir el tercer cluster, para tratar de ganar mas contexto, se utiliza el coeficiente de Silhouette promedio para identificar el numero optimo de clusters

# Indice de Silhouette promedio
fviz_nbclust(df_clus, # data
             hcut, # clustering algorithm
             method = "silhouette") # silhouette

El resultado es ahora mucho mas evidente, el numero optimo de clusters para el dataset es 2.

Creacion de clusters

# Cluster jerarquico con el método complete
hc_df <- hclust(dis_df_clus, method = 'complete')

# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_df, k = 2)

# asignamos los clusters
assigned_cluster <- df_clus %>% mutate(cluster = as.factor(cluster_assigments))
head(assigned_cluster)
##      estrato    preciom  areaconst parqueaderos     banios habitaciones cluster
## 1  1.2544421  1.2984933  0.4500474    0.1682046  1.3178809    0.3272519       1
## 2 -0.9046263  2.2812429  4.5210796    1.0779092  2.0579382    2.5814453       1
## 3  1.2544421  2.5883521  3.0748870    1.9876139  2.7979956    1.0786497       1
## 4  1.2544421  0.1714025 -0.1067367    0.1682046  0.5778235    0.3272519       1
## 5  1.2544421  1.2677824  2.2794811    1.0779092  2.0579382    1.0786497       1
## 6  0.1749079 -0.4520293 -0.6671364   -0.7415001 -0.9022913   -0.4241459       1

Analisis de resultados

cluster_results = assigned_cluster %>% group_by(cluster) #Agrupacion por clusters

#Creacion de tabla de medias
resultados <- cluster_results %>%
  summarize(
      Media_estrato = mean(estrato),
      Media_preciom = mean(preciom),
      Media_areaconst = mean(areaconst),
      Media_parqueaderos = mean(parqueaderos),
      Media_banios = mean(banios),
      Media_habitaciones = mean(habitaciones))

# Muestra los resultados
print(resultados)
## # A tibble: 2 × 7
##   cluster Media_estrato Media_preciom Media_areaconst Media_parqueaderos
##   <fct>           <dbl>         <dbl>           <dbl>              <dbl>
## 1 1            -0.00656       -0.0199         -0.0414            -0.0217
## 2 2             0.846          2.57            5.34               2.80  
## # ℹ 2 more variables: Media_banios <dbl>, Media_habitaciones <dbl>
# gráfico de puntos
ggplot(assigned_cluster, aes(x = preciom, y = areaconst, color = cluster)) +
geom_point(size = 4) +
geom_text(aes(label = cluster), vjust = -.8) + # Agregar etiquetas del clúster
theme_classic()

Como podemos ver se crean dos clusters que pueden describir dos tipos de viviendas, Viviendas Tipo 1 caracterizadas por ser de menor area y mayoritariamente tener un menor precio, y Viviendas Tipo 2, viviendas con mayor area y precios premium, pero es dificil atribuir numeros a este resultado, puesto que los valores estan escalados. Por esto, aprovechando que el orden de los registros no se altera, vamos a traer el cluster asignado a nuestro dataset original para poder repetir la grafica con valores en las unidades originales

#Se agrega el cluster al dataset original respetando el orden de los registros
df_clus_og_ = cbind(df_clus_og, Cluster = assigned_cluster$cluster)
head(df_clus_og_)
##   estrato preciom areaconst parqueaderos banios habitaciones Cluster
## 1       6     880     237.0            2      5            4       1
## 2       4    1200     800.0            3      6            7       1
## 3       6    1300     600.0            4      7            5       1
## 4       6     513     160.0            2      4            4       1
## 5       6     870     490.0            3      6            5       1
## 6       5     310      82.5            1      2            3       1
clus_og = df_clus_og_ %>% group_by(Cluster)

resultados_og <- clus_og %>%
  summarize(
      Media_estrato = mean(estrato),
      Media_preciom = mean(preciom),
      Media_areaconst = mean(areaconst),
      Media_parqueaderos = mean(parqueaderos),
      Media_banios = mean(banios),
      Media_habitaciones = mean(habitaciones))

# Muestra los resultados
print(resultados_og)
## # A tibble: 2 × 7
##   Cluster Media_estrato Media_preciom Media_areaconst Media_parqueaderos
##   <fct>           <dbl>         <dbl>           <dbl>              <dbl>
## 1 1                4.83          451.            169.               1.79
## 2 2                5.62         1292.            914.               4.89
## # ℹ 2 more variables: Media_banios <dbl>, Media_habitaciones <dbl>
# gráfico de puntos
ggplot(df_clus_og_, aes(x = preciom, y = areaconst, color = Cluster)) +
geom_point(size = 4) +
geom_text(aes(label = Cluster), vjust = -.8) + # Agregar etiquetas del clúster
theme_classic()

Viendo los resultados con las unidades originales, podemos ver que el cluster 2, lo que denominamos Viviendas Tipo 2, esta compuesto por viviendas con un area construida promedio de 900m, vs los 170m de las Viviendas Tipo 1 y un precio casi triplicado, lo que podria identificarlas como casas de campo, haciendas, o salones de eventos.

Analisis de correspondencia

Seleccion de variables

Para el analisis de correspondencia se utilizan las variables categoricas del dataset, pero dentro las categoricas, la variable barrio tiene demasiados resultados (386), por lo que vamos a omitirla y a usar la variable estrato como variable categorica ordinal

corresp = df %>% select(zona, tipo, estrato)
head(corresp)
## # A tibble: 6 × 3
##   zona       tipo        estrato
##   <chr>      <chr>         <dbl>
## 1 zona sur   casa              6
## 2 zona oeste casa              4
## 3 zona sur   casa              6
## 4 zona sur   casa              6
## 5 zona sur   casa              6
## 6 zona sur   apartamento       5

Preprocesamiento de datos

Para poder realizar analisis de correspondencia necesitamos garantizar que las variables sean categoricas, por lo que cambiaremos el tipo de la variable estrato a factor

corresp$estrato = as.factor(corresp$estrato)
str(corresp)
## tibble [4,808 × 3] (S3: tbl_df/tbl/data.frame)
##  $ zona   : chr [1:4808] "zona sur" "zona oeste" "zona sur" "zona sur" ...
##  $ tipo   : chr [1:4808] "casa" "casa" "casa" "casa" ...
##  $ estrato: Factor w/ 4 levels "3","4","5","6": 4 2 4 4 4 3 2 4 3 3 ...
##  - attr(*, "na.action")= 'omit' Named int [1:3514] 3 4 12 13 15 16 17 21 22 29 ...
##   ..- attr(*, "names")= chr [1:3514] "3" "4" "12" "13" ...

Adicional a esto verificaremos que no hay valores faltantes

md.pattern(corresp, rotate.names = TRUE)

Analisis

Realizamos una tabla cruzada con los valores de estrato y zona

tabla <- table(corresp$zona, corresp$estrato)
tabla
##               
##                   3    4    5    6
##   zona centro    33    3    0    0
##   zona norte    141  184  482   79
##   zona oeste     19   51  181  502
##   zona oriente   94    2    1    0
##   zona sur      147  973 1195  721
chisq.test(tabla)
## Warning in chisq.test(tabla): Chi-squared approximation may be incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla
## X-squared = 2172.8, df = 12, p-value < 2.2e-16

El resultado indica que se rechaza la hipótesis de independencia de las variables (p-value: 0.0000), indicando grado tipo de relación entre ellas.

Vamos a realizar el analisis de correspondencia que consiste en estimar las coordenadas para cada uno de los niveles de ambas variables

res.ca <- CA(tabla)

El grafico nos muestra las siguientes relaciones:

  • El estrato 6 se encuentra ubicado en la Zona Oeste
  • Los estratos 4 y 5 están ubicados principalmente en la Zona Sur y en menor medida en la zona Norte
  • El estrato 3 está presente en las Zonas Oriente, Centro y norte

Ahora vamos a proceder a verificar las dimensiones y su porcentaje de varianza explicado

valores_prop <- res.ca$eig 
valores_prop
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.29526876              65.338252                          65.33825
## dim 2 0.13919088              30.800716                          96.13897
## dim 3 0.01744831               3.861032                         100.00000
fviz_screeplot(res.ca, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")

Podemos observar que solo con dos dimensiones explicamos el 97.6% de la variabilidad acumulada