Pontificia Universidad Javeriana Cali

Maestría en Ciencia de Datos

Modelos Estadísticos para la toma de decisiones

Brayan Ramírez

Actividad 1

##Problema Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.

#instaladores
install.packages("learnr")          # solo una vez
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
## (as 'lib' is unspecified)
install.packages("devtools")     # solo una vez
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
## (as 'lib' is unspecified)
#carga de paquetes
devtools::install_github("dgonxalex80/paqueteMOD", force = TRUE) #descarga paquete 
## Downloading GitHub repo dgonxalex80/paqueteMOD@HEAD
## ── R CMD build ─────────────────────────────────────────────────────────────────
##   
   checking for file ‘/tmp/RtmpcGVbIB/remotes5dc17f95960/dgonxalex80-paqueteMOD-f93b750/DESCRIPTION’ ...
  
✔  checking for file ‘/tmp/RtmpcGVbIB/remotes5dc17f95960/dgonxalex80-paqueteMOD-f93b750/DESCRIPTION’
## 
  
─  preparing ‘paqueteDAT’:
##    checking DESCRIPTION meta-information ...
  
✔  checking DESCRIPTION meta-information
## 
  
─  checking for LF line-endings in source and make files and shell scripts
## ─  checking for empty or unneeded directories
## 
  
─  building ‘paqueteDAT_0.1.0.tar.gz’
## 
  
   Warning: invalid uid value replaced by that for user 'nobody'
## 
  
   
## 
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
## (as 'lib' is unspecified)
library(paqueteDAT)
data(vivienda)
str(vivienda)
## Classes 'spec_tbl_df', 'tbl_df', 'tbl' and 'data.frame': 8322 obs. of  13 variables:
##  $ id          : num  1147 1169 1350 5992 1212 ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr  NA NA NA "02" ...
##  $ estrato     : num  3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num  250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num  1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num  3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num  6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr  "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr  "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num  3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(knitr)
library(table1)
## 
## Attaching package: 'table1'
## The following objects are masked from 'package:base':
## 
##     units, units<-
library(tidyr)
vivienda
## # A tibble: 8,322 × 13
##       id zona   piso  estrato preciom areaconst parqueaderos banios habitaciones
##    <dbl> <chr>  <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
##  1  1147 Zona … <NA>        3     250        70            1      3            6
##  2  1169 Zona … <NA>        3     320       120            1      2            3
##  3  1350 Zona … <NA>        3     350       220            2      2            4
##  4  5992 Zona … 02          4     400       280            3      5            3
##  5  1212 Zona … 01          5     260        90            1      2            3
##  6  1724 Zona … 01          5     240        87            1      3            3
##  7  2326 Zona … 01          4     220        52            2      2            3
##  8  4386 Zona … 01          5     310       137            2      3            4
##  9  1209 Zona … 02          5     320       150            2      4            6
## 10  1592 Zona … 02          5     780       380            2      3            3
## # ℹ 8,312 more rows
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
class(vivienda) #verificación del tipo de tabla 
## [1] "spec_tbl_df" "tbl_df"      "tbl"         "data.frame"
head(vivienda)
## # A tibble: 6 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <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… 02          4     400       280            3      5            3
## 5  1212 Zona N… 01          5     260        90            1      2            3
## 6  1724 Zona N… 01          5     240        87            1      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
summary(vivienda)
##        id           zona               piso              estrato     
##  Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
##  1st Qu.:2080   Class :character   Class :character   1st Qu.:4.000  
##  Median :4160   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4160                                         Mean   :4.634  
##  3rd Qu.:6240                                         3rd Qu.:5.000  
##  Max.   :8319                                         Max.   :6.000  
##  NA's   :3                                            NA's   :3      
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 330.0   Median : 123.0   Median : 2.000   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9   Mean   : 1.835   Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
##  NA's   :2        NA's   :3        NA's   :1605     NA's   :3       
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8322        Length:8322        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.605                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##  NA's   :3                                              NA's   :3       
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.381  
##  Median :3.416  
##  Mean   :3.418  
##  3rd Qu.:3.452  
##  Max.   :3.498  
##  NA's   :3

#limpieza de datos

