Introducción

El presente trabajo fue realizado como entrega final de los módulos 1 y 2 de la Diplomatura en Ciencias Sociales Computacionales y Humanidades Digitales del IDAES - UNSAM.

En el mismo se busca responder algunos interrogantes acerca de la oferta de propiedades a través de la plataforma Airbnb en la Ciudad Autónoma de Buenos Aires (CABA). La muestra elegida corresponde al mes de abril de 2019 y está compuesta de 18.708 observaciones y 106 variables. La base fue recuperada del siguiente link.

Algunas de las preguntas de investigación que guían este ejercicio son:

¿Qué barrios de la CABA presentan mayores tarifas por noche en Airbnb? ¿Son los barrios de mayor poder adquisitivo los que presentan tarifas más altas? ¿Qué sucede cuando se analiza la distibución de la tarifa por noche entre comunas?

¿Cuáles son los barrios que más oferta de propiedades tienen? ¿Son barrios considerados “turísticos”?

¿Qué tipo de propiedades se ofrecen vía Airbnb en CABA?

A continuación se detallará en diferentes secciones el trabajo realizado con los datos para su posterior análisis.

Dataset y librerías

Para comenzar la exploración de los datos, se cargan las librerías a utilizar, así como el dataset elegido.

library(tidyverse)
library(ggplot2)
library(eeptools)
library(stringr)
library(sf)
library(treemapify)
library(viridis)
library(RColorBrewer)
library(waffle)
base_abril2019 <- read.csv("../TP agosto/datasets/vazquez-brust-alojamientos-listados-por-airbnb-en-buenos-aires-julio-2017/airbnb_CABA_04_2019.csv")

Exploración de bases

Una vez obtenido el dataset, se busca conocer por qué variables está compuesto y de qué tipo son, para poder elegir cuáles trabajar a la hora de responder a las preguntas de investigación. Se usan entonces las funciones names para conocer los nombres de las variables, headpara tener una mirada de las primeras filas de la base y glimpse como un resumen de los valores tomados por las variables que componen el dataset. Se omiten las salidas de estas sentencias en el código para no abrumar al lector.

names(base_abril2019)

head(base_abril2019)

glimpse(base_abril2019)

Asimismo, se utiliza la función unique para averiguar los valores únicos que toma la variable neighbourhood_cleansed. Se intenta también saber el valor promedio de la variable price.

unique(base_abril2019$neighbourhood_cleansed)
##  [1] "Palermo"           "Recoleta"          "Monserrat"        
##  [4] "Nuñez"            "Balvanera"         "Belgrano"         
##  [7] "San Nicolas"       "San Telmo"         "Caballito"        
## [10] "Constitucion"      "Puerto Madero"     "Chacarita"        
## [13] "Villa Crespo"      "Retiro"            "San Cristobal"    
## [16] "Almagro"           "Colegiales"        "Boedo"            
## [19] "Villa Ortuzar"     "Nueva Pompeya"     "Parque Chacabuco" 
## [22] "Barracas"          "Villa Urquiza"     "Villa Del Parque" 
## [25] "Coghlan"           "Parque Patricios"  "Boca"             
## [28] "Villa Devoto"      "Saavedra"          "Flores"           
## [31] "Monte Castro"      "Velez Sarsfield"   "Villa Gral. Mitre"
## [34] "Parque Chas"       "Floresta"          "Agronomia"        
## [37] "Paternal"          "Versalles"         "Mataderos"        
## [40] "Villa Pueyrredon"  "Parque Avellaneda" "Villa Luro"       
## [43] "Liniers"           "Villa Lugano"      "Villa Santa Rita" 
## [46] "Villa Real"        "Villa Riachuelo"   "Villa Soldati"
mean(base_abril2019$price, na.rm = TRUE)
## [1] NA

Limpieza

Surgen algunas evidentes necesidades de limpieza de los datos a partir de la exploración. Rápidamente se puede notar que la variable neighbourhood_cleansed es la indicada para trabajar con barrios de la ciudad, pero su nombre es muy largo. En paralelo surge otro problema, aún mayor: el precio (price en nuestra base) es tomado como una variable compuesta de caracteres por R debido a su estructura de “moneda” (con $ delante de cada valor) lo que devuelve NA luego de haber intentado calcular el promedio. Para transformar los datos averiguamos el largo máximo correspondiente a los strings que componen la variable price.

