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

Оглавление:

  • Введение проблемы
  • Исследовательский анализ данных
  • Разработка функций
  • Модели машинного обучения (SVM, GaussianNB, дерево решений, XGBoost)
  • Сводка моделей машинного обучения

Введение в проблему

Как фирма или любая компания, предоставляющая услуги, мы получаем многочисленные жалобы от клиентов. Получение жалоб — это не что иное, как еще один способ улучшить наши услуги по отношению к клиенту. При работе с клиентами крайне важно работать над их удовлетворением, и это невозможно до тех пор, пока мы не отреагируем на жалобы наших клиентов искренне и эффективно. Если мы не сможем решить или даже рассмотреть жалобы наших клиентов, они будут разочарованы нашими услугами и потеряют доверие с нашей стороны, и, следовательно, мы можем в конечном итоге потерять их. Таким образом, рассмотрение жалоб очень важно.
В этом проекте исследуется небольшой набор данных о фирме, где жалобы клиентов, наряду с другими атрибутами, тщательно анализируются, а ML-моделирование выполняется для прогнозирования того, какие типы жалоб решаются чаще всего. фирма.

Исследовательский анализ данных

Давайте совершим экскурсию по набору данных:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import nltk
import string
df = pd.read_csv('complaint.xls')
df.head()

  • Заявка №: уникальный идентификатор, создаваемый для каждой жалобы.
  • Жалоба клиента: письменная жалоба клиента.
  • Date : дата подачи жалобы
  • Date_month_year : дата подачи жалобы в другом формате.
  • Время: время подачи жалобы.
  • Получено через: способ подачи жалобы через Интернет или службу поддержки.
  • Город: город, в котором клиент столкнулся с проблемой.
  • Штат: штат, в котором клиент столкнулся с проблемой.
  • Почтовый индекс. Почтовый индекс области, в которой клиент столкнулся с проблемой.
  • Статус: Статус жалобы со стороны фирмы
  • Подача от чьего-либо имени: подал ли клиент жалобу от своего имени или от имени другого лица.

Примечание. Форма нашего набора данных (2077, 11). Таким образом, это действительно небольшой набор данных.

Давайте сначала посмотрим на «процентное распределение статуса жалобы»:

  • Закрытые: 33,17%
  • Открытый: 16,27%
  • Решено: 43,52%
  • В ожидании: 7,17%

Большинство жалоб (43,52% от общего числа жалоб) решены, а 33,17% закрыты.

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

'Состояние'

univariate_barplots(df, 'State', 'Status', False)

На приведенном выше графике показано распределение общего количества жалоб по штатам и процент разрешенных жалоб.

  • Грузия получила максимальное количество жалоб, которое составляет 264, за ней следуют Флорида (226), Калифорния (210), Иллинойс (147), Теннесси (137) и т. д.
  • Есть довольно много штатов с самой низкой жалобой как 1.
fig, ax = plt.subplots(figsize = (20,15))
df.groupby(['State','Status']).count()['Ticket #'].unstack().plot.bar(ax=ax)

На этом графике показано распределение статуса жалоб в каждом штате.

  • Мы видим, что у большинства штатов есть жалобы со всеми возможными четырьмя статусами.
  • Но есть несколько штатов, таких как округ Колумбия, Айова, Канзас, Миссури, Невада и т. д., где жалобы не распределяются между всеми четырьмя статусами.
  • Высота красных полос говорит о том, что в Джорджии больше всего решенных жалоб, за ней следуют Флорида и Калифорния.

'Город'

univariate_barplots(df, 'City', 'Status', False)

На приведенном выше графике показано распределение общего количества жалоб по городам и процент разрешенных жалоб.

  • Хотя сюжет недостаточно ясен, чтобы различать города, напечатанные данные содержат некоторую информацию.
  • Атланта получила максимальное количество жалоб — 57, за ней следуют Чикаго (41), Ноксвилл (35), Хьюстон (32), Джексонвилл (29) и т. д.
  • Есть довольно много городов с самой низкой жалобой, равной 1.