# Reemplazar los valores NA en la columna "parqueaderos" con 0
vivienda$parqueaderos <- ifelse(is.na(vivienda$parqueaderos), 0, vivienda$parqueaderos)
# Eliminar filas con valores faltantes
dfv <- na.omit(vivienda)
summary(dfv)
##        id           zona               piso              estrato     
##  Min.   :   1   Length:5684        Length:5684        Min.   :3.000  
##  1st Qu.:2141   Class :character   Class :character   1st Qu.:4.000  
##  Median :4232   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4209                                         Mean   :4.659  
##  3rd Qu.:6265                                         3rd Qu.:5.000  
##  Max.   :8316                                         Max.   :6.000  
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  35.0   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.: 217.5   1st Qu.:  78.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 320.0   Median : 117.0   Median : 1.000   Median : 3.000  
##  Mean   : 423.7   Mean   : 167.9   Mean   : 1.535   Mean   : 3.087  
##  3rd Qu.: 520.0   3rd Qu.: 217.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1500.0   Max.   :10.000   Max.   :10.000  
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:5684        Length:5684        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.555                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.380  
##  Median :3.413  
##  Mean   :3.416  
##  3rd Qu.:3.452  
##  Max.   :3.498
# Verificar el tipo de datos en cada columna del dataframe
tipos <- sapply(dfv, class)

# Mostrar el resultado
print(tipos)
##           id         zona         piso      estrato      preciom    areaconst 
##    "numeric"  "character"  "character"    "numeric"    "numeric"    "numeric" 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##    "numeric"    "numeric"    "numeric"  "character"  "character"    "numeric" 
##      latitud 
##    "numeric"

Ejercicio 1

  1. Análisis de Componentes Principales: Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y preferencias del mercado. se filtrará la base de datos para excluir columnas no numéricas, además, no se incluirá el precio de las viviendas, ya que se buscará que los componentes principales se analicen respecto a esta variable.
# Filtrar las columnas numéricas para el PCA
datos_cuanti <- dfv[, c("preciom", "estrato", "areaconst", "parqueaderos", "banios", "habitaciones", "longitud", "latitud")]
# Estandarizar los datos 
escalados<- scale(datos_cuanti)
head(escalados)
##          preciom    estrato  areaconst parqueaderos      banios habitaciones
## [1,] -0.07388895 -0.6578315  0.8208022    1.2156117  1.37212934   -0.3961809
## [2,] -0.51100887  0.3408636 -0.5699215   -0.4443371 -0.77917165   -0.3961809
## [3,] -0.57345457  0.3408636 -0.5918803   -0.4443371 -0.06207132   -0.3961809
## [4,] -0.63590027 -0.6578315 -0.8480663    0.3856373 -0.77917165   -0.3961809
## [5,] -0.35489461  0.3408636 -0.2259004    0.3856373 -0.06207132    0.3182519
## [6,] -0.32367176  0.3408636 -0.1307456    0.3856373  0.65502901    1.7471176
##        longitud    latitud
## [1,] -0.6424705  0.4381262
## [2,]  0.9038453  0.9986195
## [3,]  0.6996149 -1.0923885
## [4,]  0.5397317  0.2334793
## [5,] -0.1202242 -0.7817848
## [6,]  0.9090970  1.4855055
# Realizar el PCA en los datos estandarizados
pca_datos_cuanti <- prcomp(escalados)
# Ver los componentes principales
pca_datos_cuanti$rotation
##                     PC1         PC2         PC3         PC4         PC5
## preciom       0.4611262 -0.04242179  0.20424687 -0.12980872  0.05650474
## estrato       0.3386827 -0.44609133  0.26260112 -0.13318759 -0.61048895
## areaconst     0.4179851  0.29279461 -0.02581594 -0.02611501  0.31295973
## parqueaderos  0.4099186 -0.10470442  0.18485412 -0.28406401  0.54289449
## banios        0.4452032  0.19466713 -0.10260920  0.01860872 -0.37395093
## habitaciones  0.2656878  0.55356820 -0.35426581  0.27791516 -0.21601482
## longitud     -0.2226467  0.44976439  0.07916700 -0.83459954 -0.20601690
## latitud      -0.1074452  0.39482929  0.84391115  0.33150701 -0.04987550
##                       PC6         PC7         PC8
## preciom      -0.363710724  0.19655988  0.74356026
## estrato       0.035321834 -0.45143009 -0.14786741
## areaconst    -0.577054698 -0.29842082 -0.46714070
## parqueaderos  0.634265895 -0.05716557 -0.07645129
## banios        0.126202227  0.69960361 -0.32834792
## habitaciones  0.331047578 -0.41634654  0.30105116
## longitud     -0.003993236 -0.04836338  0.02277551
## latitud       0.075140407  0.01111467 -0.04717262

