library(readxl)
datos = read_excel("C:/Users/kaqu/Documents/Maestria Ciencia de Datos/Metodos_simulacion_estadistica/Actividad 1/Datos_Vivienda.xlsx")
attach(datos)
ID=1:dim(datos)[1]
datos=data.frame(ID,datos)

kbl(head(datos)) %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
ID Zona piso Estrato precio_millon Area_contruida parqueaderos Banos Habitaciones Tipo Barrio cordenada_longitud Cordenada_latitud
1 Zona Sur 2 6 880 237 2 5 4 Casa pance -76.46300 3.43000
2 Zona Oeste 2 4 1200 800 3 6 7 Casa miraflores -76.46400 3.42800
3 Zona Sur 3 5 250 86 NA 2 3 Apartamento multicentro -76.46400 3.42900
4 Zona Sur NA 6 1280 346 4 6 5 Apartamento ciudad jardv<U+2260>n -76.46400 3.43300
5 Zona Sur 2 6 1300 600 4 7 5 Casa pance -76.46438 3.43463
6 Zona Sur 3 6 513 160 2 4 4 Casa pance -76.46438 3.43463

Datos Viviendas OLX

Esta actividad tiene como objetivo familiarizarnos con posibles retos que nos podemos encontrar a la hora de trabajar con información acerca de viviendas. En este caso se trabaja con un Data Frame asociado a diferentes tipos de viviendas (casas, apartamentos, etc.) cuya fuente es OLX.

Descripción Inicial Pre-Filtro

Se encuentran dentro del DataFrame variables a asociadas a las caractéristicas de las viviendas, tales como ubicación (coordenadas, zona, estrato, etc), como propias (Area conatruida, número de baños, habitaciones,entre otras.)

library(dplyr)
names(datos)
##  [1] "ID"                 "Zona"               "piso"              
##  [4] "Estrato"            "precio_millon"      "Area_contruida"    
##  [7] "parqueaderos"       "Banos"              "Habitaciones"      
## [10] "Tipo"               "Barrio"             "cordenada_longitud"
## [13] "Cordenada_latitud"

¿La base trabajada cuenta con datos faltantes?

library(inspectdf) 
df3 = datos

summary(df3)
##        ID           Zona               piso              Estrato     
##  Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
##  1st Qu.:2081   Class :character   Class :character   1st Qu.:4.000  
##  Median :4162   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4162                                         Mean   :4.634  
##  3rd Qu.:6242                                         3rd Qu.:5.000  
##  Max.   :8322                                         Max.   :6.000  
##                                                       NA's   :3      
##  precio_millon    Area_contruida   parqueaderos           Banos       
##  Min.   :  58.0   Min.   :  30.0   Length:8322        Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.:  80.0   Class :character   1st Qu.: 2.000  
##  Median : 330.0   Median : 123.0   Mode  :character   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9                      Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 229.0                      3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1745.0                      Max.   :10.000  
##  NA's   :2        NA's   :3                           NA's   :3       
##   Habitaciones        Tipo              Barrio          cordenada_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         
##  Cordenada_latitud
##  Min.   :3.333    
##  1st Qu.:3.381    
##  Median :3.416    
##  Mean   :3.418    
##  3rd Qu.:3.452    
##  Max.   :3.498    
##  NA's   :3
nas = inspect_na(df3)
kbl(nas,    caption = "<center><strong>Inspección de NA's por variable </strong></center>") %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
Inspección de NA’s por variable
col_name cnt pcnt
Zona 3 0.0360490
piso 3 0.0360490
Estrato 3 0.0360490
Area_contruida 3 0.0360490
parqueaderos 3 0.0360490
Banos 3 0.0360490
Habitaciones 3 0.0360490
Tipo 3 0.0360490
Barrio 3 0.0360490
cordenada_longitud 3 0.0360490
Cordenada_latitud 3 0.0360490
precio_millon 2 0.0240327
ID 0 0.0000000

Se identifica que la cantidad de NA’s de la base se puede considerar pequeña ya que alcanza tan solo un 3% del total de la base, por lo que de inicio se piensa en ver si es posible recuperar esta información.

Con la revisión de los registros vacíos por variable, se concluye que el porcentaje de los mismos no supera el 3%, valor que podría considerarse como bajo. Dadas las condiciones como fue extraída esta información, la fuente y que la información es de carácter público, podría evaluarse no trabajar con estos datos. A continuación se presenta la tabla que contiene las frecuencias absolutas y relativas de datos faltantes por variable:

Se inspecciona por medio del siguiente gráfico la combinación de campos vacíos por registros y el número de veces que se presenta.

library(VIM) 
capture.output(aggr(df3, numbers=T, prop=F,sortVars = T,cex.axis = 0.5), file=tempfile())

De la figura anterior, se identifica que existen 8319 registros que cuentan con completitud en la data, para las observaciones con datos faltantes se presentan los siguientes casos:

  • 2 registros que cuentan con 12 variables en NA.
  • 1 registros que cuentan con 11 variables en NA.

Datos Atípicos

df4=df3

kbl(descriptivas(df3$precio_millon),    caption = "<center><strong> Comportamiento Precio de Vivienda </strong></center>") %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
Comportamiento Precio de Vivienda
MEDIDA VALOR
Observaciones 8320.000000
Mínimo 58.000000
1er Q 220.000000
Media 433.891947
Mediana 330.000000
Desv Est 328.647244
3er Q 540.000000
Máximo 1999.000000
Asimetría 1.849256
Curtosis 3.672296
atípico leve< -260.000000
atípico leve> 1020.000000
atípico extremo< -740.000000
atípico extremo> 1500.000000
Err Est Media 3.603034
IC(95%) Media Up 440.953893
IC(95%) Media Down 426.830001
par(mfrow = c(2,1))
boxplot(df4$precio_millon, horizontal = TRUE, main = "Boxplot Precio Vivienda General",col="#A6CEE3" )
hist(df4$precio_millon,main = "Histograma Precio Vivienda General" , ylab = "Frecuencia" , xlab = "Precio Vivienda General",col="#A6CEE3")

Se puede observar tanto en los gráficos como en las medidas calculadas que el precio de vivienda en promedio esta alrededor de 434 millones, mientras que la mediana es apenas de 330 millones. Lo que evidencia varios valores atípicos, identificando que el cerco superior esta al rededor de 1000 millones, asi pues, se evidencia asímetria positiva.

Tipos de variables a trabajar

str(datos)
## 'data.frame':    8322 obs. of  13 variables:
##  $ ID                : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Zona              : chr  "Zona Sur" "Zona Oeste" "Zona Sur" "Zona Sur" ...
##  $ piso              : chr  "2" "2" "3" "NA" ...
##  $ Estrato           : num  6 4 5 6 6 6 6 5 4 6 ...
##  $ precio_millon     : num  880 1200 250 1280 1300 513 870 310 240 690 ...
##  $ Area_contruida    : num  237 800 86 346 600 160 490 82.5 80 150 ...
##  $ parqueaderos      : chr  "2" "3" "NA" "4" ...
##  $ Banos             : num  5 6 2 6 7 4 6 2 2 5 ...
##  $ Habitaciones      : num  4 7 3 5 5 4 5 3 3 4 ...
##  $ Tipo              : chr  "Casa" "Casa" "Apartamento" "Apartamento" ...
##  $ Barrio            : chr  "pance" "miraflores" "multicentro" "ciudad jardv<U+2260>n" ...
##  $ cordenada_longitud: num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ Cordenada_latitud : num  3.43 3.43 3.43 3.43 3.43 ...

Categorizando algunas variables

datos$parqueaderos_cat=cut(datos$parqueaderos, breaks = c(-1,0,1,4), include.lowest = F, labels = c("0","1",">=2"))
datos$Banos_cat=cut(datos$Banos, breaks = c(-1,0,1,2,6), include.lowest = F, labels = c("0","1","2",">=3"))
datos$Habitaciones_cat=cut(datos$Habitaciones, breaks = c(-1,0,1,2,7), include.lowest = F, labels = c("0","1","2",">=3"))

1. Filtrar solo apartamentos, en un barrio de preferencia y tener en cuenta las variaciones que este puede presentar por ser un campo abierto, tener en cuenta la siguiente ayuda:

Filtrando Tipo de vivienda = Apartamento"

Conteo Tipo de vivienda

##          Tipo    n
## 1 Apartamento 5100
## 2        Casa 3219
## 3        <NA>    3
datos_sub <- datos %>%
  filter(Tipo == 'Apartamento' ) 

