Исследовательский анализ, набор слов, логистическая регрессия

Вступление

В этом проекте описывается модель классификации интеллектуального анализа текста с использованием набора слов и логистической регрессии. Мы попытаемся понять взаимосвязь между текстовыми отзывами Uber и оценками поездок. Это отличное место для начала, если вы относительно новичок в анализе неструктурированных данных, но имеете некоторый опыт работы со статистикой и / или другим опытом классификации.

Источник данных: Kaggle пользователя Purvank

Оглавление

  1. Предварительный анализ
  2. Форматирование / преобразование текста
  3. Логистическая регрессия
  4. Тестирование / Выводы

Предварительный анализ

Импортировать данные

Сначала давайте внесем данные и визуализируем фрейм данных:

#importing modules
import pandas as pd
import numpy as np
import seaborn as sns
%matplotlib inline
import matplotlib.pyplot as plt
import random
#pulling in data
df = pd.read_csv(r'C:\Users\Andrew\Desktop\Python Text Analysis\Uber_Ride_Reviews.csv')
df

Это простой набор данных. У нас есть текстовый столбец ride_review и столбец ride_rating (который варьируется от 1 как самый низкий рейтинг до 5 как самый высокий). Пользователи пишут эти текстовые обзоры, чтобы описать свой опыт, и категориальный рейтинг 5 звезд суммирует его.

Базовая статистика

Давайте проверим метод .describe (), чтобы увидеть, сколько экземпляров мы имеем дело, и другую базовую статистику:

df.describe()

Судя по всему, водители Uber не очень хорошо показали себя в этом примере. Среднее значение составляет 1,62 балла, а оценка 75% - это оценка в 1 звезду! Это говорит о непропорционально большом количестве отзывов с 1 звездой. Надеюсь, мы сможем понять это, построив модель.

Назначение модели

Упомянув об этом, давайте еще раз подчеркнем полезность набора слов / модели логистической регрессии, которую мы построим:

  • Мы сможем оценить настроения будущих текстовых обзоров и разделить их на классы «Хорошо» и «Плохо».
  • Мы сможем выделить конкретные слова, которые сильно повлияют на рейтинговые настроения. Эта информация может быть полезной для Uber (если используется для всего их внутреннего набора данных, а не для этой небольшой выборки, которая у нас есть). Например, слово «грубый», вероятно, имеет отрицательный коэффициент, что заставляет наш классификатор пометить этот отзыв как плохой. Слово «отличный», вероятно, имеет положительный коэффициент, что заставляет наш классификатор пометить этот отзыв как хороший. Наша цель - открытие. Мы не знаем, какие интересные слова могут повлиять на настроение, и это самое интересное. Слово «великий» перевешивает слово «ужасный» и является чистым положительным или отрицательным? На многие подобные вопросы ответит наша модель.

Проверка нуля

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

#checking for nulls
null_count = df.isnull().sum()
null_count

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

Распределения

Теперь давайте визуализируем распределение рейтингов наших данных:

#seperating by groups
groups = df.groupby('ride_rating').count()
Values = groups.ride_review
colors = ['r', 'g', 'b', 'c', 'm']
#making bar plot
plt.bar(([1,2,3,4,5]), Values, color= colors)
plt.title('Rating Distribution')
plt.xlabel('Rating')
plt.ylabel('Review Quantity')
plt.show()

Как мы обнаружили ранее, в этой выборке много обзоров с 1 звездой.

Чтобы выполнить логистическую регрессию позже, нам нужно найти способ превратить эти 5 категорий рейтингов в бинарные классы (1 и 0). Помните, что логистическая регрессия обрабатывает только целевые переменные «или / или». Лучший способ превратить звездные рейтинги в бинарные классы (на мой взгляд):

  • Установить рейтинги ниже 3 звезд как класс 0 (негативное мнение)
  • Установить рейтинг выше 3 как класс 1 (положительное мнение)
  • Удалите 3-звездочные рейтинги. 3 звезды нейтральны и не дают представления о настроениях.

Итак, давайте создадим новый столбец, который удаляет рейтинги 3 звезды и создает новый столбец, который классифицирует другие рейтинги по двоичным классам:

#deleting all instances with ride_rating = 3
df = df[df.ride_rating != 3]
#separating by groups
groups = df.groupby('ride_rating').count()
Values = groups.ride_review
colors = ['r', 'g', 'b', 'c']
#making bar plot
plt.bar(([1,2,4,5]), Values, color= colors)
plt.title('Rating Distribution')
plt.xlabel('Rating')
plt.ylabel('Review Quantity')
plt.show()

#creating new binary_class column
df['binary_class'] = np.where(df['ride_rating'] > 3, 1, 0)
df

Мы готовы продолжить! Давайте подготовим текст.

Форматирование / преобразование текста

Тренировка / тестовый сплит

Первым шагом в этом процессе является разделение наших данных на наборы для обучения и тестирования. Мы создадим нашу модель из данных обучения и сохраним несколько экземпляров для целей тестирования позже. Мы используем sklearn для перемешивания и разделения. Не вмешиваясь в параметры, он должен разделить наши данные на 75% обучения и 25% тестирования.

Вызвав X_train.shape, мы можем это проверить. Далее, давайте напечатаем случайный обзор, чтобы убедиться, что он работает, и напомнить себе, с чем мы работаем.