Al analizar el primer componente principal se puede observar que los pesos de las varibales área, parqueaderos y baños son similares, siendo superiores a estrato y habitaciones, y mucho más a latitud y longitud.

En PC1 parece estar relacionado principalmente con el tamaño y características de las casas. Casas con valores más altos en PC1 tendrían un mayor estrato, mayor área construida, más parqueaderos, más baños y más habitaciones. Esto sugiere que el primer componente se podría etiquetar como tamaño y comodidades de las casas.

En el segundo componente el estrato es la variable que tiene mayor peso, superando al resto por bastante. Esto puede estar relacionado con que el estrato de las casas y su ubicación geográfica (latitud y longitud con carga más o menos alta en PC2). Lo que se puede ver representado en que las casas con valores más altos en PC2 pueden estar ubicadas en áreas geográficas específicas, posiblemente hacia el nororiente.

# Ver la varianza explicada por cada componente
prop_varianza<-pca_datos_cuanti$sdev^2 / sum(pca_datos_cuanti$sdev^2) * 100
prop_varianza
## [1] 46.409438 18.454264 11.330889  8.857762  5.118213  4.638901  2.962178
## [8]  2.228356
# Crea la gráfica de barras donde se muestre la varianza por componente
ggplot(data = data.frame(prop_varianza, pc = 1:length(prop_varianza)),
       aes(x = pc, y = prop_varianza)) +
  geom_col(width = 0.8, fill = "red") +
  geom_text(aes(label = sprintf("%.1f", prop_varianza)), vjust = -0.75, size = 4) + 
  scale_y_continuous(limits = c(0, 60)) +
  scale_x_continuous(breaks = seq(1, length(prop_varianza)), labels = seq(1, length(prop_varianza))) + # Corrige los números de los PC
  theme_bw() +
  labs(x = "Componente principal",
       y = "%", title = "Variación explicada por cada componente") +
  xlab("Componente principal") +
  ylab("%") +
  ggtitle("Variación explicada por cada componente")

El componente 1 explica el 46% de la variación de los datos, mientras que el componente 2 el 18%, juntos explican el 64% de la variación de los datos.

# Proyectar los datos en el espacio de los componentes principales
PCA <- as.data.frame(predict(pca_datos_cuanti, newdata = escalados))

head(PCA)
##          PC1       PC2        PC3         PC4        PC5        PC6        PC7
## 1   7.799656 -12.58214   5.712546  34.4335713   7.855026  1.5652450  2.9705404
## 2 -14.620101  32.58380  24.109418 -36.2863516 -12.172392  1.1652737 -2.6952505
## 3  -6.471368   7.97061 -18.252803 -42.5801706  -7.464869 -2.4052329 -2.3039351
## 4  -8.018909  16.31830   7.156176 -24.5626889  -5.918441  0.3053844 -1.4556445
## 5   3.816900 -10.45964 -15.948867  -0.2941157   2.292195 -1.0629993 -0.1619117
## 6 -14.765955  38.20464  33.245020 -32.5096033 -13.145128  3.0932237 -2.5414465
##          PC8
## 1 -1.7295761
## 2  0.1737849
## 3  2.0457078
## 4  0.6314622
## 5  0.7129369
## 6 -0.2880826
biplot(x = pca_datos_cuanti, 
       scale = 1, 
       cex = 0.8, 
       col = c("gray", "blue4"))

Se analizarán los ángulos de la disposición de los vectores de cada variable teniendo en cuenta lo siguiente:

Si los vectores forman un ángulo cercano a 90°: las variables son independientes entre si

Si los vectores forman un ángulo cercano a 0°: muy relacionadas entre si

Si los vectores forman un ángulo cercano a 180°: son inversamente proporcionales

Las variables que están mejor correlacionadas entre si son: Latitud y longitud; area construida y baños; parqueaderos y precios. cabe aclarar que el área, los baños, los parqueaderos y el precio se relacionan estrechamente entre si. Pues si la vivienda tiene mayor área tiene espacio para más baños y parqueadero, lo que le dará más valor.

Las variables que son independientes entre sí: Habitaciones y estrato; longitud y habitaciones, latitud y baños.