dim(datos_sub)[1]
## [1] 5100

Se puede observar que hay mayor presencia, de datos asociados a apartamentos, en la base que actualmente se esta trabajando, pues representa mas del 50% del total.

Filtrando Barrio ‘Colseguros’:

RecordLinkage

require(RecordLinkage)
datos_colse=which(jarowinkler("colseguros",datos_sub$Barrio)>0.8)
datos_sub_colse=datos_sub[datos_colse,]
dim(datos_sub_colse)[1]
## [1] 23

Alternativa a RecordLinkage

datos_sub_barrio=datos_sub %>% filter(grepl('colse', Barrio))
dim(datos_sub_barrio)[1]
## [1] 25
kbl(table(datos_sub_barrio$Barrio), caption = "<center><strong> Barrios asociados a Colseguros </strong></center>") %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
Barrios asociados a Colseguros
Var1 Freq
colseguros 22
colseguros andes 1
urbanizaciv=n colseguros 2

Se puede observar que apartir de las dos técnicas planteadas, los resultados cambian un poco, mientras por recordlinkage queda un total de 23 apartamentos, por la opción de filtrado con dplyr, queda un total de 25 apartamentos.

summary(datos_sub_colse)
##        ID                 Zona         piso   Estrato precio_millon  
##  Min.   : 840   Zona Centro : 0   2      :4   3: 1    Min.   :120.0  
##  1st Qu.:3906   Zona Norte  : 0   5      :4   4:22    1st Qu.:144.0  
##  Median :4069   Zona Oeste  : 0   NA     :4   5: 0    Median :163.0  
##  Mean   :3977   Zona Oriente: 0   3      :3   6: 0    Mean   :209.3  
##  3rd Qu.:4580   Zona Sur    :23   4      :3           3rd Qu.:245.0  
##  Max.   :5831                     1      :2           Max.   :420.0  
##                                   (Other):3                          
##  Area_contruida    parqueaderos      Banos        Habitaciones  
##  Min.   : 55.00   Min.   :1.00   Min.   :1.000   Min.   :0.000  
##  1st Qu.: 62.00   1st Qu.:1.00   1st Qu.:1.000   1st Qu.:3.000  
##  Median : 73.00   Median :1.00   Median :2.000   Median :3.000  
##  Mean   : 96.98   Mean   :1.05   Mean   :2.261   Mean   :3.174  
##  3rd Qu.: 93.50   3rd Qu.:1.00   3rd Qu.:2.500   3rd Qu.:3.000  
##  Max.   :300.00   Max.   :2.00   Max.   :5.000   Max.   :6.000  
##                   NA's   :3                                     
##           Tipo       Barrio          cordenada_longitud Cordenada_latitud
##  Apartamento:23   Length:23          Min.   :-76.54     Min.   :3.365    
##  Casa       : 0   Class :character   1st Qu.:-76.53     1st Qu.:3.423    
##                   Mode  :character   Median :-76.53     Median :3.427    
##                                      Mean   :-76.53     Mean   :3.422    
##                                      3rd Qu.:-76.53     3rd Qu.:3.428    
##                                      Max.   :-76.50     Max.   :3.432    
##                                                                          
##  parqueaderos_cat Banos_cat Habitaciones_cat
##  0   : 0          0  : 0    0  : 1          
##  1   :19          1  : 7    1  : 0          
##  >=2 : 1          2  :10    2  : 2          
##  NA's: 3          >=3: 6    >=3:20          
##                                             
##                                             
## 

2. Presentar una exploración inicial de los datos, ejemplo: conteo de cuantos registros quedaron, precio promedio , area promedio entre otros (aprox unos 5 datos clave).

datos_sub_colse$parqueaderos = as.numeric(datos_sub_colse$parqueaderos)
datos_sub_colse$parqueaderos_cat=cut(datos_sub_colse$parqueaderos, breaks = c(-1,0,1,4), include.lowest = F, labels = c("0","1",">=2"))

parque = tabla_freq(datos_sub_colse$parqueaderos_cat)


kbl(parque, caption = "<center><strong> Número de Parqueaderos: Colseguros (apartamentos) </strong></center>") %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
Número de Parqueaderos: Colseguros (apartamentos)
Categoría Freq. Abs. Freq. Rel.
0 0 0.000000
1 19 82.608696
>=2 1 4.347826
NA 3 13.043478
Total 23 100.000000