max(nchar(base_abril2019$price))
## [1] 11

Tras averiguar que el mayor valor contenido en la variable precio es de 11 caracteres, podemos proceder a limpiar la base y transformar las variables con formato moneda a número. Para ello, construimos una sub-base de datos modificada. Otras modificaciones que se hacen en este paso son la elección de variables de interés y su selección, se renombra a la variable que contiene los barrios para mayor comodidad en el trabajo futuro, se renombra el barrio “Núñez” que traía problemas de encoding, y se agrupan los datos por barrio. A cada variable monetaria se le quita el signo “$” y las comas, para volverlas números con los que poder trabajar.

base_airbnb <- base_abril2019 %>% 
  rename(., barrio = neighbourhood_cleansed) %>% #Renombro la variable
  select(id, barrio, city, country, latitude, longitude, property_type, room_type, accommodates, bathrooms, bedrooms, beds, bed_type, square_feet, price, 60:75) %>% #Selecciono columnas
  group_by(barrio) %>% #Agrupo
  mutate(barrio = case_when(barrio == "Nuñez" ~ "Nuñez", #Renombro un barrio
                          TRUE ~ barrio),
         precio1 = substr(x = price, #Transformo la variable precio
                          start = 2,
                          stop = 13),
         precio = decomma(precio1))

Con la base modificada y para confirmar que el proceso tuvo efecto, indagamos en el precio promedio por noche para la totalidad de los datos.

mean(base_airbnb$precio, na.rm = TRUE)
## [1] 2312.905

Continuando con la limpieza, se genera una nueva base con menos variables. Solo se mantienen las que pueden ser de utilidad para el ejercicio. Se remueven del entorno de trabajo las bases que ya no se usarán.

data <- base_airbnb %>% 
  select(1:2, 5:7, 31)

rm(base_abril2019, base)

Transformación y visualización

Los próximos pasos buscan transformar los datos creando algunas tablas que puedan responder a las preguntas inciales, y que a su vez sirvan como insumo para visualizar los resultados sobre los que el lector podrá sacar conclusiones. La Tabla 1 nos presenta a cada barrio con su precio promedio por noche.

data <- data %>% 
  group_by(barrio) %>% 
  mutate(precio_prom = round(mean(precio), 2))

Tabla 1

tabla1 <-  data %>%
  select(barrio, precio) %>% #Selecciono columnas
  group_by(barrio) %>% #Agrupo
  rename(Barrio = barrio) %>% #Renombro variable
  summarise(Precio_promedio = round(mean(precio))) %>% #Calculo precio promedio
  arrange(., desc(Precio_promedio)) #Ordeno por precio promedio de manera descendente

tabla1

Una rápida exploración por la tabla nos permite acceder a una primera conclusión: existen 4 barrios con promedios considerablemente más altos que los del resto. Los outliers son Monte Castro, Mataderos, Villa Gral. Mitre y Puerto Madero. Los primeros tres barrios no parecen ser clásicos barrios de alto poder adquisitivo de la ciudad, ni barrios turísticos, por lo que se trata de un hallazgo extraño. Para poner la lente sobre estos casos, se elabora una base de outliers con los 4 barrios.

base_outliers <- data %>% 
  select(barrio, precio) %>% #Selecciono columnas
  filter(barrio %in% c("Mataderos", "Puerto Madero", "Villa Gral. Mitre", "Monte Castro")) %>% #Filtro outliers
  arrange(-precio) #Ordeno en orden descendente

head(base_outliers)

Recordemos que el promedio general de los datos es de $2.313 la noche. Evidentemente existen varios alojamientos por encima de los $10.000 en Puerto Madero, algo que no sorprende. Pero Mataderos, por ejemplo, muestra un valor muy alto, de $89.756 la noche. También Villa Gral. Mitre tiene una propiedad por $167.582 la noche, muy por encima del promedio, y Monte Castro una de $41.640. Es importante tener esto en mente a la hora de sacar conclusiones de los graficos y tablas que se presentarán a continuación.

Los 48 barrios de la ciudad representan demasiadas categorías para visualizar de manera cómoda y clara. Se busca entonces agruparlos en comunas (15), para así poder proveer mayor claridad. Pero el dataset elegido no contiene una variable acorde, por lo que para proceder en dicho sentido es necesario combinar el dataset actual con otras tablas. Se hará entonces un join de los datos de Airbnb con un dataset en formato .csv del GCBA que contiene las comunas y sus respectivos barrios, y otro en formato .geojson que nos ayudará a georreferenciar la información más adelante.