las variables que son inversamente proporcionales son: longitud (y por su cercanía latitud) con el estrato, es decir: el estrato aumenta conforme disminuye la longitud disminuye, quiere decir que, en general, el estatrato podría ser menor hacia el sur-occidente y mayor hacia el nor-oriente.

Como recomendación para reducir la dimensionalidad de la base de datos, y teniendo en cuenta los hallazgos del análisis de componente principales recomiendo no tener en cuenta las siguientes variables numéricas, esta eliminación no afectará la varianza de los datos en gran medida:

baños: el área contruida tiene una estrecha relación con esta variable y explica en gran medida el mismo fenómeno de comodidad en el inmueble.

parqueaderos: la cantidad de parqueaderos explica la variación de la información de forma similar al precio de los inmuebles, por lo tanto, sugiero que se suprima esta información.

longitud y latitud: el estrato puede explicar el comportamiento de los datos de la ubicación geográfica, al parecer la ciudad tiene una distribución de estratos relativa a la ubicación geográfica de los inmuebles, así que si no es necesario para el análisis tener que ubicar geográficamente los inmuebles, se puede obtener información que aporte a las decisiones relacionadas con los precios, sólo considerando el estrato.

Por medio del PCA se logró obtener valiosa información de la base de datos recolectada, y además, se lograron eliminar 4 variables cuantitativas para reducir la dimensionalidad y facilitar los cálculos y análisis posteriores.

Ejercico 2

  1. Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas y demandas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.
#librerias:
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ readr     2.1.4
## ✔ lubridate 1.9.2     ✔ stringr   1.5.0
## ✔ purrr     1.0.1     ✔ tibble    3.2.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(cluster)
library(leaflet)
# Elegir las variables numéricas para el análisis
datac <- dfv %>%
  select(estrato, preciom, areaconst, parqueaderos, banios, habitaciones, latitud, longitud)
datac
## # A tibble: 5,684 × 8
##    estrato preciom areaconst parqueaderos banios habitaciones latitud longitud
##      <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>   <dbl>    <dbl>
##  1       4     400       280            3      5            3    3.44    -76.5
##  2       5     260        90            1      2            3    3.46    -76.5
##  3       5     240        87            1      3            3    3.37    -76.5
##  4       4     220        52            2      2            3    3.43    -76.5
##  5       5     310       137            2      3            4    3.38    -76.5
##  6       5     320       150            2      4            6    3.48    -76.5
##  7       5     780       380            2      3            3    3.49    -76.5
##  8       6     750       445            0      7            6    3.39    -76.5
##  9       4     625       355            3      5            5    3.41    -76.5
## 10       5     750       237            2      6            6    3.37    -76.5
## # ℹ 5,674 more rows
# Hacer escalado de las variables
scaled_datac <- scale(datac)
head(scaled_datac)
##         estrato     preciom  areaconst parqueaderos      banios habitaciones
## [1,] -0.6578315 -0.07388895  0.8208022    1.2156117  1.37212934   -0.3961809
## [2,]  0.3408636 -0.51100887 -0.5699215   -0.4443371 -0.77917165   -0.3961809
## [3,]  0.3408636 -0.57345457 -0.5918803   -0.4443371 -0.06207132   -0.3961809
## [4,] -0.6578315 -0.63590027 -0.8480663    0.3856373 -0.77917165   -0.3961809
## [5,]  0.3408636 -0.35489461 -0.2259004    0.3856373 -0.06207132    0.3182519
## [6,]  0.3408636 -0.32367176 -0.1307456    0.3856373  0.65502901    1.7471176
##         latitud   longitud
## [1,]  0.4381262 -0.6424705
## [2,]  0.9986195  0.9038453
## [3,] -1.0923885  0.6996149
## [4,]  0.2334793  0.5397317
## [5,] -0.7817848 -0.1202242
## [6,]  1.4855055  0.9090970

Análisis de de conglomerados con K-means

#definición de la cantidad de clusters usando método del codo

# Aplicación del método del codo
wcss <- vector("numeric", length = 10)  # Prueba hasta 10 clústers

for (i in 1:10) {
  kmeans_model <- kmeans(scaled_datac, centers = i)
  wcss[i] <- kmeans_model$tot.withinss
}

