Análisis Exploratorio de Dataset de Airbnb (Madrid-España)

Para ver la version online de este cuaderno, visite: https://andreatj.github.io/Tech-Girls-Glovo/

Este análisis explora un conjunto de datos de Airbnb, específicamente centrado en las listas de alojamientos disponibles en Madrid, España. Los datos han sido descargados desde el portal público OpenDataSoft. Se puede acceder directamente al dataset utilizado a través del siguiente enlace: Airbnb Listings en OpenDataSoft.

Instalacion y Carga de Pre-requerimientos

Previo al análisis, vamos a instalar y cargar los paquetes necesarios:

# Silas librerias no están ya instaladas , las instala. 
if (!require("tidyverse")) install.packages("tidyverse")
## Loading required package: tidyverse
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.0     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── 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
if (!require("corrplot")) install.packages("corrplot")
## Loading required package: corrplot
## corrplot 0.92 loaded
if (!require("ggmap")) install.packages("ggmap")
## Loading required package: ggmap
## ℹ Google's Terms of Service: <https://mapsplatform.google.com>
##   Stadia Maps' Terms of Service: <https://stadiamaps.com/terms-of-service/>
##   OpenStreetMap's Tile Usage Policy: <https://operations.osmfoundation.org/policies/tiles/>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
if (!require("leaflet")) install.packages("leaflet")
## Loading required package: leaflet
if (!require("leaflet.extras")) install.packages("leaflet.extras")
## Loading required package: leaflet.extras
if (!require("GGally")) install.packages("GGally")
## Loading required package: GGally
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
if (!require("visdat")) install.packages("visdat")
## Loading required package: visdat
if (!require("naniar")) install.packages("naniar")
## Loading required package: naniar
# Cargar las librerías
library(tidyverse)
library(corrplot)
library(ggmap)
library(leaflet)
library(leaflet.extras)
library(GGally)
library(visdat)
library(naniar)

Descripción de Librerías de R usadas:

Tidyverse: La librería tidyverse carga en R un conjunto de paquetes que comparten una filosofía común de manipulación y visualización de datos, incluyendo dplyr, ggplot2 y otros paquetes que facilitan el flujo de trabajo de análisis de datos en R.

Dplyr: Proporciona un conjunto de funciones para realizar operaciones de manipulación de datos como filtrado, selección, agregación y unión de datos.

Ggplot2: Permite crear una amplia variedad de gráficos de alta calidad de forma sencilla y flexible.

Corrplot: Se utiliza para visualizar matrices de correlación de datos de una manera clara y concisa, lo que facilita la identificación de patrones de correlación entre variables.

Ggmap: Esta librería se utiliza para visualizar datos espaciales en mapas utilizando ggplot2. Permite superponer datos sobre mapas de Google Maps, OpenStreetMap y otros proveedores de mapas.

Leaflet: Es una librería para la creación de mapas interactivos en R. Permite crear mapas interactivos con características como marcadores, polígonos, superposiciones y herramientas de zoom.

Leaflet.extras: Extiende las funcionalidades de la librería leaflet proporcionando características adicionales para la creación de mapas interactivos más avanzados.

GGally: Proporciona extensiones para ggplot2, permitiendo crear gráficos de matriz y otras visualizaciones complejas de forma más sencilla.

Visdat es una librería que proporciona herramientas para visualizar la estructura de los datos. Su objetivo principal es ayudar a comprender la distribución y la integridad de los datos, incluidos los datos faltantes, ayudando a guiar las decisiones sobre la limpieza y el preprocesamiento de los datos.

Naniar es una librería que se centra específicamente en el manejo y la visualización de datos faltantes. es especialmente útil cuando se trabaja con conjuntos de datos que contienen muchos valores faltantes o cuando se necesita realizar análisis sensibles a la presencia de datos faltantes, como modelos predictivos o análisis de datos longitudinales.

Carga de datos para el análisis.

Leemos el dataset desde su ubicación y especificamos que el separador de campos es un punto y coma.

library(tidyverse)

# Leer el archivo CSV
airbnb_data_raw <- read.csv("../data/raw/air-bnb-listings.csv", sep = ';')

Mostramos las primeras filas del dataset, para comprobar que los datos se han cargado correctamente.

# Ver las primeras filas de los datos
head(airbnb_data_raw)
##    Room.ID                                               Name   Host.ID
## 1 36707573  Soggiorno Madrid-Hospitality , comfort & passion. 275911703
## 2 21859113                      Be Mate Plaza España Wild 203 157114944
## 3 21862103 Designer flat in Madrid's exclusive Salamanca area   8851341
## 4 21875158        Estudio de diseño en pleno centro de Madrid 159570292
## 5 21932504        Cosy apartment in the cosmopolitan Gran Vía 160055902
## 6 22042300             Habitación 1. Para estudiar o trabajar 160981040
##    Neighbourhood       Room.type Room.Price Minimum.nights Number.of.reviews
## 1 Centro Storico    Private room        155              1                 1
## 2      Argüelles Entire home/apt        147              1                 1
## 3      Recoletos Entire home/apt        625              5                 2
## 4            Sol    Private room        500              1               110
## 5        Palacio Entire home/apt         62              2               194
## 6       Portazgo    Private room         12             15                11
##   Date.last.review Number.of.reviews.per.month Rooms.rent.by.the.host
## 1       2019-12-19                        0.16                      3
## 2       2018-12-01                        0.05                     36
## 3       2018-08-09                        0.06                      1
## 4       2020-02-26                        3.41                      2
## 5       2020-03-11                        6.16                      1
## 6       2018-10-30                        0.40                      3
##   Availibility Updated.Date     City Country
## 1          173   2020-06-19 Florence   Italy
## 2            0   2020-07-17   Madrid   Spain
## 3          177   2020-07-17   Madrid   Spain
## 4          347   2020-07-17   Madrid   Spain
## 5          286   2020-07-17   Madrid   Spain
## 6          106   2020-07-17   Madrid   Spain
##                               Coordinates                        Location
## 1   43.78225294444298, 11.241335882500344 Italy, Florence, Centro Storico
## 2  40.421911823829916, -3.716298875039167        Spain, Madrid, Argüelles
## 3  40.42205487717857, -3.6889613202974916        Spain, Madrid, Recoletos
## 4  40.41573794398538, -3.7044166450981693              Spain, Madrid, Sol
## 5  40.421747567976034, -3.710994512030235          Spain, Madrid, Palacio
## 6 40.391493766002014, -3.6450831761057922         Spain, Madrid, Portazgo

Calculamos y mostramos las dimensiones originales del dataset, obteniendo el número de filas y columnas.

dimension_original <- dim(airbnb_data_raw)

paste('El numero de filas que tiene el dataframe airbnb_data es:', dimension_original[1])
## [1] "El numero de filas que tiene el dataframe airbnb_data es: 21278"
paste('El numero de columnas que tiene el dataframe airbnb_data es:', dimension_original[2])
## [1] "El numero de columnas que tiene el dataframe airbnb_data es: 17"

El dataset original, tiene 17 variables(columnas) y 21278 observaciones(filas).

Vamos a analizar qué variables tenemos:

colnames(airbnb_data_raw)
##  [1] "Room.ID"                     "Name"                       
##  [3] "Host.ID"                     "Neighbourhood"              
##  [5] "Room.type"                   "Room.Price"                 
##  [7] "Minimum.nights"              "Number.of.reviews"          
##  [9] "Date.last.review"            "Number.of.reviews.per.month"
## [11] "Rooms.rent.by.the.host"      "Availibility"               
## [13] "Updated.Date"                "City"                       
## [15] "Country"                     "Coordinates"                
## [17] "Location"
  1. Room.ID: Es el identificador único de cada listado en Airbnb. Es esencial para identificar los registros de manera única.

  2. Name: Es el nombre descriptivo del listado. En algunos casos, puede contener más detalles descriptivos del alojamiento.

  3. Host.ID: Es el identificador único del anfitrión del listado en Airbnb. Muy útil para asociar múltiples listados al mismo anfitrión.

  4. Neighbourhood: Nos indica el Barrio dónde se encuentra el listado. Es un dato muy importante a la hora de hacer un análisis de distribución geográfica.

  5. Room.Type: Es el tipo de alojamiento ofrecido. Nos permite diferenciar entre los tipos de alojamiento, que pueden ser: apartamento/casa entero, una habitación en un hotel, una habitación privada o una habitación compartida.

  6. Room.Price: Es el número mínimo de noches requerido para la reserva. De suma importancia, al influir en la decisión de reservas de los usuarios.

  7. Minimum.Nights: Número mínimo de noches requeridas para hacer una reserva, crucial para entender las políticas de reservas.

  8. Number.of.Reviews: Nos indica el número total de reseñas recibidas por los huéspedes del listado. Es un indicador de popularidad y experiencia del cliente.

  9. Date.Last.Review: Es la fecha de la última reseña recibida por un huésped para un alojamiento. Nos permite evaluar si la última reseña es reciente.

  10. Number.of.Reviews.Per.Month: Es el promedio de reseñas recibidas por mes. Refleja la actividad reciente del listado.

  11. Rooms.Rent.By.Host: Nos indica el número total de habitaciones o propiedades que el anfitrión ofrece en alquiler. Permite identificar a los anfitriones con múltiples propiedades.

  12. Availability: Es el número de días al año en que el listado está disponible. Nos indica la oferta disponible..

  13. Updated.Date: Es la fecha en que se actualizó la información del listado por última vez.

  14. City: Especifica la ciudad dónde se ubica el listado.

  15. Country: Especifica el país donde se ubica el listado

  16. Coordinates: Son las coordenadas geográficas del alojamiento.

  17. Location: Incluye País, Ciudad y Barrio en una sola variable.

Ahora, examinamos que tipo de variables tenemos.