El 82% de los apartamentos pertenecientes al barrio colseguros, tienen al menos 1 parqueadero.

datos_sub_colse$Banos_cat=cut(datos_sub_colse$Banos, breaks = c(-1,0,1,2,6), include.lowest = F, labels = c("0","1","2",">=3"))
banoscat = tabla_freq(datos_sub_colse$Banos_cat)


kbl(banoscat, caption = "<center><strong> Número de Baños: Colseguros (apartamentos) </strong></center>") %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
Número de Baños: Colseguros (apartamentos)
Categoría Freq. Abs. Freq. Rel.
0 0 0.00000
1 7 30.43478
2 10 43.47826
>=3 6 26.08696
Total 23 100.00000

El 73% de los apartamentos ubicados en este barrio contienen al menos 2 baños.

datos_sub_colse$Habitaciones_cat=cut(datos_sub_colse$Habitaciones, breaks = c(-1,0,1,2,7), include.lowest = F, labels = c("0","1","2",">=3"))

habi = tabla_freq(datos_sub_colse$Habitaciones_cat)
kbl(habi, caption = "<center><strong> Número de Habitaciones: Colseguros (apartamentos) </strong></center>") %>%
  kable_paper(bootstrap_options = "striped", full_width = FALSE,position = "left")
Número de Habitaciones: Colseguros (apartamentos)
Categoría Freq. Abs. Freq. Rel.
0 1 4.347826
1 0 0.000000
2 2 8.695652
>=3 20 86.956522
Total 23 100.000000

Aunque podría ser un error de la base de datos, se observa que existe un apartamento que tiene cero habitaciones, lo que podría sugerir que no precisamente es un apartamento. 20 apartamentos de los 23 evaluados (87%) contienen tres o mas habitaciones.

##Tabla de Indicadores Importantes

promedio_precio=mean(datos_sub_colse$precio_millon,na.rm=TRUE)
mediana_precio=median(datos_sub_colse$precio_millon,na.rm=TRUE)
max_precio=quantile(datos_sub_colse$precio_millon, probs = 1,na.rm=TRUE)
promedio_area=mean(datos_sub_colse$Area_contruida,na.rm=TRUE)
cantidad_ofertas=length(datos_sub_colse$Zona)


resultado=data.frame(promedio_precio,mediana_precio,promedio_area,max_precio,cantidad_ofertas)
resultado
##      promedio_precio mediana_precio promedio_area max_precio cantidad_ofertas
## 100%        209.3478            163      96.98435        420               23
  • Se observa que en promedio el precio de los apartamentos para la muestra encontrada en el barrio colseguros es de 209 millones. El precio mas alto es de 420 millones, lo que provoca que difiera la media vs la mediana.

  • El area promedio de estos apartamentos es de 97 m2 aproximadamente.

par(mfrow=c(1,3))
D1=  boxplot(precio_millon ~ Barrio , data=datos_sub_barrio,
            col=c('#A6CEE3', '#1F78B4'),
            xlab='Barrio', main='(a)',
            ylab='Precio Apto.',cex.axis = 0.7)
D2=   boxplot(precio_millon ~ Banos_cat , data=datos_sub_barrio,
              col=c('#A6CEE3', '#1F78B4'),
              xlab='Num Baños', main='(b)',
              ylab='Precio Apto.',cex.axis = 0.7)
D3=   boxplot(precio_millon ~ parqueaderos_cat, data=datos_sub_barrio,
              col=c('#A6CEE3', '#1F78B4'),
              xlab='Num. parqueaderos', main='(c)',
              ylab='Precio Apto.',cex.axis = 0.7)
mtext("Figura 6: Distribución precio según Barrios, Baños y Parqueaderos para apartamentos en Colseguros", side = 3, line = -0.8, outer = TRUE,cex=0.7,font=3)

En el gráfico a se puede observar que los barrios clasificados según urbanización colseguros parecen tener un precio mas elevado. En el gráfico 2, se observa que hay una relación directa entre el precio del apartamento y la cantidad de baños.

En el gráfico b, se puede observar que cuando hay tres o mas baños en un apartamento, al 50% de los apartamentos, tienen un precio de 270 millones aproximadamente.

En el gráfico c, se observa que cuando hay dos o mas parqueaderos, puede incrementar el valor del apartamento notablemente.