# Gráfico del método del codo
ggplot(data.frame(k = 1:10, wcss = wcss), aes(x = k, y = wcss)) +
  geom_point(color = 'blue') +
  geom_line(color = 'blue') +
  labs(title = "Método del Codo", x = "Cantidad de Centroides k", y = "WCSS")

Al revisar la gráfica creada con el método del codo se pueden observar que la variación disminuye comenzando en el intervalo k=[3,6] por lo tanto se revisará la distribución de los grupos para estos valores k y se decidirá cuál es el que mejor agrupa los datos despues de hacer una inspección de cada una de las gráficas generadas.

# Análisis de conglomerados (K-means)
set.seed(123)  # Para reproducibilidad de resultados
clust3 <- 3  # Número de clusters deseado

modelo_kmeans3 <- kmeans(scaled_datac, centers = clust3) #crear el modelo k-means

asig3 <- modelo_kmeans3$cluster # Asignación de las observaciones a los clusters

# Agregar la asignación de clusters al dataframe original
closterizado3 <- dfv %>%
  mutate(cluster = asig3)
# Análisis de conglomerados (K-means)
set.seed(123)  # Para reproducibilidad de resultados
clust4 <- 4  # Número de clusters deseado

modelo_kmeans4 <- kmeans(scaled_datac, centers = clust4) #crear el modelo k-means

asig4 <- modelo_kmeans4$cluster # Asignación de las observaciones a los clusters

# Agregar la asignación de clusters al dataframe original
closterizado4 <- dfv %>%
  mutate(cluster = asig4)
# Análisis de conglomerados (K-means)
set.seed(123)  # Para reproducibilidad de resultados
clust5 <- 5  # Número de clusters deseado

modelo_kmeans5 <- kmeans(scaled_datac, centers = clust5) #crearel modelo k-means

asig5 <- modelo_kmeans5$cluster # Asignación de las observaciones a los clusters

# Agregar la asignación de clusters al dataframe original
closterizado5 <- dfv %>%
  mutate(cluster = asig5)
# Análisis de conglomerados (K-means)
set.seed(123)  # Para reproducibilidad de resultados
clust6 <- 6  # Número de clusters deseado

modelo_kmeans6 <- kmeans(scaled_datac, centers = 6) #crearel modelo k-means

asig6 <- modelo_kmeans6$cluster # Asignación de las observaciones a los clusters

# Agregar la asignación de clusters al dataframe original
closterizado6 <- dfv %>%
  mutate(cluster = asig6)
# Análisis para cada cantidad de clústers

A_cluster3 <- closterizado3 %>%
  group_by(cluster) %>%
  summarise(
    avg_precio = mean(preciom),
    avg_area = mean(areaconst),
    avg_parqueaderos = mean(parqueaderos),
    avg_banios = mean(banios),
    avg_habitaciones = mean(habitaciones),
    avg_longitud = mean(longitud),
    avg_latitud = mean(latitud),
    num_properties = n()
  )

A_cluster4 <- closterizado4 %>%
  group_by(cluster) %>%
  summarise(
    avg_precio = mean(preciom),
    avg_area = mean(areaconst),
    avg_parqueaderos = mean(parqueaderos),
    avg_banios = mean(banios),
    avg_habitaciones = mean(habitaciones),
    avg_longitud = mean(longitud),
    avg_latitud = mean(latitud),
    num_properties = n()
  )

A_cluster5 <- closterizado5 %>%
  group_by(cluster) %>%
  summarise(
    avg_precio = mean(preciom),
    avg_area = mean(areaconst),
    avg_parqueaderos = mean(parqueaderos),
    avg_banios = mean(banios),
    avg_habitaciones = mean(habitaciones),
    avg_longitud = mean(longitud),
    avg_latitud = mean(latitud),
    num_properties = n()
  )

A_cluster6 <- closterizado6 %>%
  group_by(cluster) %>%
  summarise(
    avg_precio = mean(preciom),
    avg_area = mean(areaconst),
    avg_parqueaderos = mean(parqueaderos),
    avg_banios = mean(banios),
    avg_habitaciones = mean(habitaciones),
    avg_longitud = mean(longitud),
    avg_latitud = mean(latitud),
    num_properties = n()
  )
# Gráfico de nube de puntos con clústers
g3<-ggplot(closterizado3, aes(x = preciom, y = areaconst, color = cluster)) +
  scale_colour_gradientn(colours=rainbow(6)) +
  geom_point() +
  labs(x = "Área Construida", y = "Precio por metro cuadrado", title = "Nube de Puntos con 3 Clústers")