str(airbnb_data_raw)
## 'data.frame':    21278 obs. of  17 variables:
##  $ Room.ID                    : int  36707573 21859113 21862103 21875158 21932504 22042300 22043848 22048206 22066174 22079865 ...
##  $ Name                       : chr  "Soggiorno Madrid-Hospitality , comfort & passion." "Be Mate Plaza España Wild 203" "Designer flat in Madrid's exclusive Salamanca area" "Estudio de diseño en pleno centro de Madrid" ...
##  $ Host.ID                    : int  275911703 157114944 8851341 159570292 160055902 160981040 160967778 105797031 54343289 34563914 ...
##  $ Neighbourhood              : chr  "Centro Storico" "Argüelles" "Recoletos" "Sol" ...
##  $ Room.type                  : chr  "Private room" "Entire home/apt" "Entire home/apt" "Private room" ...
##  $ Room.Price                 : int  155 147 625 500 62 12 70 69 81 50 ...
##  $ Minimum.nights             : int  1 1 5 1 2 15 4 10 1 7 ...
##  $ Number.of.reviews          : int  1 1 2 110 194 11 109 51 87 12 ...
##  $ Date.last.review           : chr  "2019-12-19" "2018-12-01" "2018-08-09" "2020-02-26" ...
##  $ Number.of.reviews.per.month: num  0.16 0.05 0.06 3.41 6.16 0.4 3.43 1.64 2.74 0.44 ...
##  $ Rooms.rent.by.the.host     : int  3 36 1 2 1 3 6 2 6 1 ...
##  $ Availibility               : int  173 0 177 347 286 106 125 47 0 301 ...
##  $ Updated.Date               : chr  "2020-06-19" "2020-07-17" "2020-07-17" "2020-07-17" ...
##  $ City                       : chr  "Florence" "Madrid" "Madrid" "Madrid" ...
##  $ Country                    : chr  "Italy" "Spain" "Spain" "Spain" ...
##  $ Coordinates                : chr  "43.78225294444298, 11.241335882500344" "40.421911823829916, -3.716298875039167" "40.42205487717857, -3.6889613202974916" "40.41573794398538, -3.7044166450981693" ...
##  $ Location                   : chr  "Italy, Florence, Centro Storico" "Spain, Madrid, Argüelles" "Spain, Madrid, Recoletos" "Spain, Madrid, Sol" ...

Vemos que tenemos variables categóricas como Room.type, Neighbourhood, City y Country que no se están tomando como tal, sino como character. Lo mismo sucede con las fechas.

Con esta información, vamos a proceder a hacer las conversiones necesarias:

Tratamiento datos

Conversión de variables

Para asegurar la integridad del dataframe original mientras realizamos cambios, primero crearemos una copia del mismo. Todas las modificaciones y análisis se realizarán en esta copia, denominada airbnb_data.

airbnb_data <- data.frame(airbnb_data_raw)

Como hemos visto arriba, hay columnas que deberían ser de diferente clase.

str(airbnb_data[, c("Date.last.review", "Updated.Date", "Room.type", "Neighbourhood", "City", "Country")])
## 'data.frame':    21278 obs. of  6 variables:
##  $ Date.last.review: chr  "2019-12-19" "2018-12-01" "2018-08-09" "2020-02-26" ...
##  $ Updated.Date    : chr  "2020-06-19" "2020-07-17" "2020-07-17" "2020-07-17" ...
##  $ Room.type       : chr  "Private room" "Entire home/apt" "Entire home/apt" "Private room" ...
##  $ Neighbourhood   : chr  "Centro Storico" "Argüelles" "Recoletos" "Sol" ...
##  $ City            : chr  "Florence" "Madrid" "Madrid" "Madrid" ...
##  $ Country         : chr  "Italy" "Spain" "Spain" "Spain" ...

Data.last.review y Update.Date.

Deberían ser fecha: Ya que ambas variables permiten cálculos como la diferencia de días entre eventos o agrupación por períodos de tiempo (por ejemplo, mes, año), lo cual sería difícil si estas variables fueran tratadas como character.

Room.type, Neighbourhood, City y Country

Deberían ser categóricas: Ya que agrupan los datos usando etiquetas textuales y facilitan análisis por segmentos. Además, no representan cantidades ni establecen un orden, características típicas de las variables categóricas.

Data.last.review y Update.Date

Convertimos ambas columnas a fechas:

# Convertir la columna Date.last.review a tipo fecha
airbnb_data$Date.last.review <- as.Date(airbnb_data$Date.last.review, format = "%Y-%m-%d")

# Convertir la columna Updated.Date a tipo fecha
airbnb_data$Updated.Date <- as.Date(airbnb_data$Updated.Date, format = "%Y-%m-%d")

Comprobamos que se ha realizado con éxito el cambio:

str(airbnb_data[, c("Date.last.review", "Updated.Date")])
## 'data.frame':    21278 obs. of  2 variables:
##  $ Date.last.review: Date, format: "2019-12-19" "2018-12-01" ...
##  $ Updated.Date    : Date, format: "2020-06-19" "2020-07-17" ...

Como podemos comprobar arriba, el cambio se hizo correctamente. Data.last.review y Update.Date son ahora fechas.

Room.type, Neighbourhood, City y Country

Convertimos estas variables en categóricas.

# Convertir columnas a factores (categóricas)
airbnb_data$Room.type <- as.factor(airbnb_data$Room.type)
airbnb_data$Neighbourhood <- as.factor(airbnb_data$Neighbourhood)
airbnb_data$City <- as.factor(airbnb_data$City)
airbnb_data$Country <- as.factor(airbnb_data$Country)

Nos aseguramos que los cambios se han hecho bien:

summary(airbnb_data[, c("Room.type", "Neighbourhood", "City", "Country")])
##            Room.type         Neighbourhood              City      
##  Entire home/apt:12719   Embajadores: 2559   Madrid       :21255  
##  Hotel room     :  221   Universidad: 2059   Girona       :    6  
##  Private room   : 8006   Palacio    : 1490   Barcelona    :    5  
##  Shared room    :  332   Sol        : 1366   Mallorca     :    2  
##                          Justicia   : 1116   San-francisco:    2  
##                          Cortes     :  976   Brussels     :    1  
##                          (Other)    :11712   (Other)      :    7  
##           Country     
##  Spain        :21269  
##  Italy        :    2  
##  United states:    2  
##  Australia    :    1  
##  Belgium      :    1  
##  Portugal     :    1  
##  (Other)      :    2

Con la información de arriba, comprobamos que efectivamente que Room.type, Neighbourhood, City y Country son ahora variables catégoricas.

City y Country

Podemos notar que en City y Country, hay algo que llama la atención. Se incluyen otras ciudades y países que no deberían estar.

summary(airbnb_data[, c("City", "Country")])
##             City                Country     
##  Madrid       :21255   Spain        :21269  
##  Girona       :    6   Italy        :    2  
##  Barcelona    :    5   United states:    2  
##  Mallorca     :    2   Australia    :    1  
##  San-francisco:    2   Belgium      :    1  
##  Brussels     :    1   Portugal     :    1  
##  (Other)      :    7   (Other)      :    2

Vemos que no todos los registros que pertenecen a España, son de Madrid.

Hacemos un filtrado en el que solo mantenemos las filas donde el valor de la columna City es Madrid.

# Filtrar el dataset para mantener solo las filas donde la ciudad es Madrid
airbnb_data <- airbnb_data |> filter(City == "Madrid")

Una vez hecho el filtro, verificamos que en City, no aparezca nada que no sea Madrid.

# Verificar que sólo quedan entradas de Madrid en el dataset original
summary(airbnb_data[, c("City", "Country")])
##         City            Country     
##  Madrid   :21255   Spain    :21255  
##  Barcelona:    0   Australia:    0  
##  Brussels :    0   Belgium  :    0  
##  Florence :    0   Italy    :    0  
##  Girona   :    0   Portugal :    0  
##  Istanbul :    0   Turkey   :    0  
##  (Other)  :    0   (Other)  :    0

La información de arriba, es lo que esperabamos, solo quedan los registros de Madrid.
Sin embargo siguen apareciendo otros niveles tanto en City y Country. Ya no los necesitamos, así los vamos a eliminar.

airbnb_data$City <- droplevels(airbnb_data$City)
airbnb_data$Country <- droplevels(airbnb_data$Country)

#Verificamos que solo queden los niveles deseados en City y Country
summary(airbnb_data[, c("City", "Country")])
##      City        Country     
##  Madrid:21255   Spain:21255

Neighbourhood

Hemos notado que en los barrios, parecen haber barrios que no pertenecen a Madrid. Vamos a verlo:

# Revisamos las frecuencias de cada barrio
table_neighbourhoods <- table(airbnb_data$Neighbourhood)

# Mostrar los barrios con frecuencia cero
zero_freq_neighbourhoods <- table_neighbourhoods[table_neighbourhoods == 0]
print(zero_freq_neighbourhoods)
## 
##              Areeiro               Arenal               Blanes 
##                    0                    0                    0 
##            Bruxelles               Camden Castell-Platja d'Aro 
##                    0                    0                    0 
##              Catania       Centro Storico            Excelsior 
##                    0                    0                    0 
##                Fatih      les Tres Torres    Palma de Mallorca 
##                    0                    0                    0 
##                Roses                Sants               Sydney 
##                    0                    0                    0

Vemos que hay 0 alojamientos en Madrid, para esos barrios, porque no son barrios madrileños. Eliminamos esos niveles.

# Eliminar esos niveles del factor
airbnb_data$Neighbourhood <- droplevels(airbnb_data$Neighbourhood)

Ahora podemos comprobar, que esos niveles se ha removido:

# Verificar nuevamente las frecuencias para confirmar que los niveles cero han sido eliminados
table(airbnb_data$Neighbourhood)
## 
##                     Abrantes                      Acacias 
##                           48                          210 
##                      Adelfas                   Aeropuerto 
##                           78                           14 
##                      Aguilas             Alameda de Osuna 
##                           55                           62 
##                      Almagro                     Almenara 
##                          221                           93 
##                  Almendrales                       Aluche 
##                           68                          102 
##                       Ambroz                      Amposta 
##                           20                           13 
##             Apostol Santiago                     Arapiles 
##                           25                          203 
##                      Aravaca                        Arcos 
##                           38                           51 
##                    Argüelles                       Atocha 
##                          307                           20 
##                Bellas Vistas                   Berruguete 
##                          169                          154 
##                   Buenavista                     Butarque 
##                           75                           13 
##                   Campamento                     Canillas 
##                           29                          111 
##                   Canillejas                     Cármenes 
##                           87                           59 
##                Casa de Campo   Casco Histórico de Barajas 
##                          104                           36 
##  Casco Histórico de Vallecas Casco Histórico de Vicálvaro 
##                           86                           49 
##                   Castellana                     Castilla 
##                          200                           61 
##                  Castillejos                      Chopera 
##                          147                          156 
##                Ciudad Jardín         Ciudad Universitaria 
##                           93                           75 
##                       Colina                     Comillas 
##                           22                           97 
##                   Concepción                   Corralejos 
##                          107                           17 
##                       Cortes                  Costillares 
##                          976                           43 
##               Cuatro Caminos               Cuatro Vientos 
##                          224                            9 
##                     Delicias                    El Goloso 
##                          195                           14 
##                     El Pardo                   El Plantío 
##                            3                            6 
##                      El Viso                  Embajadores 
##                           96                         2559 
##                    Entrevías                     Estrella 
##                           47                           32 
##                    Fontarrón             Fuente del Berro 
##                           51                          147 
##                Fuentelareina                   Gaztambide 
##                            3                          250 
##                         Goya                   Guindalera 
##                          347                          264 
##                       Hellín               Hispanoamérica 
##                           29                          112 
##                      Horcajo                        Ibiza 
##                            4                          196 
##                     Imperial                    Jerónimos 
##                          136                          104 
##                     Justicia                       La Paz 
##                         1116                           29 
##                      Legazpi                        Lista 
##                           57                          209 
##                  Los Angeles                  Los Rosales 
##                           31                           62 
##                       Lucero                   Marroquina 
##                           97                           28 
##                  Media Legua                   Mirasierra 
##                           21                           36 
##                     Moscardó                   Niño Jesús 
##                          106                           48 
##                 Nueva España                     Numancia 
##                           64                          204 
##                       Opañel                    Orcasitas 
##                           96                           13 
##                      Orcasur                     Pacífico 
##                           27                          212 
##                      Palacio                      Palomas 
##                         1490                           17 
##              Palomeras Bajas            Palomeras Sureste 
##                           82                           40 
##              Palos de Moguer                      Pavones 
##                          373                            5 
##                   Peñagrande                        Pilar 
##                           76                           70 
##                Pinar del Rey                      Piovera 
##                           92                           32 
##                     Portazgo                   Pradolongo 
##                           55                           40 
##                  Prosperidad                 Pueblo Nuevo 
##                          156                          140 
##                Puerta Bonita             Puerta del Angel 
##                           75                          339 
##                     Quintana                    Recoletos 
##                          115                          332 
##                        Rejas                   Rios Rosas 
##                           92                          217 
##                        Rosas                     Salvador 
##                          105                           28 
##                   San Andrés                San Cristobal 
##                           49                           21 
##                    San Diego                   San Fermín 
##                          204                           35 
##                   San Isidro            San Juan Bautista 
##                          168                           32 
##                  San Pascual                Santa Eugenia 
##                           40                           10 
##                     Simancas                          Sol 
##                          131                         1366 
##                        Timón                    Trafalgar 
##                           59                          394 
##                  Universidad                Valdeacederas 
##                         2059                          117 
##                 Valdefuentes                   Valdemarín 
##                          105                            6 
##                   Valdezarza                 Vallehermoso 
##                           57                           79 
##                     Valverde                       Ventas 
##                          100                          181 
##                    Vinateros                 Vista Alegre 
##                           20                          120 
##                        Zofío 
##                           53

No aparece ningún nivel con frecuencia 0.

Availibility

Transformamos la columna Availibility en una variable categórica, definiendo rangos específicos que representan distintos niveles de disponibilidad.

Esta categorización nos permite analizar y visualizar la información de forma más eficiente.

# Rangos de disponibilidad y sus etiquetas
breaks <- c(-1, 0, 30, 60, 90, 180, 365)
labels <- c("No disponible", "Hasta 30 días", "31 a 60 días", "61 a 90 días", "91 a 180 días", "181 a 365 días")

# Categorizar 'Availability' usando cut()
airbnb_data$Availability_Cat <- cut(airbnb_data$Availibility, breaks = breaks, labels = labels, include.lowest = TRUE)


airbnb_data$Availability_Cat<- as.factor(airbnb_data$Availability_Cat)

Realizamos una revisión de la nueva columna para asegurarnos de que la asignación de categorías se hizo correctamente

summary(airbnb_data$Availability_Cat)
##  No disponible  Hasta 30 días   31 a 60 días   61 a 90 días  91 a 180 días 
##           5465           1519            815           1982           3288 
## 181 a 365 días 
##           8186

Coordinates

La columna Coordinates contiene pares de valores separados por comas que representan la latitud y longitud geográfica

head(airbnb_data$Coordinates)
## [1] "40.421911823829916, -3.716298875039167" 
## [2] "40.42205487717857, -3.6889613202974916" 
## [3] "40.41573794398538, -3.7044166450981693" 
## [4] "40.421747567976034, -3.710994512030235" 
## [5] "40.391493766002014, -3.6450831761057922"
## [6] "40.418435611771486, -3.7013169106966464"

Dividimos Coordinates en dos columnas, Latitude y Longitude que representan las coordenadas geográficas.

# Asegurándonos de que la columna Coordinates está en formato de string
airbnb_data$Coordinates <- as.character(airbnb_data$Coordinates)

# Separar la columna Coordinates en dos nuevas columnas: Latitude y Longitude
airbnb_data<- airbnb_data |>
  separate(Coordinates, into = c("Latitude", "Longitude"), sep = ",", convert = TRUE)

Nos aseguramos de que el cambio se ha hecho efectivo:

str(airbnb_data[, c("Latitude", "Longitude")])
## 'data.frame':    21255 obs. of  2 variables:
##  $ Latitude : num  40.4 40.4 40.4 40.4 40.4 ...
##  $ Longitude: num  -3.72 -3.69 -3.7 -3.71 -3.65 ...

Como podemos comprobar, ahora la columna Coordinates se ha convertido en 2: Latitude y Longitude

Location

Parece que la columna Location es la combinación de Country, City, y Neighbourhood. Vamos a probarlo:

#Crea una nueva columna temporal llamada Combined que combina Country,City y Neighbourhood, separados por ",". 

airbnb_data <- airbnb_data |> mutate(Combined = paste(Country, City, Neighbourhood, sep = ", "))

#Compara la columna Location con la columna Combined para verificar si son iguales.

airbnb_data <- airbnb_data |> mutate(Location_Match = Location == Combined)

Se ha creado una nueva columna Combined, combinando los valores de Country, City y Neighbourhood con comas como separadores. Luego se añade la columna Location_Match para probar si esta combinación coincide exactamente con los valores de Location. Si coinciden Combined y Location devuelve True, sino False.

Ahora contamos cuantas filas tienen True, en Location_Match

#Revisa cuánto filas de Location_Match tienen true

table(airbnb_data$Location_Match)
## 
##  TRUE 
## 21255

El número de filas, coincide con el número registros totales del dataset.

Location es, por tanto, redundante; por lo que se puede eliminar.

También elimaremos las columnas temporales que creamos para llegar a esta conclusiónLocation_Match y Combined:

#Eliminar Location, Location_Match y Combined
airbnb_data <- select(airbnb_data, -Location)
airbnb_data <- select(airbnb_data, -Location_Match)
airbnb_data <- select(airbnb_data, -Combined)

Estudio de los valores nulos y ceros.

Creamos un dataframe solo con las columnas que tienen nulos o ceros

# Dataframe con la contabilización de Nulos y Ceros
resultados <- data.frame(
  NA_Count = colSums(is.na(airbnb_data)),
  Zero_Count = colSums(airbnb_data == 0, na.rm = TRUE)
)

# Filtrar y mostrar solo las filas con NA o ceros
resultados_con_NA_o_ceros <- resultados[apply(resultados, 1, function(x) any(x > 0)), ]
print(resultados_con_NA_o_ceros)
##                             NA_Count Zero_Count
## Room.Price                         0          1
## Number.of.reviews                  0       5400
## Date.last.review                5400          0
## Number.of.reviews.per.month     5400          0
## Availibility                       0       5465

La columna NA_Count muestra la cantidad de valores nulos en cada variable, mientras que Zero_Count indica cuántas veces aparece el valor cero.

Room.Price carece de nulos pero tiene un cero, lo que puede indicar una oferta sin coste o un error. Number.of.reviews tiene 5400 ceros, mostrando que muchas propiedades no han sido evaluadas. Las columnas Date.last.review y Number.of.reviews.per.month tienen 5400 NAs cada una, sugiriendo que muchas propiedades no han sido revisadas recientemente. Availability no tiene NAs pero presenta 5465 ceros, indicando una alta cantidad de propiedades no disponibles.

Abajo, podemos ver un gráfico que ilustra todas las atributos que tienen valores nulos. Enseñandonos su porcentaje con respecto al total de datos.

Los valores nulos de Date.last.review y Number.of.reviews.per.month representan alrededor del 25% de sus datos.

library(visdat)
library(naniar)
vis_miss(airbnb_data, sort_miss = TRUE)

Tratamiento de los valores nulos y ceros.

Room.Price

Tiene un solo registro con valor cero.

paste("Total suma de filas de Room.Price con ceros:", sum(airbnb_data$Room.Price == 0))
## [1] "Total suma de filas de Room.Price con ceros: 1"

Eliminamos ese registro porque probablemente es un error de entrada, ya que es poco probable que una habitación de hotel tenga un coste cero. Este error podría distorsionar nuestro análisis estadístico y posterior modelo predictivo. Eliminando este dato, aseguramos la calidad y consistencia del dataset.

Number.of.reviews, Date.last.review y Number.of.reviews.per.month.