library(ggplot2)

ggplot(data = datos_sub_colse, aes(x = precio_millon)) +
  geom_histogram(aes(y = ..count.., fill = ..count..)) +
  scale_fill_gradient(low = "light blue", high = "light blue") +
  stat_function(fun = dnorm, colour = "black", args = list(mean = mean(precio_millon), sd = sd(precio_millon))) +
 ggtitle("Histograma Valor de Apartamento Colseguros") + labs(colour = "Horas")+ labs(x = "Precio $MM")

Gracias al histograma, se puede observar que evidentemente hay unos apartamentos, que tienen unos precios bastante altos, casi de 400 millones de pesos. La mayoria parece concentrarse hasta los 200 millones de pesos.

3. Presentar en un mapa interactivo los resultados de las viviendas y discutir un poco sobre posibles errores en la geocodificación dado que el campo es abierto:

require(leaflet)
leaflet() %>% addCircleMarkers(lng = datos_sub_colse$cordenada_longitud,lat = datos_sub_colse$Cordenada_latitud,radius = 0.3,color = "black",label = datos_sub_colse$ID) %>% addTiles()

Se puede observar gracias al anterior mapa interactivo, que pueden haber 3 apartamentos que estan quedando mal clasificados, esto lo explica la lejanía que existe frente a la concentración. Una de las observaciones esta quedando en el sur, y otras dos observaciones en el oriente de cali.

datos_sub_colse[datos_sub_colse$ID==5831,]
##        ID     Zona piso Estrato precio_millon Area_contruida parqueaderos Banos
## 3439 5831 Zona Sur    5       4           201         105.01            1     2
##      Habitaciones        Tipo     Barrio cordenada_longitud Cordenada_latitud
## 3439            3 Apartamento colseguros          -76.53877           3.36514
##      parqueaderos_cat Banos_cat Habitaciones_cat
## 3439                1         2              >=3
datos_sub_colse[datos_sub_colse$ID==840,]
##      ID     Zona piso Estrato precio_millon Area_contruida parqueaderos Banos
## 462 840 Zona Sur    4       4           138             60            1     1
##     Habitaciones        Tipo     Barrio cordenada_longitud Cordenada_latitud
## 462            3 Apartamento colseguros          -76.50397             3.422
##     parqueaderos_cat Banos_cat Habitaciones_cat
## 462                1         1              >=3
datos_sub_colse[datos_sub_colse$ID==863,]
##      ID     Zona piso Estrato precio_millon Area_contruida parqueaderos Banos
## 472 863 Zona Sur    4       4           128             55            1     1
##     Habitaciones        Tipo     Barrio cordenada_longitud Cordenada_latitud
## 472            2 Apartamento colseguros            -76.504               3.4
##     parqueaderos_cat Banos_cat Habitaciones_cat
## 472                1         1                2

4. Realizar una exploración bivariada entre el precio de la vivienda y el área por un gráfico de puntos con una linea de tendencia interactiva usando ggplot2 y plotly.

require(plotly)
graph1=ggplot(data = datos_sub_colse,aes(y=precio_millon,x=Area_contruida)) + geom_point() + geom_smooth()
ggplotly(graph1)

Gracias al anterior gráfico interactivo, se puede observar una relación en su mayoria directa entre el area construida y el precio del apartamento,aunque no se es una tendencia completamente marcada.

Es por esto que no se establece un punto claro, en el cual la tendencia cambie, y se decide hacer un filtro en el cual se calcula la mediana del precio de los apartamentos.

mediana=quantile(datos_sub_colse$precio_millon, probs = 0.5)
mediana
## 50% 
## 163

La mediana del precio de los apartamentos ubicados en el barrio colseguros es de 163 millones.

filter_area_construida=which(datos_sub_colse$Area_contruida>=mediana)
datos_sub_colse2=datos_sub_colse[filter_area_construida,]

graph2=ggplot(data = datos_sub_colse2,aes(y=precio_millon,x=Area_contruida)) + geom_point() + geom_smooth()
ggplotly(graph2)

Se observa que para las observaciones cuyo precio es mayor a 163 millones, no hay mucha diferencia en el area construida. Al parece se encuentran apartamentos que pueden tener un precio atípico, de mas de 390 millones que tiene una area construida menor.