Analisa Performa Bisnis Hotel Menggunakan Visualisasi Data

Ali Nasution
8 min readJan 16, 2023

--

Ilustrasi, Photo by Marten Bjork on Unsplash

Latar Belakang

Perkembangan digitalisasi yang cepat, telah menjalar ke berbagai industri yang tentunya membawa keuntungan dan dampak positif termasuk bagi industri perhotelan. Hotel merupakan salah satu sarana pendukung utama yang menunjang dalam bisnis di bidang pariwisata. Berkat kemajuan teknologi telah mengubah pengalaman pelanggan dan membantu perkembangan bisnis perhotelan. Data sains yang merupakan contoh kemajuan teknologi dapat digunakan untuk membantu pihak hotel untuk menganalisis performa bisnisnya. Dalam proyek ini, kita akan mendalami bisnis perhotelan untuk menemukan insight terkait performa bisnis perhotelan. Tujuannya adalah untuk memahami perilaku pelanggan saat melakukan reservasi hotel dan hubungannya dengan tingkat pembatalan hotel. Insight yang ditemukan akan disajikan dalam bentuk visualisasi data yang lebih mudah dipahami dan lebih meyakinkan. Dataset yang digunakan berasal dari publikasi Hotel booking demand datasets dengan beberapa modifikasi seperti lokasi hotel di indonesia. Penjelasan kolom dalam dataset tersebut sebagai berikut :

Keterangan :

  • hotel: Tipe hotel, city hotel atau resort hotel
  • is_canceled: dibatalkan(1) atau tidak dibatalkan(0)
  • lead_time: Jumlah hari yang berlalu antara tanggal masuk pemesanan ke dalam PMS dan tanggal kedatangan
  • arrival_date_year: Informasi tahun kedatangan
  • arrival_date_month: Informasi bulan kedatangan
  • arrival_date_week_number:Informasi minggu kedatangan
  • arrival_date_day_of_month: Informasi tanggal kedatangan
  • stay in_weekend_nights: Jumlah malam weekend (Sabtu atau Minggu) tamu menginap atau memesan untuk menginap di hotel
  • stay in_weekdays_nights: Jumlah malam weekday (Senin sampai Jumat) tamu menginap atau memesan untuk menginap di hotel
  • adults/children/babies: Jumlah orang dewasa/anak-anak/bayi
  • meal: Jenis makanan yang dipesan -> No Meal/Undefined, Breakfast, Dinner, Full board.
  • city: Kota/tempat asal.
  • agent: ID dari travel agency pem-booking
  • company: ID dari perusahaan pem-booking
  • customer_type: Tipe pemesanan -> Personal, Family, Contract, Business
  • reservation_status: Status terakhir pemesanan -> Check-Out, Canceled, No-Show

Data Preprocessing

Sebelum melakukan analisa, langkah yang pertama kali kita lakukan adalah mempersiapkan data mentah menjadi data yang siap dipakai.

#. Mengatasi Missing Value

Missing Value Checking
  • Missing value pada company hampir 95% dengan artian bahwa pihak perusahaan yang memesan hotel hanya 5%, sehingga dapat kita lakukan imputasi dengan angka 0.
  • Missing value pada agent sekitar 13% dengan artian bahwa 87% yang memesan hotel bukan dari pihak biro perjalanan, sehingga dapat kita lakukan imputasi dengan angka 0.
  • Missing value pada city kemungkinan beberapa pemesan tidak mengisi asal kotanya, sehingga dapat kita lakukan imputasi dengan nilai ‘tidak_diketahui’.
  • Missing value pada children hanya 4 data kemungkinan pemesan diasumsikan tidak memiliki anak sehingga dapat kita lakukan imputasi dengan angka 0.
# Imputation 0 and cast to int for Numerical, and 'Unknown' for Categorical
df['city'].fillna('Unknown', inplace=True)
df.fillna({'company':0, 'agent': 0, 'children': 0}, downcast='infer', inplace=True)

#. Mengganti nilai yang tidak sesuai

Check Undefined Column

Pada keterangan kolom meal, nilai ‘undefined’ bermakna sama dengan ‘No Meal’ sehingga kita dapat mengganti nilai ‘Undefined’ menjadi ‘No Meal

#Replace undefined to no meal because is same
df['meal'].replace({'Undefined':'No Meal'}, inplace=True)

#. Menghapus data yang tidak diperlukan

Ada beberapa kolom yang bisa kita reduksi :
1. Adults, Children, dan Babies dapat kita jadikan satu menjadi ‘Total_Guest’
2. Stays_in_weekdays_nights dan Stays_in_weekend_nights dapat kita jadikan satu menjadi ‘Stay_Duration’

df['total_guest'] = df['children'] + df['adults'] + df['babies']
df['stay_duration'] = df['stays_in_weekdays_nights'] + df['stays_in_weekend_nights']
Data yang tidak diperlukan

Data yang tidak diperlukan data yang tidak masuk akal misalnya total guest dan total stay durationnya nilainya 0, sehingga perlu kita hapus.

#Drop unnecessary data
df.drop(df.loc[(df['total_guest']==0) | (df['stay_duration']==0)].index, inplace=True, axis=0)

#. Menghapus data duplikat

Duplicated Checking

Menurut Samuel(2022) dalam hevodata , Laporan yang dihasilkan dari data duplikat kurang dapat diandalkan dan tidak dapat digunakan untuk membuat keputusan yang tepat. Bisnis juga akan sulit memperkirakan apa yang harus dilakukan untuk pertumbuhan di masa depan. Oleh karena itu kita perlu menghapus data yang duplikat.

df.drop_duplicates(keep='first', inplace=True)

karena data sudah siap digunakan, saatnya kita menganalisa kinerja performa hotel.

Analisis Pemesanan Hotel Bulanan Berdasarkan Tipe Hotel

Pada tahap ini, kita akan melihat dan menganalisis pada bulan apa terjadi kenaikan dan penurunan dari jumlah pemesanan hotel untuk setiap jenis hotel.

Tahun Kedatangan

Apabila kita perhatikan tahun kedatangan dengan bulan terlihat sebagian bulan memiki jumlah tahun kedatangan yang tidak seragam, oleh sebab itu kita perlu melakukan normalisasi terlebih dahulu dengan cara membagi total pemesanan dengan jumlah tahun baru kita dapat melakukan visualisasi.

# Normalisasi
agg_df = df.groupby(['hotel','arrival_date_month']).agg({'is_canceled':'count','arrival_date_year':'nunique'}).reset_index()
agg_df['total_norm'] = agg_df['is_canceled']/agg_df['arrival_date_year']
agg_df

# Urutkan data berdasarkan bulan
month_list = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']

agg_df['hotel'] = agg_df['hotel'].astype('category')
agg_df['arrival_date_month'] = agg_df['arrival_date_month'].astype('category')
agg_df['arrival_date_month'] = agg_df['arrival_date_month'].cat.set_categories(month_list)

#sorting data
agg_df = agg_df.sort_values(['hotel','arrival_date_month'])

#Menyingkat nama bulan untuk visualisasi
agg_df['month'] = agg_df['arrival_date_month'].apply(lambda x: x[:3])

#plot kenaikan dan penuruan
#plt.rcdefaults()
import matplotlib.style as style
style.use('fivethirtyeight')

plt.figure(figsize=(12,8))
sns.lineplot(data=agg_df, x="month", y="total_norm", hue='hotel', lw=3, marker='o', markersize=10)
plt.tick_params(axis = 'both', which = 'major', labelsize = 14)
plt.text(x=-2, y=5000, s='Pemesanan Hotel Bulanan Berdasarkan Tipe Hotel', fontsize=24, fontweight='bold')
plt.text(x=-2, y=4700, s='Pemesanan hotel meningkat dari Mei sampai Juli bertepatan dengan Puasa dan Lebaran.', fontsize=18)
plt.text(x=-2, y=4500, s='Sedikit pemesanan hotel pada Januari sampai Maret disebabkan sedikitnya hari libur.', fontsize=18)
plt.axvspan(4, 6, color='green', alpha=0.3)
plt.text(4.3, 3500, s='Musim Libur', alpha=0.8)
plt.axvspan(9, 11, color='green', alpha=0.3)
plt.text(9.3, 3500, s='Musim Libur', alpha=0.8)
plt.legend(frameon=False, bbox_to_anchor=(0.3, 1))
plt.grid(False)
plt.text(x=-2, y = -500,
s = 'Sumber data : Rakamin Mini Project',fontsize = 14, color = '#f0f0f0', backgroundcolor = 'grey')
plt.xlabel(None)
plt.ylabel('Rata-rata Pemesanan', weight='bold')
plt.ylim(0,4000)
plt.tight_layout()
plt.show()
Pemesanan Hotel Bulanan Berdasarkan Tipe Hotel
  • Sekilas, terlihat pelanggan lebih banyak memilih memesan hotel perkotaan dibandingkan dengan hotel resort.
  • Libur paling lama di Indonesia ialah Hari Raya dan Nataru.
  • Pemesanan hotel bertambah signifikan pada bulan yang memiliki lama hari libur banyak, terbukti dengan meningkatnya pemesanan antara mei sampai juli yang merupakan Hari Raya yang bertepatan sekitar bulan Juni dan Juli.
  • Kedua jenis hotel dari januari sampai agustus memiliki pola kenaikan yang sama, namun hotel resort mengalami kenaikan di bulan september, sedangkan hotel perkotaan mengalami sebaliknya.
  • Pada bulan januari hingga maret sedikit yang memesan hotel dapat diasumsikan kemungkinan masa sibuk bekerja.

Analisis Dampak Durasi Menginap terhadap Tingkat Pembatalan Pemesanan Hotel

Pada tahap ini, kita akan menganalisis bagaimana korelasi antara durasi menginap terhadap tingkat pembatalan pemesanan hotel. Jika banyak pelanggan yang membatalkan pemesanan-nya, maka hal tersebut akan berpengaruh buruk terhadap performa bisnis hotel. Untuk itu kita perlu mengetahui apa saja yang mempengaruhi pembatalannya.

#. Memeriksa distribusi data pada kolom stay_duration

Distribusi Kolom Stay_Duration
  • Berdasarkan skew terlihat bahwa kolom distribusi miring ke kanan, dengan terlihat bahwa kebanyakan data bertumpuk diantara 0–5.
  • Dilihat dari plot diatas terdapat pemesanan ada yang lebih dari 15 hari.
  • Untuk lebih memudahkan kita dapat melakukan pengelompokan data pada nilai tersebut.
#mengelompokkan data > 15 hari
df['group_stay_day'] = df['stay_duration'].apply(lambda x: ">15" if x > 15 else x)

Setelah melakukan pengelompokan kita dapat membuat grafik yang dapat membandingkan 2 tipe hotel terhadap durasi menginapnya pelanggan dengan melakukan aggregasi beserta menghitung persentase pembatalan terlebih dahulu.

#ambil kolom untuk diagregasi
booking_duration = ['is_canceled', 'group_stay_day', 'hotel']

#aggregasi kolom
canceled_df = df.pivot_table(index=['hotel','group_stay_day'], values='arrival_date_month', columns='is_canceled', aggfunc='count').reset_index()

#persentase pembatalan
canceled_df.rename(columns={0:'not_canceled',1:'canceled'}, inplace=True)
canceled_df['canceled_pct'] = (cancelled_df['canceled']/(cancelled_df['canceled']+cancelled_df['not_canceled'])*100)

#plot
cancel_df = cancelled_df.pivot(index="group_stay_day", columns="hotel", values="canceled_pct")
ax = cancel_df.plot(kind='bar', stacked=True, figsize=(15,8), width=0.8)

ax.annotate('Paling Tinggi', xy=(15, 115.5), xytext=(13, 120),
arrowprops=dict(facecolor='black', shrink=0.05))

for p in ax.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
ax.text(x+width/2,
y+height/2,
'{:.0f}%'.format(height),
horizontalalignment='center',
verticalalignment='center', color='white', weight='bold')

plt.text(x=-1, y=147, s='Durasi Menginap dengan Tingkat Pembatalan', fontsize=24, fontweight='bold')
plt.text(x=-1, y=140, s='Semakin lama menginap semakin tinggi tingkat pembatalan', fontsize=18)
plt.text(x=-1, y=133, s='Hotel Perkotaan lebih tinggi tingkat pembatalan dibanding hotel resort.', fontsize=18)

plt.legend(frameon=False, bbox_to_anchor=(0.2,0.9), fontsize = 18)

plt.grid(False)

plt.ylabel('Persentase Pembatalan', fontsize=18, weight='bold')
plt.xlabel('Jumlah Hari Menginap', fontsize=18, weight='bold')

plt.xticks(rotation=0)

plt.tight_layout()

plt.show()
Grafik Durasi Menginap dengan Tingkat Pembatalan
  • Dilihat dari grafik tingkat pembatalan hotel perkotaan lebih tinggi dari hotel resort.
  • Semakin lama hari menginap semakin tinggi tingkat pembatalan.
  • Tingkat pembatalan paling tinggi pada kedua jenis hotel lebih dari 15 hari.
  • Oleh sebab itu sebaiknya pihak hotel memberikan aturan tegas seperti pemotongan biaya sebagai biaya pembatalan.

Analisis Dampak Lead Time terhadap Tingkat Pembatalan Pemesanan Hotel

Bisnis hotel biasanya memperbolehkan pelanggan memesan hotel sebelum hari kedatangannya. Jarak waktu pun sangat beragam oleh sebab itu, kita perlu menganalisis bagaimana korelasi antara jarak waktu pemesanan hotel(lead_time) terhadap tingkat pembatalan pemesanan hotel.

#. Memeriksa distribusi kolom lead_time

Distribusi Lead Time
  • Berdasarkan skewterlihat bahwa kolom distribusi miring ke kanan dengan data menumpuk diantara 0–100 hari
  • Dilihat dari plot diatas terdapat waktu pemesanan lebih dari 365 hari
  • Untuk lebih memudahkan kita dapat melakukan pengelompokan data pada nilai tersebut per kuartal.
#melakukan binning data
def binning_lead_time(x):
if x <= 30:
label = '1 Bulan'
elif x > 30 and x <= 90:
label = '2-3 Bulan'
elif x > 90 and x <= 180:
label = '4-6 Bulan'
elif x > 180 and x <= 270:
label = '7-9 Bulan'
elif x > 270 and x <= 360:
label ='10-12 Bulan'
else:
label = '> 1 Tahun'

return label

# apply func to new feature
df['lead_time_by_month'] = df['lead_time'].apply(lambda x:binning_lead_time(x))

#pivot table
leadtime_df = df.pivot_table(index=['hotel','lead_time_by_month'], values='arrival_date_month', columns='is_canceled', aggfunc='count').rename(columns={0:'not_canceled', 1:'canceled'}).reset_index()
leadtime_df

#group by month
list_group_by_month = df['lead_time_by_month'].value_counts().index.to_list()
leadtime_df['lead_time_by_month'] = leadtime_df['lead_time_by_month'].astype('category')
leadtime_df['lead_time_by_month'] = leadtime_df['lead_time_by_month'].cat.set_categories(list_group_by_month)

# menghitung persentase pembatalan
leadtime_df['canceled_pct'] = (leadtime_df['canceled']/(leadtime_df['canceled']+leadtime_df['not_canceled']))*100

#plot
move=10

plt.figure(figsize=(12,8))
sns.barplot(data=leadtime_df.sort_values(['hotel','lead_time_by_month']), x="lead_time_by_month", y="canceled_pct", hue='hotel')
plt.text(x=-1, y=78+move, s='Jarak Waktu Pemesanan dengan Tingkat Pembatalan', fontsize=24, fontweight='bold')
plt.text(x=-1, y=73+move, s='Dalam setahun, semakin lama jarak waktu semakin tinggi tingkat pembatalan.', fontsize=18)
plt.text(x=-1, y=69+move, s='Tingkat pembatalan hotel perkotaan naik setiap bulan.', fontsize=18)
plt.text(x=-1, y=65+move, s='Tingkat pembatalan hotel resort hampir stagnan sekitar 30%.', fontsize=18)
plt.tick_params(axis = 'both', which = 'major', labelsize = 14)
plt.grid(False)
plt.legend(frameon=False, bbox_to_anchor=(0.3, 1), fontsize= 14)
plt.axvspan(-0.7,0.5, color='green', alpha=0.2)
plt.text(x=-0.3, y=60, s='Terkecil', weight='bold')
plt.axvspan(3.5,4.5, color='green', alpha=0.2, )
plt.text(x=3.7, y=60, s='Terbesar', weight='bold')
plt.xlabel('Jarak Waktu Pemesanan', weight='bold')
plt.ylabel('Tingkat Pembatalan', weight='bold')
plt.tight_layout()
plt.show()

Setelah melakukan pengelompokan data, kita dapat membuat pivot tabel dan menghitung persentase pembatalan kemudian melakukan plot sebagai berikut :

Grafik Lead Time dengan Tingkat Pembatalan
  • Sekilas terlihat tingkat pembatalan pada hotel perkotaan lebih tinggi daripada hotel resort.
  • Dalam setahun, semakin lama jarak waktu pemesanan semakin tinggi tingkat pembatalannya.
  • Tingkat pembatalan hotel perkotaan naik setiap bulan.
  • Tingkat pembatalan hotel resort hampir stagnan sekitar 30%.
  • Tingkat pembatalan hotel paling kecil berada dalam satu bulan waktu pemesanan.
  • Tingkat pembatalan hotel paling tinggi berada dalam 10–12 bulan pemesanan.
  • Dilihat dari data, maka sebaiknya pihak hotel membatasi jarak waktu pemesanan yang dibolehkan, misalnya tidak boleh lebih dari 30 hari.

Kesimpulan

Dari hasil analisis diatas dapat diambil kesimpulan sebagai berikut:

  • Pemesanan hotel bertambah signifikan pada bulan yang memiliki lama hari libur banyak, terbukti dengan meningkatnya pemesanan antara mei sampai juli yang merupakan Hari Raya yang bertepatan sekitar bulan Juni dan Juli.
  • Semakin lama durasi menginap yang dipesan semakin tinggi tingkat pembatalan.
  • Semakin lama antara jarak waktu pemesanan dengan hari kedatangan, semakin tinggi tingkat pembatalannya.
  • Saran bisnis yang dapat direkomendasikan yaitu sebaiknya pihak hotel memberikan aturan tegas seperti pemotongan biaya sebagai biaya pembatalan dan membatasi jarak waktu pemesanan yang dibolehkan.

--

--