yandex_project / pages /3_booking_cancel_pred.py
Sazuppy's picture
Upload 32 files
540fe64 verified
import streamlit as st
import numpy as np
import pandas as pd
import pickle
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler, OneHotEncoder
import datetime
st.set_page_config(page_title="# Прогнозирование оттока клиентов в сети отелей «Как в гостях»")
st.markdown('# Прогнозирование оттока клиентов в сети отелей «Как в гостях»')
with st.expander("Описание проекта"):
st.write(
"""Заказчик этого исследования — сеть отелей «Как в гостях».
Чтобы привлечь клиентов, эта сеть отелей добавила на свой сайт возможность забронировать номер без предоплаты.
Однако если клиент отменял бронирование, то компания терпела убытки. Сотрудники отеля могли, например, закупить продукты к приезду гостя
или просто не успеть найти другого клиента.
Чтобы решить эту проблему, вам нужно разработать систему, которая предсказывает отказ от брони. Если модель покажет,
что бронь будет отменена, то клиенту предлагается внести депозит. Размер депозита — 80% от стоимости номера за одни сутки и затрат на разовую уборку.
Деньги будут списаны со счёта клиента, если он всё же отменит бронь.
Бизнес-метрика и другие данные:
- Основная бизнес-метрика для любой сети отелей — её прибыль.
Прибыль отеля — это разница между стоимостью номера за все ночи и затраты на обслуживание: как при подготовке номера, так и при проживании постояльца.
В отеле есть несколько типов номеров. В зависимости от типа номера назначается стоимость за одну ночь.
Есть также затраты на уборку. Если клиент снял номер надолго, то убираются каждые два дня.
Стоимость номеров отеля:
- категория A: за ночь — 1 000, разовое обслуживание — 400;
- категория B: за ночь — 800, разовое обслуживание — 350;
- категория C: за ночь — 600, разовое обслуживание — 350;
- категория D: за ночь — 550, разовое обслуживание — 150;
- категория E: за ночь — 500, разовое обслуживание — 150;
- категория F: за ночь — 450, разовое обслуживание — 150;
- категория G: за ночь — 350, разовое обслуживание — 150.""")
with st.expander("Описание данных:"):
st.write("""
- id — номер записи;
- adults — количество взрослых постояльцев;
- arrival_date_year — год заезда;
- arrival_date_month — месяц заезда;
- arrival_date_week_number — неделя заезда;
- arrival_date_day_of_month — день заезда;
- babies — количество младенцев;
- booking_changes — количество изменений параметров заказа;
- children — количество детей от 3 до 14 лет;
- country — гражданство постояльца;
- customer_type — тип заказчика:
- Contract — договор с юридическим лицом;
- Group — групповой заезд;
- Transient — не связано с договором или групповым заездом;
- Transient-party — не связано с договором или групповым заездом, но связано с бронированием типа Transient.
- days_in_waiting_list — сколько дней заказ ожидал подтверждения;
- distribution_channel — канал дистрибуции заказа:
- "Direct" (Прямой)
- "TA/TO" (Туристические агентства/Туроператоры)
- "Corporate" (Корпоративный)
- "GDS" (Глобальные системы бронирования)
- is_canceled — отмена заказа;
- is_repeated_guest — признак того, что гость бронирует номер второй раз;
- lead_time — количество дней между датой бронирования и датой прибытия;
- meal — опции заказа:
- SC — нет дополнительных опций;
- BB — включён завтрак;
- HB — включён завтрак и обед;
- FB — включён завтрак, обед и ужин.
- previous_bookings_not_canceled — количество подтверждённых заказов у клиента;
- previous_cancellations — количество отменённых заказов у клиента;
- required_car_parking_spaces — необходимость места для автомобиля;
- reserved_room_type — тип забронированной комнаты;
- stays_in_weekend_nights — количество ночей в выходные дни;
- stays_in_week_nights — количество ночей в будние дни;
- total_nights — общее количество ночей;
- total_of_special_requests — количество специальных отметок.
"""
)
st.sidebar.header("Признаки для модели машинного обучения")
def changes(df):
pass
def user_input_features():
meal = st.sidebar.selectbox('опции заказа', ('BB', 'FB', 'HB', 'SC'))
country = st.sidebar.selectbox('гражданство постояльца', ('GBR', 'PRT', 'ESP', 'IRL', 'FRA', 'Others', 'USA', 'DEU', 'BEL', 'CHE', 'NLD', 'ITA', 'BRA', 'AUT'))
distribution_channel = st.sidebar.selectbox('канал дистрибуции заказа', ('Direct', 'TA/TO', 'Corporate', 'GDS'))
reserved_room_type = st.sidebar.selectbox('тип забронированной комнаты', ('A', 'C', 'D', 'E', 'G', 'F', 'B'))
customer_type = st.sidebar.selectbox('тип заказчика', ('Transient', 'Contract', 'Transient-Party', 'Group'))
adults = st.sidebar.slider('количество взрослых постояльцев', 0, 6, 2)
children = st.sidebar.slider('количество детей от 3 до 14 лет', 0, 5, 2)
babies = st.sidebar.slider('количество младенцев', 0, 1, 5)
days_in_waiting_list = st.sidebar.slider('сколько дней заказ ожидал подтверждения', 0, 250, 0)
previous_cancellations = st.sidebar.slider('количество отменённых заказов у клиента', 0, 30, 0)
data_lead = st.sidebar.date_input("день бронирования", datetime.date(2019, 7, 6))
end_time = st.sidebar.date_input("день заезда", datetime.date(2019, 7, 20))
count_day = st.sidebar.slider('Количество дней проживания', 0, 31, 0)
data_back = end_time + datetime.timedelta(days=count_day)
lead_time = (end_time-data_lead).days
total_of_special_requests = st.sidebar.slider('количество специальных отметок', 0, 8, 0)
arrival_date_day_of_month = end_time.day
arrival_date_year = end_time.year
arrival_date_month = end_time.month
arrival_date_week_number = end_time.isocalendar()[1]
stays_in_weekend_nights = 0
stays_in_week_nights = 0
total_nights = stays_in_weekend_nights + stays_in_week_nights
current_date = end_time
while current_date < data_back:
if current_date.weekday() < 5: # Понедельник (0) - Пятница (4)
stays_in_week_nights += 1
else:
stays_in_weekend_nights += 1
current_date += datetime.timedelta(days=1)
is_repeated_guest = st.sidebar.selectbox('признак того, что гость бронирует номер второй раз', ('Yes', 'No'))
previous_bookings_not_canceled = st.sidebar.slider('количество подтверждённых заказов у клиента', 0, 60, 0)
required_car_parking_spaces = st.sidebar.selectbox('необходимость места для автомобиля', ('Yes', 'No'))
booking_changes = st.sidebar.slider('количество измененных вами параметров', 0, 10, 0)
data = {'meal': meal,
'country': country,
'distribution_channel': distribution_channel,
'reserved_room_type': reserved_room_type,
'customer_type': customer_type,
'lead_time': lead_time,
'adults': adults,
'children': children,
'booking_changes': booking_changes,
'babies': babies,
'days_in_waiting_list': days_in_waiting_list,
'previous_cancellations': previous_cancellations,
'total_nights': total_nights,
'total_of_special_requests': total_of_special_requests,
'arrival_date_day_of_month': arrival_date_day_of_month,
'arrival_date_year': arrival_date_year,
'arrival_date_month': arrival_date_month,
'arrival_date_week_number': arrival_date_week_number,
'stays_in_weekend_nights': stays_in_weekend_nights,
'stays_in_week_nights': stays_in_week_nights,
'is_repeated_guest': is_repeated_guest,
'previous_bookings_not_canceled': previous_bookings_not_canceled,
'required_car_parking_spaces': required_car_parking_spaces,
}
features = pd.DataFrame(data, index=[0])
return features
df = user_input_features()
df = df.sort_index(axis=1)
st.subheader('Таблица с введенными вами параметрами:')
# st.write(df)
def pre_category(data):
if data == "Yes":
return 1
else:
return 0
def preprocessing_data(df, scaler, ohe):
df['is_repeated_guest'] = df['is_repeated_guest'].apply(pre_category)
df['required_car_parking_spaces'] = df['required_car_parking_spaces'].apply(pre_category)
numeric = ['adults', 'children', 'booking_changes', 'babies', 'days_in_waiting_list', 'previous_cancellations', 'lead_time',
'total_nights', 'total_of_special_requests', 'arrival_date_day_of_month', 'arrival_date_year', 'arrival_date_month',
'arrival_date_week_number', 'stays_in_weekend_nights', 'stays_in_week_nights', 'is_repeated_guest', 'previous_bookings_not_canceled',
'required_car_parking_spaces', 'booking_changes']
categorical = ['meal', 'country', 'distribution_channel', 'reserved_room_type', 'customer_type']
df[numeric] = scaler.transform(df[numeric])
tmp = pd.DataFrame(ohe.transform(df[categorical]).toarray(),
columns=ohe.get_feature_names_out(),
index=df.index)
df.drop(categorical, axis=1, inplace=True)
df = df.join(tmp).sort_index(axis=1)
return pd.DataFrame(df, index=[0])
@st.cache_resource
def get_model():
load_model = pickle.load(open('models/booking_cancel_pred.pkl', 'rb'))
ohe_model = pickle.load(open('models/ohe_booking_cancel_pred.pkl', 'rb'))
scaler_model = pickle.load(open('models/scaler_booking_cancel_pred.pkl', 'rb'))
return load_model, scaler_model, ohe_model
model, sc_model, ohe_model = get_model()
df_new = preprocessing_data(df, sc_model, ohe_model)
# st.write(df_new)
prediction = model.predict(df_new)
prediction_proba = model.predict_proba(df_new)
st.subheader('Рекомендация')
exited = np.array(['Клиент вероятно оставит бронь','Клиент вероятно отменит бронь'])
st.write(exited[prediction])
st.subheader('Вероятность рекомендации')
st.write(prediction_proba)