Эта задача связана с прогнозированием годовой доступности различных отелей на основе определенных характеристик, чтобы компания могла принять решение о сотрудничестве с отелем в отношении средств размещения.

Если вы хотите попрактиковаться, данные и полный код для справки можно найти по адресу: https://github.com/negiadventures/predicting_hotel_availability.git.

Описание данных

Обработка данных и визуализация

data = pd.read_csv("train.csv")
data.head()

data.describe()

# Checking data types for each column
data.dtypes
'''
id                       int64
region                  object
latitude               float64
longitude              float64
accommodation_type      object
cost                     int64
minimum_nights           int64
number_of_reviews        int64
reviews_per_month      float64
owner_id                 int64
owned_hotels             int64
yearly_availability      int64
dtype: object 
'''

# Checking if any column have nulls
data.isnull().sum(axis=0)
'''
id                       0
region                   0
latitude                 0
longitude                0
accommodation_type       0
cost                     0
minimum_nights           0
number_of_reviews        0
reviews_per_month      676
owner_id                 0
owned_hotels             0
yearly_availability      0
dtype: int64
'''

Заполнение пустых значений в столбце Reviews_per_month с помощью 0. Это связано с тем, что этот столбец может быть очень ценным как функция, и эти строки с нулевыми значениями нельзя игнорировать, поскольку все остальные столбцы имеют некоторое значение. количество отзывов в месяц может указывать на то, что это будет зависеть от того, сколько людей посетило это место. т. е. чем больше людей посетило отель, тем больше шансов, что они напишут отзыв, и, следовательно, это также означает, что отель может быть менее доступен из года в год из-за спроса.

data.reviews_per_month = data.reviews_per_month.fillna(0)
# Converting the categorical column values to independent columns with values 0 and 1
data  = pd.get_dummies(data, drop_first =True)
data

# removing non-relevant fields - these may not be usefull for model training
train_data = data.drop(['id','latitude','longitude','owner_id'], axis = 1)

Визуализация, моделирование, машинное обучение

#Loading Test data
test_data=pd.read_csv('test.csv')
test_data.head()

Преобразование и очистка тестовых данных

test_data.isnull().sum(axis=0)
'''
id                      0
region                  0
latitude                0
longitude               0
accommodation_type      0
cost                    0
minimum_nights          0
number_of_reviews       0
reviews_per_month     173
owner_id                0
owned_hotels            0
dtype: int64
'''
test_data.reviews_per_month = test_data.reviews_per_month.fillna(0)
test_data  = pd.get_dummies(test_data, drop_first =True)
test_data_feature = test_data.drop(['id','latitude','longitude','owner_id'], axis = 1)
test_data_feature

Проверка корреляции между функциями

import seaborn as sns
import matplotlib.pyplot as plt
corr_df = train_data.corr()
corr_df

ax = plt.axes()
sns.heatmap(corr_df, ax = ax)
ax.set_title('Correlation between features')
plt.show()

corr_df_viz = corr_df
corr_df_viz['feature'] = corr_df_viz.index

plt.figure(figsize=(10,6))
# make barplot
sns.barplot(x='feature',
            y="yearly_availability", 
            data=corr_df_viz, 
            order=corr_df_viz.sort_values('yearly_availability', ascending = False).feature)
# set labels
plt.xlabel("Feature", size=15)
plt.ylabel("Correlation between Yearly Availability", size=15)
plt.title("Top 20 Features", size=18)
plt.tight_layout()
plt.xticks(rotation=80)
#(array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11]),
# <a list of 12 Text xticklabel objects>)

Приведенный выше график показывает нам, что тип жилья, минимальное количество ночей пребывания человека, стоимость и регион имеют положительную корреляцию, в то время как такие характеристики, как число_отзывов, количество отзывов_за_месяц, имеют отрицательную корреляцию, т. е. если эти числа больше, годовая доступность с меньшей вероятностью равна 1.

Создание модели

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score, confusion_matrix ,classification_report
import numpy as np
# Checking distribution of yearly_availability column in the training data
f, ax = plt.subplots(figsize=(7, 5))
sns.countplot(x='yearly_availability', data=train_data)
plt.title('# Availability vs Non Availability')
plt.xlabel('Class (1==Availability)')
#Text(0.5, 0, 'Class (1==Availability)')

Поскольку у нас есть сопоставимое количество записей для обоих классов, мы можем использовать показатели точности для производительности.

# Splitting data to train and test
np.random.seed(42)
X = train_data.drop(['yearly_availability'
                    ], axis = 1)
y = train_data['yearly_availability']
X_train, X_test, y_train, y_test = train_test_split(X, y)
# Using Logistic regression for Binary Classification
scaler = StandardScaler()
lr = LogisticRegression(solver = 'lbfgs')
model1 = Pipeline([('standardize', scaler),
                    ('log_reg', lr)])
model1.fit(X_train, y_train)
Pipeline(memory=None,
         steps=[('standardize',
                 StandardScaler(copy=True, with_mean=True, with_std=True)),
                ('log_reg',
                 LogisticRegression(C=1.0, class_weight=None, dual=False,
                                    fit_intercept=True, intercept_scaling=1,
                                    l1_ratio=None, max_iter=100,
                                    multi_class='warn', n_jobs=None,
                                    penalty='l2', random_state=None,
                                    solver='lbfgs', tol=0.0001, verbose=0,
                                    warm_start=False))],
         verbose=False)
# Accuracy for Train Split
y_train_h = model1.predict(X_train)
y_train_h_probs = model1.predict_proba(X_train)[:,1]

train_accuracy = accuracy_score(y_train, y_train_h)*100

print('Confusion matrix:\n', confusion_matrix(y_train, y_train_h))

print('Training accuracy: %.4f %%' % train_accuracy) 
'''
Confusion matrix:
 [[945 137]
 [169 901]]
Training accuracy: 85.7807 %
'''
# Accuracy for Test Split
y_test_h = model1.predict(X_test)
y_test_h_probs = model1.predict_proba(X_test)[:,1]

test_accuracy = accuracy_score(y_test, y_test_h)*100

print('Confusion matrix:\n', confusion_matrix(y_test, y_test_h))

print('Testing accuracy: %.4f %%' % test_accuracy)
'''
Confusion matrix:
 [[313  44]
 [ 56 305]]
Testing accuracy: 86.0724 %
'''
print(classification_report(y_test, y_test_h, digits=5))
'''
precision    recall  f1-score   support

           0    0.84824   0.87675   0.86226       357
           1    0.87393   0.84488   0.85915       361

    accuracy                        0.86072       718
   macro avg    0.86108   0.86081   0.86071       718
weighted avg    0.86115   0.86072   0.86070       718
'''
# Combining prediction results to IDs.
pred_list = list(model1.predict(test_data_feature))
pred_id = list(test_data['id'])
submission_df = pd.DataFrame(list(zip(pred_list,pred_id )), columns = ['id','yearly_availability'])
submission_df

submission_df.to_csv('submissions.csv',index=False)

Результаты. Мы смогли достичь точности 86 % в модели логистической регрессии и предсказать значения тестовых данных с помощью этой модели. Дальнейшие улучшения можно внести, проанализировав, какие функции можно игнорировать при обучении модели.