Análisis Exploratorio de Datos – Demanda de Reservas Hoteleras
Para este proyecto de Análisis Exploratorio de Datos (EDA), vamos a utilizar el conjunto de datos «Demanda de reservas de hotel», que puede encontrarse aquí
Este conjunto de datos contiene información sobre las reservas de un hotel urbano y un hotel turístico, e incluye datos como la fecha de la reserva, la duración de la estancia, el número de adultos, niños y/o bebés, y el número de plazas de aparcamiento disponibles, entre otros.
Se ha eliminado de los datos toda la información de identificación personal.
Los datos proceden originalmente del artículo Hotel Booking Demand Datasets, escrito por Nuno Antonio, Ana Almeida y Luis Nunes para Data in Brief, Volumen 22, febrero de 2019.
Los datos fueron descargados y limpiados por Thomas Mock y Antoine Bichat para #TidyTuesday durante la semana del 11 de febrero de 2020.
Preparación y limpieza de datos
El primer paso es importar las principales librerías que utilizaremos para este proyecto.
In [ ]:
import pandas as pd import seaborn as sns import numpy as np from matplotlib import pyplot as plt %matplotlib inline from sklearn.preprocessing import StandardScaler pd.set_option('display.max_columns', 500)
Ahora, descargamos el conjunto de datos para utilizarlo como un pandas dataframe.
In [ ]:
url = 'https://raw.githubusercontent.com/salves94/hotel-exploratory-data-analysis/master/hotel_bookings.csv' df = pd.read_csv(url)
Comprobamos cuántas filas tiene el conjunto de datos
In [ ]:
len(df.index)
Out[ ]:
119390
Ahora sabemos que nuestro conjunto de datos tiene 119390 filas.
Ahora vamos a comprobar cuántas celdas faltan en nuestro conjunto de datos.
In [ ]:
df.isnull().sum()
Out[ ]:
hotel 0 is_canceled 0 lead_time 0 arrival_date_year 0 arrival_date_month 0 arrival_date_week_number 0 arrival_date_day_of_month 0 stays_in_weekend_nights 0 stays_in_week_nights 0 adults 0 children 4 babies 0 meal 0 country 488 market_segment 0 distribution_channel 0 is_repeated_guest 0 previous_cancellations 0 previous_bookings_not_canceled 0 reserved_room_type 0 assigned_room_type 0 booking_changes 0 deposit_type 0 agent 16340 company 112593 days_in_waiting_list 0 customer_type 0 adr 0 required_car_parking_spaces 0 total_of_special_requests 0 reservation_status 0 reservation_status_date 0 dtype: int64
Podemos ver que tenemos 4 columnas con valores perdidos. Vamos a comprobar estos valores como porcentajes.
In [ ]:
df.isnull().sum()/len(df.index)*100
Out[ ]:
hotel 0.000000 is_canceled 0.000000 lead_time 0.000000 arrival_date_year 0.000000 arrival_date_month 0.000000 arrival_date_week_number 0.000000 arrival_date_day_of_month 0.000000 stays_in_weekend_nights 0.000000 stays_in_week_nights 0.000000 adults 0.000000 children 0.003350 babies 0.000000 meal 0.000000 country 0.408744 market_segment 0.000000 distribution_channel 0.000000 is_repeated_guest 0.000000 previous_cancellations 0.000000 previous_bookings_not_canceled 0.000000 reserved_room_type 0.000000 assigned_room_type 0.000000 booking_changes 0.000000 deposit_type 0.000000 agent 13.686238 company 94.306893 days_in_waiting_list 0.000000 customer_type 0.000000 adr 0.000000 required_car_parking_spaces 0.000000 total_of_special_requests 0.000000 reservation_status 0.000000 reservation_status_date 0.000000 dtype: float64
Las columnas «agente» y «empresa» tienen un alto porcentaje de valores perdidos. Como estas columnas no serán relevantes para nuestro análisis, podemos eliminarlas.
In [ ]:
df=df.drop(['agent','company'],axis=1) # Eliminamos las columnas de agente y empresa
Las columnas «niños» y «país» tienen un bajo porcentaje de valores omitidos. Eliminaremos la fila completa en las celdas que faltan.
In [ ]:
df = df.dropna(axis = 0) # Eliminamos las filas con celdas vacías
Ahora eliminaremos la columna days_in_waiting_list porque no la utilizaremos para este análisis
In [ ]:
df = df.drop(labels='days_in_waiting_list', axis=1) # Eliminamos la columna days_in_waiting_list
Comprobemos de nuevo los valores que faltan.
In [ ]:
df.isnull().sum()
Out[ ]:
hotel 0 is_canceled 0 lead_time 0 arrival_date_year 0 arrival_date_month 0 arrival_date_week_number 0 arrival_date_day_of_month 0 stays_in_weekend_nights 0 stays_in_week_nights 0 adults 0 children 0 babies 0 meal 0 country 0 market_segment 0 distribution_channel 0 is_repeated_guest 0 previous_cancellations 0 previous_bookings_not_canceled 0 reserved_room_type 0 assigned_room_type 0 booking_changes 0 deposit_type 0 customer_type 0 adr 0 required_car_parking_spaces 0 total_of_special_requests 0 reservation_status 0 reservation_status_date 0 dtype: int64
Perfecto, ahora no nos falta ningún valor.
Vamos a echar un vistazo más de cerca a lo que tenemos.
In [ ]:
df.describe()
Out[ ]:
is_canceled | lead_time | arrival_date_year | arrival_date_week_number | arrival_date_day_of_month | stays_in_weekend_nights | stays_in_week_nights | adults | children | babies | is_repeated_guest | previous_cancellations | previous_bookings_not_canceled | booking_changes | adr | required_car_parking_spaces | total_of_special_requests | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 | 118898.000000 |
mean | 0.371352 | 104.311435 | 2016.157656 | 27.166555 | 15.800880 | 0.928897 | 2.502145 | 1.858391 | 0.104207 | 0.007948 | 0.032011 | 0.087142 | 0.131634 | 0.221181 | 102.003243 | 0.061885 | 0.571683 |
std | 0.483168 | 106.903309 | 0.707459 | 13.589971 | 8.780324 | 0.996216 | 1.900168 | 0.578576 | 0.399172 | 0.097380 | 0.176029 | 0.845869 | 1.484672 | 0.652785 | 50.485862 | 0.244172 | 0.792678 |
min | 0.000000 | 0.000000 | 2015.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | -6.380000 | 0.000000 | 0.000000 |
25% | 0.000000 | 18.000000 | 2016.000000 | 16.000000 | 8.000000 | 0.000000 | 1.000000 | 2.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 70.000000 | 0.000000 | 0.000000 |
50% | 0.000000 | 69.000000 | 2016.000000 | 28.000000 | 16.000000 | 1.000000 | 2.000000 | 2.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 95.000000 | 0.000000 | 0.000000 |
75% | 1.000000 | 161.000000 | 2017.000000 | 38.000000 | 23.000000 | 2.000000 | 3.000000 | 2.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 126.000000 | 0.000000 | 1.000000 |
max | 1.000000 | 737.000000 | 2017.000000 | 53.000000 | 31.000000 | 16.000000 | 41.000000 | 55.000000 | 10.000000 | 10.000000 | 1.000000 | 26.000000 | 72.000000 | 21.000000 | 5400.000000 | 8.000000 | 5.000000 |
Aquí podemos ver algunos valores atípicos.
Vamos a construir gráficos de caja para verlo mejor.
In [ ]:
import numpy as np import seaborn as sns import matplotlib.pyplot as plt columns = ['lead_time', 'stays_in_weekend_nights', 'stays_in_week_nights', 'adults', 'children', 'babies', 'required_car_parking_spaces', 'adr', 'previous_cancellations', 'previous_bookings_not_canceled', 'booking_changes'] n = 1 plt.figure(figsize=(20,15)) for column in columns: plt.subplot(4,4,n) n = n+1 sns.boxplot( df[ column ] ) plt.tight_layout()
Eliminamos los valores atípicos utilizando condicionales para obtener y actualizar estos valores.
In [ ]:
df.loc[df.lead_time > 500, 'lead_time'] = 500 df.loc[df.stays_in_weekend_nights >= 5, 'stays_in_weekend_nights'] = 5 df.loc[df.adults > 4, 'adults'] = 4 df.loc[df.previous_bookings_not_canceled > 0, 'previous_bookings_not_canceled'] = 1 df.loc[df.previous_cancellations > 0, 'previous_cancellations'] = 1 df.loc[df.stays_in_week_nights > 10, 'stays_in_week_nights'] = 10 df.loc[df.booking_changes > 5, 'booking_changes'] = 5 df.loc[df.babies > 8, 'babies'] = 0 df.loc[df.required_car_parking_spaces > 5, 'required_car_parking_spaces'] = 0 df.loc[df.children > 8, 'children'] = 0 df.loc[df.adr > 1000, 'adr'] = 1000
Hemos eliminado los valores atípicos. Ahora nuestros datos están limpios.
Análisis exploratorio y visualización
En esta sección exploraremos los datos para obtener información sobre ellos.
Vamos a combinar las columnas «niños» y «bebés» en la columna «niños».
A continuación, establecemos el tipo de cadena en las columnas correspondientes.
In [ ]:
df['kids'] = df.children + df.babies #Combine total mumbers by adding kids and adults df['total_members'] = df.kids + df.adults #convert the datatypes to string df['arrival_date_year'] = df['arrival_date_year'].astype('str') df['arrival_date_month'] = df['arrival_date_month'].astype('str') df['arrival_date_day_of_month'] = df['arrival_date_day_of_month'].astype('str') df['is_canceled'] = df['is_canceled'].astype('str') df['is_repeated_guest'] = df['is_repeated_guest'].astype('str')
Convirtamos la fecha de llegada en fecha-hora
In [ ]:
df['arrival_date'] = df['arrival_date_day_of_month'] + '-' + df['arrival_date_month'] + '-' + df['arrival_date_year'] df['arrival_date'] = pd.to_datetime(df['arrival_date'], errors='coerce')
A) Reservas confirmadas
Veamos cuántas reservas confirmadas hay al mes.
In [ ]:
import datetime as dt confirmed_bookings = df[df.is_canceled=='0'] confirmed_bookings['arrival_date_month'] = df['arrival_date'].dt.month final=confirmed_bookings['arrival_date_month'].value_counts().sort_index() final
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:5: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy """
Out[ ]:
1 4068 2 5317 3 6591 4 6533 5 7102 6 6393 7 7892 8 8618 9 6367 10 6867 11 4632 12 4365 Name: arrival_date_month, dtype: int64
Las reservas confirmadas pasan de su valor más bajo (4068) en enero a su valor más alto (8618) en agosto.
B) Reservas canceladas
Ahora vamos a comprobar las reservas canceladas.
In [ ]:
print('Total Bookings cancelled') print(df.is_canceled.value_counts()) print('Cancelation percentage') print(df.is_canceled.value_counts(normalize=True))
Total Bookings cancelled 0 74745 1 44153 Name: is_canceled, dtype: int64 Cancelation percentage 0 0.628648 1 0.371352 Name: is_canceled, dtype: float64
Durante el año, tenemos un 37,13% de cancelaciones.
Ahora vamos a trazar este resultado.
In [ ]:
plt.figure(figsize=(8,8)) #Canceled=1, Not canceled= 0 sns.countplot(df['is_canceled'], palette='husl') plt.show()
C) País
Aquí comprobaremos el país de origen. Las categorías se representan en el formato ISO 3155-3:2013.
In [ ]:
df.country.value_counts(normalize=True)
Out[ ]:
PRT 0.408636 GBR 0.102012 FRA 0.087596 ESP 0.072062 DEU 0.061288 ... NCL 0.000008 MMR 0.000008 CYM 0.000008 MRT 0.000008 SDN 0.000008 Name: country, Length: 177, dtype: float64
D) Mes
Ahora vamos a comprobar la fecha de llegada por meses.
In [ ]:
df.arrival_date_month.value_counts(normalize=True)
Out[ ]:
August 0.116503 July 0.106209 May 0.099068 October 0.093315 April 0.092895 June 0.091902 September 0.088033 March 0.081911 February 0.067385 November 0.056788 December 0.056586 January 0.049404 Name: arrival_date_month, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['arrival_date_month'], palette='husl') plt.show()
E) Segmento de mercado
Comprobemos la designación del segmento de mercado. En las categorías, el término «AT» significa «agencias de viajes» y «TO» significa «turoperadores».
In [ ]:
df.market_segment.value_counts(normalize=True)
Out[ ]:
Online TA 0.474373 Offline TA/TO 0.203199 Groups 0.166580 Direct 0.104695 Corporate 0.042986 Complementary 0.006173 Aviation 0.001993 Name: market_segment, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['market_segment'], palette='husl') plt.show()
F) Año
Año de la fecha de llegada.
In [ ]:
df.arrival_date_year.value_counts(normalize=True)
Out[ ]:
2016 0.474651 2017 0.341503 2015 0.183847 Name: arrival_date_year, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['arrival_date_year'], palette='husl') plt.show()
G) Comida
Tipo de comida reservada. Las categorías se presentan en paquetes de comidas de hospitalidad estándar:
- Undefined/SC – sin paquete de comidas.
- BB – Bed & Breakfast.
- HB – Half board (desayuno y otra comida, normalmente la cena).
- FB – Full board (desayuno, comida y cena).
In [ ]:
df.meal.value_counts(normalize=True)
Out[ ]:
BB 0.772620 HB 0.121398 SC 0.089472 Undefined 0.009798 FB 0.006712 Name: meal, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['meal'], palette='husl') plt.show()
H) Customer Type
Tipo de reserva, asumiendo una de las cuatro categorías:
- Contract – cuando la reserva tiene asociada una adjudicación u otro tipo de contrato.
- Group – cuando la reserva está asociada a un grupo.
- Transient – cuando la reserva no forma parte de un grupo o contrato, y no está asociada a otra reserva transitoria.
- Transient-party – cuando la reserva es transitoria, pero está asociada al menos a otra reserva transitoria.
In [ ]:
df.customer_type.value_counts(normalize=True)
Out[ ]:
Transient 0.750004 Transient-Party 0.210920 Contract 0.034281 Group 0.004794 Name: customer_type, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['customer_type'], palette='husl') plt.show()
I) Tipo de habitación reservada
Código del tipo de habitación reservada. El código se presenta en lugar de la designación por razones de anonimato.
In [ ]:
df.reserved_room_type.value_counts(normalize=True)
Out[ ]:
A 0.719953 D 0.161256 E 0.054643 F 0.024307 G 0.017519 B 0.009369 C 0.007830 H 0.005055 L 0.000050 P 0.000017 Name: reserved_room_type, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['reserved_room_type'], palette='husl') plt.show()
J) Tipo de habitación asignada
Código del tipo de habitación asignado a la reserva. A veces, el tipo de habitación asignado difiere del tipo de habitación reservado por motivos de funcionamiento del hotel (por ejemplo, exceso de reservas) o a petición del cliente. El código se presenta en lugar de la designación por razones de anonimato.
In [ ]:
df.assigned_room_type.value_counts(normalize=True)
Out[ ]:
A 0.621230 D 0.211660 E 0.065081 F 0.031388 G 0.021354 C 0.019798 B 0.018158 H 0.005955 I 0.003003 K 0.002347 P 0.000017 L 0.000008 Name: assigned_room_type, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['assigned_room_type'], palette='husl') plt.show()
K) Cambios en las reservas
Número de cambios/enmiendas realizados en la reserva desde el momento en que se introdujo la reserva en el PMS hasta el momento de la facturación o cancelación.
In [ ]:
df.booking_changes.value_counts(normalize=True)
Out[ ]:
0 0.848643 1 0.106301 2 0.031876 3 0.007780 4 0.003154 5 0.002246 Name: booking_changes, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['booking_changes'], palette='husl') plt.show()
l) Canal de distribución
Canal de distribución de reservas. El término «AT» significa «agencias de viajes» y «TO» significa «turoperadores».
In [ ]:
df.distribution_channel.value_counts(normalize=True)
Out[ ]:
TA/TO 0.821965 Direct 0.121810 Corporate 0.054593 GDS 0.001623 Undefined 0.000008 Name: distribution_channel, dtype: float64
In [ ]:
plt.figure(figsize=(14,7)) sns.countplot(df['distribution_channel'], palette='husl') plt.show()
M) Invitado repetido
Compruebe si el nombre de la reserva procede de un huésped repetido.
In [ ]:
df.is_repeated_guest.value_counts(normalize=True)
Out[ ]:
0 0.967989 1 0.032011 Name: is_repeated_guest, dtype: float64
In [ ]:
plt.figure(figsize=(6,6)) sns.countplot(df['is_repeated_guest'], palette='husl') plt.show()
N) Tipo de depósito
Indicación de si el cliente hizo un depósito para garantizar la reserva. Esta variable puede asumir tres categorías:
- No Deposit – no se ha realizado ningún depósito.
- Non Refund – se ha realizado un depósito por valor del coste total de la estancia.
- Refundable – se ha realizado un depósito con un valor inferior al coste total de la estancia.
In [ ]:
df.deposit_type.value_counts(normalize=True)
Out[ ]:
No Deposit 0.876070 Non Refund 0.122567 Refundable 0.001363 Name: deposit_type, dtype: float64
In [ ]:
plt.figure(figsize=(6,6)) sns.countplot(df['deposit_type'], palette='husl') plt.show()
O) Plazas de aparcamiento obligatorias
Número de plazas de aparcamiento que necesita el cliente.
In [ ]:
df.required_car_parking_spaces.value_counts(normalize=True)
Out[ ]:
0 0.938536 1 0.061204 2 0.000235 3 0.000025 Name: required_car_parking_spaces, dtype: float64
In [ ]:
plt.figure(figsize=(6,6)) sns.countplot(df['required_car_parking_spaces'], palette='husl') plt.show()
P) Total de miembros
Total de miembros por reserva.
In [ ]:
df.total_members.value_counts(normalize=True)
Out[ ]:
2.0 0.688674 1.0 0.187472 3.0 0.088134 4.0 0.033154 0.0 0.001430 5.0 0.001135 Name: total_members, dtype: float64
In [ ]:
plt.figure(figsize=(6,6)) sns.countplot(df['total_members'], palette='husl') plt.show()
Q) Estado de la reserva
Estado de última reserva, asumiendo una de las tres categorías:
- Canceled – El cliente ha cancelado la reserva.
- Check-Out – el cliente se ha registrado pero ya se ha marchado.
- No-Show – El cliente no se registró e informó al hotel del motivo.
In [ ]:
df.reservation_status.value_counts(normalize=True)
Out[ ]:
Check-Out 0.628648 Canceled 0.361234 No-Show 0.010118 Name: reservation_status, dtype: float64
In [ ]:
plt.figure(figsize=(6,6)) sns.countplot(df['reservation_status'], palette='husl') plt.show()
R) Tipo de hotel
Veamos la proporción de reservas entre tipos de hotel.
In [ ]:
df.hotel.value_counts(normalize=True)
Out[ ]:
City Hotel 0.666975 Resort Hotel 0.333025 Name: hotel, dtype: float64
In [ ]:
plt.figure(figsize=(6,6)) sns.countplot(df['hotel'], palette='husl') plt.show()
S) Relación entre los precios y el mes
In [ ]:
plt.figure(figsize=(12,5)) # Calculating average daily rate per person df['adr_pp'] = df['adr'] / (df['adults'] + df['children']) actual_guests = df.loc[df["is_canceled"] == '0'] actual_guests['price'] = actual_guests['adr'] * (actual_guests['stays_in_weekend_nights'] + actual_guests['stays_in_week_nights']) sns.lineplot(data = actual_guests, x = 'arrival_date_month', y = 'price', hue = 'hotel') plt.show()
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:6: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
Aquí podemos ver que los precios de los hoteles vacacionales son más altos y fluctúan más que los de los hoteles urbanos.
T) ADR
In [ ]:
plt.figure(figsize=(12,6)) sns.lineplot(x='arrival_date_month', y='adr', hue='hotel', data= df) plt.show()
En los hoteles turísticos, la tarifa media diaria es más cara en agosto, julio y septiembre.
En los hoteles urbanos, la tarifa media diaria es más cara en agosto, julio, junio y mayo.
Formulación de Preguntas
Ahora formularemos y responderemos algunas preguntas interesantes sobre los datos.
¿Cuáles son los tres países de origen más comunes y los menos comunes?
In [ ]:
print('Most common countries:') print(df.country.value_counts().head(3)) print('-'*50) print('\n Most uncommon countries:') print(df.country.value_counts().tail(35))
Most common countries: PRT 48586 GBR 12129 FRA 10415 Name: country, dtype: int64 -------------------------------------------------- Most uncommon countries: MWI 2 COM 2 SYC 2 MYT 2 TGO 2 MDG 1 DMA 1 MLI 1 GUY 1 PLW 1 KIR 1 PYF 1 LCA 1 BHS 1 FJI 1 NAM 1 UMI 1 NIC 1 DJI 1 NPL 1 BDI 1 BWA 1 VGB 1 SLE 1 BFA 1 SMR 1 AIA 1 ATF 1 ASM 1 HND 1 NCL 1 MMR 1 CYM 1 MRT 1 SDN 1 Name: country, dtype: int64
Como vemos, Portugal encabeza la lista con 48.586 de los casos, seguido de Gran Bretaña con 12.129 y Francia con 10.415.
Hay 30 países que pueden considerarse como el país de origen más infrecuente, con 1 huésped por país. Entre ellos figuran Madagascar, Dominica, Malí, Guyana, Palaos, Kiribati, Sudán, etc.
¿Cuáles son los meses de mayor y menor ocupación?
In [ ]:
df.arrival_date_month.value_counts(normalize=True)
Out[ ]:
August 0.116503 July 0.106209 May 0.099068 October 0.093315 April 0.092895 June 0.091902 September 0.088033 March 0.081911 February 0.067385 November 0.056788 December 0.056586 January 0.049404 Name: arrival_date_month, dtype: float64
El mes de mayor ocupación es agosto con el 11,65% de las reservas. El mes de menor ocupación es enero con el 4,94% de las reservas.
¿Cuál es el paquete de comidas más popular?
In [ ]:
df.meal.value_counts(normalize=True)
Out[ ]:
BB 0.772620 HB 0.121398 SC 0.089472 Undefined 0.009798 FB 0.006712 Name: meal, dtype: float64
La opción Bed & Breakfast es la más popular, con una frecuencia del 77,26%.
¿Cuál es el tipo de habitación más reservado?
In [ ]:
df.reserved_room_type.value_counts(normalize=True)
Out[ ]:
A 0.719953 D 0.161256 E 0.054643 F 0.024307 G 0.017519 B 0.009369 C 0.007830 H 0.005055 L 0.000050 P 0.000017 Name: reserved_room_type, dtype: float64
El tipo de habitación «A» es el más popular entre los clientes, con el 71,99% de las reservas.
¿Cuántos cambios de reservas se han realizado durante el periodo estudiado?
In [ ]:
df.booking_changes.sum()
Out[ ]:
25829
Durante este periodo se registraron 25.829 cambios en las reservas.
¿Cuántas personas se han registrado en el hotel?
In [ ]:
df.total_members.sum()
Out[ ]:
233934.0
233.934 personas se han registrado en el hotel.
¿Cuántas plazas de aparcamiento se han utilizado?
In [ ]:
df.required_car_parking_spaces.sum()
Out[ ]:
7342
Se han utilizado 7.342 plazas de aparcamiento.
¿Cuál es el tipo de cliente más habitual?
In [ ]:
df.customer_type.value_counts(normalize=True)
Out[ ]:
Transient 0.750004 Transient-Party 0.210920 Contract 0.034281 Group 0.004794 Name: customer_type, dtype: float64
Los clientes de tipo Transient son los más comunes, representando el 75% del total de clientes.
Inferencias y conclusión
- La mayoría de los huéspedes proceden de países de Europa Occidental.
- La mayoría de las reservas son para hoteles urbanos.
- El número de huéspedes que repiten es muy bajo.
- La mayoría de las reservas se convierten en transacciones con éxito.
Referencias
- Guía del usuario de Pandas: https://pandas.pydata.org/docs/user_guide/index.html
- Guía del usuario de Matplotlib: https://matplotlib.org/3.3.1/users/index.html
- Guía del usuario y tutorial de Seaborn: https://seaborn.pydata.org/tutorial.html