File size: 12,980 Bytes
540fe64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
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)