comunas <- read.csv("../TP agosto/datasets/barrios_comunas_p_Ciencia_de_Datos_y_PP.csv")

comunas_geo <- st_read("https://cdn.buenosaires.gob.ar/datosabiertos/datasets/comunas/CABA_comunas.geojson")
## Reading layer `comunas' from data source 
##   `https://cdn.buenosaires.gob.ar/datosabiertos/datasets/comunas/CABA_comunas.geojson' 
##   using driver `GeoJSON'
## Simple feature collection with 15 features and 6 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -58.53152 ymin: -34.70529 xmax: -58.33515 ymax: -34.52649
## Geodetic CRS:  WGS 84

Limpieza de datasets de Comunas

Algunas aspectos de este dataset deben ser modificados. Los barrios están escritos en mayúscula, para empezar. En la base original los tenemos en forma de título, solo con la primera letra en mayúscula. Además, nuevamente debemos renombrar al barrio de Núñez con la ayuda de la función rename.

comunas <- comunas %>% 
  mutate(barrio = str_to_title(BARRIO), #Tranformo la variable barrio de mayúsculas a forma de título
         barrio1 = case_when(barrio == "Nuã‘ez" ~ "Nuñez", #Renombro el barrio de Nuñez
                   TRUE ~ barrio))

Join

Ahora sí se pueden combinar ambos datasets a través de la variable barrio.

datos <- left_join(data, comunas, by = c("barrio" = "barrio1"))

Precio por comuna

Realizamos una segunda tabla, esta vez agrupando el precio promedio por noche por comuna.

Tabla 2

tabla2 <- datos %>% 
  group_by(COMUNA) %>% #Agrupo por comuna
  filter(!is.na(COMUNA)) %>% #Filtro nulos
  summarise(precio_comuna = round(mean(precio), 2)) #Calculo precio promedio por comuna

tabla2

Con el dato de precio promedio por comuna ya elaborado, podemos graficar.

Gráfico 1

ggplot(tabla2, aes(x = precio_comuna, y = fct_reorder(as.factor(COMUNA), precio_comuna), color = precio_comuna)) +
  geom_point(size = 2) +
  guides(color = "none", size = "none") +
  labs(title = "Precio promedio por noche en Airbnb",
       subtitle = "comunas de la Ciudad Autónoma de Buenos Aires, abril 2019",
       x = "Precio promedio",
       y = "",
       caption = "Fuente: Airbnb 2019") +
  scale_color_gradient(low = "darkolivegreen1", high = "forestgreen") +
  theme_minimal()

Lo que obtenemos es un gráfico de puntos que muestra en orden ascendente el precio promedio por noche en alojamientos de Airbnb para las comunas de CABA. Algunas conclusiones que se pueden sacar de esta visusalización:

  • La comuna 9 es la más cara, contiene a los barrios Parque Avellaneda, Liniers y Mataderos. El outlier que encontramos antes en Mataderos posiblemente sea el motivo.

  • El segundo puesto lo tiene la comuna 14, compuesta únicamente por el barrio de Palermo, barrio turístico por excelencia y de poder adquisitivo relativamente alto.

  • La tercera comuna más cara es la 11, compuesta por Villa General Mitre, Villa Devoto, Villa del Parque y Villa Santa Rita, donde el outlier de Villa Gral Mitre probablemente sea el que le confiere ese puesto.

  • El cuarto puesto es para la comuna 2, integrada por el barrio de Recoleta, de alto poder adquisitivo y gran presencia del turismo.

  • La quinta más cara es la comuna 1, contiene a los barrios de Retiro, San Nicolás, Puerto Madero, San Telmo, Monserrat y Constitución. Aquí sí estamos hablando ya del barrio más caro de la ciudad en términos inmobiliarios (Puerto Madero) pero además, de varios barrios turísticos y céntricos. Es llamativo que recién en el quinto puesto entre Puerto Madero, que se encontraba en el podio al ordenar estos mismos datos por barrio.

Se puede decir entonces que lo más llamativo de este top 5 es la presencia de comunas más bien residenciales y de ingresos bajos o medios, como la 9 y la 11.

