От опросов к инсайтам: прогнозирование удовлетворенности пассажиров с помощью классификатора XGBoost.

Добро пожаловать в мой всеобъемлющий учебник, в котором мы изучим искусство прогнозирования удовлетворенности пассажиров с помощью машинного обучения! В этом руководстве я подробно расскажу о пошаговом процессе создания мощной модели с помощью классификатора XGBoost. К концу у вас будет глубокое понимание того, как анализировать данные опроса и точно определять, довольны ли пассажиры или нет.

Привет, я Da Data Guy!

Чтобы начать, вот ссылка на мой репозиторий GitHub, где вы найдете все необходимые ресурсы, чтобы следовать инструкциям и убедиться, что у вас настроена среда Jupyter Notebook. Из репозитория загрузите два файла Excel, расположенные в папке Vanilla_Data, а также XGBoost Classifer File из папки Jupyter Notebook. После загрузки настройте записную книжку и путь к папке, чтобы импортировать ванильный набор данных в локальную записную книжку.

Этот набор данных был загружен из Maven Analytics. Если вам интересно узнать больше о других общедоступных наборах данных, нажмите здесь: Бесплатные наборы данных и образцы наборов данных | Мейвен Аналитика

Какова цель?

Цель этого анализа состояла в том, чтобы определить, какое «лицо/идентификатор» будет предсказано как «удовлетворенный» или «неудовлетворенный» респондент на основе их ответов на опрос. Прогнозы алгоритма, полученные на основе выборки данных, сравнивались со столбцом «Удовлетворенность» для оценки точности с использованием матрицы путаницы. Наконец, полный набор данных был запущен с использованием сохраненной модели, чтобы определить ее точность по отношению к 129 880 респондентам.

Зачем использовать алгоритм XGBoost?

Я выбрал алгоритм XGBoost из-за его исключительной скорости и точности, что делает его предпочтительным в качестве классификатора. Его структура повышения градиента позволяет мне объединять слабые модели в надежный ансамбль, что приводит к очень точным прогнозам. Кроме того, XGBoost предлагает встроенные инструменты определения важности функций, которые позволяют мне уверенно определять ключевые факторы, влияющие на удовлетворенность пассажиров. Этот аспект особенно важен, поскольку он дает ценную информацию о производительности модели и основных факторах, определяющих ее прогнозы.

Давай начнем

  1. Загрузите библиотеки и набор данных в блокнот Jupiter.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import xgboost as xgb
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
#sns.set(color_codes = True)
#sns.set(style="whitegrid")
#sns.set_palette("Set3")

# Get multiple outputs in the same cell
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

# Ignore all warnings
import warnings
warnings.filterwarnings('ignore')
warnings.filterwarnings(action='ignore', category=DeprecationWarning)
pd.set_option('display.max_columns', None)
# For cluster analysis
df = pd.read_csv("airline_passenger_satisfaction.csv")

2. Определите пустые значения, которые имеют только 1 столбец «Задержка прибытия» со значением 393.

df.isnull().sum()

3. Изменение текста на 1 и 0 для столбца удовлетворенности для «Цели».

# Showing the data before changing it in the cell below
df['Satisfaction'].value_counts()

# Output: 
# Neutral or Dissatisfied    73,452
# Satisfied                  56,428
# If Satisfaction is found, then it equals 1. Everything else equals 0
df['Satisfaction'] = np.where(df['Satisfaction'] == "Satisfied", 1, 0)
df['Satisfaction'].value_counts()

# 0    73,452
# 1    56,428

4. Заполнение пустых значений с помощью интерполяции. Интерполяция заполняет нулевые значения оценками, основанными на окружающих ненулевых значениях.

df['Arrival Delay'].interpolate(method='linear', inplace=True)

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

df2=df.copy()

6. Примените однократное кодирование к параметрам «Удовлетворенность» (цель), «Класс», «Тип путешествия», «Тип клиента», «Пол».

# Encoding the selected columns
columns_to_encode = ['Gender', 'Customer Type', 'Type of Travel','Class']

# Perform one-hot encoding
df_encoded = pd.get_dummies(df, columns=columns_to_encode)

# Overwrite original dataframe 
df=df_encoded

До горячего кодирования

После горячего кодирования столбцов

7. Нормализуйте данные для недвоичных столбцов и столбцов без шкалы Лайкерта (шкала 1–5).

Это столбцы «Возраст, Дистанция боя, Задержка вылета, Задержка прибытия».

from sklearn.preprocessing import StandardScaler

# Normalizing 
# No need to normalize binary or Likert Scaled columns. 
columns_to_normalize = df.columns[1:5] 

# Initialize the StandardScaler
scaler = StandardScaler()

# Fit and transform the selected columns
df[columns_to_normalize] = scaler.fit_transform(df[columns_to_normalize])

Почему после использования Standard Scaler получаются отрицательные значения?
Отрицательные значения после нормализации с помощью StandardScaler ожидаются, потому что этот масштабатор выполняет стандартизацию, которая преобразует данные в среднее значение, равное нулю, и стандартное значение. отклонение одного. Результирующие значения могут быть как положительными, так и отрицательными, в зависимости от их исходного распределения.

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

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

8. Удаление столбца «ID» перед запуском набора данных по алгоритму K-средних. Это устранит ненужную информацию, уменьшит размерность и улучшит интерпретируемость.

df_without_id = df.drop('ID', axis=1)

# Exporting dataframe for the "saved" model later
df.to_csv('Cleaned DF\_Normalized_XGBoost_DF.csv', index=False)

9. Прогон через XGBoost для классификации.

# Dropping the two columns so it's not overfitting the algorithm
columns_to_drop = ['ID','Satisfaction']

# Features
X = df.drop(columns_to_drop, axis=1)  

# Target
y = df['Satisfaction']  
from sklearn.model_selection import train_test_split

# Create a train/test split with 20% as test size
# I selected random state 42 to keep testing consistent
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
import xgboost as xgb

#adding additional parameters to reduce the chance of overfitting. 
xgb_model = xgb.XGBClassifier(booster='gbtree',max_depth = 3,
learning_rate=0.1,min_child_weight = 6, subsample = 0.5,
                             n_estimators = 200)
xgb_model.fit(X_train, y_train)

10. Прогнозирование целевой переменной с использованием модели XGBoost на тестовых данных.

y_pred = xgb_model.predict(X_test)

11. Анализ точности, отзыва, точности и оценки F1 модели.

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score

# I use the time function/library to see how long this takes. 
import time
time_begin = time.time()


accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.2f}")
print(f"Precision: {precision:.2f}")
print(f"Recall: {recall:.2f}")
print(f"F1-Score: {f1:.2f}")
print(f'Run time: {round(((time.time()-time_begin)/60), 3)} mins')Accuracy: 0.95
Precision: 0.96
Recall: 0.93
F1-Score: 0.94
Run time: 0.001 mins

Результаты:

Accuracy: 0.95
Precision: 0.96
Recall: 0.93
F1-Score: 0.94

Run time: 0.001 mins

12. Создание матрицы путаницы.

from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_pred)

13. Построение матрицы путаницы.

plt.figure(figsize=(10, 8))
sns.heatmap(confusion_matrix, annot=True, fmt='d', cmap='Blues')


# Adding labels to the confusion matrix squares
class_names = ['Negative', 'Positive']
tick_marks = [0.5, 1.5]
plt.xticks(tick_marks, class_names)
plt.yticks(tick_marks, class_names)
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.title('Confusion Matrix')

# Adding context to the confusion matrix
plt.text(0, 2, 'True Negative:    Predicted Negative\nActual Negative', fontsize=8, ha='left', va='center', bbox=dict(facecolor='white', edgecolor='black', boxstyle='square,pad=0.2'))
plt.text(0, 1, 'False Positive:   Predicted Positive\nActual Negative', fontsize=8, ha='left', va='center', bbox=dict(facecolor='white', edgecolor='black', boxstyle='square,pad=0.2'))
plt.text(1, 2, 'False Negative:   Predicted Negative\nActual Positive', fontsize=8, ha='left', va='center', bbox=dict(facecolor='white', edgecolor='black', boxstyle='square,pad=0.2'))
plt.text(1, 1, 'True Positive:    Predicted Positive\nActual Positive', fontsize=8, ha='left', va='center', bbox=dict(facecolor='white', edgecolor='black', boxstyle='square,pad=0.2'))


plt.show()

14. GridSearchCV — оптимизация модели. Это может занять некоторое время.

# This does take a while, not always worth it when testing things quickly. Comment out this block when needed
from sklearn.model_selection import train_test_split, GridSearchCV

# Define the parameter grid for hyperparameter tuning
param_grid = {
    'learning_rate': [0.1, 0.01],
    'max_depth': [3, 5, 7],
    'n_estimators': [100, 150, 200]
}

# Perform a grid search or random search to find the best combination of hyperparameters
xgb_classifier = xgb.XGBClassifier()
grid_search = GridSearchCV(estimator=xgb_classifier, param_grid=param_grid, cv=5)
grid_search.fit(X_train, y_train)

best_params = grid_search.best_params_

# Train the XGBoost model with the best parameters
xgb_classifier = xgb.XGBClassifier(**best_params)
xgb_classifier.fit(X_train, y_train)

# Evaluate the model performance on the test set
y_pred = xgb_classifier.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)

print(f"Accuracy: {accuracy:.2f}")
print(f"Precision: {precision:.2f}")
print(f"Recall: {recall:.2f}")
print(f"F1-Score: {f1:.2f}")

Результаты:

Accuracy: 0.96
Precision: 0.97
Recall: 0.94
F1-Score: 0.96

15. Определение атрибутов важности функции.

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

import xgboost as xgb

# Train the XGBoost model with the best parameters
xgb_classifier = xgb.XGBClassifier(booster='gbtree',max_depth = 3,learning_rate=0.1,min_child_weight = 6, subsample = 0.5,
                             n_estimators = 200)
xgb_classifier.fit(X_train, y_train)

# Get the feature importance scores
feature_importance = xgb_classifier.feature_importances_

# Create a DataFrame with feature names and importances
importance_df = pd.DataFrame({'Feature': X_train.columns, 'Importance': feature_importance})

# Sort the DataFrame by importance in descending order
importance_df = importance_df.sort_values('Importance', ascending=False)

# Print the feature importance table
print(importance_df)
                                    Feature  Importance
7                          Online Boarding    0.300765
24                          Class_Business    0.139854
22                 Type of Travel_Business    0.100772
15                  In-flight Wifi Service    0.067950
16                 In-flight Entertainment    0.063865
11                        Leg Room Service    0.049058
6                         Check-in Service    0.046128
20                Customer Type_First-time    0.042331
9                         On-board Service    0.035012
10                            Seat Comfort    0.022724
17                        Baggage Handling    0.019988
12                             Cleanliness    0.019663
14                       In-flight Service    0.018393
3                            Arrival Delay    0.017437
8                            Gate Location    0.011977
4   Departure and Arrival Time Convenience    0.009147
0                                      Age    0.008880
25                           Class_Economy    0.007338
2                          Departure Delay    0.006160
1                          Flight Distance    0.005883
5                   Ease of Online Booking    0.003594
13                          Food and Drink    0.002835
26                      Class_Economy Plus    0.000247
21                 Customer Type_Returning    0.000000
23                 Type of Travel_Personal    0.000000
19                             Gender_Male    0.000000
18                           Gender_Female    0.000000

16. Добавление целевой переменной и прогноза обратно в исходный фрейм данных.

# Taking the dataframe of Y and overwriting the satisfaction column
df['Satisfaction'] = y
# Merging the null values inthe churn prediction dataframe. 
df['Churn_Prediction'] = np.nan
df = df.merge(pd.DataFrame({'Churn_Prediction': y_pred}), 
  how='left', left_index=True, right_index=True)
df['Prediction_Match'] = np.where(df['Churn_Prediction_y'].isnull(), np.nan,
                                  np.where(df['Churn_Prediction_y'] == df['Satisfaction'], 'Match', 'Mismatch'))
# Dropping the addional column it made due to duplication of the field. 
df=df.drop(['Churn_Prediction_x'], axis=1)

17. Сохранение модели.

import pickle

# Assuming `model` is your trained model object
# Specify the desired file path and name for the saved model
file_path = 'Saved_Models\XGBoost_model.pkl'

# Save the model using pickle
with open(file_path, 'wb') as file:
    pickle.dump(xgb_model, file)

18. Экспорт различных фреймов данных.

# Exporting Predictions to the non-noramlized dataframe
df2.to_csv('Cleaned DF\Predictions_OG_XGBoost_DF.csv', index=False)

# Exporting Predictions to the original DF
df.to_csv('Cleaned DF\Predictions_XGBoost_DF.csv', index=False)

Вывод: я успешно построил сохраненную модель, используя 20% подмножество тестовых данных, с целью прогнозирования уровня удовлетворенности пассажиров.

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

19. Импортируйте новый набор данных, содержащий очищенные и нормализованные данные из шага 7 выше.

# For cluster analysis
df = pd.read_csv("Cleaned DF/_Normalized_XGBoost_DF.csv") 

20. Создание нового фрейма данных из копии оригинала, но только со столбцами ID и Satisfaction.

selected_columns = ['ID', 'Satisfaction']
new_df = df[selected_columns].copy()

21. Импорт сохраненной модели.

import joblib

# Load the model from the file
model = joblib.load('Saved_Models\XGBoost_model.pkl')

22. Запуск модели с использованием полного набора данных.

# Drop any unnecessary columns if needed
columns_to_drop = ['ID','Satisfaction']
df = df.drop(columns=columns_to_drop)

# Make predictions on the dataset
predictions = model.predict(df)

# Add the predictions to the DataFrame
df['Predicted_Target'] = predictions

23. Добавление new_df ID и Satisfaction в исходный фрейм данных (df) с прогнозами.

df2 = pd.concat([new_df,df], axis=1)

24. Добавление сведений о прогнозе, чтобы увидеть, что «совпадает».

df2['Comparison']=np.where(df2['Satisfaction'] == df2['Predicted_Target'], 
'Match', 'Non-Matching')

25. Реорганизация столбцов для облегчения чтения/сравнения.

columns = list(df2.columns)
predicted_target_index = columns.index('Predicted_Target')
comparison_index = columns.index('Comparison')

columns.insert(2, columns.pop(predicted_target_index))
columns.insert(3, columns.pop(comparison_index))

df2 = df2[columns]

27. Просмотр новой матрицы путаницы по всему набору данных.

import matplotlib.pyplot as plt
import numpy as np

# Get the actual and predicted labels
y_actual = df2['Satisfaction']
y_predicted = df2['Predicted_Target']

# Create the confusion matrix
confusion_matrix = np.zeros((2, 2))

for actual, predicted in zip(y_actual, y_predicted):
    confusion_matrix[actual, predicted] += 1

# Set the labels for the matrix
labels = ['Negative', 'Positive']

# Plot the confusion matrix
fig, ax = plt.subplots()
im = ax.imshow(confusion_matrix, cmap='Blues')

# Add colorbar
#cbar = ax.figure.colorbar(im, ax=ax)

# Set tick labels
ax.set_xticks(np.arange(2))
ax.set_yticks(np.arange(2))
ax.set_xticklabels(labels)
ax.set_yticklabels(labels)

# Set axis labels and title
ax.set_xlabel('Predicted Target')
ax.set_ylabel('Actual Satisfaction')
ax.set_title('Confusion Matrix')

# Loop over data dimensions and create text annotations
for i in range(2):
    for j in range(2):
        text = ax.text(j, i, int(confusion_matrix[i, j]),
                       ha="center", va="center", color="black")


# Display the plot
plt.show()

Вы сделали это!

Поздравляем с завершением этого захватывающего путешествия по очистке, нормализации и запуску набора данных через XGBoost Classifier.

В этом руководстве вы прошли основные этапы предварительной обработки данных, обеспечив готовность нашего набора данных к анализу. Очистив и нормализовав данные, мы заложили прочную основу, гарантирующую точные и надежные результаты.

Спасибо!

Если вам понравилась эта статья, пожалуйста, подпишитесь на меня, и если вы заинтересованы в использовании алгоритма K-средних в том же наборе данных для поиска кластеров, нажмите здесь: «Удовлетворенность авиакомпанией: K-средние для науки о данных | Середина""."