Vemos que existe la misma cantidad de ceros en Number.of.reviews, que nulos en Date.last.review y Number.of.reviews.per.month.

¿Están relacionados estos tres valores?

Vamos a verlo:

# Añadir una columna temporal para verificar la condición
airbnb_data <- airbnb_data |>
  mutate(
    check = Number.of.reviews == 0 & is.na(Date.last.review) & is.na(Number.of.reviews.per.month)
  )

# Sumar para comprobar la condición basada en Number.of.reviews
result <- airbnb_data |>
  filter(Number.of.reviews == 0) |>
  summarise(
    Total_Zero_Reviews = n(),
    Correct_Cases = sum(check)
  )

print(result)
##   Total_Zero_Reviews Correct_Cases
## 1               5400          5400

Creamos una columna llamada check. Esta columna nos ayuda a ver si el número de reseñas = 0 y, al mismo tiempo, si las fechas de la última reseña y el promedio de reseñas por mes están vacíos. Si se cumplen estas condiciones, la columna indica True; si no, muestra False.

Comparamos, la columna check con la cuenta de los registros donde el número de reseñas es cero.

Este paso nos permite comprobar si nuestra teoría sobre la relación entre estas variables se sostiene.

Y si, tiene sentido, ya que si un alojamiento no ha recibido reseñas, no podría tener una fecha de última reseña ni un promedio de reseñas mensuales.

Ahora elimaremos la columna temporal check.

#Eliminar check
airbnb_data <- select(airbnb_data, -check)
Categorización de Number.of.reviews y Number.of.reviews.per.month

Vamos a categorizar Number.of.reviews y Number.of.reviews.per.monthpara un mejor análisis

Empezamos colocando ceros en los valores nulos de Number.of.reviews.per.month, paraasegurarmos podamos operar sobre esta columna, más adelante.

sum(is.na(airbnb_data$Number.of.reviews.per.month))
## [1] 5400

Tenemos 5400 registros vacíos de Number.of.reviews.per.month.

Sustituimos NA por el valor cero.

# Sustituir directamente los NA por 0 en la columna específica
airbnb_data$Number.of.reviews.per.month[is.na(airbnb_data$Number.of.reviews.per.month)] <- 0
#Calculo el total de filas con NA 
paste("Total suma de filas NA:", sum(is.na(airbnb_data$Number.of.reviews.per.month)))
## [1] "Total suma de filas NA: 0"
#Calculo el total de filas con ceros 
paste("Total suma de filas con ceros:", sum(airbnb_data$Number.of.reviews.per.month == 0))
## [1] "Total suma de filas con ceros: 5400"

Como podemos comprobar, el cambio se ha hecho correctamente. Los 5400 registros vacíos de Number.of.reviews.per.month ahora contienen ceros.


Ahora categorizamos la variable:

airbnb_data <- airbnb_data |> 
  mutate(
    Review_category = factor(
      cut(
        Number.of.reviews.per.month,
        breaks = c(-Inf, 0, 1, 5, Inf),  # Incluyendo  la categoría de "sin reseñas" en los breaks
        labels = c('sin reseñas', '0-1/mes', '1-5/mes', '+5/mes'),
        include.lowest = TRUE,
        right = TRUE
      )
    )
  )


summary(airbnb_data$Review_category)
## sin reseñas     0-1/mes     1-5/mes      +5/mes 
##        5400        8771        6452         632
#NOTA: 
#El intervalo sin reseñas, incluye todos los ceros
#El intervalo 0-1/mes, incluye los valores desde 0.01 reviews.per.month a 1 (incluido)
#El intervalo 1-5/mes, incluye los valores desde 1,01 reviews.per.month a 5 (incluido) y asi sucesivamente 

Si el valor es cero, lo categorizamos como “sin reseñas”. Luego, los valores se dividen en los siguientes rangos: 0-1/mes, 1-5/mes, +5/mes. Finalmente, creamos una nueva variable llamada Review_category que contiene estas categorías.

Ahora categorizamos la variable Number.of.reviews:

airbnb_data <- airbnb_data |> 
  mutate(
    Review_Count_Category = factor(
      cut(
        Number.of.reviews,
        breaks = c(-Inf, 0, 1, 10, 50, 100, Inf),  # Incluyendo la condición de 0 reseñas directamente en los breaks
        labels = c('sin reseñas', '1 reseña', '2-10 reseñas', '11-50 reseñas', '51-100 reseñas', '+ 101 reseñas'),
        include.lowest = TRUE,
        right = TRUE
      ),
      levels = c('sin reseñas', '1 reseña', '2-10 reseñas', '11-50 reseñas', '51-100 reseñas', '+ 101 reseñas')
    )
  )

# Puedes verificar el resultado con:
summary(airbnb_data$Review_Count_Category)
##    sin reseñas       1 reseña   2-10 reseñas  11-50 reseñas 51-100 reseñas 
##           5400           1791           4871           4749           2078 
##  + 101 reseñas 
##           2366
#NOTA: 
#El intervalo sin reseñas, incluye todos los ceros
#El intervalo1 reseña, incluye solos los valor 1 
#El intervalo '2-10 reseñas', incluye los valores desde 2 a 10 (incluido)
#El intervalo '11-50 reseñas', incluye los valores desde 11 a 50 (incluido) y asi sucesivamente 

Si hay cero reseñas, se etiqueta como “sin reseñas”. De lo contrario, se divide en varios rangos, como 1 reseña, 2-10 reseñas, 11-50 reseñas, etc. Luego, se crea una nueva variable llamada Review_Count_Category con estas categorías.

Date.last.review

Es fundamental considerar la actualidad de la información y su influencia en los análisis y decisiones basadas en estos datos. Vamos a calcular la cantidad de días transcurridos desde la última review hasta la fecha de extracción de los datos*.

*La fecha de extracción, ha sido obtenida directamente del apartado de "Information --> Last processing -> data" del dataset: Information Airbnb Listings en OpenDataSoft.

# Calcular el número de días desde la última revisión hasta la fecha de extracción
#La fecha del ultimo scrapping fue
date_scraped <- as.Date("2020-08-07")

#Calculamos la diferencia entre ambas fechas
airbnb_data$time_since_last_review <- as.numeric(difftime(date_scraped, airbnb_data$Date.last.review, units = "days"))

Se calcula el tiempo transcurrido en días desde la fecha de extracción de datos, representada por “2020-08-07”, hasta la fecha de la última reseña. La diferencia se almacena en una nueva columna llamada time_since_last_review

Ahora veamos la distribución de los datos en time_since_last_review

# Histograma del tiempo transcurrido desde la última reseña
ggplot(airbnb_data, aes(x = time_since_last_review)) +
  geom_histogram(bins = 30, fill = "#69b3a2", color = "#e9ecef") + # Colores personalizados para las barras
  theme_minimal() +
  theme(
    text = element_text(family = "Helvetica", size = 12), # Cambia la fuente y el tamaño del texto
    plot.title = element_text(face = "bold", hjust = 0.5), # Negrita y centrado para el título
    plot.subtitle = element_text(face = "italic", hjust = 0.5), # Subtítulo en cursiva y centrado
    axis.title = element_text(face = "bold", size = 13) # Títulos de los ejes en negrita y tamaño aumentado
  ) +
  labs(
    title = "Distribución de días desde la última reseña",
    subtitle = "Visualización de la frecuencia de días transcurridos",
    x = "Días desde la última reseña", y = "Frecuencia"
  )
## Warning: Removed 5400 rows containing non-finite outside the scale range
## (`stat_bin()`).

La mayoría de las reseñas se agrupan en los primeros 250 días, donde vemos que los datos se concentran entre 0 y 50 días, lo que indica que muchas propiedades fueron reseñadas poco antes de la fecha en que se extrajeron los datos.

Después de este pico inicial, se ve una disminución gradual en la cantidad de reseñas a medida que aumentan los días desde la última reseña, lo que sugiere una menor actividad en propiedades con reseñas más antiguas.

En el gráfico, se eliminaron 5400 filas que contenían valores nulos . Dado que estas filas no tienen la fecha de la última reseña (Date.last.review es nulo), el cálculo de los días transcurridos desde la última reseña hasta la fecha de extracción también resulta en valores nulos.

Para poder realizar un mejor análisis de los datos, vamos a categorizar las nueva columna: time_since_last_review

airbnb_data <- airbnb_data |>
  mutate(
    Time_category = factor(
      if_else(
        is.na(time_since_last_review),
        'sin reseñas',  # Asignar directamente 'sin reseñas' si el valor es NA
        cut(  # Usar cut solo en valores no-NA
          time_since_last_review,
          breaks = c(0, 60, 182, 365, Inf),  # Definiendo los rangos
          labels = c('hasta 8 semanas', '8 semanas - 6 meses', '6-12 meses', '1+ año'),
          include.lowest = TRUE,
          right = TRUE
        )
      ),
      levels = c('sin reseñas', 'hasta 8 semanas', '8 semanas - 6 meses', '6-12 meses', '1+ año')
    )
  )

summary(airbnb_data$Time_category)
##         sin reseñas     hasta 8 semanas 8 semanas - 6 meses          6-12 meses 
##                5400                1672                7150                3802 
##              1+ año 
##                3231
#NOTA: 
#El intervalo sin reseñas, incluye todos los nulos
#El intervalo hasta 8 semanas, incluye los valores hasta e 60 (incluida)
#El intervalo 8 semanas - 6 meses, incluye los valores desde 61 a 182 (incluido)  y asi sucesivamente 

Una vez categorizada time_since_last_review , ya no es necesaria para los siguientes pasos, porque usaremos Time_category, en su lugar; así que la eliminamos:

#Eliminar time_since_last_review
airbnb_data <- select(airbnb_data, -time_since_last_review)

Availibility

#Calculo el total de filas con ceros 
paste("Total suma de filas con ceros en la columna Availibility es:", sum(airbnb_data$Availibility == 0))
## [1] "Total suma de filas con ceros en la columna Availibility es: 5465"

