<jupyter_start><jupyter_text>Predicting Cancellation RatesIn this notebook, you will build a machine learning model to predict whether or not a customer cancelled a hotel booking. You will be introduced to the `scikit-learn` framework to do machine learning in Python. We will use a dataset on hotel bookings from the article ["Hotel booking demand datasets"](https://www.sciencedirect.com/science/article/pii/S2352340918315191), published in the Elsevier journal, [Data in Brief](https://www.sciencedirect.com/journal/data-in-brief). The abstract of the article states > This data article describes two datasets with hotel demand data. One of the hotels (H1) is a resort hotel and the other is a city hotel (H2). Both datasets share the same structure, with 31 variables describing the 40,060 observations of H1 and 79,330 observations of H2. Each observation represents a hotel booking. Both datasets comprehend bookings due to arrive between the 1st of July of 2015 and the 31st of August 2017, including bookings that effectively arrived and bookings that were canceled. For convenience, the two datasets have been combined into a single csv file `data/hotel_bookings.csv`. Let us start by importing all the functions needed to import, visualize and model the data.<jupyter_code># Data imports
import pandas as pd
import numpy as np

# Visualization imports
%config InlineBackend.figure_format = 'svg'
import matplotlib.pyplot as plt
import plotly.express as px
plt.rcParams['figure.figsize'] = [8, 4]


# ML Imports
from sklearn.model_selection import train_test_split, KFold, cross_validate, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score<jupyter_output><empty_output><jupyter_text>0. Get the dataThe first step in any machine learning workflow is to get the data and explore it.<jupyter_code>hotel_bookings = pd.read_csv('data/hotel_bookings.csv')
hotel_bookings.head()<jupyter_output><empty_output><jupyter_text>Let us look at the number of bookings by month.<jupyter_code>bookings_by_month = hotel_bookings.groupby('arrival_date_month', as_index=False)[['hotel']].count().rename(columns={"hotel": "nb_bookings"})
months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] 
fig = px.bar(
    bookings_by_month, 
    x='arrival_date_month', 
    y='nb_bookings', 
    title=f'Hotel Bookings by Month', 
    category_orders={"arrival_date_month": months}
)
fig.show(config={"displayModeBar": False})<jupyter_output><empty_output><jupyter_text>Our objective is to build a model that predicts whether or not a user cancelled a hotel booking. 1. Split the data into training and test sets.Let us start by defining a split to divide the data into training and test sets. The basic idea is to train the model on a portion of the data and test its performance on the other portion that has not been seen by the model. This is done in order to prevent __overfitting__. We will use four-fold cross validation with shuffling.<jupyter_code>split = KFold(n_splits=4, shuffle=True, random_state=1234)<jupyter_output><empty_output><jupyter_text>2. Choose a class of models, and hyperparameters.The next step is to choose a class of models and specify hyperparameters. This is just for starters and we will see later how we can specify a range of values for hyperparameters and tune the model for optimal performance! We will pick the simple, yet very effective Decision Tree and Random Forest models.We will use `scikit-learn` to fit the models and evaluate their performance.<jupyter_code>from IPython.display import Image
Image("http://scikit-learn.org/dev/_static/ml_map.png", width=750)
models = [
  ("Decision Tree", DecisionTreeClassifier(random_state=1234)),
  ("Random Forest", RandomForestClassifier(random_state=1234,n_jobs=-1)),
]<jupyter_output><empty_output><jupyter_text>3. Preprocess the dataThe next step is to set up a pipeline to preprocess the features. We will impute all missing values with a constant, and one-hot encode all categorical features.<jupyter_code># Preprocess numerical features:
features_num = [
    "lead_time", "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", "agent", "company", 
    "required_car_parking_spaces", "total_of_special_requests", "adr"
]
transformer_num = SimpleImputer(strategy="constant")

# Preprocess categorical features:
features_cat = [
    "hotel", "arrival_date_month", "meal", "market_segment", "distribution_channel", 
    "reserved_room_type", "deposit_type", "customer_type"
]
transformer_cat = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="constant", fill_value="Unknown")),
    ("onehot", OneHotEncoder(handle_unknown='ignore'))
])

# Create a preprocessing pipeline
preprocessor = ColumnTransformer(transformers=[
    ("num", transformer_num, features_num),
    ("cat", transformer_cat, features_cat)
])<jupyter_output><empty_output><jupyter_text>4. Fit the models and evaluate performanceFinally, we will fit the Decision Tree and Random Forest models on the training data and use 4-fold cross-validation to evaluate their performance.<jupyter_code>features = features_num + features_cat
X = hotel_bookings[features]
y = hotel_bookings["is_canceled"]
for name, model in models:
    # Compose data preprocessing and model into a single pipeline
    steps = Pipeline(steps=[
        ('preprocessor', preprocessor),
        ('model', model)
    ])
    
    # Compute cross validation accuracy for each model
    cv_results = cross_val_score(steps, X, y, cv=split, scoring="accuracy", n_jobs=-1)
    
    # output:
    min_score = round(np.min(cv_results), 4)
    max_score = round(np.max(cv_results), 4)
    mean_score = round(np.mean(cv_results), 4)
    std_dev = round(np.std(cv_results), 4)
    print(f"[{name}] Cross Validation Accuarcy Score: {mean_score} +/- {std_dev} (std) min: {min_score}, max: {max_score}")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09025270758122744, 'fraction_numerical': 0.011458169832051484, 'mean_word_length': 5.836909871244635, 'pattern_counts': {'":': 3, '<': 29, '<?xml version=': 0, '>': 30, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '393572', 'license': 'MIT', 'n_tokens_mistral': 1954, 'n_tokens_neox': 1841, 'n_words': 652, 'path': 'projects/machine_learning_template/machine_learning_template.ipynb', 'repo_name': 'albertoarenas/datacamp', 'size': '6371'}