g3

Al observar los datos, se puede visualizar una nube de puntos concentrada sobre cuadrante formado por el rectángulo (0,0)(0,1000)(1000,500)(500,0), en la parte restante se ve una nube de puntos de gran dispersión. los que no deja claro a simple vista la creación de grupos fácilmente diferenciados. Para revisar si estos efectos se reducen realizaré un análisis con 4 clústers pero sin normalizar los datos, usando las variables área construida y precio. No sin antes analizar el gráfico de 4 clústers con los datos normalizados.

En la gráfica se puede observar cada cluster con un color diferente, como puede verse, con k = 3 el modelo asigna clases medianamente consistentes a los datos, al agrupar los datos que existen en la zona superior y derecha de la gráfica, en donde los grupos son evidentes. Mientras tanto, los datos del grupo inferior izquierdo son agrupados en 3 clases distintas pero se observa que la distribución de puntos es adecuada para cada grupo.

Se validará con k = 4, 5 y 6:

# Gráfico de nube de puntos con clústers
g4<-ggplot(closterizado4, aes(x = preciom, y = areaconst, color = cluster)) +
  scale_colour_gradientn(colours=rainbow(6)) +
  geom_point() +
  labs(x = "Área Construida", y = "Precio por metro cuadrado", title = "Nube de Puntos con 4 Clústers")
g4

# Gráfico de nube de puntos con clústers
g5<-ggplot(closterizado5, aes(x = preciom, y = areaconst, color = cluster)) +
  scale_colour_gradientn(colours=rainbow(6)) +
  geom_point() +
  labs(x = "Área Construida", y = "Precio por metro cuadrado", title = "Nube de Puntos con 5 Clústers")
g5

# Gráfico de nube de puntos con clústers
g6<-ggplot(closterizado6, aes(x = preciom, y = areaconst, color = cluster)) +
  scale_colour_gradientn(colours=rainbow(4)) +
  geom_point() +
  labs(x = "areacons", y = "Precio por metro cuadrado", title = "Nube de Puntos con 6 Clústers")
g6

Al incrementar el valor de k se dan agrupamientos que recogen partes específicas de los datos, incluso dividiendo en dos grupos distintos lo que inicialmente aparecía como un solo grupo, es el caso de los datos presentes en la parte inferior izquierda de la gráfica cuando k = 5 y 6.

Teniendo en cuenta lo anterior se optará por tomar la división en 4 clústers y así poder generar análisis como el siguiente:

# Crear un mapa interactivo
mapa <- leaflet(A_cluster4) %>%
  addTiles() %>%
  addMarkers(
    lng = ~avg_longitud,
    lat = ~avg_latitud,
    label = ~as.character(cluster),
    popup = ~paste("Cluster:", cluster, "<br>",
                   "Precio Promedio:", avg_precio, "<br>",
                   "Área Construida Promedio:", avg_area, "<br>",
                   "Número de Propiedades:", num_properties)
  )

mapa

Al interactur con el mapa se pueden ver las métricas de cada clúster:

Cluster 1 Precio Promedio: 539.684460260973

Área Construida Promedio: 192.70265717675

Número de Propiedades: 1686

Cluster 2 Precio Promedio: 237.480500722195

Área Construida Promedio: 90.3705536831969

Número de Propiedades: 2077

Cluster 3 Precio Promedio: 241.98296836983

Área Construida Promedio: 120.86801297648

Número de Propiedades: 1233

Cluster 4 Precio Promedio: 1027.02325581395

Área Construida Promedio: 425.150174418605

Número de Propiedades: 688

Al igual que como se puede observar en el gráfico de dispersión, los centroides que representan los clusters se aglomeran sobre una zona geográfica particular, que muestra que la mayor cantidad de viviendas se concentra en el centro de la ciudad.

El clúster 1 tiene la segunda cantidad de viviendas más grande y con un precio aproximadamente de 100 millones por encima del promedio.

En el clúster 2 y 3 se agrupan viviendas con precios y áreas similares.

En el clúster 4 se agrupan la menor cantidad de viviendas y corresponden a las que son más grandes y costosas.

# Seleccionar las columnas relevantes para el análisis
data_for_clustering <- dfv %>% select(areaconst, preciom)

