Предобработка данных

Импорт необходимых библиотек, которых пока будет достаточно для первичного анализа

import pandas as pd
import numpy as np

Импортируем данные из файла в переменную и просматриваем содержимое

data_frame = pd.read_csv('bank.csv')
data_frame
data_frame.info()

Начинаю с самого простого - бинарные признаки default, housing, loan.

data_frame['default'] = np.where(data_frame['default'] == 'yes', 1, 0)
data_frame['housing'] = np.where(data_frame['housing'] == 'yes', 1, 0)
data_frame['loan'] = np.where(data_frame['loan'] == 'yes', 1, 0)

Надобность столбцов DayMounth и Contact под сомнением, так как по логике я считаю, что они не влияют на конечный результат.

Дело в том, что то, как эти данные представлены в наборе в конечном итоге не совсем будут понятны алгоритмам. Я считаю, что вместо дня и месяца с последнего контакта корректнее было бы рассматривать количество прошедших дней с последнего контакта. Но так как в наборе нет опорной точки для расчета срока, так как неизвестна дата, до какой считать. Так что я их удаляю.

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

data_frame.drop(['day', 'month', 'contact'], axis = 1, inplace = True)

Для удобства в дальнейшей работе разделю исходный набор данных - на числовой и объектовый

object_df = data_frame.select_dtypes(include=['object']).copy()
value_df = data_frame.select_dtypes(include=[int]).copy()
value_df
object_df

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

object_df['job'].value_counts()

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

target = value_df['y']
value_df.drop('y', axis = 1, inplace = True)

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

object_df_encoded = pd.get_dummies(object_df)
object_df_encoded

Выделить наиболее влияющие признаки на 'подпишется ли клиент на срочный депозит' и в дальнейшем использовать только их.

from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectFromModel
randomforest = RandomForestClassifier(random_state = 0, n_jobs = -1)
selector = SelectFromModel(randomforest, threshold = 0.1)
val_important = selector.fit_transform(value_df, target)
val_important

Так, уже какие-то зависимости найдены, а что если попробовать обработать числовые данные? Например прошкалировать или нормализовать?

from sklearn.preprocessing import StandardScaler, Normalizer

scaler = StandardScaler()
value_df_scaled = scaler.fit_transform(value_df) 

normalizer = Normalizer()
value_df_normalized = normalizer.fit_transform(value_df)

val_important_sc = selector.fit_transform(value_df_scaled, target)
val_important_sc

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

val_important_nr = selector.fit_transform(value_df_normalized, target)
val_important_nr

Признаков стало больше. Но хорошо ли это? Проверю в конечном анализе.
С помощью модели случайного леса можно так же посмотреть на коэффициенты важности признаков и на их основе построить диаграмму.

import matplotlib.pyplot as plt
rf_model = randomforest.fit(value_df, target)
names = value_df.columns.values
ticks = [i for i in range(len(rf_model.feature_importances_))]

plt.figure()
plt.title('Важность признаков')
plt.bar(names, rf_model.feature_importances_)
plt.xticks(ticks, names, rotation = 90)
plt.show()

Ну вот, и на диаграмме сразу стало видно, какие столбцы выводит SelectFromModel с порогом 0.1. А что покажет нормализация?

rf_model = randomforest.fit(value_df_normalized, target)
names = value_df.columns.values
ticks = [i for i in range(len(rf_model.feature_importances_))]

plt.figure()
plt.title('Важность признаков')
plt.bar(names, rf_model.feature_importances_)
plt.xticks(ticks, names, rotation = 90)
plt.show()

Как-то не однозначно, не стоит сразу этому доверять, проверим это на обучении.

Так же не забываем найти и значимые признаки среди закодированных категорий.

obj_important = selector.fit_transform(object_df_encoded, target)
obj_important

Не густо, надо вывести диаграмму.

rf_model = randomforest.fit(object_df_encoded, target)
names = object_df_encoded.columns.values
ticks = [i for i in range(len(rf_model.feature_importances_))]

plt.figure()
plt.title('Важность признаков')
plt.bar(names, rf_model.feature_importances_)
plt.xticks(ticks, names, rotation = 90)
plt.show()
Важность признаков
Важность признаков

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

Необходимо разделить данные на обучающую и тестовую выборки в соотношении 80%, 20%.

from sklearn.model_selection import train_test_split

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

features = np.array(pd.DataFrame(val_important).join(pd.DataFrame(obj_important), rsuffix='_obj'))
features_train, features_test, target_train, target_test = train_test_split(features, target, test_size = 0.20, random_state = 0)

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

# импортируем модели, с помощью которых будеи проводить анализ
from sklearn.neighbors import KNeighborsClassifier 
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.naive_bayes import GaussianNB 
# этот медот понадобится для оценки точности обченной модели
from sklearn.metrics import accuracy_score 

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

kn_model = KNeighborsClassifier(n_neighbors = 20)
rfc_model = RandomForestClassifier(random_state = 0)
ada_model = AdaBoostClassifier(random_state = 0)
gnb_model = GaussianNB()

Обучаем все выбранные модельки на тренировочных выборках.

kn_model.fit(features_train, target_train)
rfc_model.fit(features_train, target_train)
ada_model.fit(features_train, target_train)
gnb_model.fit(features_train, target_train)

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

kn_predict = kn_model.predict(features_test)
rfc_predict = rfc_model.predict(features_test)
ada_predict = ada_model.predict(features_test)
gnb_predict = gnb_model.predict(features_test)

И на основе этих предсказаний проверяем точность моделей по ответам из тестовой выборки.

print('KNeighbors', accuracy_score(kn_predict, target_test))
print('RandomForest', accuracy_score(rfc_predict, target_test))
print('AdaBoost', accuracy_score(ada_predict, target_test))
print('GaussianNB', accuracy_score(gnb_predict, target_test))

Теперь для наглядности построим диаграмму точности каждой из моделей(хоть и значения отличаются на сотые).

names = ['KNeighbors', 'RandomForest', 'AdaBoost', 'GaussianNB']
ticks = [i for i in range(len(names))]
values = [accuracy_score(kn_predict, target_test), accuracy_score(rfc_predict, target_test),
         accuracy_score(ada_predict, target_test), accuracy_score(gnb_predict, target_test)]

plt.figure()
plt.title('Важность признаков')
plt.barh(names, values)
plt.yticks(ticks, names, rotation = 0)
plt.show()

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

Но точность все равно не дотягивает до идеальной, что можно сделать? Неужели придется перебирать еще кучу моделей? Так же не стоит забывать и про сам набор данных. Стоит вернуться к предобработке исходного набора и поэкспериментировать с данными. Вероятно может быть даже так, что удаленный столбец улучшит результат, а тот, который остался ухудшает и его наоборот стоит удалить.

Комментарии (1)


  1. MockBeard
    14.12.2021 13:02
    +1

    Когда-нибудь, из-за таких КПВД, как ваша, я перестану заходить на хабр. Помогите мне сэкономить время!!!