Описание проекта
Оператор связи «ТелеДом» хочет бороться с оттоком клиентов. Для этого его сотрудники начнут предлагать промокоды и специальные условия всем, кто планирует отказаться от услуг связи. Чтобы заранее находить таких пользователей, «ТелеДому» нужна модель, которая будет предсказывать, разорвёт ли абонент договор. Команда оператора собрала персональные данные о некоторых клиентах, информацию об их тарифах и услугах. Ваша задача — обучить на этих данных модель для прогноза оттока клиентов.
Описание услуг
Оператор предоставляет два основных типа услуг:
- Стационарную телефонную связь. Телефон можно подключить к нескольким линиям одновременно.
- Интернет. Подключение может быть двух типов: через телефонную линию (
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 |