Alojamientos disponibles por barrio

Ahora bien, los promedios pueden no siempre ser representativos. Un motivo importante para no tomar promedios como la mejor representación de la realidad, es que no siempre se reparte la “carga” entre la misma cantidad de elementos. En el caso que estamos analizando, puede suceder que en Palermo y Recoleta por ejemplo se esté dividiendo el total de los precios por noche por una cantidad mucho mayor de alojamientos ofertados que en zonas más residenciales. Podemos intentar indagar sobre esta hipótesis.

La cantidad de propiedades disponibles por barrio en la plataforma Airbnb depende probablemente de un sinfín de factores como si el barrio es turístico o más bien residencial, si tiene mayor presencia de casas o de edificios, si tiene o no vida nocturna, etc.

Analizamos entonces en una tabla cuántos avisos hubo en abril de 2019 por barrio en la plataforma. Se calcula el valor absoluto pero también la proporción, para saber el peso que tiene en el total cada barrio.

conteo <- datos %>%
  select(barrio) %>% #Selecciono columna barrio
  group_by(barrio) %>% #Agrupo
  summarise(cant_avisos = n()) %>% #Calculo cantidad total de avisos
  mutate(proporcion = round((cant_avisos/sum(cant_avisos)), 2) *100) #Calculo cantidad de avisos por barrio

conteo

Los números son muy dispares. Evidentemente Palermo y Recoleta parten de una muestra de datos mucho mayor (5647 y 3045 avisos respectivamente) mientras en Villa Soldati solo hubo 1 anuncio en abril de 2019.

Para seguir con la lógica anterior, repetimos el ejercicio para las comunas.

conteo_comunas <- datos %>%
  select(COMUNA) %>% #Selecciono columna comuna
  group_by(COMUNA) %>% #Agrupo
  summarise(cant_avisos = n()) %>% #Calculo cantidad total de anuncios
  mutate(proporcion = round(cant_avisos/sum(cant_avisos), 4)*100) #Calculo la proporción por comuna

conteo_comunas

Podemos ver así más de un tercio de los avisos de propiedades disponibles en la plataforma se encuentran en las comunas 1 y 2. Además, la comuna 14 (Palermo) concentra otro 30% de los datos. Veamos esto en una visualización. Para poder hacer un gráfico de waffle, generamos un vector que contiene la proporción de avisos por comuna, llamado comunas_plot.

Gráfico 2

comunas_plot <- c(`1` = 21, `2` = 16, `3` = 5, `4` = 2, `5` = 5, `6` = 2, `7` = 1, `8` = 0, `9` = 0, `10` = 1, `11` = 1, `12` = 2, `13` = 8, `14` = 30, `15` = 6)

g <- waffle(comunas_plot,
            rows = 10,
            colors = c("#3182BD", "#6BAED6", "#9ECAE1", "#C6DBEF", "#E6550D", "#FD8D3C", "#FDAE6B", "#FDD0A2", "#31A354", "#74C476", "#A1D99B", "#C7E9C0", "#756BB1", "#9E9AC8", "#BCBDDC", "#DADAEB"),
            legend_pos = "right",
            title = "Avisos de Airbnb por comuna en CABA, abril 2019")


plot(g)

Tipo de propiedad

Otro dato interesante que puede provee este dataset es el de tipos de propiedades presentes en Airbnb. Por el tipo de ciudad que es CABA, se espera que se trate mayoritariamente de departamentos, pero veamos con mayor certeza si esto es así. Nuevamente elaboramos una tabla con los datos que nos interesan: cantidades absolutas y proporción según tipo de propiedad.

Tabla 3

data_prop <- datos %>%
  select(property_type) %>% #Selecciono columna de tipo de propiedad
  group_by(property_type, barrio) %>% #Agrupo
  summarise(cant_casos = n()) %>% #Calculo cantidad total de casos
  mutate(proporcion = round((cant_casos/sum(cant_casos)), 2) *100) %>% #Calculo proporción 
  arrange(-proporcion) #Reordeno

head(data_prop)

Al parecer existen 30 tipos de propiedad distintos, y el tipo departamento se lleva 14.826 observaciones del total de 18.708 que tiene la base, el 79%. Otras propiedades de relevancia, muy por detrás de la abrumadora presencia de los departamentos, son las casas, los lofts, los condominios, los departamentos con servicios, bed and breakfasts y hostels, entre otros.