fig, ax = plt.subplots(figsize = (20,15))
df.groupby(['City','Status']).count()['Ticket #'].unstack().plot.bar(ax=ax)

Этот график распределения статуса жалоб по каждому городу совершенно не помогает из-за слишком большого трафика данных.

«Подача от имени кого-либо»

univariate_barplots(df, 'Filing on Behalf of Someone', 'Status', False)

На приведенном выше графике показано распределение общего числа жалоб и процента разрешенных жалоб на основе того факта, была ли жалоба подана от чьего-либо имени или нет.

  • 90,7% жалоб (1884 из 2077 жалоб) были поданы от своего имени, а не от чьего-либо имени.
  • Вероятность решения жалобы, если она подана от имени другого лица, ниже почти на 3%.
  • Таким образом, можно сказать, что вероятность решения жалобы будет больше, если вы подадите жалобу самостоятельно.
fig, ax = plt.subplots(figsize = (15,10))
df.groupby(['Filing on Behalf of Someone','Status']).count()['Ticket #'].unstack().plot.bar(ax=ax)

Этот график распределения статуса жалобы на основании того, была ли она подана от чьего-то имени, мало помогает.

«Получено через»

univariate_barplots(df, 'Received Via', 'Status', False)

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

  • Через оба канала подается почти одинаковое количество жалоб.
  • Вероятность того, что жалоба будет разрешена, если она будет подана через службу поддержки клиентов, ниже почти на 2,5%.
  • Таким образом, можно сказать, что вероятность решения жалобы будет больше, если вы подадите жалобу через Интернет, а не позвоните в службу поддержки.
fig, ax = plt.subplots(figsize = (15,10))
df.groupby(['Received Via','Status']).count()['Ticket #'].unstack().plot.bar(ax=ax)

Анализ даты и времени

Мы создадим три новых функции для «День», «Месяц» и «Год», используя столбец «Дата».

df['Day'] = pd.to_datetime(df['Date']).dt.day
df['Month'] = pd.to_datetime(df['Date']).dt.month
df['Year'] = pd.to_datetime(df['Date']).dt.year
df.head()

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

'День'

univariate_barplots(df, 'Day', 'Status', False)

На приведенном выше графике показано распределение общего количества жалоб и процента разрешенных жалоб в зависимости от «Дня» подачи жалобы.

  • Очень своеобразное наблюдение, что на 24, 23, 25 день очень много жалоб.
  • На 31 число жалоб очень мало, всего 7.
  • Остальные дни имеют хорошо распределенные жалобы.
fig, ax = plt.subplots(figsize = (15,10))
df.groupby(['Day','Status']).count()['Ticket #'].unstack().plot.bar(ax=ax)

В начальные дни (1,2,3..) месяца вероятность закрытия жалобы выше, но наблюдается внезапный необычный пик разрешенных жалоб 23, 24, 25 числа. Таким образом, если жалоба подана 23, 24, 25 числа, вероятность решения намного выше.

'Месяц'

univariate_barplots(df, 'Month', 'Status', False)

На приведенном выше графике показано распределение общего количества жалоб и процента разрешенных жалоб в зависимости от месяца подачи жалобы.

  • Похоже, что самый загруженный месяц — июнь (6), так как большая часть жалоб подается в этом месяце.
  • Вероятность решения жалобы в апреле составляет 0,0098.
  • Так что подавать жалобу в апреле, конечно, плохая идея.
  • Жалобы, поданные в июне и мае, имеют хорошие вероятности, 0,58 и 0,54, для решения.
fig, ax = plt.subplots(figsize = (15,10))
df.groupby(['Month','Status']).count()['Ticket #'].unstack().plot.bar(ax=ax)

'Жалоба клиента'

Эта функция является текстовой функцией и, следовательно, будет анализировать ее на основе количества слов.

#https://stackoverflow.com/a/37483537/4084039
word_count = df['Customer Complaint'].str.split().apply(len).value_counts()
word_dict = dict(word_count)
word_dict = dict(sorted(word_dict.items(), key=lambda kv: kv[1]))
ind = np.arange(len(word_dict))
plt.figure(figsize=(20,5))
p1 = plt.bar(ind, list(word_dict.values()))
plt.ylabel('Numeber of complaints')
plt.title('Words for each complaint')
plt.xticks(ind, list(word_dict.keys()))
plt.show()