# Realizar el K-means con 4 clusters
num_clusters <- 4
kmeans_model <- kmeans(data_for_clustering, centers = num_clusters)
cluster_assignments <- kmeans_model$cluster

# Agregar la asignación de clústeres al dataframe original
vivienda_clustered <- dfv %>%
  mutate(cluster = cluster_assignments)

# Crear el gráfico de dispersión con los clusters
ggplot(vivienda_clustered, aes(x = areaconst, y = preciom, color = factor(cluster))) +
  geom_point() +
  labs(x = "Área Construida", y = "Precio por metro cuadrado", title = "Agrupación de Datos con K-means") +
  theme_minimal()

A_clusterO <- vivienda_clustered %>%
  group_by(cluster) %>%
  summarise(
    m_precio = mean(preciom),
    m_area = mean(areaconst),
    m_parqueaderos = mean(parqueaderos),
    m_banios = mean(banios),
    m_habitaciones = mean(habitaciones),
    m_longitud = mean(longitud),
    m_latitud = mean(latitud),
    num_properties = n()
  )
# Crear un mapa interactivo
mapa <- leaflet(A_clusterO) %>%
  addTiles() %>%
  addMarkers(
    lng = ~m_longitud,
    lat = ~m_latitud,
    label = ~as.character(cluster),
    popup = ~paste("Cluster:", cluster, "<br>",
                   "Precio Promedio:", m_precio, "<br>",
                   "Área Construida Promedio:", m_area, "<br>",
                   "Número de Propiedades:", num_properties)
  )

mapa

Al observar la agrupación realizada en las dos componentes principales creadas en el primer ejercicio se verifica que los centros de los mismos en relación con su ubicación geográfica tienden a ubicarse en la zona sur occidental de la ciudad, el peso que dan la cantidad de viviendas en este sector puede inferir en esta ubicación lo que indica una zona de bastante comercio y variedad de precios.

Ejercicio 3

  1. Análisis de Correspondencia : Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio) y las variables numéricas (precio, área construida, número de parqueaderos, baños, habitaciones) para identificar patrones de comportamiento del mercado inmobiliario.

Primero realizaré el análisis con las variables tipo de vivienda y zona

#Librerias
library(FactoMineR)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
# Crear una tabla de contingencia entre las variables "zona" y "tipo de vivienda"
tabla_contingencia1 <- table(dfv$zona, dfv$tipo)

tabla_contingencia1
##               
##                Apartamento Casa
##   Zona Centro           15   54
##   Zona Norte           789  350
##   Zona Oeste           703  102
##   Zona Oriente          47  161
##   Zona Sur            2165 1298
chisq.test(tabla_contingencia1)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_contingencia1
## X-squared = 418.03, df = 4, p-value < 2.2e-16
# análisis de correspondencias
re_ac1 <- CA(tabla_contingencia1, graph = TRUE)

re_ac1
## **Results of the Correspondence Analysis (CA)**
## The row variable has  5  categories; the column variable has 2 categories
## The chi square of independence between the two variables is equal to 418.0344 (p-value =  3.525675e-89 ).
## *The results are available in the following objects:
## 
##    name              description                   
## 1  "$eig"            "eigenvalues"                 
## 2  "$col"            "results for the columns"     
## 3  "$col$coord"      "coord. for the columns"      
## 4  "$col$cos2"       "cos2 for the columns"        
## 5  "$col$contrib"    "contributions of the columns"
## 6  "$row"            "results for the rows"        
## 7  "$row$coord"      "coord. for the rows"         
## 8  "$row$cos2"       "cos2 for the rows"           
## 9  "$row$contrib"    "contributions of the rows"   
## 10 "$call"           "summary called parameters"   
## 11 "$call$marge.col" "weights of the columns"      
## 12 "$call$marge.row" "weights of the rows"

El resultado sugiere que hay una asociación significativa entre las zonas y los tipos de vivienda en el conjunto de datos. es decir, el lugar donde se encuentra una vivienda parece estar relacionado con el tipo de vivienda que es más probable encontrar en esa zona.

Teniendo en cuenta lo anterior, se generará un factor map para analizar más a profundidad esta relación.

Ahora añadiré una columna adicional para convetir los valores del área en una variable categórica, usaré los valores de los cuartiles para definir 4 grupos de tamaño de viviendas según como se muestra a continuación.