data_prop_filtrada <- data_prop %>% 
  filter(cant_casos > 10) #Filtro los tipos de propiedad con más de 10 casos

ggplot(data = data_prop_filtrada, aes(x = cant_casos, y = fct_reorder(property_type, cant_casos), fill = property_type)) +
  geom_col() +
  guides(fill = "none", color = "none") +
  labs(title = "Tipos de propiedad más populares ofrecidos en Airbnb",
       subtitle = "CABA, abril 2019",
       x = "",
       y = "",
       caption = "Fuente: Airbnb 2019") +
  scale_fill_manual(values =  c("#3182BD", "#6BAED6", "#9ECAE1", "#C6DBEF", "#E6550D", "#FD8D3C", "#FDAE6B", "#FDD0A2", "#31A354", "#74C476", "#A1D99B", "#C7E9C0", "#756BB1", "#9E9AC8", "#BCBDDC", "#DADAEB")) +
  theme_minimal()

Mapa de la ciudad de buenos aires

Para poder acceder a un mapa de la ciudad que refleje el análisis de precio por noche que hicimos más arriba, es necesario convertir el dataset de Airbnb en un dataset espacial. Para ello, se filtran los valores nulos y luego se indican cuáles son las columnas que proveen coordenadas para los datos trabajados. Por último, unimos este data frame espacial al dataset de comunas con datos espaciales que habíamos cargado antes.

data <- data %>% 
    filter(!is.na(latitude), !is.na(longitude)) %>% #Filtro nulos
    st_as_sf(coords = c("longitude", "latitude"), crs = 4326) #Convierto el dataframe a uno espacial

data_comunas <- st_join(data, comunas_geo) #Join espacial

Para este nuevo dataset georreferenciado necesitamos repetir la Tabla 2, filtrando valores nulos, agrupando la tabla por comunas y calculando un precio promedio por noche para cada una.

airbnb_comunas <- data_comunas %>%
  filter(!is.na(COMUNAS)) %>% #Filtro nulos
  group_by(COMUNAS) %>% #Agrupo
  summarise(precio_comuna = round(mean(precio))) #Calculo precio promedio para las comunas

En el siguiente paso se despoja al dataset compuesto por comunas y su precio promedio de datos espaciales, para poder unirlo con comunas_geo luego.

airbnb_comunas <- airbnb_comunas %>% 
  st_set_geometry(NULL) #Quitamos variable espacial

comunas_geo <- comunas_geo %>% 
  left_join(airbnb_comunas, by = "COMUNAS") #Unimos ambas tablas

Ahora sí podemos visualizar el mapa.

ggplot() +
  geom_sf(data = comunas_geo, aes(fill = precio_comuna)) +
  geom_sf_text(data = comunas_geo, aes(label = COMUNAS), size = 3, color = "black") +
  scale_fill_gradient(low = "antiquewhite1", high = "orangered2")+
  labs(title = "Precio promedio por noche en Airbnb por comuna",
       subtitle = "abril de 2019",
       fill = "Precio promedio por noche",
       caption = "Fuente: Airbnb 2019") + 
  theme_void()

Conclusiones

A partir del análisis de la base de datos de Airbnb se puede establecer, en primer lugar, que la muestra presenta algunos outlier que podrían llevar a pensar que los barrios y comunas con precios más caros por noche son Mataderos, Monte Castro y Villa Gral. Mitre. Sin embargo, una segunda mirada agrupando por comunas (y teniendo en cuenta que algunas observaciones que funcionan como outliers de las comunas 9 y 11 distorsionan un poco el mapa) permite ver que los alojamientos más caros por noche se encuentran en las comunas turísticas del norte de la ciudad (14, 12 y 2). Las mismas que concentran, además, dos tercios de la oferta de Airbnb de la ciudad.

Por otro lado, se pudo averiguar que la mayor oferta disponible en CABA está asociada a los departamentos, que representan un 80% de los anuncios de abril de 2019.

Una exploración más profunda, que tome datos de distintos momentos del tiempo, sería interesante para averiguar si la muestra tomada en abril de 2019, compuesta de 18.708 observaciones, es representativa. También sería interesante indagar otras variables del dataset, que por los propios límites de este trabajo no pudieron ser abarcadas en este análisis.