Приведенный выше график представляет собой количество слов для каждой жалобы в сравнении с количеством жалоб с этим количеством слов.

  • Большинство жалоб, почти 24% жалоб, содержат всего два слова.
  • На самом деле очень мало жалоб, которые очень описательные и длинные.

На графике выше показаны диаграммы разрешенных и нерешенных жалоб с учетом количества слов в каждой жалобе

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

Количество слов в PDF-файлах как решенных, так и нерешенных жалоб перекрывается. Так что никакой полезной информации.

Разработка функций

Мы выполним одно горячее кодирование для всех категориальных признаков («Получено через», «Город», «Штат», «Подача от чьего-либо имени») и векторизацию TfIDF для признака «Жалоба клиента».

Например, приведенные ниже коды предназначены для векторизации TfIDF «Жалобы клиентов».

vec = TfidfVectorizer().fit(X_train['Customer Complaint'].values)
X_train_cc = vec.transform(X_train['Customer Complaint'].values)
X_test_cc = vec.transform(X_test['Customer Complaint'].values)

Ниже приведены коды для кодировки «City» One Hot:

vec2 = CountVectorizer()
vec2.fit(X_train[‘City’].values)
X_train_city = vec2.transform(X_train[‘City’].values)
X_test_city = vec2.transform(X_test[‘City’].values)

Вы можете получить полный код в моем профиле Github:



ML-модели

Итак, наша цель здесь — предсказать, будет ли жалоба, поданная клиентом, решена или нет с помощью функций:

  • Векторизованные жалобы клиентов TfIDF (1235 признаков)
  • Один город с горячим кодированием (750 объектов)
  • Одно состояние горячего кодирования (42 функции)
  • Один горячий код «Received Via» (2 функции)
  • Одна горячая закодированная «Подача от чьего-либо имени» (2 функции)

Форма тренировочного набора: (1557, 2033)

Форма тренировочного набора: (520, 2033)

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

Мы обучим 4 модели машинного обучения GaussianNB, Decision Tree, SVM и GBDT (XGBoost) и сравним их производительность.

SVM

from sklearn.svm import SVC 
sv_model = SVC(kernel = 'linear', C = 1).fit(X_tr, y_train)
sv_predictions_tr = sv_model.predict(X_tr) 
sv_predictions_te = sv_model.predict(X_te)
cm_sv_tr = confusion_matrix(y_train, sv_predictions_tr)
cm_sv_te = confusion_matrix(y_test, sv_predictions_te)
print('Confusion Matrix Train Data')
print(cm_sv_tr)
print('='*50)
print('Confusion Matrix Test Data')
print(cm_sv_te )
print('='*50)
print('Train Accuracy: ',sv_model.score(X_tr,y_train))
print('Test Accuracy: ',sv_model.score(X_te,y_test))
print('='*50)
cm_te_df3 = pd.DataFrame(cm_sv_te)
plt.figure(figsize =(6,6))
sns.heatmap(cm_te_df3, annot=True)
plt.title('Test Confusion Matrix \nAccuracy: {}'.format(sv_model.score(X_te,y_test)))
plt.xlabel("Predicted")
plt.ylabel('True')
plt.show()
print()

GaussianNB

Древо решений

ГБДТ (XGBoost)

Сводка всех моделей машинного обучения

Итак, как вы можете видеть из приведенной выше таблицы, XGBoost показал лучшие результаты с тестом AUC 0,69, тогда как GaussianNB показал худшие результаты с тестом AUC 0,267. SVM имеет тенденцию к переоснащению, поскольку его оценка поезда намного выше, чем оценка теста. Дерево решений также показало хорошие результаты. Итак, производительность модели в порядке убывания:

XGBoost › Дерево решений › SVM ›› gaussianNB

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

Для получения всех кодов и блокнота Jupyter посетите:



Использованная литература: