Описание проекта
Оператор связи «ТелеДом» хочет бороться с оттоком клиентов. Для этого его сотрудники начнут предлагать промокоды и специальные условия всем, кто планирует отказаться от услуг связи. Чтобы заранее находить таких пользователей, «ТелеДому» нужна модель, которая будет предсказывать, разорвёт ли абонент договор. Команда оператора собрала персональные данные о некоторых клиентах, информацию об их тарифах и услугах. Ваша задача — обучить на этих данных модель для прогноза оттока клиентов.
Описание услуг
Оператор предоставляет два основных типа услуг:
- Стационарную телефонную связь. Телефон можно подключить к нескольким линиям одновременно.
- Интернет. Подключение может быть двух типов: через телефонную линию (
DSL
, от англ. digital subscriber line — «цифровая абонентская линия») или оптоволоконный кабель (Fiber optic
).
Также доступны такие услуги:
- Интернет-безопасность: антивирус (
DeviceProtection
) и блокировка небезопасных сайтов (OnlineSecurity
); - Выделенная линия технической поддержки (
TechSupport
); - Облачное хранилище файлов для резервного копирования данных (
OnlineBackup
); - Стриминговое телевидение (
StreamingTV
) и каталог фильмов (StreamingMovies
).
Клиенты могут платить за услуги каждый месяц или заключить договор на 1–2 года. Возможно оплатить счёт разными способами, а также получить электронный чек.
Описание данных
Данные состоят из нескольких файлов, полученных из разных источников:
contract_new.csv
— информация о договоре;personal_new.csv
— персональные данные клиента;internet_new.csv
— информация об интернет-услугах;phone_new.csv
— информация об услугах телефонии.
Файл contract_new.csv
:
customerID
— идентификатор абонента;BeginDate
— дата начала действия договора;EndDate
— дата окончания действия договора;Type
— тип оплаты: раз в год-два или ежемесячно;PaperlessBilling
— электронный расчётный лист;PaymentMethod
— тип платежа;MonthlyCharges
— расходы за месяц;TotalCharges
— общие расходы абонента.
Файл personal_new.csv
:
customerID
— идентификатор пользователя;gender
— пол;SeniorCitizen
— является ли абонент пенсионером;Partner
— есть ли у абонента супруг или супруга;Dependents
— есть ли у абонента дети.
Файл internet_new.csv
:
customerID
— идентификатор пользователя;InternetService
— тип подключения;OnlineSecurity
— блокировка опасных сайтов;OnlineBackup
— облачное хранилище файлов для резервного копирования данных;DeviceProtection
— антивирус;TechSupport
— выделенная линия технической поддержки;StreamingTV
— стриминговое телевидение;StreamingMovies
— каталог фильмов.
Файл phone_new.csv
:
customerID
— идентификатор пользователя;MultipleLines
— подключение телефона к нескольким линиям одновременно.
Во всех файлах столбец customerID содержит код клиента. Информация о договорах актуальна на 1 февраля 2020 года.
Данные также находятся в тренажёре, в папке /datasets/
.
Содержание
- 1 Подготовка тетради
- 2 Загрузка данных
- 3 Исследовательский анализ и предобработка данных
- 3.1 Полезные функции для предобработки данных
- 3.2 Анализ и предобработка
contract_new
(информация о договоре) - 3.3 Анализ и предобработка
personal_new
(персональные данные клиента) - 3.4 Анализ и предобработка
internet_new
(информация об интернет-услугах) - 3.5 Анализ и предобработка
phone_new
(информация об услугах телефонии)
- 4 Объединение данных
- 5 Исследовательский анализ данных объединённого датафрейма
- 6 Подготовка данных
- 7 Обучение моделей машинного обучения
- 8 Выбор лучшей модели
- 9 Общий вывод и рекомендации заказчику
Подготовка тетради¶
Обновление существующих и установка новых библиотек¶
# Обновление библиотек
# Ядро тетради требует обновление этого модуля
!pip install -U pyodbc
!pip install -U numpy
!pip install -U pandas
!pip install -U scikit-learn
Collecting pyodbc Downloading pyodbc-5.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (331 kB) |████████████████████████████████| 331 kB 944 kB/s eta 0:00:01 Installing collected packages: pyodbc Successfully installed pyodbc-5.0.1 Requirement already satisfied: numpy in /opt/conda/lib/python3.9/site-packages (1.21.1) Collecting numpy Downloading numpy-1.26.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.2 MB) |████████████████████████████████| 18.2 MB 1.0 MB/s eta 0:00:01 |████████████████████████████▏ | 16.0 MB 1.0 MB/s eta 0:00:03 Installing collected packages: numpy Attempting uninstall: numpy Found existing installation: numpy 1.21.1 Uninstalling numpy-1.21.1: Successfully uninstalled numpy-1.21.1 ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. scipy 1.9.1 requires numpy<1.25.0,>=1.18.5, but you have numpy 1.26.1 which is incompatible. numba 0.56.0 requires numpy<1.23,>=1.18, but you have numpy 1.26.1 which is incompatible. Successfully installed numpy-1.26.1 Requirement already satisfied: pandas in /opt/conda/lib/python3.9/site-packages (1.2.4) Collecting pandas Downloading pandas-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.3 MB) |████████████████████████████████| 12.3 MB 952 kB/s eta 0:00:01 Requirement already satisfied: pytz>=2020.1 in /opt/conda/lib/python3.9/site-packages (from pandas) (2021.1) Collecting tzdata>=2022.1 Downloading tzdata-2023.3-py2.py3-none-any.whl (341 kB) |████████████████████████████████| 341 kB 85.6 MB/s eta 0:00:01 Requirement already satisfied: numpy<2,>=1.22.4 in /opt/conda/lib/python3.9/site-packages (from pandas) (1.26.1) Collecting python-dateutil>=2.8.2 Downloading python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB) |████████████████████████████████| 247 kB 54.9 MB/s eta 0:00:01 Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.9/site-packages (from python-dateutil>=2.8.2->pandas) (1.16.0) Installing collected packages: tzdata, python-dateutil, pandas Attempting uninstall: python-dateutil Found existing installation: python-dateutil 2.8.1 Uninstalling python-dateutil-2.8.1: Successfully uninstalled python-dateutil-2.8.1 Attempting uninstall: pandas Found existing installation: pandas 1.2.4 Uninstalling pandas-1.2.4: Successfully uninstalled pandas-1.2.4 Successfully installed pandas-2.1.3 python-dateutil-2.8.2 tzdata-2023.3 Requirement already satisfied: scikit-learn in /opt/conda/lib/python3.9/site-packages (0.24.1) Collecting scikit-learn Downloading scikit_learn-1.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.9 MB) |████████████████████████████████| 10.9 MB 946 kB/s eta 0:00:01 Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.9/site-packages (from scikit-learn) (3.1.0) Collecting joblib>=1.1.1 Downloading joblib-1.3.2-py3-none-any.whl (302 kB) |████████████████████████████████| 302 kB 70.3 MB/s eta 0:00:01 Requirement already satisfied: scipy>=1.5.0 in /opt/conda/lib/python3.9/site-packages (from scikit-learn) (1.9.1) Requirement already satisfied: numpy<2.0,>=1.17.3 in /opt/conda/lib/python3.9/site-packages (from scikit-learn) (1.26.1) Collecting numpy<2.0,>=1.17.3 Downloading numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.3 MB) |████████████████████████████████| 17.3 MB 77.9 MB/s eta 0:00:01 Installing collected packages: numpy, joblib, scikit-learn Attempting uninstall: numpy Found existing installation: numpy 1.26.1 Uninstalling numpy-1.26.1: Successfully uninstalled numpy-1.26.1 Attempting uninstall: joblib Found existing installation: joblib 1.1.0 Uninstalling joblib-1.1.0: Successfully uninstalled joblib-1.1.0 Attempting uninstall: scikit-learn Found existing installation: scikit-learn 0.24.1 Uninstalling scikit-learn-0.24.1: Successfully uninstalled scikit-learn-0.24.1 ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts. numba 0.56.0 requires numpy<1.23,>=1.18, but you have numpy 1.24.4 which is incompatible. Successfully installed joblib-1.3.2 numpy-1.24.4 scikit-learn-1.3.2
# Загрузка новых библиотек
# Анализ мультиколлинеарности
!pip3 install phik
# Pipeline с поддержкой несбалансированных классов
!pip3 install imblearn
# CatBoost - CatBoosting от Яндекс
!pip3 install catboost
# LightGBM
!pip3 install lightgbm
# pytest - требуется для использования statsmodels.stats.tests.test_influence
!pip3 install pytest
Collecting phik Downloading phik-0.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (679 kB) |████████████████████████████████| 679 kB 938 kB/s eta 0:00:01 Requirement already satisfied: matplotlib>=2.2.3 in /opt/conda/lib/python3.9/site-packages (from phik) (3.3.4) Requirement already satisfied: scipy>=1.5.2 in /opt/conda/lib/python3.9/site-packages (from phik) (1.9.1) Requirement already satisfied: numpy>=1.18.0 in /opt/conda/lib/python3.9/site-packages (from phik) (1.24.4) Requirement already satisfied: joblib>=0.14.1 in /opt/conda/lib/python3.9/site-packages (from phik) (1.3.2) Requirement already satisfied: pandas>=0.25.1 in /opt/conda/lib/python3.9/site-packages (from phik) (2.1.3) Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=2.2.3->phik) (1.4.4) Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=2.2.3->phik) (0.11.0) Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=2.2.3->phik) (2.4.7) Requirement already satisfied: python-dateutil>=2.1 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=2.2.3->phik) (2.8.2) Requirement already satisfied: pillow>=6.2.0 in /opt/conda/lib/python3.9/site-packages (from matplotlib>=2.2.3->phik) (8.4.0) Requirement already satisfied: pytz>=2020.1 in /opt/conda/lib/python3.9/site-packages (from pandas>=0.25.1->phik) (2021.1) Requirement already satisfied: tzdata>=2022.1 in /opt/conda/lib/python3.9/site-packages (from pandas>=0.25.1->phik) (2023.3) Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.9/site-packages (from python-dateutil>=2.1->matplotlib>=2.2.3->phik) (1.16.0) Installing collected packages: phik Successfully installed phik-0.12.3 Collecting imblearn Downloading imblearn-0.0-py2.py3-none-any.whl (1.9 kB) Collecting imbalanced-learn Downloading imbalanced_learn-0.11.0-py3-none-any.whl (235 kB) |████████████████████████████████| 235 kB 922 kB/s eta 0:00:01 Requirement already satisfied: scikit-learn>=1.0.2 in /opt/conda/lib/python3.9/site-packages (from imbalanced-learn->imblearn) (1.3.2) Requirement already satisfied: numpy>=1.17.3 in /opt/conda/lib/python3.9/site-packages (from imbalanced-learn->imblearn) (1.24.4) Requirement already satisfied: scipy>=1.5.0 in /opt/conda/lib/python3.9/site-packages (from imbalanced-learn->imblearn) (1.9.1) Requirement already satisfied: joblib>=1.1.1 in /opt/conda/lib/python3.9/site-packages (from imbalanced-learn->imblearn) (1.3.2) Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.9/site-packages (from imbalanced-learn->imblearn) (3.1.0) Installing collected packages: imbalanced-learn, imblearn Successfully installed imbalanced-learn-0.11.0 imblearn-0.0 Requirement already satisfied: catboost in /opt/conda/lib/python3.9/site-packages (1.0.3) Requirement already satisfied: six in /opt/conda/lib/python3.9/site-packages (from catboost) (1.16.0) Requirement already satisfied: numpy>=1.16.0 in /opt/conda/lib/python3.9/site-packages (from catboost) (1.24.4) Requirement already satisfied: pandas>=0.24.0 in /opt/conda/lib/python3.9/site-packages (from catboost) (2.1.3) Requirement already satisfied: matplotlib in /opt/conda/lib/python3.9/site-packages (from catboost) (3.3.4) Requirement already satisfied: scipy in /opt/conda/lib/python3.9/site-packages (from catboost) (1.9.1) Requirement already satisfied: plotly in /opt/conda/lib/python3.9/site-packages (from catboost) (5.4.0) Requirement already satisfied: graphviz in /opt/conda/lib/python3.9/site-packages (from catboost) (0.20.1) Requirement already satisfied: tzdata>=2022.1 in /opt/conda/lib/python3.9/site-packages (from pandas>=0.24.0->catboost) (2023.3) Requirement already satisfied: pytz>=2020.1 in /opt/conda/lib/python3.9/site-packages (from pandas>=0.24.0->catboost) (2021.1) Requirement already satisfied: python-dateutil>=2.8.2 in /opt/conda/lib/python3.9/site-packages (from pandas>=0.24.0->catboost) (2.8.2) Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.9/site-packages (from matplotlib->catboost) (1.4.4) Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in /opt/conda/lib/python3.9/site-packages (from matplotlib->catboost) (2.4.7) Requirement already satisfied: pillow>=6.2.0 in /opt/conda/lib/python3.9/site-packages (from matplotlib->catboost) (8.4.0) Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.9/site-packages (from matplotlib->catboost) (0.11.0) Requirement already satisfied: tenacity>=6.2.0 in /opt/conda/lib/python3.9/site-packages (from plotly->catboost) (8.0.1) Requirement already satisfied: lightgbm in /opt/conda/lib/python3.9/site-packages (3.3.1) Requirement already satisfied: wheel in /opt/conda/lib/python3.9/site-packages (from lightgbm) (0.36.2) Requirement already satisfied: scikit-learn!=0.22.0 in /opt/conda/lib/python3.9/site-packages (from lightgbm) (1.3.2) Requirement already satisfied: scipy in /opt/conda/lib/python3.9/site-packages (from lightgbm) (1.9.1) Requirement already satisfied: numpy in /opt/conda/lib/python3.9/site-packages (from lightgbm) (1.24.4) Requirement already satisfied: threadpoolctl>=2.0.0 in /opt/conda/lib/python3.9/site-packages (from scikit-learn!=0.22.0->lightgbm) (3.1.0) Requirement already satisfied: joblib>=1.1.1 in /opt/conda/lib/python3.9/site-packages (from scikit-learn!=0.22.0->lightgbm) (1.3.2) Collecting pytest Downloading pytest-7.4.3-py3-none-any.whl (325 kB) |████████████████████████████████| 325 kB 1.0 MB/s eta 0:00:01 Collecting iniconfig Downloading iniconfig-2.0.0-py3-none-any.whl (5.9 kB) Collecting tomli>=1.0.0 Downloading tomli-2.0.1-py3-none-any.whl (12 kB) Requirement already satisfied: packaging in /opt/conda/lib/python3.9/site-packages (from pytest) (21.3) Collecting exceptiongroup>=1.0.0rc8 Downloading exceptiongroup-1.1.3-py3-none-any.whl (14 kB) Collecting pluggy<2.0,>=0.12 Downloading pluggy-1.3.0-py3-none-any.whl (18 kB) Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /opt/conda/lib/python3.9/site-packages (from packaging->pytest) (2.4.7) Installing collected packages: tomli, pluggy, iniconfig, exceptiongroup, pytest Successfully installed exceptiongroup-1.1.3 iniconfig-2.0.0 pluggy-1.3.0 pytest-7.4.3 tomli-2.0.1
Загрузка библиотек в тетрадь¶
# Базовые библиотеки
import numpy as np
import pandas as pd
# Вспомогательные библиотеки
import os # Загрузка датафрейма
import time # Оценка времени выполнения кода
import re # Изменение названий столбцов
# Работа с датафреймом
from sklearn.model_selection import(
#cross_val_score,
train_test_split # Автоматическое разделение датафреймов на трейн и тест
)
# Предобработка данных
from sklearn.preprocessing import(
MaxAbsScaler, # стандартизация по максимальному абсолютном значению в пределах 0-1
PowerTransformer, # Приведение к нормальному распределению
OneHotEncoder, # OHE кодирование для "линейных" моделей с созданием отдельных столбцов для каждого категориального значения, drop='first' (удаление первого столбца против dummy-ловушки), sparse=False (?)
OrdinalEncoder # порядковое кодирование для "деревянных" моделей
)
# Автоматизация раздельного декодирования признаков
from sklearn.compose import(
make_column_transformer,
ColumnTransformer
)
# Pipeline (пайплайн)
from imblearn.pipeline import Pipeline # Более предпочтительнее для TfidfVectorizer
from imblearn.over_sampling import SMOTE # Балансировка классов в imblearn Pipeline
# Функция для поддержки экспериментальной функции HavingGridSearchSV
from sklearn.experimental import enable_halving_search_cv
# Ускоренная автоматизация поиска лучших моделей и их параметров
from sklearn.model_selection import HalvingGridSearchCV
# Модели
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier # Случайный лес. Классификация
from sklearn.naive_bayes import ComplementNB # Улучшенный классификатор для не сбалансированных классов
from catboost import CatBoostClassifier # CatBoost (Яндекс). Классификация# LightGBM
from lightgbm import LGBMClassifier # LightGBM. Классификация
# Метрики
from sklearn.metrics import(
roc_curve, # Кривая "ROC"
auc, # Параметр "AUC"
roc_auc_score, # Площадь под кривой рабочих характеристик приемника "ROC AUC"
accuracy_score, # Accuracy
confusion_matrix, # Матрица "путаницы" для матрицы ошибок
ConfusionMatrixDisplay # Матрица ошибок
)
# Анализ мультиколлениарности
import phik
# Визуализация графиков
import seaborn as sns
import matplotlib
%matplotlib inline
from matplotlib import pyplot as plt
from matplotlib import rcParams, rcParamsDefault
from pandas.plotting import scatter_matrix
Оптимизация отображения контента в тетради¶
# Отображение всех столбцов таблицы
pd.set_option('display.max_columns', None)
# Обязательно для нормального отображения графиков plt
rcParams['figure.figsize'] = 10, 6
%config InlineBackend.figure_format = 'svg'
# Дополнительно и не обязательно для декорирования графиков
factor = .8
default_dpi = rcParamsDefault['figure.dpi']
rcParams['figure.dpi'] = default_dpi * factor
Глобальные переменные¶
# Глобальная константа
# для фиксации случайных значений
STATE = 30112023
Загрузка данных¶
Загрузка данных и их первичный осмотр. В зависимости от места запуска этой тетради, могут быть использованы источники данных:
datasets/…_new.csv
;/datasets/…_new.csv
;https://code.s3.yandex.net/datasets/…_new.csv
,
где ...
первая часть названия файла данных.
# Функция загрузки данных из файлов в датафреймы
def read_files(file_name):
data = pd.DataFrame()
path_1 = 'datasets/' + file_name + '.csv'
path_2 = '/datasets/' + file_name + '.csv'
path_3 = 'https://code.s3.yandex.net/datasets/' + file_name + '.csv'
if os.path.exists(path_1):
data = pd.read_csv(path_1)
elif os.path.exists(path_2):
data = pd.read_csv(path_2)
elif os.path.exists(path_3):
data = pd.read_csv(path_3)
return data
# Загрузка файла "contract_new.csv" — информация о договоре
contract_new = read_files('contract_new')
#contract_new = pd.read_csv('https://code.s3.yandex.net/datasets/contract_new.csv')
# Анализ данных из файла
contract_new.head()
customerID | BeginDate | EndDate | Type | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | |
---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | 2020-01-01 | No | Month-to-month | Yes | Electronic check | 29.85 | 31.04 |
1 | 5575-GNVDE | 2017-04-01 | No | One year | No | Mailed check | 56.95 | 2071.84 |
2 | 3668-QPYBK | 2019-10-01 | No | Month-to-month | Yes | Mailed check | 53.85 | 226.17 |
3 | 7795-CFOCW | 2016-05-01 | No | One year | No | Bank transfer (automatic) | 42.30 | 1960.6 |
4 | 9237-HQITU | 2019-09-01 | No | Month-to-month | Yes | Electronic check | 70.70 | 353.5 |
# Загрузка файла "personal_new.csv" — персональные данные клиента
personal_new = read_files('personal_new')
# Анализ данных из файла
personal_new.head()
customerID | gender | SeniorCitizen | Partner | Dependents | |
---|---|---|---|---|---|
0 | 7590-VHVEG | Female | 0 | Yes | No |
1 | 5575-GNVDE | Male | 0 | No | No |
2 | 3668-QPYBK | Male | 0 | No | No |
3 | 7795-CFOCW | Male | 0 | No | No |
4 | 9237-HQITU | Female | 0 | No | No |
# Загрузка файла "internet_new.csv" — информация об интернет-услугах
internet_new = read_files('internet_new')
# Анализ данных из файла
internet_new.head()
customerID | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | |
---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | DSL | No | Yes | No | No | No | No |
1 | 5575-GNVDE | DSL | Yes | No | Yes | No | No | No |
2 | 3668-QPYBK | DSL | Yes | Yes | No | No | No | No |
3 | 7795-CFOCW | DSL | Yes | No | Yes | Yes | No | No |
4 | 9237-HQITU | Fiber optic | No | No | No | No | No | No |
# Загрузка файла "phone_new.csv" — информация об услугах телефонии
phone_new = read_files('phone_new')
# Анализ данных из файла
phone_new.head()
customerID | MultipleLines | |
---|---|---|
0 | 5575-GNVDE | No |
1 | 3668-QPYBK | No |
2 | 9237-HQITU | No |
3 | 9305-CDSKC | Yes |
4 | 1452-KIOVK | Yes |
Исследовательский анализ и предобработка данных¶
Исследовательский анализ каждого датафрейма и при необходимости выполнение предобработки. Формирование выводов об имеющихся признаках: понадобятся ли они для обучения моделей.
Анализ и предобработка contract_new
(информация о договоре)¶
# Анализ датафрейма "contract_new"
contract_new.info()
contract_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 7043 entries, 0 to 7042 Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customerID 7043 non-null object 1 BeginDate 7043 non-null object 2 EndDate 7043 non-null object 3 Type 7043 non-null object 4 PaperlessBilling 7043 non-null object 5 PaymentMethod 7043 non-null object 6 MonthlyCharges 7043 non-null float64 7 TotalCharges 7043 non-null object dtypes: float64(1), object(7) memory usage: 440.3+ KB
customerID | BeginDate | EndDate | Type | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | |
---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | 2020-01-01 | No | Month-to-month | Yes | Electronic check | 29.85 | 31.04 |
1 | 5575-GNVDE | 2017-04-01 | No | One year | No | Mailed check | 56.95 | 2071.84 |
2 | 3668-QPYBK | 2019-10-01 | No | Month-to-month | Yes | Mailed check | 53.85 | 226.17 |
3 | 7795-CFOCW | 2016-05-01 | No | One year | No | Bank transfer (automatic) | 42.30 | 1960.6 |
4 | 9237-HQITU | 2019-09-01 | No | Month-to-month | Yes | Electronic check | 70.70 | 353.5 |
# Анализ уникальных значений
for i in contract_new.columns:
print(f'Признак "{i}" имеет {len(contract_new[i].unique())} уникальных значений:')
print(contract_new[i].unique(), '\n')
Признак "customerID" имеет 7043 уникальных значений: ['7590-VHVEG' '5575-GNVDE' '3668-QPYBK' ... '4801-JZAZL' '8361-LTMKD' '3186-AJIEK'] Признак "BeginDate" имеет 77 уникальных значений: ['2020-01-01' '2017-04-01' '2019-10-01' '2016-05-01' '2019-09-01' '2019-03-01' '2018-04-01' '2019-04-01' '2017-07-01' '2014-12-01' '2019-01-01' '2018-10-01' '2015-04-01' '2015-09-01' '2018-01-01' '2014-05-01' '2015-10-01' '2014-03-01' '2018-05-01' '2019-11-01' '2019-02-01' '2016-01-01' '2017-08-01' '2015-12-01' '2014-02-01' '2018-06-01' '2019-12-01' '2017-11-01' '2019-06-01' '2016-04-01' '2017-02-01' '2018-12-01' '2014-04-01' '2018-09-01' '2014-11-01' '2016-07-01' '2015-02-01' '2018-07-01' '2014-08-01' '2016-03-01' '2018-08-01' '2014-10-01' '2015-06-01' '2016-08-01' '2019-05-01' '2017-03-01' '2016-02-01' '2017-09-01' '2014-09-01' '2017-12-01' '2016-12-01' '2017-06-01' '2015-05-01' '2016-10-01' '2016-09-01' '2019-08-01' '2019-07-01' '2017-05-01' '2017-10-01' '2014-07-01' '2018-03-01' '2015-01-01' '2018-11-01' '2015-03-01' '2018-02-01' '2016-06-01' '2015-08-01' '2015-11-01' '2014-06-01' '2017-01-01' '2015-07-01' '2020-02-01' '2016-11-01' '2013-11-01' '2014-01-01' '2013-10-01' '2013-12-01'] Признак "EndDate" имеет 67 уникальных значений: ['No' '2017-05-01' '2016-03-01' '2018-09-01' '2018-11-01' '2018-12-01' '2019-08-01' '2018-07-01' '2017-09-01' '2015-09-01' '2016-07-01' '2016-06-01' '2018-03-01' '2019-02-01' '2018-06-01' '2019-06-01' '2020-01-01' '2019-11-01' '2016-09-01' '2015-06-01' '2016-12-01' '2019-05-01' '2019-04-01' '2017-06-01' '2017-08-01' '2018-04-01' '2018-08-01' '2018-02-01' '2019-07-01' '2015-12-01' '2014-06-01' '2018-10-01' '2019-01-01' '2017-07-01' '2017-12-01' '2018-05-01' '2015-11-01' '2019-10-01' '2019-03-01' '2016-02-01' '2016-10-01' '2018-01-01' '2017-11-01' '2015-10-01' '2019-12-01' '2015-07-01' '2017-04-01' '2015-02-01' '2017-03-01' '2016-05-01' '2016-11-01' '2015-08-01' '2019-09-01' '2017-10-01' '2017-02-01' '2016-08-01' '2016-04-01' '2015-05-01' '2014-09-01' '2014-10-01' '2017-01-01' '2015-03-01' '2015-01-01' '2016-01-01' '2015-04-01' '2014-12-01' '2014-11-01'] Признак "Type" имеет 3 уникальных значений: ['Month-to-month' 'One year' 'Two year'] Признак "PaperlessBilling" имеет 2 уникальных значений: ['Yes' 'No'] Признак "PaymentMethod" имеет 4 уникальных значений: ['Electronic check' 'Mailed check' 'Bank transfer (automatic)' 'Credit card (automatic)'] Признак "MonthlyCharges" имеет 1585 уникальных значений: [29.85 56.95 53.85 ... 63.1 44.2 78.7 ] Признак "TotalCharges" имеет 6658 уникальных значений: ['31.04' '2071.84' '226.17' ... '325.6' '520.8' '7251.82']
# Проверка на наличие не числовых значений в "BeginDate"
contract_new.loc[
contract_new['BeginDate'].str.contains(' |,|[a-z]|[A-Z]|[а-я]|[А-Я]'),
'BeginDate'
].unique()
array([], dtype=object)
# Проверка на наличие не числовых значений в "EndDate"
contract_new.loc[
contract_new['EndDate'].str.contains(' |,|[a-z]|[A-Z]|[а-я]|[А-Я]'),
'EndDate'
].unique()
array(['No'], dtype=object)
# Проверка на наличие не числовых значений в "TotalCharges"
contract_new.loc[
contract_new['TotalCharges'].str.contains(' |,|[a-z]|[A-Z]|[а-я]|[А-Я]'),
'TotalCharges'
].unique()
array([' '], dtype=object)
# Проверка "BeginDate", "EndDate" и "TotalCharges" на совпадение не числовых значений
contract_new.loc[contract_new['EndDate'] == 'No', ['BeginDate', 'EndDate', 'TotalCharges']]
BeginDate | EndDate | TotalCharges | |
---|---|---|---|
0 | 2020-01-01 | No | 31.04 |
1 | 2017-04-01 | No | 2071.84 |
2 | 2019-10-01 | No | 226.17 |
3 | 2016-05-01 | No | 1960.6 |
4 | 2019-09-01 | No | 353.5 |
… | … | … | … |
7038 | 2018-02-01 | No | 2035.2 |
7039 | 2014-02-01 | No | 7430.4 |
7040 | 2019-03-01 | No | 325.6 |
7041 | 2019-07-01 | No | 520.8 |
7042 | 2014-08-01 | No | 7251.82 |
5942 rows × 3 columns
# Проверка "BeginDate", "EndDate" и "TotalCharges" на совпадение не числовых значений
contract_new.loc[contract_new['TotalCharges'] == ' ', ['BeginDate', 'EndDate', 'TotalCharges']]
BeginDate | EndDate | TotalCharges | |
---|---|---|---|
488 | 2020-02-01 | No | |
753 | 2020-02-01 | No | |
936 | 2020-02-01 | No | |
1082 | 2020-02-01 | No | |
1340 | 2020-02-01 | No | |
3331 | 2020-02-01 | No | |
3826 | 2020-02-01 | No | |
4380 | 2020-02-01 | No | |
5218 | 2020-02-01 | No | |
6670 | 2020-02-01 | No | |
6754 | 2020-02-01 | No |
Предварительные выводы
- Датафрейм
contract_new
содержит 8 признаков и 7043 объекта. - Датафрейм не содержит пропусков.
- Признак
customerID
содерижт идентификаторы клиентов. - Признаки
BeginDate
иEndDate
содержат информацию о дате и месяце начала и окончания действия договора соответственно. ЕслиEndDate
содержитNo
, значит договор был активен 01.02.2020. Оба признака требуется удалить из датафрейма, т.к. они «привязаны» к определенным временным промежуткам и приведут к ошибочным предсказаниям при использовании более поздних дат. Перед их удалением на их основе требуется создать признакиactive_contract
иduration_contract
. Для корректного расчета продолжительности контракта, после создания поризнакаactive_contract
и до созданияduration_contract
следует значенияNo
признакаEndDate
заменить на числовое значение2020-02-01
(актуальная дата информации, согласно данным от руководителя проекта).- Признак
active_contract
будет содержать0
, еслиEndDate
содержитNo
, и1
, еслиEndDate
содержит дату. Данный признак будет основой целевого признака в объедененном датафрейме при обучении моделей. - Признак
duration_contract
будет содержать информацию о продолжительности действия договора.
- Признак
- Признаки
Type
,PaperlessBilling
иPaymentMethod
категориальные. Их требуется трансформировать методомOneHotEncoder
для «линейных» моделей иOrdinalEncoder
для «деревянных» моделей вPipeline
перед обучением модели. - Признаки
MonthlyCharges
иTotalCharges
содержат числовые значения. При этом, признакTotalCharges
имеет типobject
и требует преобразования в типfloat64
. Пробелы в признакеTotalCharges
требуется заменить на числовое значение, например, на0
, т.к. предполагаем, что отсутствие значения в данном признаке говорит об отсутствии оплат. - Названия признаков желательно привести к «змеиному стилю».
# Создание признака "active_contract"
contract_new['active_contract'] = 1
contract_new.loc[contract_new['EndDate'] == 'No', 'active_contract'] = 0
# Оптимизация типа данных признака "BeginDate"
contract_new['BeginDate'] = pd.to_datetime(
contract_new['BeginDate'],
format='%Y-%m-%d'
)
# Оптимизация типа данных признака "EndDate"
contract_new.loc[contract_new['EndDate'] == 'No', 'EndDate'] = '2020-02-01'
contract_new['EndDate'] = pd.to_datetime(
contract_new['EndDate'],
format='%Y-%m-%d'
)
# Создание признака "duration_contract"
contract_new['duration_contract'] = (contract_new['EndDate'] - contract_new['BeginDate']).dt.components.days
# Удаление признкаов "BeginDate" и "EndDate"
contract_new = contract_new.drop(['BeginDate', 'EndDate'], axis=1)
# Обработка признака "total_charges"
contract_new.loc[contract_new['TotalCharges'] == ' ', 'TotalCharges'] = 0
contract_new['TotalCharges'] = contract_new['TotalCharges'].astype('float64')
# Приведение названий признаков датафрейма к "змеиному стилю"
contract_new.columns = [re.sub(r'(?<!^)(?=[A-Z])', '_', i).lower() for i in contract_new.columns]
contract_new = contract_new.rename(columns={'customer_i_d':'customer_id'})
# Проверка полученных названий
contract_new.columns
Index(['customer_id', 'type', 'paperless_billing', 'payment_method', 'monthly_charges', 'total_charges', 'active_contract', 'duration_contract'], dtype='object')
# Проверка датафрейма после после предобработки данных
print(contract_new.info())
contract_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 7043 entries, 0 to 7042 Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customer_id 7043 non-null object 1 type 7043 non-null object 2 paperless_billing 7043 non-null object 3 payment_method 7043 non-null object 4 monthly_charges 7043 non-null float64 5 total_charges 7043 non-null float64 6 active_contract 7043 non-null int64 7 duration_contract 7043 non-null int64 dtypes: float64(2), int64(2), object(4) memory usage: 440.3+ KB None
customer_id | type | paperless_billing | payment_method | monthly_charges | total_charges | active_contract | duration_contract | |
---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | Month-to-month | Yes | Electronic check | 29.85 | 31.04 | 0 | 31 |
1 | 5575-GNVDE | One year | No | Mailed check | 56.95 | 2071.84 | 0 | 1036 |
2 | 3668-QPYBK | Month-to-month | Yes | Mailed check | 53.85 | 226.17 | 0 | 123 |
3 | 7795-CFOCW | One year | No | Bank transfer (automatic) | 42.30 | 1960.60 | 0 | 1371 |
4 | 9237-HQITU | Month-to-month | Yes | Electronic check | 70.70 | 353.50 | 0 | 153 |
Анализ и предобработка personal_new
(персональные данные клиента)¶
# Анализ датафрейма "personal_new"
personal_new.info()
personal_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 7043 entries, 0 to 7042 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customerID 7043 non-null object 1 gender 7043 non-null object 2 SeniorCitizen 7043 non-null int64 3 Partner 7043 non-null object 4 Dependents 7043 non-null object dtypes: int64(1), object(4) memory usage: 275.2+ KB
customerID | gender | SeniorCitizen | Partner | Dependents | |
---|---|---|---|---|---|
0 | 7590-VHVEG | Female | 0 | Yes | No |
1 | 5575-GNVDE | Male | 0 | No | No |
2 | 3668-QPYBK | Male | 0 | No | No |
3 | 7795-CFOCW | Male | 0 | No | No |
4 | 9237-HQITU | Female | 0 | No | No |
# Анализ уникальных значений
for i in personal_new.columns:
print(f'Признак "{i}" имеет {len(personal_new[i].unique())} уникальных значений:')
print(personal_new[i].unique(), '\n')
Признак "customerID" имеет 7043 уникальных значений: ['7590-VHVEG' '5575-GNVDE' '3668-QPYBK' ... '4801-JZAZL' '8361-LTMKD' '3186-AJIEK'] Признак "gender" имеет 2 уникальных значений: ['Female' 'Male'] Признак "SeniorCitizen" имеет 2 уникальных значений: [0 1] Признак "Partner" имеет 2 уникальных значений: ['Yes' 'No'] Признак "Dependents" имеет 2 уникальных значений: ['No' 'Yes']
Предварительные выводы
- Датафрейм
personal_new
содержит 5 признаков и 7043 объекта. - Датафрейм не содержит пропусков.
- Признак
customerID
содерижт идентификаторы клиентов. - Признаки
gender
,SeniorCitizen
,Partner
иDependents
бинарные категориальные. Их требуется трансформировать методомOneHotEncoder
(для «линейных» моделей) иOrdinalEncoder
(для «деревянных» моделей) вPipeline
перед обучением модели. вPipeline
перед обучением модели. - Признак
SeniorCitizen
отличается от других категориальных бинарных признаков наличием числовых значений. Для его унификации с другими категориальными значениями требуется его значения0
и1
привести кNo
иYes
соответственно, а тип данныхSeniorCitizen
привести к типуobject
. - Названия признаков желательно привести к «змеиному стилю».
# Трансформация категориальных признаков, кроме "SeniorCitizen"
personal_new.loc[personal_new['SeniorCitizen'] == 0, 'SeniorCitizen'] = 'No'
personal_new.loc[personal_new['SeniorCitizen'] == 1, 'SeniorCitizen'] = 'Yes'
/tmp/ipykernel_63/2905468989.py:2: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise in a future error of pandas. Value 'No' has dtype incompatible with int64, please explicitly cast to a compatible dtype first. personal_new.loc[personal_new['SeniorCitizen'] == 0, 'SeniorCitizen'] = 'No'
# Приведение названий признаков датафрейма к "змеиному стилю"
personal_new.columns = [re.sub(r'(?<!^)(?=[A-Z])', '_', i).lower() for i in personal_new.columns]
personal_new = personal_new.rename(columns={'customer_i_d':'customer_id'})
# Проверка полученных названий
personal_new.columns
Index(['customer_id', 'gender', 'senior_citizen', 'partner', 'dependents'], dtype='object')
# Анализ датафрейма после предобработки данных
print(personal_new.info())
personal_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 7043 entries, 0 to 7042 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customer_id 7043 non-null object 1 gender 7043 non-null object 2 senior_citizen 7043 non-null object 3 partner 7043 non-null object 4 dependents 7043 non-null object dtypes: object(5) memory usage: 275.2+ KB None
customer_id | gender | senior_citizen | partner | dependents | |
---|---|---|---|---|---|
0 | 7590-VHVEG | Female | No | Yes | No |
1 | 5575-GNVDE | Male | No | No | No |
2 | 3668-QPYBK | Male | No | No | No |
3 | 7795-CFOCW | Male | No | No | No |
4 | 9237-HQITU | Female | No | No | No |
Анализ и предобработка internet_new
(информация об интернет-услугах)¶
# Анализ датафрейма "internet_new"
print(internet_new.info())
internet_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 5517 entries, 0 to 5516 Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customerID 5517 non-null object 1 InternetService 5517 non-null object 2 OnlineSecurity 5517 non-null object 3 OnlineBackup 5517 non-null object 4 DeviceProtection 5517 non-null object 5 TechSupport 5517 non-null object 6 StreamingTV 5517 non-null object 7 StreamingMovies 5517 non-null object dtypes: object(8) memory usage: 344.9+ KB None
customerID | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | |
---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | DSL | No | Yes | No | No | No | No |
1 | 5575-GNVDE | DSL | Yes | No | Yes | No | No | No |
2 | 3668-QPYBK | DSL | Yes | Yes | No | No | No | No |
3 | 7795-CFOCW | DSL | Yes | No | Yes | Yes | No | No |
4 | 9237-HQITU | Fiber optic | No | No | No | No | No | No |
# Анализ уникальных значений
for i in internet_new.columns:
print(f'Признак "{i}" имеет {len(internet_new[i].unique())} уникальных значений:')
print(internet_new[i].unique(), '\n')
Признак "customerID" имеет 5517 уникальных значений: ['7590-VHVEG' '5575-GNVDE' '3668-QPYBK' ... '4801-JZAZL' '8361-LTMKD' '3186-AJIEK'] Признак "InternetService" имеет 2 уникальных значений: ['DSL' 'Fiber optic'] Признак "OnlineSecurity" имеет 2 уникальных значений: ['No' 'Yes'] Признак "OnlineBackup" имеет 2 уникальных значений: ['Yes' 'No'] Признак "DeviceProtection" имеет 2 уникальных значений: ['No' 'Yes'] Признак "TechSupport" имеет 2 уникальных значений: ['No' 'Yes'] Признак "StreamingTV" имеет 2 уникальных значений: ['No' 'Yes'] Признак "StreamingMovies" имеет 2 уникальных значений: ['No' 'Yes']
Предварительные выводы
- Датафрейм
internet_new
содержит 8 признаков и 5517 объектов. - Датафрейм не содержит пропусков.
- Признак
customerID
содерижт идентификаторы клиентов. - Признаки
InternetService
,OnlineSecurity
,OnlineBackup
,DeviceProtection
,TechSupport
,StreamingTV
иStreamingMovies
бинарные категориальные. Их требуется кодировать с методомOneHotEncoder
(для «линейных» моделей) иOrdinalEncoder
(для «деревянных» моделей) вPipeline
перед обучением модели. - Названия признаков желательно привести к «змеиному стилю».
# Приведение названий признаков датафрейма к "змеиному стилю"
internet_new.columns = [re.sub(r'(?<!^)(?=[A-Z])', '_', i).lower() for i in internet_new.columns]
internet_new = internet_new.rename(columns={
'customer_i_d':'customer_id',
'streaming_t_v':'streaming_tv'
})
# Проверка полученных названий
internet_new.columns
Index(['customer_id', 'internet_service', 'online_security', 'online_backup', 'device_protection', 'tech_support', 'streaming_tv', 'streaming_movies'], dtype='object')
# Анализ датафрейма после предобработки данных
print(internet_new.info())
internet_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 5517 entries, 0 to 5516 Data columns (total 8 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customer_id 5517 non-null object 1 internet_service 5517 non-null object 2 online_security 5517 non-null object 3 online_backup 5517 non-null object 4 device_protection 5517 non-null object 5 tech_support 5517 non-null object 6 streaming_tv 5517 non-null object 7 streaming_movies 5517 non-null object dtypes: object(8) memory usage: 344.9+ KB None
customer_id | internet_service | online_security | online_backup | device_protection | tech_support | streaming_tv | streaming_movies | |
---|---|---|---|---|---|---|---|---|
0 | 7590-VHVEG | DSL | No | Yes | No | No | No | No |
1 | 5575-GNVDE | DSL | Yes | No | Yes | No | No | No |
2 | 3668-QPYBK | DSL | Yes | Yes | No | No | No | No |
3 | 7795-CFOCW | DSL | Yes | No | Yes | Yes | No | No |
4 | 9237-HQITU | Fiber optic | No | No | No | No | No | No |
Анализ и предобработка phone_new
(информация об услугах телефонии)¶
# Анализ датафрейма "phone_new"
print(phone_new.info())
phone_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 6361 entries, 0 to 6360 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customerID 6361 non-null object 1 MultipleLines 6361 non-null object dtypes: object(2) memory usage: 99.5+ KB None
customerID | MultipleLines | |
---|---|---|
0 | 5575-GNVDE | No |
1 | 3668-QPYBK | No |
2 | 9237-HQITU | No |
3 | 9305-CDSKC | Yes |
4 | 1452-KIOVK | Yes |
Предварительные выводы
- Датафрейм
phone_new
содержит 2 признака и 6361 объект. - Датафрейм не содержит пропусков.
- Признак
customerID
содерижт идентификаторы клиентов. - Признак
MultipleLines
бинарный категориальный. Его требуется трансформировать методомOneHotEncoder
(для «линейных» моделей) иOrdinalEncoder
(для «деревянных» моделей) вPipeline
перед трансформировать моделей. - Названия признаков желательно привести к «змеиному стилю».
# Приведение названий признаков датафрейма к "змеиному стилю"
phone_new.columns = [re.sub(r'(?<!^)(?=[A-Z])', '_', i).lower() for i in phone_new.columns]
phone_new = phone_new.rename(columns={'customer_i_d':'customer_id'})
# Проверка полученных названий
phone_new.columns
Index(['customer_id', 'multiple_lines'], dtype='object')
# Проверка датафрейма после изменения
print(phone_new.info())
phone_new.head()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 6361 entries, 0 to 6360 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customer_id 6361 non-null object 1 multiple_lines 6361 non-null object dtypes: object(2) memory usage: 99.5+ KB None
customer_id | multiple_lines | |
---|---|---|
0 | 5575-GNVDE | No |
1 | 3668-QPYBK | No |
2 | 9237-HQITU | No |
3 | 9305-CDSKC | Yes |
4 | 1452-KIOVK | Yes |
Объединение данных¶
Объединение выбранных признаков в один датафрейм по ключу. Ключом является признак customer_id
(идентификатор клиента) во всех датафреймах.
print('Размерность датафрейма "contract_new":', contract_new.shape)
print('Размерность датафрейма "personal_new":', personal_new.shape)
print('Размерность датафрейма "internet_new":', internet_new.shape)
print('Размерность датафрейма "phone_new" :', phone_new.shape)
Размерность датафрейма "contract_new": (7043, 8) Размерность датафрейма "personal_new": (7043, 5) Размерность датафрейма "internet_new": (5517, 8) Размерность датафрейма "phone_new" : (6361, 2)
# Пробное объединение исходных датафреймов
# для проверки уникальности идентификаторов
# (если количество объектов после объединения
# будет одинаковым с исходным, значит идентификаторы совпадают)
personal_new.join(contract_new.set_index('customer_id'), on='customer_id', how='outer').shape
(7043, 12)
Датафреймы contract_new
и personal_new
содержат ключевую информацию проекта о договорах и клиентах соответственно. Учитывая тот факт, что оба эти датафрейма до и после объединения имеют одинаковое количество объектов, значит идентификаторы клиентов в них полностью совпадают. Эти датафреймы можно объединять в общий датафрейм total_new
через join
. Остальные датафреймы имеют меньшее количество объектов. Следовательно, их следует объединять с общим датафреймом через левый join
.
Также следует предположить, что отсутствие значений в меньших датафреймах с информацией о приобретенных услугах — это либо не приобретенные услуги, либо отсутствие информации о приобретении. Значит, NaN
можно интерпретировать как No
в рамках принятой здесь типология, но лучше NoValue
, то есть отсутствие значения.
# Последовательное объединение датафреймов в "total_new"
total_new = personal_new.join(contract_new.set_index('customer_id'), on='customer_id')
total_new = total_new.join(internet_new.set_index('customer_id'), on='customer_id', how='left')
total_new = total_new.join(phone_new.set_index('customer_id'), on='customer_id', how='left')
# "NaN" - это не приобретенные значения
total_new = total_new.fillna('NoValue')
Предварительное выделение тренировочной выборки¶
Для наиболее эффективного обучения модели дальнейший анализ и трансформация объединенного датафрейма будет производиться только на треннировочной выборке полученной методом train_test_split
. При разделении выборок будут использоваться параметры random_state=30112023
и test_size=.25
, заданные требованиями к данному проекту. Для анализа объединенных данных треннировочные выборки с «фичами» и целевой переменной будут объеденены в общий датафрейм. Это требуется для анализа зависимостей переменных. Также это позволит сохранить разделенные выборки для использования в моделях.
# Разделение датафрейма
# на целевые и не целевые выборки
#features = data['text']
features = total_new.drop('active_contract', axis=1)
target = total_new['active_contract']
# Разделение выборок
# на обучающие и тестовые
features_train, features_test, target_train, target_test = train_test_split(
features,
target,
test_size=.25,
stratify=target,
random_state=STATE
)
# Объединение обучающих выборок
total_new_train = features_train.join(target_train)
# Проверка баланса классов
target_train_0 = target_train.loc[target_train == 0].count(),
target_train_1 = target_train.loc[target_train == 1].count(),
target_test_0 = target_test.loc[target_test == 0].count()
target_test_1 = target_test.loc[target_test == 1].count()
target_train_0 = target_train_0[0]
target_train_1 = target_train_1[0]
display(pd.DataFrame({
'Тип выборки':['Тренировочная', 'Тестовая'],
'Класс 0':[
target_train_0,
target_test_0
],
'Класс 1':[
target_train_1,
target_test_1
]
}))
if target_train_0 == target_train_1:
print('Классы в тренировочной выборке сбалансированы.')
else:
print('Классы в тренировочной выборке не сбалансированы.')
print(f'Класс 0: {(target_train_0/(target_train_0+target_train_1)*100).round(2)}%')
print(f'Класс 1: {(target_train_1/(target_train_0+target_train_1)*100).round(2)}%')
print()
if target_test_0 == target_test_1:
print('Классы в тестовой выборке сбалансированы.')
else:
print('Классы в тестовой выборке не сбалансированы.')
print(f'Класс 0: {(target_test_0/(target_test_0+target_test_1)*100).round(2)}%')
print(f'Класс 1: {(target_test_1/(target_test_0+target_test_1)*100).round(2)}%')
Тип выборки | Класс 0 | Класс 1 | |
---|---|---|---|
0 | Тренировочная | 4456 | 826 |
1 | Тестовая | 1486 | 275 |
Классы в тренировочной выборке не сбалансированы. Класс 0: 84.36% Класс 1: 15.64% Классы в тестовой выборке не сбалансированы. Класс 0: 84.38% Класс 1: 15.62%
# Анализ выборки
total_new_train.head()
customer_id | gender | senior_citizen | partner | dependents | type | paperless_billing | payment_method | monthly_charges | total_charges | duration_contract | internet_service | online_security | online_backup | device_protection | tech_support | streaming_tv | streaming_movies | multiple_lines | active_contract | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3798 | 2800-QQUSO | Male | No | No | No | Month-to-month | Yes | Electronic check | 100.30 | 4212.60 | 1279 | Fiber optic | No | No | No | Yes | Yes | Yes | Yes | 0 |
3516 | 7587-RZNME | Male | No | No | No | Month-to-month | Yes | Electronic check | 43.30 | 259.80 | 184 | DSL | No | No | No | No | No | No | No | 0 |
5031 | 8118-TJAFG | Male | No | Yes | Yes | Month-to-month | Yes | Electronic check | 101.50 | 977.45 | 276 | Fiber optic | No | Yes | No | Yes | Yes | Yes | No | 0 |
3922 | 6173-GOLSU | Male | Yes | Yes | No | Month-to-month | Yes | Credit card (automatic) | 94.65 | 6341.55 | 2041 | Fiber optic | No | No | No | No | Yes | Yes | Yes | 0 |
3275 | 1245-HARPS | Female | No | Yes | No | Two year | No | Mailed check | 20.40 | 204.00 | 304 | NoValue | NoValue | NoValue | NoValue | NoValue | NoValue | NoValue | No | 1 |
# Анализ данных в обучающей выборке
total_new_train.describe()
monthly_charges | total_charges | duration_contract | active_contract | |
---|---|---|---|---|
count | 5282.000000 | 5282.000000 | 5282.000000 | 5282.00000 |
mean | 64.914691 | 2118.084824 | 895.525559 | 0.15638 |
std | 30.054313 | 2119.483435 | 684.487255 | 0.36325 |
min | 18.250000 | 0.000000 | 0.000000 | 0.00000 |
25% | 35.750000 | 435.075000 | 276.000000 | 0.00000 |
50% | 70.500000 | 1349.755000 | 761.000000 | 0.00000 |
75% | 89.800000 | 3247.812500 | 1432.000000 | 0.00000 |
max | 118.750000 | 9221.380000 | 2314.000000 | 1.00000 |
Исследовательский анализ данных объединённого датафрейма¶
Выполнение исследовательского анализа объединённого датафрейма, визуализация распределения признаков и при необходимости выполнение предобработки. Корреляционный анализ. Возможно использование не только имеющиеся признаки, но и генерираци новых.
Исследовательский анализ объединённого датафрейма¶
# Анализ объединенного датафрейма
print(total_new_train.info())
total_new_train.head()
<class 'pandas.core.frame.DataFrame'> Index: 5282 entries, 3798 to 670 Data columns (total 20 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 customer_id 5282 non-null object 1 gender 5282 non-null object 2 senior_citizen 5282 non-null object 3 partner 5282 non-null object 4 dependents 5282 non-null object 5 type 5282 non-null object 6 paperless_billing 5282 non-null object 7 payment_method 5282 non-null object 8 monthly_charges 5282 non-null float64 9 total_charges 5282 non-null float64 10 duration_contract 5282 non-null int64 11 internet_service 5282 non-null object 12 online_security 5282 non-null object 13 online_backup 5282 non-null object 14 device_protection 5282 non-null object 15 tech_support 5282 non-null object 16 streaming_tv 5282 non-null object 17 streaming_movies 5282 non-null object 18 multiple_lines 5282 non-null object 19 active_contract 5282 non-null int64 dtypes: float64(2), int64(2), object(16) memory usage: 995.6+ KB None
customer_id | gender | senior_citizen | partner | dependents | type | paperless_billing | payment_method | monthly_charges | total_charges | duration_contract | internet_service | online_security | online_backup | device_protection | tech_support | streaming_tv | streaming_movies | multiple_lines | active_contract | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3798 | 2800-QQUSO | Male | No | No | No | Month-to-month | Yes | Electronic check | 100.30 | 4212.60 | 1279 | Fiber optic | No | No | No | Yes | Yes | Yes | Yes | 0 |
3516 | 7587-RZNME | Male | No | No | No | Month-to-month | Yes | Electronic check | 43.30 | 259.80 | 184 | DSL | No | No | No | No | No | No | No | 0 |
5031 | 8118-TJAFG | Male | No | Yes | Yes | Month-to-month | Yes | Electronic check | 101.50 | 977.45 | 276 | Fiber optic | No | Yes | No | Yes | Yes | Yes | No | 0 |
3922 | 6173-GOLSU | Male | Yes | Yes | No | Month-to-month | Yes | Credit card (automatic) | 94.65 | 6341.55 | 2041 | Fiber optic | No | No | No | No | Yes | Yes | Yes | 0 |
3275 | 1245-HARPS | Female | No | Yes | No | Two year | No | Mailed check | 20.40 | 204.00 | 304 | NoValue | NoValue | NoValue | NoValue | NoValue | NoValue | NoValue | No | 1 |
# Справочник признаков объединенного датафрейма
total_new_features = {
'customer_id':'идентификатор абонента',
'gender':'пол',
'senior_citizen':'является ли абонент пенсионером',
'partner':'есть ли у абонента супруг или супруга',
'dependents':'есть ли у абонента дети',
'type':'тип оплаты',
'paperless_billing':'электронный расчётный лист',
'payment_method':'тип платежа',
'monthly_charges':'расходы за месяц',
'total_charges':'общие расходы абонента',
'active_contract':'активный договор (целевой признак)',
'duration_contract':'продолжительность договора в днях',
'internet_service':'тип подключения',
'online_security':'блокировка опасных сайтов',
'online_backup':'облачное хранилище файлов для резервного копирования данных',
'device_protection':'антивирус',
'tech_support':'выделенная линия технической поддержки',
'streaming_tv':'стриминговое телевидение',
'streaming_movies':'каталог фильмов',
'multiple_lines':'подключение телефона к нескольким линиям одновременно'
}
# Проверка справочника признаков
total_new_features
{'customer_id': 'идентификатор абонента', 'gender': 'пол', 'senior_citizen': 'является ли абонент пенсионером', 'partner': 'есть ли у абонента супруг или супруга', 'dependents': 'есть ли у абонента дети', 'type': 'тип оплаты', 'paperless_billing': 'электронный расчётный лист', 'payment_method': 'тип платежа', 'monthly_charges': 'расходы за месяц', 'total_charges': 'общие расходы абонента', 'active_contract': 'активный договор (целевой признак)', 'duration_contract': 'продолжительность договора в днях', 'internet_service': 'тип подключения', 'online_security': 'блокировка опасных сайтов', 'online_backup': 'облачное хранилище файлов для резервного копирования данных', 'device_protection': 'антивирус', 'tech_support': 'выделенная линия технической поддержки', 'streaming_tv': 'стриминговое телевидение', 'streaming_movies': 'каталог фильмов', 'multiple_lines': 'подключение телефона к нескольким линиям одновременно'}
# Анализ данных объединенного датафрейма
for i in total_new_train.columns:
if i not in ['customer_id']:
for j in total_new_train:
if i == j:
print(f'Распределение значений признака "{i}" ({total_new_features[j]}):')
t0 = total_new_train.loc[total_new_train['active_contract'] == 0, i]
t1 = total_new_train.loc[total_new_train['active_contract'] == 1, i]
t0 = t0.rename('Класс 0')
t1 = t1.rename('Класс 1')
display(pd.DataFrame([t0.describe(), t1.describe()]))
t0.hist(legend=True, alpha=.8)
t1.hist(legend=True, alpha=.8)
plt.title(f'Распределение значений признака "{i}"')
plt.xlabel(f'Значения признака "{i}"')
plt.ylabel('Количество значений')
plt.show()
Распределение значений признака "gender" (пол):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 2 | Male | 2237 |
Класс 1 | 826 | 2 | Male | 438 |
Распределение значений признака "senior_citizen" (является ли абонент пенсионером):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 2 | No | 3756 |
Класс 1 | 826 | 2 | No | 661 |
Распределение значений признака "partner" (есть ли у абонента супруг или супруга):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 2 | No | 2433 |
Класс 1 | 826 | 2 | Yes | 537 |
Распределение значений признака "dependents" (есть ли у абонента дети):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 2 | No | 3146 |
Класс 1 | 826 | 2 | No | 551 |
Распределение значений признака "type" (тип оплаты):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | Month-to-month | 2610 |
Класс 1 | 826 | 3 | Month-to-month | 314 |
Распределение значений признака "paperless_billing" (электронный расчётный лист):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 2 | Yes | 2591 |
Класс 1 | 826 | 2 | Yes | 552 |
Распределение значений признака "payment_method" (тип платежа):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 4 | Electronic check | 1533 |
Класс 1 | 826 | 4 | Electronic check | 268 |
Распределение значений признака "monthly_charges" (расходы за месяц):
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
Класс 0 | 4456.0 | 62.982080 | 29.831779 | 18.25 | 29.9000 | 69.55 | 87.2125 | 118.60 |
Класс 1 | 826.0 | 75.340496 | 29.108727 | 18.40 | 55.8875 | 84.10 | 99.4875 | 118.75 |
Распределение значений признака "total_charges" (общие расходы абонента):
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
Класс 0 | 4456.0 | 2080.726842 | 2205.653256 | 0.00 | 375.3625 | 1202.0 | 3193.425 | 9221.38 |
Класс 1 | 826.0 | 2319.618923 | 1561.054801 | 77.84 | 1010.2875 | 2062.7 | 3359.675 | 7594.92 |
Распределение значений признака "duration_contract" (продолжительность договора в днях):
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
Класс 0 | 4456.0 | 894.219031 | 719.270635 | 0.0 | 245.00 | 702.0 | 1523.0 | 2314.0 |
Класс 1 | 826.0 | 902.573850 | 453.170555 | 28.0 | 548.25 | 884.0 | 1247.0 | 2068.0 |
Распределение значений признака "internet_service" (тип подключения):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | Fiber optic | 1891 |
Класс 1 | 826 | 3 | Fiber optic | 444 |
Распределение значений признака "online_security" (блокировка опасных сайтов):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 2233 |
Класс 1 | 826 | 3 | No | 396 |
Распределение значений признака "online_backup" (облачное хранилище файлов для резервного копирования данных):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 2018 |
Класс 1 | 826 | 3 | Yes | 420 |
Распределение значений признака "device_protection" (антивирус):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 2021 |
Класс 1 | 826 | 3 | Yes | 412 |
Распределение значений признака "tech_support" (выделенная линия технической поддержки):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 2198 |
Класс 1 | 826 | 3 | No | 412 |
Распределение значений признака "streaming_tv" (стриминговое телевидение):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 1823 |
Класс 1 | 826 | 3 | Yes | 429 |
Распределение значений признака "streaming_movies" (каталог фильмов):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 1812 |
Класс 1 | 826 | 3 | Yes | 456 |
Распределение значений признака "multiple_lines" (подключение телефона к нескольким линиям одновременно):
count | unique | top | freq | |
---|---|---|---|---|
Класс 0 | 4456 | 3 | No | 2281 |
Класс 1 | 826 | 3 | Yes | 509 |
Распределение значений признака "active_contract" (активный договор (целевой признак)):
count | mean | std | min | 25% | 50% | 75% | max | |
---|---|---|---|---|---|---|---|---|
Класс 0 | 4456.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
Класс 1 | 826.0 | 1.0 | 0.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1.0 |
# Числовые признаки финального датафрейма,
# требующие нормализации
total_new_numeric = ['monthly_charges', 'total_charges', 'duration_contract']
print('Количество числовых признаков, требующих нормализации:', len(total_new_numeric))
total_new_train[total_new_numeric].head()
Количество числовых признаков, требующих нормализации: 3
monthly_charges | total_charges | duration_contract | |
---|---|---|---|
3798 | 100.30 | 4212.60 | 1279 |
3516 | 43.30 | 259.80 | 184 |
5031 | 101.50 | 977.45 | 276 |
3922 | 94.65 | 6341.55 | 2041 |
3275 | 20.40 | 204.00 | 304 |
# Категоиальные признаки финального датафрейма,
# требующие кодирования методом `OneHotEncoder` (для "линейных" моделей)
# и `OrdinalEncoder` (для "деревянных" моделей).
# Это все признаки, кроме тех, названия которых входят
# в переменную "total_new_numeric",
# и признака, содержащего идентификаторы
total_new_categories = []
for i in total_new_train.columns:
if (i not in total_new_numeric) & (i not in ['customer_id', 'active_contract']):
total_new_categories.append(i)
print('Количество категориальных признаков, требующих кодирования методом "OneHotEncoder" (для "линейных" моделей) и "OrdinalEncoder" (для "деревянных" моделей):', len(total_new_categories))
total_new_train[total_new_categories].head()
Количество категориальных признаков, требующих кодирования методом "OneHotEncoder" (для "линейных" моделей) и "OrdinalEncoder" (для "деревянных" моделей): 15
gender | senior_citizen | partner | dependents | type | paperless_billing | payment_method | internet_service | online_security | online_backup | device_protection | tech_support | streaming_tv | streaming_movies | multiple_lines | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3798 | Male | No | No | No | Month-to-month | Yes | Electronic check | Fiber optic | No | No | No | Yes | Yes | Yes | Yes |
3516 | Male | No | No | No | Month-to-month | Yes | Electronic check | DSL | No | No | No | No | No | No | No |
5031 | Male | No | Yes | Yes | Month-to-month | Yes | Electronic check | Fiber optic | No | Yes | No | Yes | Yes | Yes | No |
3922 | Male | Yes | Yes | No | Month-to-month | Yes | Credit card (automatic) | Fiber optic | No | No | No | No | Yes | Yes | Yes |
3275 | Female | No | Yes | No | Two year | No | Mailed check | NoValue | NoValue | NoValue | NoValue | NoValue | NoValue | NoValue | No |
Предварительные выводы
- Тренировочная выборка
total_new_train
, полученная из объединенного датафреймаtotal_new
содержит 20 признаков и 7043 объекта. - В объединенном датафрейме имеются 3 категориальных и 27 числовых признака. Часть из них не сбалансированы. Категориальные имеют дисбаланс классов, а числовые не не имеют нормального распределения или имеют существенное количество выбросов.
- Признак
active_contract
является целевым для обучения. Он бинарный категориальный и не сбалансирован с перевесом в пользу значения1
(контракт активен). Данный признак не содержит пропусков. Следовательно, по всем объектам выборки есть целевые значения. - Многие категориальные признаки содержат пропуски. Эти пропуски не требуется обрабатывать, т.к. не известны причины их появления.
- Все категориальные признаки, в том числе те, что ранее содержали только
0
и1
, но кроме целевого, требуется кодировать методомOneHotEncoder
(для «линейных» моделей) иOrdinalEncoder
(для «деревянных» моделей). - Все числовые признаки, названия которых входят в переменную
total_new_numeric
(monthly_charges
,total_charges
,duration_contract
), требуется нормализовать. - Все признаки требуется проверить на наличие корреляции. Сильно выраженные мультиколленеарные признаки и признаки со статистически не значемой корреляцией требуется исключить из объединенного датафрейма при обучении моделей.
Анализ мультиколлинеарности¶
# Анализ мультиколлинеарности
# с помощью методов библиотеки Phik
multicollinearity = total_new_train.drop('customer_id', axis=1).phik_matrix(interval_cols=total_new_numeric)
display(multicollinearity)
# Подсветка результатов с помощью цветовой карты
#multicollinearity.style.background_gradient(cmap='YlOrRd')
sns.heatmap(multicollinearity.copy().round(1), annot=True);
gender | senior_citizen | partner | dependents | type | paperless_billing | payment_method | monthly_charges | total_charges | duration_contract | internet_service | online_security | online_backup | device_protection | tech_support | streaming_tv | streaming_movies | multiple_lines | active_contract | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
gender | 1.000000 | 0.000000 | 0.006566 | 0.000000 | 0.000000 | 0.011572 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.022799 |
senior_citizen | 0.000000 | 1.000000 | 0.017424 | 0.331492 | 0.087711 | 0.238674 | 0.291426 | 0.301566 | 0.135638 | 0.056006 | 0.161784 | 0.125800 | 0.109632 | 0.110695 | 0.137321 | 0.110441 | 0.112665 | 0.088044 | 0.060924 |
partner | 0.006566 | 0.017424 | 1.000000 | 0.644202 | 0.177126 | 0.000000 | 0.238667 | 0.197576 | 0.380985 | 0.448887 | 0.000000 | 0.087660 | 0.087738 | 0.108014 | 0.072649 | 0.081819 | 0.073701 | 0.079918 | 0.220231 |
dependents | 0.000000 | 0.331492 | 0.644202 | 1.000000 | 0.145894 | 0.170514 | 0.226053 | 0.189303 | 0.089412 | 0.198359 | 0.109556 | 0.115012 | 0.095640 | 0.095338 | 0.111950 | 0.084742 | 0.081822 | 0.022756 | 0.042390 |
type | 0.000000 | 0.087711 | 0.177126 | 0.145894 | 1.000000 | 0.105117 | 0.278014 | 0.391819 | 0.467148 | 0.630725 | 0.506019 | 0.637745 | 0.583680 | 0.640261 | 0.669856 | 0.551836 | 0.552991 | 0.255877 | 0.090360 |
paperless_billing | 0.011572 | 0.238674 | 0.000000 | 0.170514 | 0.105117 | 1.000000 | 0.359655 | 0.468074 | 0.203461 | 0.000000 | 0.229926 | 0.210475 | 0.197299 | 0.197612 | 0.202854 | 0.207174 | 0.203204 | 0.101023 | 0.097563 |
payment_method | 0.000000 | 0.291426 | 0.238667 | 0.226053 | 0.278014 | 0.359655 | 1.000000 | 0.404730 | 0.337116 | 0.357240 | 0.325465 | 0.318070 | 0.296217 | 0.304161 | 0.321200 | 0.284987 | 0.286679 | 0.177943 | 0.200485 |
monthly_charges | 0.000000 | 0.301566 | 0.197576 | 0.189303 | 0.391819 | 0.468074 | 0.404730 | 1.000000 | 0.710069 | 0.380074 | 0.918715 | 0.813962 | 0.820570 | 0.826953 | 0.818585 | 0.865701 | 0.862921 | 0.714656 | 0.221155 |
total_charges | 0.000000 | 0.135638 | 0.380985 | 0.089412 | 0.467148 | 0.203461 | 0.337116 | 0.710069 | 1.000000 | 0.850265 | 0.488722 | 0.517825 | 0.552905 | 0.557239 | 0.523894 | 0.554019 | 0.550379 | 0.469162 | 0.299329 |
duration_contract | 0.000000 | 0.056006 | 0.448887 | 0.198359 | 0.630725 | 0.000000 | 0.357240 | 0.380074 | 0.850265 | 1.000000 | 0.058646 | 0.346647 | 0.373740 | 0.379760 | 0.347049 | 0.308803 | 0.297715 | 0.350429 | 0.378422 |
internet_service | 0.000000 | 0.161784 | 0.000000 | 0.109556 | 0.506019 | 0.229926 | 0.325465 | 0.918715 | 0.488722 | 0.058646 | 1.000000 | 0.949841 | 0.942828 | 0.942856 | 0.949908 | 0.946307 | 0.945961 | 0.738624 | 0.054731 |
online_security | 0.000000 | 0.125800 | 0.087660 | 0.115012 | 0.637745 | 0.210475 | 0.318070 | 0.813962 | 0.517825 | 0.346647 | 0.949841 | 1.000000 | 0.947628 | 0.947048 | 0.952966 | 0.943023 | 0.943180 | 0.540095 | 0.059206 |
online_backup | 0.000000 | 0.109632 | 0.087738 | 0.095640 | 0.583680 | 0.197299 | 0.296217 | 0.820570 | 0.552905 | 0.373740 | 0.942828 | 0.947628 | 1.000000 | 0.947803 | 0.947288 | 0.945168 | 0.945244 | 0.566072 | 0.088436 |
device_protection | 0.000000 | 0.110695 | 0.108014 | 0.095338 | 0.640261 | 0.197612 | 0.304161 | 0.826953 | 0.557239 | 0.379760 | 0.942856 | 0.947048 | 0.947803 | 1.000000 | 0.950434 | 0.953105 | 0.952855 | 0.563521 | 0.084696 |
tech_support | 0.000000 | 0.137321 | 0.072649 | 0.111950 | 0.669856 | 0.202854 | 0.321200 | 0.818585 | 0.523894 | 0.347049 | 0.949908 | 0.952966 | 0.947288 | 0.950434 | 1.000000 | 0.946266 | 0.946221 | 0.541000 | 0.050852 |
streaming_tv | 0.000000 | 0.110441 | 0.081819 | 0.084742 | 0.551836 | 0.207174 | 0.284987 | 0.865701 | 0.554019 | 0.308803 | 0.946307 | 0.943023 | 0.945168 | 0.953105 | 0.946266 | 1.000000 | 0.965023 | 0.583205 | 0.072660 |
streaming_movies | 0.000000 | 0.112665 | 0.073701 | 0.081822 | 0.552991 | 0.203204 | 0.286679 | 0.862921 | 0.550379 | 0.297715 | 0.945961 | 0.943180 | 0.945244 | 0.952855 | 0.946221 | 0.965023 | 1.000000 | 0.585280 | 0.084716 |
multiple_lines | 0.000000 | 0.088044 | 0.079918 | 0.022756 | 0.255877 | 0.101023 | 0.177943 | 0.714656 | 0.469162 | 0.350429 | 0.738624 | 0.540095 | 0.566072 | 0.563521 | 0.541000 | 0.583205 | 0.585280 | 1.000000 | 0.102937 |
active_contract | 0.022799 | 0.060924 | 0.220231 | 0.042390 | 0.090360 | 0.097563 | 0.200485 | 0.221155 | 0.299329 | 0.378422 | 0.054731 | 0.059206 | 0.088436 | 0.084696 | 0.050852 | 0.072660 | 0.084716 | 0.102937 | 1.000000 |
Отбор признаков для использования в обучении моделей¶
В обучающую и тестовую выборки должны попасть только признаки, относительно высокойкорелируемые с целевым признаком. Из мультиколлинеарных признаков требуется оставить в выборках только те, что имеют большую корреляцию с целевым признаком.
Предлагается установить минимальный порог коррелируемости с целевым признаком в 0.3. Также предлагается установить максимальный порог мультиколлинеарности в 0.75.
# Создание списка не мультиколлинеарных признаков
# со статистически значимой корреляцией с целевым признаком
def non_multicollinear_features(m, target_feature, v_min, v_max):
columns = []
append = True
for c in m.columns:
if ((m[c][target_feature] > v_min) & (c != target_feature)):
for i in m.index:
if ((c != i) & (c != target_feature) & (c not in columns) & (i not in columns)):
if m[c][i] > v_max:
if m[c][target_feature] < m[i][target_feature]:
append = False
break
if append == True: columns.append(c)
append = True
return columns
columns = non_multicollinear_features(multicollinearity, 'active_contract', .03, .75)
print('Не мультиколлинеарные признаки, обладающие статистически значимой парной корреляцией с целевым признаком:')
columns
Не мультиколлинеарные признаки, обладающие статистически значимой парной корреляцией с целевым признаком:
['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'monthly_charges', 'duration_contract', 'online_backup', 'streaming_movies', 'multiple_lines']
# Разделение отобранных параметров на категориальные и числовые
features_categories = []
for i in columns:
for j in total_new_categories:
if i == j: features_categories.append(i)
features_numeric = []
for i in columns:
for j in total_new_numeric:
if i == j: features_numeric.append(i)
print('Категориальные признаки:', features_categories)
print()
print('Числовые признаки :', features_numeric)
Категориальные признаки: ['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'online_backup', 'streaming_movies', 'multiple_lines'] Числовые признаки : ['monthly_charges', 'duration_contract']
Подготовка данных¶
Подготовка данных для обучения модели. Разделение данных на две выборки, при масштабировании и кодировании должны быть учтены особенности данных и моделей.
Объединенный датафрейм ранее уже был разделен на обучающую и тренировочную выборки, а также на целевой и не целевые признаки. На треннировочной выборке выбраны не мультиколлинеарные признаки, обладающие статистически значимой парной корреляцией с целевым признаком.
Названия этих признаков собраны в переменную columns
. Учитывая выше сказанное требуется использовать ранее созданные выборки, но в выборках с «фичами» оставить только признаки, перечисленные в переменной columns
.
Также перед обучением моделей следует удалить далее не используемые переменные, т.к. процесс обучения моделей ресурсоемкий.
# Очистка выборок с фичами
# от статистически не значимых признаков
features_train = features_train[columns]
features_test = features_test[columns]
# Очистка памяти
# от неиспользуемых переменных
#del contract_new, personal_new, internet_new, phone_new
#del total_new, features, target
#del total_new_train
# Проверка выборки "features_train"
print(features_train.info())
features_train.head()
<class 'pandas.core.frame.DataFrame'> Index: 5282 entries, 3798 to 670 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 senior_citizen 5282 non-null object 1 partner 5282 non-null object 2 dependents 5282 non-null object 3 type 5282 non-null object 4 paperless_billing 5282 non-null object 5 payment_method 5282 non-null object 6 monthly_charges 5282 non-null float64 7 duration_contract 5282 non-null int64 8 online_backup 5282 non-null object 9 streaming_movies 5282 non-null object 10 multiple_lines 5282 non-null object dtypes: float64(1), int64(1), object(9) memory usage: 624.2+ KB None
senior_citizen | partner | dependents | type | paperless_billing | payment_method | monthly_charges | duration_contract | online_backup | streaming_movies | multiple_lines | |
---|---|---|---|---|---|---|---|---|---|---|---|
3798 | No | No | No | Month-to-month | Yes | Electronic check | 100.30 | 1279 | No | Yes | Yes |
3516 | No | No | No | Month-to-month | Yes | Electronic check | 43.30 | 184 | No | No | No |
5031 | No | Yes | Yes | Month-to-month | Yes | Electronic check | 101.50 | 276 | Yes | Yes | No |
3922 | Yes | Yes | No | Month-to-month | Yes | Credit card (automatic) | 94.65 | 2041 | No | Yes | Yes |
3275 | No | Yes | No | Two year | No | Mailed check | 20.40 | 304 | NoValue | NoValue | No |
# Проверка выборки "target_train"
print(pd.DataFrame(target_train).info())
target_train.head()
<class 'pandas.core.frame.DataFrame'> Index: 5282 entries, 3798 to 670 Data columns (total 1 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 active_contract 5282 non-null int64 dtypes: int64(1) memory usage: 211.6 KB None
3798 0 3516 0 5031 0 3922 0 3275 1 Name: active_contract, dtype: int64
# Проверка выборки "features_test"
print(features_test.info())
features_test.head()
<class 'pandas.core.frame.DataFrame'> Index: 1761 entries, 5707 to 538 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 senior_citizen 1761 non-null object 1 partner 1761 non-null object 2 dependents 1761 non-null object 3 type 1761 non-null object 4 paperless_billing 1761 non-null object 5 payment_method 1761 non-null object 6 monthly_charges 1761 non-null float64 7 duration_contract 1761 non-null int64 8 online_backup 1761 non-null object 9 streaming_movies 1761 non-null object 10 multiple_lines 1761 non-null object dtypes: float64(1), int64(1), object(9) memory usage: 165.1+ KB None
senior_citizen | partner | dependents | type | paperless_billing | payment_method | monthly_charges | duration_contract | online_backup | streaming_movies | multiple_lines | |
---|---|---|---|---|---|---|---|---|---|---|---|
5707 | No | No | No | Month-to-month | No | Electronic check | 45.55 | 62 | No | No | No |
2139 | No | No | No | Month-to-month | Yes | Electronic check | 66.85 | 215 | No | Yes | No |
1083 | No | No | No | Month-to-month | Yes | Electronic check | 80.95 | 31 | No | Yes | No |
4035 | No | Yes | Yes | Month-to-month | Yes | Bank transfer (automatic) | 93.25 | 1584 | Yes | Yes | Yes |
4553 | No | Yes | No | Two year | Yes | Bank transfer (automatic) | 115.80 | 2191 | Yes | Yes | Yes |
# Проверка выборки "target_test"
print(pd.DataFrame(target_test).info())
target_test.head()
<class 'pandas.core.frame.DataFrame'> Index: 1761 entries, 5707 to 538 Data columns (total 1 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 active_contract 1761 non-null int64 dtypes: int64(1) memory usage: 27.5 KB None
5707 0 2139 0 1083 1 4035 0 4553 0 Name: active_contract, dtype: int64
Обучение моделей машинного обучения¶
Обучение как минимум двух моделей. Подбор хотя бы для одной из них как минимум двух гиперпараметров.
Полезные функции¶
# Model + Params + Pipeline + HalvingGridSearchCV
def model_pipeline_gridsearch(
features_train,
target_train,
pipeline,
params,
data_grids,
data_times
):
# Начало отслеживания времени
start_time = time.time()
# HalvingGridSearchCV
# (о подборе оптимальных параметров:
# https://scikit-learn.ru/3-2-tuning-the-hyper-parameters-of-an-estimator/)
#grid = HalvingRandomSearchCV(
grid = HalvingGridSearchCV(
pipeline,
params,
#resorce=
cv=4, # параметр KFold для кроссвалидации (обучющая и валидационная выборки 75:25)
#n_jobs=-1, # количество параллельно выполняемых заданий (-1 - задействованы все процессоры)
scoring='roc_auc', # функция оценки (ошибки)
error_score='raise',
random_state=STATE
)
# Обучение
grid.fit(features_train, target_train)
# Подсчет времени выполнения скрипта
finish_time = time.time()
funtion_time = finish_time - start_time
data_grids.append(grid)
data_times.append(funtion_time)
return data_grids, data_times
# Вывод на печать результатов модели
def print_model_result(grids, data_times, model_name, process_print=False):
print()
print('Модель :', model_name)
print('Метрика ROC AUC:', grids[-1].best_score_)
print(f'Время : {data_times[-1]} секунд')
print('Параметры :', grids[-1].best_estimator_[-1].get_params())
print()
if process_print == True:
print('Результаты выбора параметров модели:')
display(pd.DataFrame(grids[-1].cv_results_)[[
'params',
'mean_train_score',
'mean_test_score'
]])
print('-' * 30)
def print_model_graphicresult(grids):
pd.DataFrame(grids[-1].cv_results_)[[
'mean_test_score',
'mean_train_score'
]].plot();
# Переменная, содержащая баланс
# классов целевой переменной
class_weights = {
1:target_train.loc[target_train == 1].count() / target_train.count(),
0:target_train.loc[target_train == 0].count() / target_train.count()
}
# Проверка переменной
class_weights
{1: 0.1563801590306702, 0: 0.8436198409693298}
# Функция уточнения значений гиперпараметров моделей
# путем выделения центров левого и правого
# диапазнов значений интервальных гиперпараметров
def two_range(a, b, int_type=False):
center = (b - a) / 2
left = a + (center / 2)
right = b - (center / 2)
center = a + center
# Приведение значений к целому
# (в некоторых случаях не допустимы
# значения с плавающей точкой)
if int_type == True:
left = int(left)
center = int(center)
right = int(right)
return [left, center, right]
# Поиск лучших моделей и их параметров
data_grids = []
data_times = []
Обучение модели LogisticRegression¶
# LogisticRegression (solver, C)
# Модель
model = LogisticRegression(
random_state=STATE,
class_weight='balanced'
)
# Преобразование признаков
column_trans = ColumnTransformer([
('ohe', OneHotEncoder(drop='first', handle_unknown='ignore'), features_categories), # кодирование категорий через OneHotEncoder (для линейной модели)
#('std', PowerTransformer(method='box-cox'), features_numeric), # Приведение к нормальному распределению
('std', PowerTransformer(), features_numeric), # Приведение к нормальному распределению
], remainder='drop', verbose_feature_names_out=False)
# Pipeline
pipeline = Pipeline([
('transform', column_trans), # трансформация выборки
('clf', model) # модель классификации
])
# Диапазон значений искомого параметра
n_С = [.111, .5, .999]
# Обход диапазона значений
while n_С[1] - n_С[0] > .01:
n_С = two_range(n_С[0], n_С[-1], False)
# Параметры модели
params = [{
'clf__solver': ('liblinear', 'lbfgs', 'newton-cg'),
'clf__C': n_С
}]
# Обучение модели
data_grids, data_times = model_pipeline_gridsearch(
features_train,
target_train,
pipeline,
params,
data_grids,
data_times
)
# Вывод результата обучения модели
print_model_result(data_grids, data_times, 'LogisticRegression', process_print=True)
# Лучшее значение искомого параметра
this_best_params = data_grids[-1].best_estimator_[-1].get_params()['C']
# Корректировка диапазона значений искомого параметра
if len(n_С) > 1:
if n_С[0] == this_best_params:
n_С = [n_С[0], (n_С[0] + (n_С[1] - n_С[0])/2), n_С[1]]
if n_С[2] == this_best_params:
n_С = [n_С[1], (n_С[1] + (n_С[2] - n_С[1])/2), n_С[2]]
# График финальной версии модели этой ячейки
print_model_graphicresult(data_grids)
Модель : LogisticRegression Метрика ROC AUC: 0.7267741438350803 Время : 8.754640102386475 секунд Параметры : {'C': 0.777, 'class_weight': 'balanced', 'dual': False, 'fit_intercept': True, 'intercept_scaling': 1, 'l1_ratio': None, 'max_iter': 100, 'multi_class': 'auto', 'n_jobs': None, 'penalty': 'l2', 'random_state': 30112023, 'solver': 'lbfgs', 'tol': 0.0001, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__C’: 0.333, ‘clf__solver’: ‘liblinear’} | 0.750635 | 0.672675 |
1 | {‘clf__C’: 0.333, ‘clf__solver’: ‘lbfgs’} | 0.753359 | 0.677325 |
2 | {‘clf__C’: 0.333, ‘clf__solver’: ‘newton-cg’} | 0.753323 | 0.677226 |
3 | {‘clf__C’: 0.555, ‘clf__solver’: ‘liblinear’} | 0.753197 | 0.678034 |
4 | {‘clf__C’: 0.555, ‘clf__solver’: ‘lbfgs’} | 0.755023 | 0.679600 |
5 | {‘clf__C’: 0.555, ‘clf__solver’: ‘newton-cg’} | 0.755023 | 0.679600 |
6 | {‘clf__C’: 0.777, ‘clf__solver’: ‘liblinear’} | 0.754340 | 0.679522 |
7 | {‘clf__C’: 0.777, ‘clf__solver’: ‘lbfgs’} | 0.755755 | 0.679621 |
8 | {‘clf__C’: 0.777, ‘clf__solver’: ‘newton-cg’} | 0.755719 | 0.679533 |
9 | {‘clf__C’: 0.555, ‘clf__solver’: ‘lbfgs’} | 0.739170 | 0.708645 |
10 | {‘clf__C’: 0.555, ‘clf__solver’: ‘newton-cg’} | 0.739160 | 0.708667 |
11 | {‘clf__C’: 0.777, ‘clf__solver’: ‘lbfgs’} | 0.739191 | 0.708770 |
12 | {‘clf__C’: 0.777, ‘clf__solver’: ‘lbfgs’} | 0.731876 | 0.726774 |
------------------------------ Модель : LogisticRegression Метрика ROC AUC: 0.7268027416455016 Время : 11.394300699234009 секунд Параметры : {'C': 0.666, 'class_weight': 'balanced', 'dual': False, 'fit_intercept': True, 'intercept_scaling': 1, 'l1_ratio': None, 'max_iter': 100, 'multi_class': 'auto', 'n_jobs': None, 'penalty': 'l2', 'random_state': 30112023, 'solver': 'newton-cg', 'tol': 0.0001, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__C’: 0.6105, ‘clf__solver’: ‘liblinear’} | 0.753543 | 0.678651 |
1 | {‘clf__C’: 0.6105, ‘clf__solver’: ‘lbfgs’} | 0.754966 | 0.679848 |
2 | {‘clf__C’: 0.6105, ‘clf__solver’: ‘newton-cg’} | 0.754992 | 0.679848 |
3 | {‘clf__C’: 0.666, ‘clf__solver’: ‘liblinear’} | 0.753853 | 0.679224 |
4 | {‘clf__C’: 0.666, ‘clf__solver’: ‘lbfgs’} | 0.755273 | 0.679890 |
5 | {‘clf__C’: 0.666, ‘clf__solver’: ‘newton-cg’} | 0.755264 | 0.679890 |
6 | {‘clf__C’: 0.7215, ‘clf__solver’: ‘liblinear’} | 0.754075 | 0.678943 |
7 | {‘clf__C’: 0.7215, ‘clf__solver’: ‘lbfgs’} | 0.755541 | 0.679841 |
8 | {‘clf__C’: 0.7215, ‘clf__solver’: ‘newton-cg’} | 0.755549 | 0.679841 |
9 | {‘clf__C’: 0.6105, ‘clf__solver’: ‘newton-cg’} | 0.739136 | 0.708586 |
10 | {‘clf__C’: 0.666, ‘clf__solver’: ‘lbfgs’} | 0.739211 | 0.708719 |
11 | {‘clf__C’: 0.666, ‘clf__solver’: ‘newton-cg’} | 0.739202 | 0.708729 |
12 | {‘clf__C’: 0.666, ‘clf__solver’: ‘newton-cg’} | 0.731871 | 0.726803 |
------------------------------ Модель : LogisticRegression Метрика ROC AUC: 0.7267885583928695 Время : 9.398901462554932 секунд Параметры : {'C': 0.6937500000000001, 'class_weight': 'balanced', 'dual': False, 'fit_intercept': True, 'intercept_scaling': 1, 'l1_ratio': None, 'max_iter': 100, 'multi_class': 'auto', 'n_jobs': None, 'penalty': 'l2', 'random_state': 30112023, 'solver': 'lbfgs', 'tol': 0.0001, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__C’: 0.63825, ‘clf__solver’: ‘liblinear’} | 0.753789 | 0.678706 |
1 | {‘clf__C’: 0.63825, ‘clf__solver’: ‘lbfgs’} | 0.755094 | 0.679836 |
2 | {‘clf__C’: 0.63825, ‘clf__solver’: ‘newton-cg’} | 0.755085 | 0.679836 |
3 | {‘clf__C’: 0.666, ‘clf__solver’: ‘liblinear’} | 0.753853 | 0.679224 |
4 | {‘clf__C’: 0.666, ‘clf__solver’: ‘lbfgs’} | 0.755273 | 0.679890 |
5 | {‘clf__C’: 0.666, ‘clf__solver’: ‘newton-cg’} | 0.755264 | 0.679890 |
6 | {‘clf__C’: 0.6937500000000001, ‘clf__solver’: … | 0.753941 | 0.679209 |
7 | {‘clf__C’: 0.6937500000000001, ‘clf__solver’: … | 0.755450 | 0.679891 |
8 | {‘clf__C’: 0.6937500000000001, ‘clf__solver’: … | 0.755459 | 0.679977 |
9 | {‘clf__C’: 0.666, ‘clf__solver’: ‘newton-cg’} | 0.739202 | 0.708729 |
10 | {‘clf__C’: 0.6937500000000001, ‘clf__solver’: … | 0.739220 | 0.708742 |
11 | {‘clf__C’: 0.6937500000000001, ‘clf__solver’: … | 0.739218 | 0.708732 |
12 | {‘clf__C’: 0.6937500000000001, ‘clf__solver’: … | 0.731871 | 0.726789 |
------------------------------ Модель : LogisticRegression Метрика ROC AUC: 0.7267939804399587 Время : 10.204588413238525 секунд Параметры : {'C': 0.6868125, 'class_weight': 'balanced', 'dual': False, 'fit_intercept': True, 'intercept_scaling': 1, 'l1_ratio': None, 'max_iter': 100, 'multi_class': 'auto', 'n_jobs': None, 'penalty': 'l2', 'random_state': 30112023, 'solver': 'newton-cg', 'tol': 0.0001, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__C’: 0.6729375000000001, ‘clf__solver’: … | 0.753854 | 0.679129 |
1 | {‘clf__C’: 0.6729375000000001, ‘clf__solver’: … | 0.755335 | 0.679802 |
2 | {‘clf__C’: 0.6729375000000001, ‘clf__solver’: … | 0.755371 | 0.679802 |
3 | {‘clf__C’: 0.679875, ‘clf__solver’: ‘liblinear’} | 0.753879 | 0.679119 |
4 | {‘clf__C’: 0.679875, ‘clf__solver’: ‘lbfgs’} | 0.755407 | 0.679887 |
5 | {‘clf__C’: 0.679875, ‘clf__solver’: ‘newton-cg’} | 0.755398 | 0.679887 |
6 | {‘clf__C’: 0.6868125, ‘clf__solver’: ‘liblinear’} | 0.753914 | 0.679109 |
7 | {‘clf__C’: 0.6868125, ‘clf__solver’: ‘lbfgs’} | 0.755371 | 0.679891 |
8 | {‘clf__C’: 0.6868125, ‘clf__solver’: ‘newton-cg’} | 0.755388 | 0.679891 |
9 | {‘clf__C’: 0.679875, ‘clf__solver’: ‘newton-cg’} | 0.739210 | 0.708710 |
10 | {‘clf__C’: 0.6868125, ‘clf__solver’: ‘lbfgs’} | 0.739221 | 0.708742 |
11 | {‘clf__C’: 0.6868125, ‘clf__solver’: ‘newton-cg’} | 0.739213 | 0.708742 |
12 | {‘clf__C’: 0.6868125, ‘clf__solver’: ‘newton-cg’} | 0.731870 | 0.726794 |
------------------------------
Обучение модели RandomForestClassifier¶
# RandomForestRegressor (max_features, max_depth)
# Модель
model = RandomForestClassifier(
random_state=STATE
)
# Преобразование признаков
column_trans = ColumnTransformer([
('ohe', OrdinalEncoder(), features_categories), # кодирование категорий через OrdinalEncoder (для деревьев и бустингов)
('std', MaxAbsScaler(), features_numeric), # Стандартизация числовых данных от 0 до 1
], remainder='drop', verbose_feature_names_out=False)
# Pipeline
pipeline = Pipeline([
('transform', column_trans), # трансформация выборки
('clf', model) # модель классификации
])
# Диапазон значений искомого параметра
# max_depth
n_max_depth = [1, 100, 200]
# n_estimators
n_n_estimators = [1, 100, 200]
# Обход диапазона значений
while (
(n_max_depth[1] - n_max_depth[0] > 10)
| (n_n_estimators[1] - n_n_estimators[0] > 10)
):
# max_depth
n_max_depth = two_range(n_max_depth[0], n_max_depth[-1], True)
# n_estimators
n_n_estimators = two_range(n_n_estimators[0], n_n_estimators[-1], True)
# Параметры модели
params = [{
'clf__max_features':('sqrt', 'log2', None, 1.),
'clf__max_depth': n_max_depth,
'clf__n_estimators': n_n_estimators
}]
# Обучение модели
data_grids, data_times = model_pipeline_gridsearch(
features_train,
target_train,
pipeline,
params,
data_grids,
data_times
)
# Вывод результата обучения модели
print_model_result(data_grids, data_times, 'RandomForestRegressor', process_print=True)
# Корректировка диапазона значений искомого параметра
# Лучшее значение искомого параметра
this_best_params = data_grids[-1].best_estimator_[-1].get_params()['max_depth']
# max_depth
if len(n_max_depth) > 1:
if n_max_depth[0] == this_best_params:
n_max_depth = [n_max_depth[0], int(n_max_depth[0] + (n_max_depth[1] - n_max_depth[0])/2), n_max_depth[1]]
if n_max_depth[2] == this_best_params:
n_max_depth = [n_max_depth[1], int(n_max_depth[1] + (n_max_depth[2] - n_max_depth[1])/2), n_max_depth[2]]
else:
n_max_depth = n_max_depth
else:
n_max_depth = n_max_depth
# Лучшее значение искомого параметра
this_best_params = data_grids[-1].best_estimator_[-1].get_params()['n_estimators']
# n_estimators
if len(n_n_estimators) > 1:
if n_n_estimators[0] == this_best_params:
n_n_estimators = [n_n_estimators[0], int(n_n_estimators[0] + (n_n_estimators[1] - n_n_estimators[0])/2), n_n_estimators[1]]
if n_n_estimators[2] == this_best_params:
n_n_estimators = [n_n_estimators[1], int(n_n_estimators[1] + (n_n_estimators[2] - n_n_estimators[1])/2), n_n_estimators[2]]
# График финальной версии модели этой ячейки
print_model_graphicresult(data_grids)
Модель : RandomForestRegressor Метрика ROC AUC: 0.8102019536468412 Время : 38.58482789993286 секунд Параметры : {'bootstrap': True, 'ccp_alpha': 0.0, 'class_weight': None, 'criterion': 'gini', 'max_depth': 50, 'max_features': 'sqrt', 'max_leaf_nodes': None, 'max_samples': None, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_estimators': 100, 'n_jobs': None, 'oob_score': False, 'random_state': 30112023, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.746230 |
1 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.736584 |
2 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.736183 |
3 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘l… | 1.0 | 0.746230 |
4 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘l… | 1.0 | 0.736584 |
5 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘l… | 1.0 | 0.736183 |
6 | {‘clf__max_depth’: 50, ‘clf__max_features’: No… | 1.0 | 0.723803 |
7 | {‘clf__max_depth’: 50, ‘clf__max_features’: No… | 1.0 | 0.735105 |
8 | {‘clf__max_depth’: 50, ‘clf__max_features’: No… | 1.0 | 0.725254 |
9 | {‘clf__max_depth’: 50, ‘clf__max_features’: 1…. | 1.0 | 0.723803 |
10 | {‘clf__max_depth’: 50, ‘clf__max_features’: 1…. | 1.0 | 0.735105 |
11 | {‘clf__max_depth’: 50, ‘clf__max_features’: 1…. | 1.0 | 0.725254 |
12 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.746230 |
13 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.736584 |
14 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.736183 |
15 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.746230 |
16 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.736584 |
17 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.736183 |
18 | {‘clf__max_depth’: 100, ‘clf__max_features’: N… | 1.0 | 0.723803 |
19 | {‘clf__max_depth’: 100, ‘clf__max_features’: N… | 1.0 | 0.735105 |
20 | {‘clf__max_depth’: 100, ‘clf__max_features’: N… | 1.0 | 0.725254 |
21 | {‘clf__max_depth’: 100, ‘clf__max_features’: 1… | 1.0 | 0.723803 |
22 | {‘clf__max_depth’: 100, ‘clf__max_features’: 1… | 1.0 | 0.735105 |
23 | {‘clf__max_depth’: 100, ‘clf__max_features’: 1… | 1.0 | 0.725254 |
24 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.746230 |
25 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.736584 |
26 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.736183 |
27 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.746230 |
28 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.736584 |
29 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.736183 |
30 | {‘clf__max_depth’: 150, ‘clf__max_features’: N… | 1.0 | 0.723803 |
31 | {‘clf__max_depth’: 150, ‘clf__max_features’: N… | 1.0 | 0.735105 |
32 | {‘clf__max_depth’: 150, ‘clf__max_features’: N… | 1.0 | 0.725254 |
33 | {‘clf__max_depth’: 150, ‘clf__max_features’: 1… | 1.0 | 0.723803 |
34 | {‘clf__max_depth’: 150, ‘clf__max_features’: 1… | 1.0 | 0.735105 |
35 | {‘clf__max_depth’: 150, ‘clf__max_features’: 1… | 1.0 | 0.725254 |
36 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.743447 |
37 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.743447 |
38 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.743447 |
39 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘l… | 1.0 | 0.743447 |
40 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.743447 |
41 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.743447 |
42 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.728754 |
43 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.728754 |
44 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.728754 |
45 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.728754 |
46 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘l… | 1.0 | 0.728754 |
47 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.728754 |
48 | {‘clf__max_depth’: 150, ‘clf__max_features’: ‘… | 1.0 | 0.771462 |
49 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘l… | 1.0 | 0.771462 |
50 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.771462 |
51 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.771462 |
52 | {‘clf__max_depth’: 50, ‘clf__max_features’: ‘s… | 1.0 | 0.810202 |
53 | {‘clf__max_depth’: 100, ‘clf__max_features’: ‘… | 1.0 | 0.810202 |
------------------------------ Модель : RandomForestRegressor Метрика ROC AUC: 0.8112027888493061 Время : 43.28140997886658 секунд Параметры : {'bootstrap': True, 'ccp_alpha': 0.0, 'class_weight': None, 'criterion': 'gini', 'max_depth': 62, 'max_features': 'sqrt', 'max_leaf_nodes': None, 'max_samples': None, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_estimators': 125, 'n_jobs': None, 'oob_score': False, 'random_state': 30112023, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘s… | 1.0 | 0.732380 |
1 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘s… | 1.0 | 0.736584 |
2 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘s… | 1.0 | 0.737170 |
3 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘l… | 1.0 | 0.732380 |
4 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘l… | 1.0 | 0.736584 |
5 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘l… | 1.0 | 0.737170 |
6 | {‘clf__max_depth’: 62, ‘clf__max_features’: No… | 1.0 | 0.737403 |
7 | {‘clf__max_depth’: 62, ‘clf__max_features’: No… | 1.0 | 0.735105 |
8 | {‘clf__max_depth’: 62, ‘clf__max_features’: No… | 1.0 | 0.735968 |
9 | {‘clf__max_depth’: 62, ‘clf__max_features’: 1…. | 1.0 | 0.737403 |
10 | {‘clf__max_depth’: 62, ‘clf__max_features’: 1…. | 1.0 | 0.735105 |
11 | {‘clf__max_depth’: 62, ‘clf__max_features’: 1…. | 1.0 | 0.735968 |
12 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘s… | 1.0 | 0.732380 |
13 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘s… | 1.0 | 0.736584 |
14 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘s… | 1.0 | 0.737170 |
15 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘l… | 1.0 | 0.732380 |
16 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘l… | 1.0 | 0.736584 |
17 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘l… | 1.0 | 0.737170 |
18 | {‘clf__max_depth’: 75, ‘clf__max_features’: No… | 1.0 | 0.737403 |
19 | {‘clf__max_depth’: 75, ‘clf__max_features’: No… | 1.0 | 0.735105 |
20 | {‘clf__max_depth’: 75, ‘clf__max_features’: No… | 1.0 | 0.735968 |
21 | {‘clf__max_depth’: 75, ‘clf__max_features’: 1…. | 1.0 | 0.737403 |
22 | {‘clf__max_depth’: 75, ‘clf__max_features’: 1…. | 1.0 | 0.735105 |
23 | {‘clf__max_depth’: 75, ‘clf__max_features’: 1…. | 1.0 | 0.735968 |
24 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘s… | 1.0 | 0.732380 |
25 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘s… | 1.0 | 0.736584 |
26 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘s… | 1.0 | 0.737170 |
27 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘l… | 1.0 | 0.732380 |
28 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘l… | 1.0 | 0.736584 |
29 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘l… | 1.0 | 0.737170 |
30 | {‘clf__max_depth’: 87, ‘clf__max_features’: No… | 1.0 | 0.737403 |
31 | {‘clf__max_depth’: 87, ‘clf__max_features’: No… | 1.0 | 0.735105 |
32 | {‘clf__max_depth’: 87, ‘clf__max_features’: No… | 1.0 | 0.735968 |
33 | {‘clf__max_depth’: 87, ‘clf__max_features’: 1…. | 1.0 | 0.737403 |
34 | {‘clf__max_depth’: 87, ‘clf__max_features’: 1…. | 1.0 | 0.735105 |
35 | {‘clf__max_depth’: 87, ‘clf__max_features’: 1…. | 1.0 | 0.735968 |
36 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘l… | 1.0 | 0.744512 |
37 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘s… | 1.0 | 0.744512 |
38 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘s… | 1.0 | 0.744512 |
39 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘l… | 1.0 | 0.744512 |
40 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘s… | 1.0 | 0.744512 |
41 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘l… | 1.0 | 0.744512 |
42 | {‘clf__max_depth’: 62, ‘clf__max_features’: No… | 1.0 | 0.742024 |
43 | {‘clf__max_depth’: 62, ‘clf__max_features’: 1…. | 1.0 | 0.742024 |
44 | {‘clf__max_depth’: 75, ‘clf__max_features’: No… | 1.0 | 0.742024 |
45 | {‘clf__max_depth’: 87, ‘clf__max_features’: No… | 1.0 | 0.742024 |
46 | {‘clf__max_depth’: 87, ‘clf__max_features’: 1…. | 1.0 | 0.742024 |
47 | {‘clf__max_depth’: 75, ‘clf__max_features’: 1…. | 1.0 | 0.742024 |
48 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘s… | 1.0 | 0.771273 |
49 | {‘clf__max_depth’: 87, ‘clf__max_features’: ‘l… | 1.0 | 0.771273 |
50 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘s… | 1.0 | 0.771273 |
51 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘l… | 1.0 | 0.771273 |
52 | {‘clf__max_depth’: 62, ‘clf__max_features’: ‘s… | 1.0 | 0.811203 |
53 | {‘clf__max_depth’: 75, ‘clf__max_features’: ‘l… | 1.0 | 0.811203 |
------------------------------ Модель : RandomForestRegressor Метрика ROC AUC: 0.8110573515684645 Время : 46.0589439868927 секунд Параметры : {'bootstrap': True, 'ccp_alpha': 0.0, 'class_weight': None, 'criterion': 'gini', 'max_depth': 65, 'max_features': 'sqrt', 'max_leaf_nodes': None, 'max_samples': None, 'min_impurity_decrease': 0.0, 'min_samples_leaf': 1, 'min_samples_split': 2, 'min_weight_fraction_leaf': 0.0, 'n_estimators': 118, 'n_jobs': None, 'oob_score': False, 'random_state': 30112023, 'verbose': 0, 'warm_start': False} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.737445 |
1 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.737653 |
2 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.742193 |
3 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘l… | 1.0 | 0.737445 |
4 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘l… | 1.0 | 0.737653 |
5 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘l… | 1.0 | 0.742193 |
6 | {‘clf__max_depth’: 65, ‘clf__max_features’: No… | 1.0 | 0.733404 |
7 | {‘clf__max_depth’: 65, ‘clf__max_features’: No… | 1.0 | 0.733003 |
8 | {‘clf__max_depth’: 65, ‘clf__max_features’: No… | 1.0 | 0.736687 |
9 | {‘clf__max_depth’: 65, ‘clf__max_features’: 1…. | 1.0 | 0.733404 |
10 | {‘clf__max_depth’: 65, ‘clf__max_features’: 1…. | 1.0 | 0.733003 |
11 | {‘clf__max_depth’: 65, ‘clf__max_features’: 1…. | 1.0 | 0.736687 |
12 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘s… | 1.0 | 0.737445 |
13 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘s… | 1.0 | 0.737653 |
14 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘s… | 1.0 | 0.742193 |
15 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.737445 |
16 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.737653 |
17 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.742193 |
18 | {‘clf__max_depth’: 68, ‘clf__max_features’: No… | 1.0 | 0.733404 |
19 | {‘clf__max_depth’: 68, ‘clf__max_features’: No… | 1.0 | 0.733003 |
20 | {‘clf__max_depth’: 68, ‘clf__max_features’: No… | 1.0 | 0.736687 |
21 | {‘clf__max_depth’: 68, ‘clf__max_features’: 1…. | 1.0 | 0.733404 |
22 | {‘clf__max_depth’: 68, ‘clf__max_features’: 1…. | 1.0 | 0.733003 |
23 | {‘clf__max_depth’: 68, ‘clf__max_features’: 1…. | 1.0 | 0.736687 |
24 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘s… | 1.0 | 0.737445 |
25 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘s… | 1.0 | 0.737653 |
26 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘s… | 1.0 | 0.742193 |
27 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘l… | 1.0 | 0.737445 |
28 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘l… | 1.0 | 0.737653 |
29 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘l… | 1.0 | 0.742193 |
30 | {‘clf__max_depth’: 71, ‘clf__max_features’: No… | 1.0 | 0.733404 |
31 | {‘clf__max_depth’: 71, ‘clf__max_features’: No… | 1.0 | 0.733003 |
32 | {‘clf__max_depth’: 71, ‘clf__max_features’: No… | 1.0 | 0.736687 |
33 | {‘clf__max_depth’: 71, ‘clf__max_features’: 1…. | 1.0 | 0.733404 |
34 | {‘clf__max_depth’: 71, ‘clf__max_features’: 1…. | 1.0 | 0.733003 |
35 | {‘clf__max_depth’: 71, ‘clf__max_features’: 1…. | 1.0 | 0.736687 |
36 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘s… | 1.0 | 0.746659 |
37 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘s… | 1.0 | 0.746659 |
38 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘l… | 1.0 | 0.746659 |
39 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘l… | 1.0 | 0.746659 |
40 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.746659 |
41 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.746659 |
42 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘s… | 1.0 | 0.746778 |
43 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘s… | 1.0 | 0.746778 |
44 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘l… | 1.0 | 0.746778 |
45 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘l… | 1.0 | 0.746778 |
46 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.746778 |
47 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.746778 |
48 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘l… | 1.0 | 0.770919 |
49 | {‘clf__max_depth’: 71, ‘clf__max_features’: ‘l… | 1.0 | 0.770919 |
50 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.770919 |
51 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.770919 |
52 | {‘clf__max_depth’: 65, ‘clf__max_features’: ‘s… | 1.0 | 0.811057 |
53 | {‘clf__max_depth’: 68, ‘clf__max_features’: ‘l… | 1.0 | 0.811057 |
------------------------------
Обучение модели CatBoostClassifier¶
# CatBoostClassifier (depth)
# Модель
model = CatBoostClassifier(
#class_weights=class_weights,
logging_level='Silent',
random_state=STATE
)
# Преобразование признаков
column_trans = ColumnTransformer([
('ohe', OrdinalEncoder(), features_categories), # кодирование категорий через OrdinalEncoder (для деревьев и бустингов)
('std', MaxAbsScaler(), features_numeric), # Стандартизация числовых данных от 0 до 1
], remainder='drop', verbose_feature_names_out=False)
# Pipeline
pipeline = Pipeline([
('transform', column_trans), # трансформация выборки
('clf', model) # модель классификации
])
# Диапазон значений искомого параметра
# depth
n_depth = [1, 10, 15]
# n_estimators
n_n_estimators = [500, 1000, 1500]
# learning_rate
n_learning_rate = [.001, .5, .999]
# Обход диапазона значений
while (
(n_depth[1] - n_depth[0] > 1)
& (n_n_estimators[1] - n_n_estimators[0] > 10)
& (n_learning_rate[1] - n_learning_rate[0] > .01)
):
# depth
n_depth = two_range(n_depth[0], n_depth[-1], True)
# n_estimators
n_n_estimators = two_range(n_n_estimators[0], n_n_estimators[-1], True)
# learning_rate
n_learning_rate = two_range(n_learning_rate[0], n_learning_rate[-1], False)
# Параметры модели
params = [{
'clf__depth': n_depth,
'clf__n_estimators': n_n_estimators,
'clf__learning_rate': n_learning_rate
}]
# Обучение модели
data_grids, data_times = model_pipeline_gridsearch(
features_train,
target_train,
pipeline,
params,
data_grids,
data_times
)
# Вывод результата обучения модели
print_model_result(data_grids, data_times, 'CatBoostClassifier', process_print=True)
# Корректировка диапазона значений искомого параметра
# Лучшее значение искомого параметра
this_best_params = data_grids[-1].best_estimator_[-1].get_params()['depth']
# depth
if len(n_depth) > 1:
if n_depth[0] == this_best_params:
n_depth = [n_depth[0], int(n_depth[0] + (n_depth[1] - n_depth[0])/2), n_depth[1]]
if n_depth[2] == this_best_params:
n_depth = [n_depth[1], int(n_depth[1] + (n_depth[2] - n_depth[1])/2), n_depth[2]]
else:
n_depth = n_depth
else:
n_depth = n_depth
# Лучшее значение искомого параметра
this_best_params = data_grids[-1].best_estimator_[-1].get_params()['n_estimators']
# n_estimators
if len(n_n_estimators) > 1:
if n_n_estimators[0] == this_best_params:
n_n_estimators = [n_n_estimators[0], int(n_n_estimators[0] + (n_n_estimators[1] - n_n_estimators[0])/2), n_n_estimators[1]]
if n_n_estimators[2] == this_best_params:
n_n_estimators = [n_n_estimators[1], int(n_n_estimators[1] + (n_n_estimators[2] - n_n_estimators[1])/2), n_n_estimators[2]]
else:
n_n_estimators = n_n_estimators
else:
n_n_estimators = n_n_estimators
# Лучшее значение искомого параметра
this_best_params = data_grids[-1].best_estimator_[-1].get_params()['learning_rate']
# learning_rate
if len(n_learning_rate) > 1:
if n_learning_rate[0] == this_best_params:
n_learning_rate = [n_learning_rate[0], int(n_learning_rate[0] + (n_learning_rate[1] - n_learning_rate[0])/2), n_learning_rate[1]]
if n_n_estimators[2] == this_best_params:
n_learning_rate = [n_learning_rate[1], int(n_learning_rate[1] + (n_learning_rate[2] - n_learning_rate[1])/2), n_learning_rate[2]]
# График финальной версии модели этой ячейки
print_model_graphicresult(data_grids)
Модель : CatBoostClassifier Метрика ROC AUC: 0.9186625677422635 Время : 413.962012052536 секунд Параметры : {'logging_level': 'Silent', 'random_state': 30112023, 'depth': 4, 'learning_rate': 0.2505, 'n_estimators': 1000} Результаты выбора параметров модели:
params | mean_train_score | mean_test_score | |
---|---|---|---|
0 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.740765 |
1 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.741636 |
2 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.741795 |
3 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.711752 |
4 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.710653 |
5 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.711365 |
6 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.753797 |
7 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.753956 |
8 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.753956 |
9 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.722648 |
10 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.721382 |
11 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.721382 |
12 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.754924 |
13 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.754924 |
14 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.753182 |
15 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.722444 |
16 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.723315 |
17 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.722444 |
18 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.250… | 1.000000 | 0.670383 |
19 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.250… | 1.000000 | 0.668795 |
20 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.250… | 1.000000 | 0.667375 |
21 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.5, … | 1.000000 | 0.719171 |
22 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.5, … | 1.000000 | 0.719883 |
23 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.5, … | 1.000000 | 0.719883 |
24 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.749… | 1.000000 | 0.703656 |
25 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.749… | 1.000000 | 0.701914 |
26 | {‘clf__depth’: 11, ‘clf__learning_rate’: 0.749… | 1.000000 | 0.703339 |
27 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.763349 |
28 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.760586 |
29 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.760135 |
30 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.738038 |
31 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.740314 |
32 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.740359 |
33 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.7495… | 1.000000 | 0.739483 |
34 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.738732 |
35 | {‘clf__depth’: 8, ‘clf__learning_rate’: 0.5, ‘… | 1.000000 | 0.738223 |
36 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.835423 |
37 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.837120 |
38 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 1.000000 | 0.836155 |
39 | {‘clf__depth’: 4, ‘clf__learning_rate’: 0.2505… | 0.999982 | 0.918663 |
------------------------------
Выбор лучшей модели¶
Выбор лучшей модели и проверка её качества на тестовой выборке.
# Лучшая модель из расчета ROC AUC
data_grids_best = data_grids[0]
data_times_best = data_times[0]
n = 0
for i in range(0, len(data_grids)):
if data_grids[i].best_score_ > data_grids_best.best_score_:
#if (data_grids[i].best_score_ > data_grids_best.best_score_) & (data_times[i] < data_times_best):
data_grids_best = data_grids[i]
data_times_best = data_times[i]
print('Лучшее время :', data_times_best)
print('Лучший показатель ROC AUC:', data_grids_best.best_score_)
print('Лучшая модель :')
print(data_grids_best)
print('Лучшие параметры модели :')
data_grids_best.get_params()
Лучшее время : 413.962012052536 Лучший показатель ROC AUC: 0.9186625677422635 Лучшая модель : HalvingGridSearchCV(cv=4, error_score='raise', estimator=Pipeline(steps=[('transform', ColumnTransformer(transformers=[('ohe', OrdinalEncoder(), ['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'online_backup', 'streaming_movies', 'multiple_lines']), ('std', MaxAbsScaler(), ['monthly_charges', 'duration_contract'])], verbose_feature_names_out=False)), ('clf', <catboost.core.CatBoostClassifier object at 0x7feed7f865b0>)]), param_grid=[{'clf__depth': [4, 8, 11], 'clf__learning_rate': [0.2505, 0.5, 0.7495], 'clf__n_estimators': [750, 1000, 1250]}], random_state=30112023, scoring='roc_auc') Лучшие параметры модели :
{'aggressive_elimination': False, 'cv': 4, 'error_score': 'raise', 'estimator__memory': None, 'estimator__steps': [('transform', ColumnTransformer(transformers=[('ohe', OrdinalEncoder(), ['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'online_backup', 'streaming_movies', 'multiple_lines']), ('std', MaxAbsScaler(), ['monthly_charges', 'duration_contract'])], verbose_feature_names_out=False)), ('clf', <catboost.core.CatBoostClassifier at 0x7feed7f865b0>)], 'estimator__verbose': False, 'estimator__transform': ColumnTransformer(transformers=[('ohe', OrdinalEncoder(), ['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'online_backup', 'streaming_movies', 'multiple_lines']), ('std', MaxAbsScaler(), ['monthly_charges', 'duration_contract'])], verbose_feature_names_out=False), 'estimator__clf': <catboost.core.CatBoostClassifier at 0x7feed7f865b0>, 'estimator__transform__n_jobs': None, 'estimator__transform__remainder': 'drop', 'estimator__transform__sparse_threshold': 0.3, 'estimator__transform__transformer_weights': None, 'estimator__transform__transformers': [('ohe', OrdinalEncoder(), ['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'online_backup', 'streaming_movies', 'multiple_lines']), ('std', MaxAbsScaler(), ['monthly_charges', 'duration_contract'])], 'estimator__transform__verbose': False, 'estimator__transform__verbose_feature_names_out': False, 'estimator__transform__ohe': OrdinalEncoder(), 'estimator__transform__std': MaxAbsScaler(), 'estimator__transform__ohe__categories': 'auto', 'estimator__transform__ohe__dtype': numpy.float64, 'estimator__transform__ohe__encoded_missing_value': nan, 'estimator__transform__ohe__handle_unknown': 'error', 'estimator__transform__ohe__max_categories': None, 'estimator__transform__ohe__min_frequency': None, 'estimator__transform__ohe__unknown_value': None, 'estimator__transform__std__copy': True, 'estimator__clf__logging_level': 'Silent', 'estimator__clf__random_state': 30112023, 'estimator': Pipeline(steps=[('transform', ColumnTransformer(transformers=[('ohe', OrdinalEncoder(), ['senior_citizen', 'partner', 'dependents', 'type', 'paperless_billing', 'payment_method', 'online_backup', 'streaming_movies', 'multiple_lines']), ('std', MaxAbsScaler(), ['monthly_charges', 'duration_contract'])], verbose_feature_names_out=False)), ('clf', <catboost.core.CatBoostClassifier object at 0x7feed7f865b0>)]), 'factor': 3, 'max_resources': 'auto', 'min_resources': 'exhaust', 'n_jobs': None, 'param_grid': [{'clf__depth': [4, 8, 11], 'clf__n_estimators': [750, 1000, 1250], 'clf__learning_rate': [0.2505, 0.5, 0.7495]}], 'random_state': 30112023, 'refit': True, 'resource': 'n_samples', 'return_train_score': True, 'scoring': 'roc_auc', 'verbose': 0}
Выводы из выбора обученной модели
В резуьтате выбора модели на основе метрики ROC AUC лучшие результат у модели CatBoostClassifier
с гиперпараметрами {'logging_level': 'Silent', 'random_state': 30112023, 'depth': 4, 'learning_rate': 0.2505, 'n_estimators': 1000}
показатель ROC AUC равен 0.9186625677422635. Время обучения более 24 секунд.
Тестирование лучшей модели¶
start_time = time.time()
# Предсказание лучшей модели
predict = data_grids_best.predict(features_test)
predict_proba = data_grids_best.predict_proba(features_test)
finish_time = time.time()
funtion_time = finish_time - start_time
Анализ протестированной модели¶
# Расчет "ROC AUC" и времени выполнения предсказания
roc_auc = roc_auc_score(target_test, predict_proba[:, 1])
print('Показатель ROC AUC:', roc_auc)
print(f'Время предсказания: {funtion_time} секунд')
Показатель ROC AUC: 0.9261042456870182 Время предсказания: 0.026419639587402344 секунд
Цель данного проекта достигнута. В результате тестирования выбранной модели машинного обучения с подобранными гиперпараметрами получена метрика ROC AUC выше требуемого уровня данного параметра в 0.85 более чем на 0.05.
# ROC кривая
fpr, tpr, treshold = roc_curve(target_test, predict_proba[:, 1])
roc_auc = auc(fpr, tpr)
# Построение графика
plt.plot(
fpr,
tpr, color='darkorange',
label='ROC кривая (площадь под кривой = %0.4f)' % roc_auc
)
plt.plot([0, 1], [0, 1], color='navy', linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC кривая')
plt.legend(loc="lower right")
plt.show()
ROC кривая сильно выпуклая вверх. Под ней большая часть всего прямоугольника, в который она вписана. Это означает, что модель в большинстве случаев правильно предсказывает целевые значения.
# Метрика "Accuracy"
print('Показатель Accuracy:', accuracy_score(target_test, predict))
Показатель Accuracy: 0.9381033503691084
Почти в 94% случаев выбранная модель правильно предсказывает класс объекта. Это хороший результат, но здесь важно учесть дисбаланс классов, который может привести к лучшей предстказуемости большего класса. В случае данного проекта это класс 0
(договор не расторгнут). Точный расклад по количеству предсказанных объектов разного класса видно на матрице ошибок.
# Матрица ошибок
cm = confusion_matrix(target_test, predict)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=["Negative", "Positive"])
disp.plot()
plt.show()
Анализ матрицы ошибок показывает, что протестированная модель правильно предсказывает большую часть класса 0
(на графике горизонталь «Negative», договор не расторгнут). Ошибки в этом случае составляют около 2%. Значительно хуже обстоят дела с предсказанием класса 1
(на графике горизонталь «Positive», договор расторгнут). Здесь правильно предсказано только 2/3 всех значений. Вывод. Модель эффективно предскажет пользователя, который не собирается расторгать договор, но только в 2 из 3 случаях правильно предскажет будет ли пользователь расторгать договор.
# Анализ важности признаков
pd.DataFrame(
data_grids_best.best_estimator_[-1].feature_importances_,
index=data_grids_best.best_estimator_[:-1].get_feature_names_out(),
columns=['Важность признака']
).sort_values(by='Важность признака', ascending=False)
Важность признака | |
---|---|
duration_contract | 53.036833 |
monthly_charges | 19.611513 |
type | 6.667458 |
payment_method | 4.441345 |
multiple_lines | 3.692476 |
partner | 3.311501 |
online_backup | 2.543628 |
dependents | 1.987133 |
streaming_movies | 1.930383 |
paperless_billing | 1.561446 |
senior_citizen | 1.216284 |
Анализ важности признаков выявил бесспорного лидера — duration_contract
(продолжительность договора в днях). Следом за ним с большим отрывом располагается признак monthly_charges
. Не смотря на разрыв с лидером по важности, этот признак тоже кратно превосходит третью позицию в этом рейтинге — type
. Изменение этих признаков приведет к значительному изменению предсказаний. Замыкает этот рейтинг признак senior_citizen
. Изменение этого признака в наименьшей степени повлияет на предсказание.
Общий вывод и рекомендации заказчику¶
Общий вывод о проделанной работе: описание основных этапов работы, полученных результатов и рекомендации для бизнеса.
Общий вывод
Цель данного проекта достигнута. Для предсказания оттока клиентов компании «ТелеДом» в продакшене рекоммендуется использовать модель машинного обучения CatBoostClassifier
с гиперпараметрами {'logging_level': 'Silent', 'random_state': 30112023, 'depth': 4, 'learning_rate': 0.2505, 'n_estimators': 1000}
. При тестировании данная модель имеет значение показателя ROC AUC равное 0.9261042456870182, что выше требуемого уровня в 0.85.
Рекоммендации заказчику
Модель CatBoostClassifier
с подобранными гиперпараметрами дает значение показателя ROC AUC выше требуемого. При возникновении потребности доведения этого показателя выше полученного здесь значения, предлагается рассмотреть возможность в дальнешем использовать модели нейронных сетей. Например, Keras
(TensorFlow
), которая на тех же данных может показать еще более высокий результат.
Также, возможно, следует изменить подход к требованиям данного проекта. Так, после расчета «технической» метрики, коей является ROC AUC, можно расчитать бизнес-метрику. Например, финансовые потери от ухода клиента. Тогда, предсказанные только 2 из 3 ушедших клиентов могут оказаться существенной потерей и потребуется дообучить модель так, чтобы она чаще правильно предсказывала уход клиентов.
Прошу обратить внимание на исходные данные для данного проекта. Типы данных не оптимальны. Возможно, требуется оптимизация системы хранения данных. Так, например, параметр TotalCharges
из файла contract_new.csv
имеет тип object
при том, что он содержит числовые значения с плавающей точкой. Еще более существенным является хранение категориальных бинарных признаков, таких как PaperlessBilling
из файла contract_new.csv
, с типом object
содержащих текстовые значения. Изменение типа подобных переменных на bool
или даже int8
с бинарными значениями сделает таблицы легче без потери информативности. Также, прошу обратить внимание на пропуски в данных о клиентах. Это не очевидно при использовании разрозненных таблиц, но при их объединении проявляются объекты, содержащие пропуски по некоторым параметрам. Т.е., отсутствует информация по некоторым продуктам, которые используют клиенты.
Важно также обратить внимание на то, что большая часть признаков всех исходных датафреймов не пригодны для использования в моделях машинного обучения из-за их мультиколлинеарности и не достаточной парной корреляции с целевым признаком. Так, в результате исследовательского анализа для дальнейшего использования были выбраны:
- Целевой признак
active_contract
. Он указывает на наличие или отсутствие действующего договора с клиентом. Возможно, в следствие его значимости, требуется хранить в используемой системе хранения в бинарном виде. - Категориальные признаки:
multiple_lines
,paperless_billing
,payment_method
,tech_support
,internet_service
. - Числовые признаки:
total_charges
,duration_contract
.
Выводы о проделанной работе
В проекте использованы наиболее распространенные типы моделей машинного обучения LogisticRegression
, RandomForestRegressor
и CatBoostClassifier
. Однако, минимальный порог по метрике ROC AUC преодолела только модель CatBoostClassifier
. В перспективе ее промышленной эксплуатации на схожих данных ей будет помогать задействованные в проекте кросс-валидация и оптимизация показателей с использованием Pipeline
и HalvingGridSearchCV
.
Для достижения поставленной цели проекта было выполнено:
- Подготовлена тетрадь Jupyter Notebook. Обновлены существующие и установлены новые библиотеки. В тетрадь загружены требуемые в проекте библиотеки. Оптимизировано отображение контента в тетради. Добавлены глобальные переменные.
- Загружены и проверены данные из файлов.
- Проведены исследовательский анализ и предобработка данных исходных датафреймов
contract_new
(информация о договоре),personal_new
(персональные данные клиента),internet_new
(информация об интернет-услугах) иphone_new
(информация об услугах телефонии). - Данные исходных датафреймов объеденены в единый датафрейм. Из него предварительно выделены тренировочные выборки для анализа.
- Проведены исследовательский анализ и предобработка данных предварительно выделенной треннировочной выборки из объединённого датафрейма. Были проведены анализ мультиколлинеарности и парной корреляции. Отобраны признаки для использования в обучении моделей.
- Подготовлены данные к использованию в обучении моделей.
- Обучены модели машинного обучения
LogisticRegression
,RandomForestRegressor
иCatBoostClassifier
. - На основе метрики ROC AUC выбрана и протестирована лучшая модель.
- Описан общий вывод проекта и даны рекомендации заказчику.