“Compacta”: Esta categoría podría describir viviendas con un área de construcción relativamente pequeña. entre 35 y 78 mts^2 “Moderada”: Para viviendas de tamaño mediano que no son ni muy pequeñas ni muy grandes. entre 78 y 117 mts^2 “Espaciosa”: Para viviendas más grandes que ofrecen un espacio amplio y cómodo. entre 117 y 217 mts^2 “Amplia”: Esta categoría podría englobar viviendas significativamente grandes en comparación con el promedio. más de 217 mts^2

# Crear una nueva columna "tamano" en el dataframe
dfv$tamano <- cut(dfv$areaconst, 
                  breaks = c(35, 78, 117, 217, Inf), 
                  labels = c("Pequeña", "Moderada", "Espaciosa", "Amplia"), 
                  right = FALSE)

# Convertir la columna "tamano" a tipo de dato character
dfv$tamano <- as.character(dfv$tamano)

# Verificar los cambios
str(dfv$tamano)
##  chr [1:5684] "Amplia" "Moderada" "Moderada" "Pequeña" "Espaciosa" ...
# Mostrar df
head(dfv)
## # A tibble: 6 × 14
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  5992 Zona S… 02          4     400       280            3      5            3
## 2  1212 Zona N… 01          5     260        90            1      2            3
## 3  1724 Zona N… 01          5     240        87            1      3            3
## 4  2326 Zona N… 01          4     220        52            2      2            3
## 5  4386 Zona N… 01          5     310       137            2      3            4
## 6  1209 Zona N… 02          5     320       150            2      4            6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   tamano <chr>
summary(dfv)
##        id           zona               piso              estrato     
##  Min.   :   1   Length:5684        Length:5684        Min.   :3.000  
##  1st Qu.:2141   Class :character   Class :character   1st Qu.:4.000  
##  Median :4232   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4209                                         Mean   :4.659  
##  3rd Qu.:6265                                         3rd Qu.:5.000  
##  Max.   :8316                                         Max.   :6.000  
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  35.0   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.: 217.5   1st Qu.:  78.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 320.0   Median : 117.0   Median : 1.000   Median : 3.000  
##  Mean   : 423.7   Mean   : 167.9   Mean   : 1.535   Mean   : 3.087  
##  3rd Qu.: 520.0   3rd Qu.: 217.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1500.0   Max.   :10.000   Max.   :10.000  
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:5684        Length:5684        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.555                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##     latitud         tamano         
##  Min.   :3.333   Length:5684       
##  1st Qu.:3.380   Class :character  
##  Median :3.413   Mode  :character  
##  Mean   :3.416                     
##  3rd Qu.:3.452                     
##  Max.   :3.498
# Crear una tabla de contingencia entre las variables "zona" y "tamano"
tabla_contingencia <- table(dfv$zona, dfv$tamano)

tabla_contingencia
##               
##                Amplia Espaciosa Moderada Pequeña
##   Zona Centro      19        30       13       7
##   Zona Norte      252       221      285     381
##   Zona Oeste      268       309      154      74
##   Zona Oriente     61        80       23      44
##   Zona Sur        826       778      961     898
chisq.test(tabla_contingencia)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_contingencia
## X-squared = 295.93, df = 12, p-value < 2.2e-16

Al observar el valor p se puede verificar que es, en efecto, demasiado pequeño, rechazando la hipótesis nula y dejando ver que existe una relación entre la zona en la que está ubicada el inmueble y su tamaño. En conclusió, la distribución de tamaños de viviendas parece variar según la zona geográfica en la que se encuentran, y esta variación no es probable que sea resultado del azar. Este dato es importante pues permitirá saber de entrada en qué zonas de la ciudad buscar viviendas según el tamaño preferido por los clientes.

#análsis de correspondencias
resultados_ac <- CA(tabla_contingencia)

El gráfico permite establecer las siguientes conclusiones: -Las viviendas pequeñas se ubican por lo general en la zona Norte -Las viviendas de tamaño moderado se ubican al sur de la ciudad -Las viviendas de tamaño espacioso y Amplias, aparecen con más frecuencia en las zonas centro, Oeste y oriente

#Referencias [1] A. Kassambara, “Practical Guide To Principal Component Methods in R,” 2014. [2] CA - Correspondence Analysis in R: Essentials,” Accessed from http://www.sthda.com/english/articles/31-principal-component-methods-in-r-practical-guide/113-ca-correspondence-analysis-in-r-essentials/.