Los ceros de Availibility, se han tratado en la columna Availibility_Cat como alojamientos “No disponibles”

paste("Total suma de filas con 'No disponible' en la columna Availability_Cat es:", sum(airbnb_data$Availability_Cat == "No disponible"))
## [1] "Total suma de filas con 'No disponible' en la columna Availability_Cat es: 5465"

Vemos que el recuento de “No disponible” en Availibility_Cat, coincide con el recuento de ceros en Availibility.

Una vez categorizada Availibility, y comprobando que el tratamiento de ceros, se ha realizado correctamente, ya no es necesaria para los siguientes pasos. Usaremos Availibility_Cat, en su lugar, así que la eliminamos:

#Eliminar Availibility
airbnb_data <- select(airbnb_data, -Availibility)

Comprobación de Duplicados

Veamos sin en nuestro dataset, hay filas duplicadas.

filas_duplicadas <- airbnb_data[duplicated(airbnb_data), ]
if (nrow(filas_duplicadas) == 0) {
  print("No hay filas duplicadas en el dataset.")
} else {
  print("Se han encontrado filas duplicadas en el dataset.")
}
## [1] "No hay filas duplicadas en el dataset."

Utilizamos duplicated() para crear un vector que identifica filas repetidas en airbnb_data, almacenando dichas filas en filas_duplicadas. Luego, contamos cuantas filas hay en filas_duplicadas, con nrow().

El resultado es que no hay filas duplicadas.

Resumen cambios en las variables.

Resumen de las transformaciones aplicadas:

1. Date.last.review y Updated.Date

  • Convertidas a formato de fecha (%Y-%m-%d).
  • Calculada la diferencia en días desde la fecha del último scraping (2020-08-07) hasta Date.last.review. El resultado se almacena en Time_category y se categoriza en:
    • “sin reseñas” (casos donde la fecha es nula)
    • “hasta 8 semanas”
    • “8 semanas - 6 meses”
    • “6-12 meses”
    • “1+ año”

2. Room.type, Neighbourhood, City, Country

  • Transformadas en variables categóricas (factores) para facilitar análisis por segmentos.

3. Availability

  • Categorizada en la columna Availibility_Catdesde ‘No disponible’ hasta ‘181 a 365 días’ en rangos que representan distintos niveles de disponibilidad:

    • “No disponible” (para alojamientos con cero días disponibles)
    • “Menos de 30 días”
    • “31 a 60 días”
    • “61 a 90 días”
    • “91 a 180 días”
    • “181 a 365 días”
  • Tras categorizar Availibility y comprobar que el tratamiento de ceros, en la nueva columna Availibility_Cat era correcto. Availibility se eliminó.

4. City

  • Filtrados los datos para incluir solo entradas de Madrid.

5. Coordinates

  • Separada en dos columnas numéricas: Latitude y Longitude.

6. Location

  • Eliminada tras confirmar redundancia con Country, City, y Neighbourhood.

7. Number.of.reviews & Number.of.reviews.per.month

  • Análisis de valores nulos y ceros; categorizados para facilitar análisis:
    • Review_Count_Category (basado en Number.of.reviews):

      • “sin reseñas”
      • “1 reseña”
      • “2-10 reseñas”
      • “11-50 reseñas”
      • “51-100 reseñas”
      • “+ 101 reseñas”
    • Review_category (basado en Number.of.reviews.per.month):

      • “sin reseñas” (incluye registros con ceros)
      • “0-1/mes”
      • “1-5/mes”
      • “+5/mes”

8. Neighbourhood

  • Filtrados los datos para incluir solo barrios de Madrid.

También se ha comprobado que no existan duplicados en el dataset.

Análisis Descriptivo

Análisis Outliers

Outliers variables numéricas

# Boxplot para Room.Price
ggplot(airbnb_data, aes(y = Room.Price)) +
  geom_boxplot(fill = "lightblue", color = "darkblue") +
  labs(title = "Distribución del Precio de la Habitación",
       y = "Precio (€)",
       x = "") +
  theme_minimal()

# Boxplot para Minimum.nights
ggplot(airbnb_data, aes(y = Minimum.nights)) +
  geom_boxplot(fill = "lightgreen", color = "darkgreen") +
  labs(title = "Distribución del Número Mínimo de Noches Requeridas",
       y = "Número Mínimo de Noches",
       x = "") +
  theme_minimal()

# Boxplot para Rooms.rent.by.the.host
ggplot(airbnb_data, aes(y = Rooms.rent.by.the.host)) +
  geom_boxplot(fill = "lightyellow", color = "goldenrod") +
  labs(title = "Distribución del Número de Habitaciones alquiladas por el host",
       y = "Habitaciones alquiladas por el host",
       x = "") +
  theme_minimal()

library(dplyr)
library(tidyr)  # Para pivotar el dataframe

# Filtrar sólo columnas numéricas
numeric_data <- airbnb_data %>% 
  select(where(is.numeric))

# Calcular y mostrar el porcentaje de outliers para cada columna numérica
outliers_summary <- numeric_data %>% 
  summarise(across(everything(), ~ {
    q1 <- quantile(., 0.25, na.rm = TRUE)
    q3 <- quantile(., 0.75, na.rm = TRUE)
    irq <- q3 - q1
    lower_bound <- q1 - 1.5 * irq
    upper_bound <- q3 + 1.5 * irq
    perc_outliers <- mean(. < lower_bound | . > upper_bound, na.rm = TRUE) * 100
    sprintf("%.2f%%", perc_outliers)
  }, .names = "outliers_in_{.col}")) %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Percentage_Outliers")

# Imprimir el resumen de outliers como una tabla
print(outliers_summary)
## # A tibble: 9 × 2
##   Variable                                Percentage_Outliers
##   <chr>                                   <chr>              
## 1 outliers_in_Room.ID                     0.00%              
## 2 outliers_in_Host.ID                     0.00%              
## 3 outliers_in_Room.Price                  11.40%             
## 4 outliers_in_Minimum.nights              11.39%             
## 5 outliers_in_Number.of.reviews           11.77%             
## 6 outliers_in_Number.of.reviews.per.month 6.72%              
## 7 outliers_in_Rooms.rent.by.the.host      15.43%             
## 8 outliers_in_Latitude                    8.48%              
## 9 outliers_in_Longitude                   11.83%

Al analizar los outliers de los atributos numéricos, percibimos que aquellos que tienen datos extraños son el precio por habitación, el mínimo de noches, el número de reviews, el número de reviews por mes, las habitaciones alquiladas por cada host, latitud y longitud. Estos datos se normalizarán en el paso siguiente de pre-procesamiento. Por otra parte, el Room ID y el Host ID no presentan outliers.

Outliers variables catégoricas

library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)

# Filtrar solo columnas categóricas de tipo factor
categorical_data <- airbnb_data %>%
  select(where(is.factor))

# Calcula las tablas de frecuencia para cada variable categórica, las ordena y crea gráficos
graphs <- imap(categorical_data, ~ {
    freq <- table(.x)
    df <- tibble(
      Category = names(freq),
      Frequency = as.integer(freq),
      Percentage = round(100 * prop.table(freq), 2)
    ) %>%
    arrange(desc(Percentage))

    # Verificar si estamos trabajando con la variable 'Neighbourhood'
    if (.y == "Neighbourhood") {
      # Filtrar para obtener los top 3 y bottom 10
      df <- df %>%
        mutate(Position = ifelse(row_number() <= 3, "Top 3", 
                                 ifelse(row_number() > n() - 10, "Bottom 10", "Otros"))) %>%
        filter(Position != "Otros") %>%
        arrange(desc(Frequency))
      # Crear un gráfico para la variable 'Neighbourhood'
      ggplot(df, aes(x = reorder(Category, -Frequency), y = Frequency, fill = Position)) +
        geom_bar(stat = "identity") +
        labs(title = "Top 3 y Bottom 10 Barrios por Frecuencia",
             x = "Barrio",
             y = "Frecuencia") +
        theme_minimal() +
        coord_flip()
    } else {
      # Crear un gráfico para cualquier otra variable categórica
      ggplot(df, aes(x = reorder(Category, -Percentage), y = Percentage, fill = Category)) +
        geom_bar(stat = "identity") +
        labs(title = paste("Tabla de Frecuencia para la Variable:", .y),
             x = "Categoría",
             y = "Porcentaje (%)") +
        theme_minimal() +
        coord_flip()
    }
})

# Iterar sobre la lista de gráficos y imprimir cada uno
walk(graphs, ~ suppressWarnings(print(.x)))

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

## Don't know how to automatically pick scale for object of type <table>.
## Defaulting to continuous.

Para analizar los valores extremos de los atributos categóricos, hemos utilizado las frecuencias de cada uno. Gracias a esto, podemos percibir que hay muchos barrios de Madrid en los que apenas hay listings, mientras que aquellos en los que más hay son aquellos que se encuentran cerca de la universidad o cerca del centro. Del mismo modo, podemos ver que las habitaciones de hotel y las habitaciones compartidas son poco comunes entre los tipos de habitación. En el resto de variables categóricas no se perciben valores inusualmente extremos.

Análisis Variables Categóricas

1. Neighbourhood

#Histogramas

# Grafico para barrio  

# Calcular la frecuencia de cada barrio
neighbourhood_freq <- airbnb_data |>
  count(Neighbourhood) |>
  arrange(desc(n)) |>
  top_n(10, n)  # Selecciona solo los top 10

# Graficar la distribución por barrio solo para los top 10
ggplot(neighbourhood_freq, aes(x = n, y = reorder(Neighbourhood, n))) +
  geom_bar(stat = "identity", fill = "steelblue") +  # Cambia el color de las barras
  theme_minimal() +
  theme(axis.text.y = element_text(size = 15),  # Aumenta el tamaño del texto del eje y
        plot.title = element_text(size = 22, face = "bold")) +  # Aumenta y pone en negrita el título
  labs(title = "Distribución por Barrio", x = "Frecuencia", y = "Barrio")

Observaciones clave del gráfico:

El gráfico representa la frecuencia de listados de Airbnb en los top 10 barrios más populares. Cada barra representa un barrio diferente, ordenados de mayor a menor frecuencia de arriba hacia abajo.

  1. Barrios (Eje Y): Desde la parte superior, los barrios listados son Embajadores, Universidad, Palacio, Sol, Justicia, Cortes, Trafalgar, Palos de Moguer, Goya, y Puerta del Angel.

  2. Frecuencia (Eje X): La escala del eje X va desde 0 hasta más de 2000, indicando el número de listados en cada barrio.

El barrio de Embajadores tiene la mayor cantidad de listados, seguido por Universidad y Palacio. Los barrios de Goya y Puerta del Angel, al final de la lista, tienen considerablemente menos listados comparados con los más altos en la gráfica.

Suposiciones

  • Los barrios con mayor número de listados, como Embajadores y Universidad, probablemente demanden precios más altos debido a su popularidad y atractivos locales.

2. Room.type

# Gráfico para Room.type
ggplot(airbnb_data, aes(x = Room.type)) +
  geom_bar(fill = "coral") +
  labs(title = "Distribución por Tipo de Habitación", x = "Tipo de Habitación", y = "Frecuencia")


Las barras representan la frecuencia de cada tipo de habitación en el dataset.

  • Tipos de Habitación (Eje X): Las categorías mostradas son “Entire home/apt” (casa o apartamento completo), “Hotel room” (habitación de hotel), “Private room” (habitación privada) y “Shared room” (habitación compartida).

  • Frecuencia (Eje Y): El eje vertical muestra la cantidad de listados, con una escala que va de 0 a más de 10,000.

Observaciones clave del gráfico:

  • Casa o apartamento completo tiene la frecuencia más alta, indicando que es el tipo de alojamiento más comúnmente listado.

  • Habitación privada es el segundo tipo más común, aunque con una frecuencia significativamente menor en comparación con las casas o apartamentos completos.

  • Habitación de hotel y habitación compartida tienen las menores frecuencias, con la habitación compartida mostrando la menor popularidad entre los listados.

Suposiciones

  • Los alojamientos tipo “Entire home/apt” (casa o apartamento completo) pueden comandar precios más altos debido a su mayor demanda y la privacidad que ofrecen.

3. Availability_Cat

# Gráfico para Availability_Cat
ggplot(airbnb_data, aes(x = Availability_Cat)) +
  geom_bar(fill = "purple") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  labs(title = "Distribución por Categoría de Disponibilidad", x = "Categoría de Disponibilidad", y = "Frecuencia")

Las barras representan la cantidad de propiedades disponibles según diferentes rangos de días al año en que están disponibles para ser alquiladas.

  • Categorías de Disponibilidad (Eje X): Se dividen en seis categorías distintas: “No disponible”, “Hasta 30 días”, “31 a 60 días”, “61 a 90 días”, “91 a 180 días”, y “181 a 365 días”.

  • Frecuencia (Eje Y): El eje vertical indica la cantidad de listados, con una escala que va desde 0 hasta más de 8000.

Observaciones clave del gráfico:

  • No disponible y 181 a 365 días son las categorías con la mayor frecuencia, indicando que una gran cantidad de propiedades no están disponibles para alquiler o están disponibles casi todo el año.

  • Las demás categorías presentan una menor frecuencia, con la categoría “31 a 60 días” mostrando la menor cantidad de propiedades. Esto sugiere que menos propiedades están disponibles por períodos intermedios de tiempo.

  • Las propiedades que se alquilan por “91 a 180 días” tienen una frecuencia considerablemente más alta que las de corto plazo pero menor que las de muy largo plazo, reflejando posiblemente una preferencia por alquileres de medio término.

Suposiciones:

  • Las propiedades listadas como disponibles “181 a 365 días” pueden establecer precios más altos durante temporadas de alta demanda, aprovechando su constante disponibilidad.

4. Time_category

# Gráfico para Time_category
ggplot(airbnb_data, aes(x = Time_category)) +
  geom_bar(fill = "yellow") +
  labs(title = "Distribución de Time_category (Resta entre Scrapping date y Date.last.review)", x = "Time_category", y = "Frecuencia")

Observaciones clave del gráfico:

El gráfico clasifica las propiedades de Airbnb según el tiempo transcurrido desde la última reseña, distribuyendo esta información a lo largo de cinco categorías distintas. El eje X representa las categorías de tiempo, y el eje Y muestra la frecuencia de listados en cada categoría.

  • Sin reseñas: Mayor frecuencia, indicando muchas propiedades sin reseñas recientes.

  • Hasta 8 semanas: Frecuencia significativamente más baja, menos propiedades revisadas recientemente.

  • 8 semanas - 6 meses: Mayor frecuencia, refleja un volumen considerable de propiedades con reseñas moderadamente recientes.

  • 6-12 meses: Frecuencia más baja que la categoría “8 semanas - 6 meses”, muestra menos actividad reciente.

  • 1+ año: Frecuencia similar a “sin reseñas”, muchas propiedades sin reseñas en el último año.

Suposiciones:

  • Propiedades con reseñas recientes (“Hasta 8 semanas”) pueden aprovechar su reciente popularidad para establecer precios ligeramente más altos, atrayendo huéspedes que valoran la frescura y la actividad positiva reciente.

5. Review_category

# Gráfico para Review_category
ggplot(airbnb_data, aes(x = Review_category)) +
  geom_bar(fill = "green") +
  labs(title = "Distribución de Review_category", subtitle ="(Categorizacion de Number.of.reviews.per.month)", x = "Review_category", y = "Frecuencia")

Observaciones clave del gráfico:

El gráfico “Distribución de Review_category” muestra la frecuencia de propiedades de Airbnb según el número de reseñas que reciben por mes. Las categorías van desde “sin reseñas” hasta “más de 5 reseñas por mes”. El eje X categoriza el número de reseñas mensuales y el eje Y muestra la frecuencia de propiedades en cada categoría.

  • Sin Reseñas: Representa una frecuencia moderada, lo que indica que un número considerable de propiedades no ha recibido reseñas.

  • 0-1/mes: Esta categoría tiene la frecuencia más alta, sugiriendo que la mayoría de las propiedades reciben entre cero y una reseña por mes.

  • 1-5/mes: La segunda categoría más frecuente, mostrando una actividad moderada en términos de reseñas.

  • +5/mes: Tiene menos frecuencia en comparación con las categorías de 0-1 y 1-5 reseñas por mes, lo que indica que pocas propiedades reciben más de cinco reseñas mensuales.

Suposiciones:

  • Propiedades en la categoría “+5/mes” demuestran alta demanda y satisfacción del cliente, lo que podría justificar precios más elevados debido a la percepción de calidad y experiencia superior.

6. Review_Count_Category

# Gráfico para Review_Count_Category
ggplot(airbnb_data, aes(x = Review_Count_Category)) +
  geom_bar(fill = "blue") +
  labs(title = "Distribución de Review_Count_Category", subtitle ="(Categorizacion de Number.of.reviews)", x = "Review_Count_Category", y = "Frecuencia")

Observaciones clave del gráfico:

El gráfico muestra la distribución de propiedades de Airbnb según el número total de reseñas recibidas, categorizando desde “sin reseñas” hasta “más de 101 reseñas”. En el eje X se categoriza el número total de reseñas y el eje Y muestra la frecuencia de propiedades por categoría.

  • Sin reseñas: Mayor frecuencia, muchas propiedades sin reseñas.

  • 1 Reseña: Frecuencia menor que “sin reseñas”, notable descenso.

  • 2-10 Reseñas: Mayor que “1 Reseña”, indica varias propiedades moderadamente revisadas.

  • 11-50 Reseñas: Frecuencia similar a “2-10 Reseñas”, refleja estabilidad.

  • 51-100 Reseñas: Menor que las categorías intermedias, aún significativa.

  • +101 Reseñas: La menor frecuencia, pocas propiedades muy revisadas.

Suposiciones:

  • Las propiedades con un alto número de reseñas (51-100 y más de 101 reseñas) pueden aprovechar su estatus comprobado de popularidad y satisfacción del cliente para justificar precios más altos y atraer a huéspedes dispuestos a pagar más por una experiencia de calidad asegurada.

Análisis Variables Numéricas

1. Room.Price

ggplot(airbnb_data, aes(x = Room.Price)) +
  geom_density(fill = "lightblue", alpha = 0.7) +
  labs(title = "Densidad del Precio de la Habitación",
       x = "Precio (€)",
       y = "Densidad") +
  theme_minimal()

Observaciones clave del gráfico:

  1. Alta concentración de precios bajos: La mayoría de los precios se agrupan en el extremo inferior, con un pico pronunciado cerca del origen.

  2. Cola larga hacia precios altos: Existe una extensión hacia precios más elevados, indicando que algunas propiedades son considerablemente más caras.

  3. Escasez de opciones caras: Los puntos dispersos hacia el final de la gráfica muestran que las propiedades muy caras son raras.

  4. Predominio de opciones económicas: Una gran proporción de las habitaciones son asequibles, lo que sugiere que Airbnb es accesible para una amplia gama de usuarios.

Suposiciones:

  • Preferencia por precios bajos: Los huéspedes de Airbnb probablemente prefieren alojamientos más económicos, ya que hay una mayor concentración de habitaciones en rangos de precios más bajos.

2. Minimum.nights

ggplot(airbnb_data, aes(x = Minimum.nights)) +
  geom_density(fill = "lightgreen", alpha = 0.7) +
  labs(title = "Densidad del Número Mínimo de Noches Requeridas",
       x = "Número Mínimo de Noches",
       y = "Densidad") +
  theme_minimal()

Observaciones clave del gráfico:

  1. Dominio de estancias cortas: Una gran mayoría de los listados requieren un número mínimo de noches bajo, indicado por el pico prominente cerca de cero.

  2. Raras exigencias de estancias largas: Hay algunas propiedades que requieren un número significativamente mayor de noches mínimas, como se observa en los picos menores y la cola hacia el derecho del gráfico.

  3. Poca frecuencia en requisitos extremos: El gráfico tiene una cola muy larga con densidad cercana a cero, lo que sugiere que es muy poco común que se requieran estancias muy largas.

Suposiciones:

  • Preferencia por flexibilidad: Los huéspedes prefieren y los anfitriones ofrecen opciones que no exigen compromisos de estancia prolongada.

3. Rooms.rent.by.the.host

ggplot(airbnb_data, aes(x = Rooms.rent.by.the.host)) +
  geom_density(fill = "lightyellow", alpha = 0.7) +
  labs(title = "Densidad del Número de Habitaciones alquiladas por el Host",
       x = "Habitaciones alquiladas por el host",
       y = "Densidad") +
  theme_minimal()

Observaciones clave del gráfico:

  1. Concentración en pocos listados: La mayoría de los hosts en Airbnb alquilan pocas habitaciones, como muestra el pico pronunciado cerca del origen.

  2. Cola larga hacia más habitaciones: Existen algunos hosts que gestionan un número mayor de habitaciones, aunque son bastante menos frecuentes, evidenciado por la cola larga y baja hacia la derecha.

  3. Hosts con múltiples propiedades son raros: La densidad se reduce drásticamente a medida que aumenta el número de habitaciones alquiladas, sugiriendo que los hosts con muchas propiedades son excepcionales.

Suposiciones:

  • Dominio de pequeños operadores: La mayoría de los hosts probablemente gestionan sus listados a pequeña escala, indicando que Airbnb sigue siendo una plataforma accesible para individuos o familias.

EDA: Análisis Exploratorio de Datos

Top10 Barrios Más Populares Airbnb

# Calcular la frecuencia de cada barrio
barrio_frecuencia <- airbnb_data %>%
  count(Neighbourhood) %>%
  arrange(desc(n))  # Ordenar en orden descendente por frecuencia

# Obtener los top 10 barrios más frecuentes
top10_barrios <- barrio_frecuencia %>%
  top_n(10, n)

# Reorganizar el factor Neighbourhood en función de su frecuencia
airbnb_data$Neighbourhood <- factor(airbnb_data$Neighbourhood, levels = barrio_frecuencia$Neighbourhood)

# Filtrar solo las filas que corresponden a los 10 principales barrios
airbnb_data_top10 <- airbnb_data %>%
  filter(Neighbourhood %in% top10_barrios$Neighbourhood)

# Trazar el histograma
ggplot(airbnb_data_top10, aes(x = Neighbourhood)) +
  geom_bar(fill = "skyblue", color = "black") +
  labs(title = "Top10 Barrios Más Populares Airbnb", subtitle="(Top 10, Orden Descendente)", 
       x = "Barrio", 
       y = "Frecuencia") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

¿Cuáles son los barrios más populares en Airbnb?

  • Embajadores es el barrio con la mayor frecuencia de listados, seguido de Universidad y Palacio, indicando que estas áreas son probablemente las más demandadas o con más oferta de alojamientos en Airbnb.

  • A partir de Sol en adelante, la frecuencia de los listados comienza a disminuir progresivamente.

  • Los barrios como Goya y Puerta del Angel muestran las menores frecuencias dentro de este top 10, lo que podría sugerir una menor popularidad o una oferta más limitada de alojamientos en estas áreas comparado con barrios como Embajadores o Universidad.

Distribución de precio vs número de reseñas

ggplot(airbnb_data, aes(x = Room.Price, y = Number.of.reviews)) +

geom_point(color = "blue") +

labs(title = "Distribución de Precio vs Número de Reseñas", x = "Precio de la Habitación (€)", y = "Número de Reseñas")

¿Existe una correlación entre los precios de las habitaciones y el número de reseñas que reciben, indicando la popularidad o la percepción de valor por parte de los usuarios?

  1. Predominio de precios bajos y reseñas moderadas: La mayoría de los listados tienen precios inferiores a 1,000 euros y menos de 200 reseñas, indicando que las propiedades más asequibles son comunes pero no acumulan muchas reseñas.

  2. Propiedades caras con reseñas dispersas: Algunas propiedades con precios superiores a 2,500 euros tienen desde cero hasta muchas reseñas, lo que sugiere que un precio más alto no garantiza una mayor popularidad.

  3. Picos de popularidad a precios accesibles: Un número significativo de reseñas se observa en propiedades con precios razonables, lo que indica que ciertas propiedades a precios moderados son muy populares.

En resumen, no existe una correlación clara entre precios altos y un mayor número de reseñas, implicando que otros factores como ubicación y calidad podrían influir más en la popularidad.

Precio promedio vs tipo de habitación

ggplot(airbnb_data, aes(x = Room.type, y = Room.Price)) +
  geom_bar(stat = "summary", fun = "mean", fill = "lightgreen", color = "darkgreen") +
  geom_hline(yintercept = mean(airbnb_data$Room.Price), color = "red", linetype = "dashed", linewidth = 1) +
  labs(title = "Precio Promedio vs Tipo de Habitación", x = "Tipo de Habitación", y = "Precio Promedio (€)")

¿Cómo varían los precios promedios de las diferentes tipos de habitaciones en Airbnb, y qué tipo de habitación tiene el precio promedio más alto en Madrid?

  • Casa o Apartamento Completo: Este tipo de alojamiento tiene el precio promedio más alto, considerablemente más alto que los otros tipos, lo que indica que alquilar una propiedad completa es generalmente más costoso.

  • Habitación de Hotel: Tiene un precio promedio significativamente más bajo que un apartamento o casa completa, pero más alto que una habitación privada o compartida.

  • Habitación Privada: Presenta un precio promedio más bajo que la habitación de hotel.

  • Habitación Compartida: Es la opción más económica, con el precio promedio más bajo entre los tipos de habitaciones mostrados.

Top 10 - Tipo de propiedad por barrios en Madrid

library(dplyr)
library(ggplot2)

# Calcula la cantidad total de listados por barrio
barrios_counts <- airbnb_data %>%
  group_by(Neighbourhood) %>%
  summarise(Total = n()) %>%
  arrange(desc(Total))

# Selecciona los top 10 barrios
top_barrios <- barrios_counts %>%
  top_n(10, Total) %>%
  pull(Neighbourhood)

# Filtra el dataset original para incluir solo esos barrios
filtered_data <- airbnb_data %>%
  filter(Neighbourhood %in% top_barrios)

# Crear el gráfico de barras apiladas para los top 10 barrios
ggplot(data = filtered_data, aes(x = Neighbourhood, fill = `Room.type`)) +
  geom_bar() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(x = "barrio", y = "Cantidad") +
  ggtitle("Distribución de tipos de propiedad por barrios en Madrid")

# 

¿Cómo se distribuyen los diferentes tipos de habitaciones en el top 10 de barrios de Madrid y qué barrios presentan una mayor diversidad de opciones de alojamiento?

Diversidad de Habitaciones:

  • Embajadores y Universidad: Son los barrios con más alojamientos y una amplia variedad de tipos de habitaciones, predominando los apartamentos enteros y habitaciones privadas, con escasas opciones de habitaciones compartidas y de hotel.

  • Palacio, Sol y Justicia: Presentan un balance entre apartamentos enteros y habitaciones privadas, con Palacio ofreciendo algo más de habitaciones de hotel.

Mayor y Menor Diversidad:

  • Mayor Diversidad: Embajadores y Universidad destacan por su alta cantidad de listados y diversidad, con una mezcla de casi todos los tipos de alojamiento.

  • Menor Diversidad: Barrios como Tetuán, Triángulo del Arte, Chamberí, y Puerta del Ángel tienen menos opciones y se enfocan más en apartamentos enteros y habitaciones privadas, con limitadas ofertas de habitaciones de hotel o compartidas.

Top 10 - Distribución de precios vs barrio 

library(dplyr)
library(ggplot2)

# Calcula la cantidad total de listados por barrio
barrios_counts <- airbnb_data %>%
  group_by(Neighbourhood) %>%
  summarise(Total = n()) %>%
  arrange(desc(Total))

# Selecciona los top 10 barrios
top_barrios <- barrios_counts %>%
  slice_max(Total, n = 10) %>%
  pull(Neighbourhood)

# Filtra el dataset original para incluir solo esos barrios
filtered_data <- airbnb_data %>%
  filter(Neighbourhood %in% top_barrios)

# Crea el gráfico de boxplot para los top 10 barrios
ggplot(data = filtered_data, aes(x = Neighbourhood, y = Room.Price)) +
  geom_boxplot(outlier.colour = "red", outlier.shape = 1) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(x = "barrio", y = "Precio (€)", title = "Variabilidad de precios por barrio en Madrid - Top 10 barrios") +
  theme_minimal()

¿Cómo varían los precios de los alojamientos de Airbnb entre diferentes barrios del top10 de Madrid y cuáles presentan la mayor variabilidad en sus precios?

  • barrios con Mayor Variabilidad: barrios como Embajadores, Universidad, y Palacio muestran una amplia variabilidad en precios, con numerosos valores atípicos que indican precios extremadamente altos comparados con la mediana.

  • barrios con Menor Variabilidad: barrios como Justicia, Cortes, y Puerta del Ángel parecen tener una variabilidad más contenida, con menos valores atípicos y cajas más estrechas, lo que sugiere precios más consistentes y predecibles. 

Gráfico de Distribución en Madrid

Las columnas Latitude y Longitude están disponibles, podemos crear un mapa de puntos en Madrid:

# Crear un mapa con Leaflet
madrid_map <- leaflet(data = airbnb_data) |>
  addTiles()  # Añadir el mapa base

# Añadir la capa de mapa de calor
madrid_map <- madrid_map |>
  addHeatmap(
    lng = ~Longitude, lat = ~Latitude, intensity = ~1,
    blur = 20, max = 0.05, radius = 15
  )

# Establecer la vista inicial del mapa y mostrarlo
madrid_map <- madrid_map |>
  setView(lng = -3.703, lat = 40.416, zoom = 11)

# Imprimir el mapa
madrid_map

¿Cómo se distribuyen geográficamente los listados de Airbnb en Madrid y qué áreas muestran mayor concentración de alojamientos?

  • Alta Concentración en el Centro: La mayor densidad de listados de Airbnb se observa en el centro de Madrid. Las áreas alrededor del centro de la ciudad, particularmente barrios como Sol, Gran Vía, y La Latina, muestran los puntos más calientes en rojo intenso, indicando una concentración muy alta de alojamientos.

  • Dispersión hacia las Afueras: A medida que te alejas del centro, la concentración de listados disminuye, como se puede ver en las áreas que rodean el núcleo central de la ciudad. Sin embargo, hay bolsas de moderada actividad (áreas en amarillo y verde) en barrios periféricos y municipios cercanos como Pozuelo de Alarcón y Alcobendas.

# Definir una función para determinar el color basado en el precio
get_color <- function(price) {
  if (price < 50) {
    "#00FF00"  # Verde para precios bajos
  } else if (price < 100) {
    "#FFFF00"  # Amarillo para precios medios
  } else {
    "#FF0000"  # Rojo para precios altos
  }
}

# Crear el mapa usando leaflet, aplicando directamente la función de colores
madrid_map <- leaflet(data = airbnb_data) %>%
  addProviderTiles(providers$CartoDB.Positron) %>%
  addCircleMarkers(
    ~Longitude, ~Latitude,
    radius = 5,
    color = ~sapply(Room.Price, get_color),
    fillOpacity = 0.5,
    stroke = FALSE,  # No dibujar bordes alrededor de los círculos
    group = "Airbnb Locations"
  ) %>%
  setView(lng = -3.703, lat = 40.416, zoom = 11)

# Imprimir el mapa
madrid_map

Observaciones del Gráfico:

  • Zonas Verdes (Precios Bajos): Las áreas con alojamientos de precios bajos (menos de 50 €) están dispersas pero menos concentradas que las de precios más altos. Parecen estar más presentes en las periferias de la ciudad y en municipios alejados del centro de Madrid.

  • Zonas Amarillas (Precios Medios): Las zonas que representan precios medios (entre 50 y 100 €) forman una transición entre las áreas más baratas y las más caras, y están distribuidas de forma bastante uniforme en toda la ciudad, indicando una opción de alojamiento común en muchas áreas.

  • Zonas Rojas (Precios Altos): Las áreas con alojamientos de precios altos (más de 100 €) están altamente concentradas en el centro de Madrid, especialmente en barrios céntricos y turísticos. Estas zonas rojas destacan en el corazón de la ciudad, donde se localizan muchas de las principales atracciones y servicios.

Mapa de Calor de Correlación

# Seleccionando columnas relevantes para la correlación
cor_data <- airbnb_data |>
  select(Room.Price, Minimum.nights, Number.of.reviews, Number.of.reviews.per.month, Rooms.rent.by.the.host) |>
  na.omit()  # Eliminando filas con valores NA para evitar errores

# Calculando la matriz de correlación
cor_matrix <- cor(cor_data)

# Generando el mapa de calor
corrplot(cor_matrix, method = "color", type = "upper", order = "hclust",
         tl.col = "black", tl.srt = 45, addrect = 2, 
         title = "Mapa de Calor de Correlación para AirBnB en Madrid",
         mar = c(0, 0, 2, 0))  # Ajusta el margen superior para dar más espacio al título

¿Qué variables están más fuertemente correlacionadas con el precio de los alojamientos en Airbnb?

  • “Rooms.rent.by.the.host”: Esta variable muestra una correlación negativa con “Room.Price”, indicada por el tono azul oscuro en el gráfico. Esto sugiere que cuanto más habitaciones alquila un host, podría haber una tendencia a precios más bajos por habitación.

  • “Number.of.reviews” y “Number.of.reviews.per.month”: Ambas variables tienen una correlación muy baja con “Room.Price”, como se ve por los tonos más claros en el gráfico. Esto indica que la cantidad de reseñas no influye significativamente en el precio.

  • “Minimum.nights”: La correlación entre “Minimum.nights” y “Room.Price” también es baja, lo que indica que la cantidad mínima de noches requeridas no es un factor significativo en el precio de los alojamientos.

Matriz de Dispersión y Correlación de Variables

# Las columnas relevantes para la correlación, están guardadas en la variable cor_data
 
ggpairs(cor_data, 
        lower = list(continuous = wrap("points", alpha = 0.3,size=0.3  ,color='blue')), 
        upper = list(continuous = wrap("cor", size=4))
               )

  • Correlaciones Notables:

    • Rooms.rent.by.the.host vs. Room.Price: Presenta una correlación positiva moderada (0.170***), sugiriendo que los anfitriones que alquilan más habitaciones tienden a cobrar precios más altos, aunque la relación no es muy fuerte.

    • Number.of.reviews.per.month vs. Number.of.reviews: Muestra una correlación muy alta (0.828***), lo cual es esperado ya que estas variables están intrínsecamente relacionadas.

  • Correlaciones Débiles o Negativas:

    • La mayoría de las otras variables muestran correlaciones débiles o negativas con el “Room.Price”, como “Minimum.nights” y “Number.of.reviews”, indicando que estos factores tienen menos influencia sobre el precio.

KPIs

Basado en nuestro análisis exploratorio, estos son nuestros KPIs elegidos:

  1. Distribución geográfica de listados

  2. Barrios más populares en Airbnb y su distribución según los de tipos de habitación

  3. Correlación entre precio y número de reseñas

  4. Variación de precios por tipo de habitación

  5. Variabilidad de precios por barrio

Conclusiones del Análisis

Datos Atípicos y Variables Numéricas

  • Datos Atípicos: Al observar gráficos de cajas y la tabla de outliers, se nota la presencia de valores atípicos en precio por habitación, el mínimo de noches, el número de reviews, el número de reviews por mes, las habitaciones alquiladas por cada host, latitud y longitud que podrían complicar análisis más detallados si no se manejan adecuadamente.

Variables Categóricas

  • Barrios populares: Los barrios como Embajadores y Universidad tienen muchos alojamientos listados, lo que puede indicar que son zonas muy buscadas o bien ubicadas.
  • Tipos de habitación: Las casas o apartamentos completos son los más ofrecidos, mostrando que la gente prefiere este tipo de alojamiento sobre otros como habitaciones compartidas o de hotel.

Análisis General de los Datos

  • Distribución de los alojamientos: La mayoría de los alojamientos están en el centro de Madrid, algo común en las grandes ciudades donde hay más turistas y actividades.
  • Precios y número de reseñas: No hay una relación clara entre los precios y el número de reseñas, lo que sugiere que el precio podría depender más de donde está el alojamiento o qué tipo es.

Sugerencias para Mejorar los Datos

  • Manejo de datos atípicos: Sería bueno usar métodos como transformaciones logarítmicas para reducir el efecto de los valores atípicos.
  • Agregar más información: Incluir más detalles como características del barrio o eventos cercanos podría ayudar a entender mejor qué afecta los precios y la popularidad de los alojamientos.
  • Impacto de la Pandemia: Notamos que la pandemia de COVID-19 pudo haber sesgado los datos, dado que los datos fueron recolectados el 20 de agosto de 2020, en medio de restricciones globales de viaje que alteraron significativamente el turismo y la hospitalidad.

Tratamiento del Dataset procesado

Variables finales.

Una vez tratado el dataset, este es el set de datos con el trabajaremos en el pre-procesamiento y modelado.

colnames(airbnb_data)
##  [1] "Room.ID"                     "Name"                       
##  [3] "Host.ID"                     "Neighbourhood"              
##  [5] "Room.type"                   "Room.Price"                 
##  [7] "Minimum.nights"              "Number.of.reviews"          
##  [9] "Date.last.review"            "Number.of.reviews.per.month"
## [11] "Rooms.rent.by.the.host"      "Updated.Date"               
## [13] "City"                        "Country"                    
## [15] "Latitude"                    "Longitude"                  
## [17] "Availability_Cat"            "Review_category"            
## [19] "Review_Count_Category"       "Time_category"

Con las siguientes variables:

  1. Room.ID: Identificador único de cada listado en Airbnb.
  2. Name: Nombre descriptivo del listado.
  3. Host.ID: Identificador único del anfitrión del listado.
  4. Neighbourhood: Barrio donde se encuentra el listado.
  5. Room.type: Tipo de alojamiento ofrecido.
  6. Room.Price: Precio por noche del alojamiento.
  7. Minimum.nights: Número mínimo de noches requerido para la reserva.
  8. Number.of.reviews: Número total de reseñas recibidas.
  9. Date.last.review: Fecha de la última reseña recibida.
  10. Number.of.reviews.per.month: Promedio de reseñas recibidas por mes.
  11. Rooms.rent.by.the.host: Número total de habitaciones o propiedades que el anfitrión ofrece en alquiler.
  12. Updated.Date: Fecha en que se actualizó la información del listado por última vez.
  13. City: Ciudad donde se ubica el listado, en nuestro caso, siempre Madrid.
  14. Country: País donde se ubica el listado, en nuestro caso, siempre España.
  15. Latitude: Latitud geográfica del listado. Es el uno de los resultados de dividir la variable Coordinates.
  16. Longitude: Longitud geográfica del listado. Es el uno de los resultados de dividir la variable Coordinates.
  17. Availability_Cat: Categorización de la variable “Availability” en grupos de disponibilidad.
  18. Review_category: Categoriza listados basándose en la frecuencia de reseñas por mes (se ha catergorizado la variable Number.of.reviews.per.month).
  19. ReviewCountCategory: Categoriza listados según el número total de reseñas recibidas(se ha catergorizado la variable Number.of.reviews).
  20. Time_category: Categoriza listados basado en el tiempo transcurrido desde la última reseña hasta el dia del scrapping.

Se eliminó Location ya que era redundante con País, Ciudad y Barrio.

Exportación

Ahora vamos a exportar el dataset procesado:

# Exportar el dataframe procesado a CSV 
write.csv2(airbnb_data, "../data/processed/processed-air-bnb-listings.csv", row.names = FALSE, fileEncoding = "UTF-8")