#splitting into train and test
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df['ride_review'], df['binary_class'], random_state = 0)
#setting random number between 1 and 1000
number = random.randint(1,1000)
#printing random training text and X_train shape
print ('Random Review:')
print(' ')
print(X_train[number])
print(' ')
print('X_train shape: ' + str(X_train.shape))

Как и ожидалось, 970/1294 = 75% нашей выборки.

Превращение слов в числа

Теперь наступает захватывающая часть - перевод наших текстовых данных в числовые функции. Чтобы запустить регрессию по этим данным позже, нам нужно иметь функцию для каждого слова в нашей выборке. По сути, мы будем переводить каждое слово для присвоения ему числа, а затем подсчитывать частоту использования этих слов / чисел для каждого экземпляра в матрицах. Этот процесс называется мешком слов. Важно отметить, что порядок слов не имеет значения, набор слов учитывает только частоту использования каждого экземпляра слов. Для начала мы будем использовать Sklearn CountVectorizer

#importing countvectorizer
from sklearn.feature_extraction.text import CountVectorizer
#creating variable which assigns X_train to numbers
vect = CountVectorizer().fit(X_train)
#translates numbers back to text
vect.get_feature_names()[1:10]

Мы видим, что с помощью метода len (vect.get_feature_names ()) во всех обзорах всего 6607 слов:

#length of total words
len(vect.get_feature_names())

Теперь давайте преобразуем наши данные X_train в матрицу, которая содержит документы (экземпляры) в виде строк и количество новых функций (6 607) в виде столбцов. Например, наше первое слово (0), как показано выше, - «брошенный». Это будет первый столбец в матрице. Какие бы обзоры Uber ни содержали слово "брошенные", подсчитают, сколько раз оно использовалось, и добавят его в этот столбец. Кстати, использование слова «брошенный», скорее всего, отрицательно повлияет на рейтинг. Позже мы сможем поэкспериментировать.

#creating matrix array for logistic regression
X_train_vectorized = vect.transform(X_train)
print (X_train_vectorized.toarray())

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

Логистическая регрессия

Модель здания

Наконец-то мы дошли до регресса. Если вам нужно напомнить о логистической регрессии, прежде чем мы продолжим, отметьте здесь.

Опять же, мы будем использовать sklearn для выполнения этой модели:

#creating log regression
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X_train_vectorized, y_train)

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

#calculating AUC
from sklearn.metrics import roc_auc_score
predictions = model.predict(vect.transform(X_test))
print('AUC: ', roc_auc_score(y_test, predictions))

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

Тестирование / Выводы

Положительные и отрицательные слова

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

#creating array variable of all the words
feature_names = np.array(vect.get_feature_names())
#creating array of all the regression coefficients per word
coef_index = model.coef_[0]
#creating df with both arrays in it
df = pd.DataFrame({'Word':feature_names, 'Coef': coef_index})
#sorting by coefficient
df.sort_values('Coef')

Отрицательное мнение:

Меня это завораживает.

  • «Заряженный» - слово с наиболее отрицательной корреляцией.
  • «Lyft» мне тоже показался интересным. Если у клиентов есть отрицательный опыт, я уверен, что они имеют склонность комментировать: «Ваш конкурент лучше вас!».
  • «Бесплатно» меня смутило. Я бы подумал, что «бесплатно» будет положительным словом.

Положительный настрой:

По мере того, как вы перемещаетесь вниз по фреймворку, слова становятся более положительно коррелированными.

  • «Отлично» - самый высокий, с такой положительной оценкой, что даже перевешивает «заряжено».
  • «Безопасный», «Удобный», «Чистый»… многие из этих слов ожидаются интуитивно.
  • Неожиданное положительно коррелированное слово - «она». Оказывают ли водители-женщины более положительное влияние на настроения клиентов?

Эти результаты могут предоставить потенциально бесценную информацию о бизнесе заинтересованным сторонам и руководству Uber.

Тестирование пользовательских обзоров

Наконец, мы можем экспериментировать и тестировать собственные отзывы. Ниже жирным шрифтом выделены входы, а ниже - соответствующие выходы (1 = положительный; 0 = отрицательный). Последнее должно быть положительным, но наш классификатор помечает его как отрицательный. Остальное кажется точным.

print(model.predict(vect.transform(['abandoned great'])))
print(model.predict(vect.transform(['great she the best'])))
print(model.predict(vect.transform(['charged slow horrible'])))
print(model.predict(vect.transform(['it was as average as a trip could be'])))
print(model.predict(vect.transform(['my family felt safe we got to our destination with ease'])))
print(model.predict(vect.transform(['i got to my destination quickly and affordably i had a smile on my face from start to finish'])))

Спасибо за прочтение.

У меня есть ядро ​​на Kaggle с этим анализом здесь

Также, если вы хотите подключиться, мой LinkedIn находится здесь

Подпишитесь, если вы нашли это полезным. Если вам нравится мой контент, ознакомьтесь с еще несколькими проектами:

Случайный лес лучше логистической регрессии? (сравнение)

Индекс Джини против информационной энтропии

Простая линейная регрессия против полиномиальной регрессии

Прогнозирование рака с помощью логистической регрессии в Python

Пример двумерной логистической регрессии (Python)

Вычисление R-квадрата с нуля (с использованием Python)