Predicting Hotel Bookings with User Search Parameters | by Susan Li | Towards Data Science

A hotel market data and benchmark company STR and Google have published a report shows that tracking hotel search results can be a reliable estimate of hotel bookings. So, our goal today is to try to build a machine learning model to predict the event outcome (booking or just a click) for a user event, based on their search and other attributes associated with that user event.
The data is public available that can be downloaded from Kaggle. we are only using the training set. The table below provides the schema of the data set.
import datetimeimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as sns%matplotlib inlinefrom sklearn.ensemble import RandomForestClassifierfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.decomposition import PCAfrom sklearn.metrics import classification_reportfrom sklearn.metrics import confusion_matrix  from sklearn.metrics import accuracy_scoredf = pd.read_csv('train.csv.gz', sep=',').dropna()df = df.sample(frac=0.01, random_state=99)
To be able to process locally, we will use 1% of the data. After that, we still have a large number of 241,179 records.
df.shape
(241179, 24)
count_classes = pd.value_counts(df['is_booking'], sort = True).sort_index()count_classes.plot(kind = 'bar')plt.title("Booking or Not booking")plt.xlabel("Class")plt.ylabel("Frequency")
It is obvious that our data is very imbalanced. We will have to deal with it.
The process includes create new columns such as year, month, plan time and hotel nights. And remove the columns we do not need anymore afterwards.
df["date_time"] = pd.to_datetime(df["date_time"]) df["year"] = df["date_time"].dt.year  df["month"] = df["date_time"].dt.monthdf['srch_ci']=pd.to_datetime(df['srch_ci'],infer_datetime_format = True,errors='coerce')df['srch_co']=pd.to_datetime(df['srch_co'],infer_datetime_format = True,errors='coerce')df['plan_time'] = ((df['srch_ci']-df['date_time'])/np.timedelta64(1,'D')).astype(float)df['hotel_nights']=((df['srch_co']-df['srch_ci'])/np.timedelta64(1,'D')).astype(float)cols_to_drop = ['date_time', 'srch_ci', 'srch_co', 'user_id']df.drop(cols_to_drop, axis=1, inplace=True)
Plot a correlation matrix using a heatmap to explore the correlation between features.
correlation = df.corr()plt.figure(figsize=(18, 18))sns.heatmap(correlation, vmax=1, square=True,annot=True,cmap='viridis')plt.title('Correlation between different fearures')
We do not see any two variables are very closely correlated.
I will use under-sampling method to create re-sampled dataframe.
booking_indices = df[df.is_booking == 1].indexrandom_indices = np.random.choice(booking_indices, len(df.loc[df.is_booking == 1]), replace=False)booking_sample = df.loc[random_indices]not_booking = df[df.is_booking == 0].indexrandom_indices = np.random.choice(not_booking, sum(df['is_booking']), replace=False)not_booking_sample = df.loc[random_indices]df_new = pd.concat([not_booking_sample, booking_sample], axis=0)print("Percentage of not booking clicks: ", len(df_new[df_new.is_booking == 0])/len(df_new))print("Percentage of booking clicks: ", len(df_new[df_new.is_booking == 1])/len(df_new))print("Total number of records in resampled data: ", len(df_new))
Shuffle the resampled dataframe.
df_new = df_new.sample(frac=1).reset_index(drop=True)
Assign features and label for the new dataframe.
X = df_new.loc[:, df_new.columns != 'is_booking']y = df_new.loc[:, df_new.columns == 'is_booking']
Principal component analysis, or PCA, is a statistical technique to convert high dimensional data to low dimensional data by selecting the most important features that capture maximum information about the dataset. We hope PCA will help us find out the components with maximum variance.
Standardize the dataset.
scaler = StandardScaler()X=scaler.fit_transform(X)X
Apply PCA. And we have 23 features in our data.
pca = PCA(n_components=23)pca.fit(X)
Calculate Eigenvalues.
var=np.cumsum(np.round(pca.explained_variance_ratio_, decimals=3)*100)var
In the above array we see that the first feature explains 9.6% of the variance within our data set while the first two explain 17.8% and so on. If we employ all features we capture 100% of the variance within the dataset, thus we gain some by implementing an additional feature. No any single feature outstanding.
Sort and select.
plt.ylabel('% Variance Explained')plt.xlabel('# of Features')plt.title('PCA Analysis')plt.style.context('seaborn-whitegrid')plt.plot(var)
Based on the plot above it’s clear that we should keep all the 23 features.
Random Forest Classifier
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)pca = PCA()  X_train = pca.fit_transform(X_train)  X_test = pca.transform(X_test)classifier = RandomForestClassifier(max_depth=2, random_state=0)  classifier.fit(X_train, y_train)y_pred = classifier.predict(X_test)cm = confusion_matrix(y_test, y_pred)  print(cm)  print('Accuracy', accuracy_score(y_test, y_pred))
Logistic Regression
from sklearn.linear_model import LogisticRegressionfrom sklearn.pipeline import PipelineX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)pca = PCA(n_components=23)logReg = LogisticRegression()pipe = Pipeline([('pca', pca), ('logistic', logReg)])pipe.fit(X_train, y_train)y_pred = pipe.predict(X_test)cm = confusion_matrix(y_test, y_pred)  print(cm)  print('Accuracy', accuracy_score(y_test, y_pred))
We may be able to boost the results to 70% by Exhaustive Grid Search. But it takes forever to run locally. I will not try it here.
As you can see, it is not an easy task to predict hotel bookings from our current available data. We need more features, such as Average Daily Rate, users previous booking history, whether there is a special promotion, hotel online reviews and so on.
Getting started with data collection before Machine Learning!
Source code can be found on Github. Enjoy the rest of the week!