Кластеризация обзоров Шардоне с использованием K-средних с использованием Scikit-Learn и NLTK
Шардоне - самое популярное белое вино в мире. Виноград легко выращивать, он известен своей сложностью и адаптируем к различным технологиям и вкусам. Например, вчера вечером я попробовала сладкое неочищенное Шардоне. Насколько популярно Шардоне? Как описано в Отчете о площадях под виноградом в Калифорнии за 2018 год, из 176 092 гектаров, отведенных под белое вино, Шардоне потребляло более половины из 93 148 акров! Следующий по величине сорт - французский Colombard, занимающий 18 246 акров; жалкий по сравнению. Это много винограда!
Поскольку я знаком с набором данных обзоров вин, доступным на kaggle, я решил загрузить блокнот и проанализировать Шардоне.
Может ли кластеризация помочь нам определить взаимосвязь между описанием и рейтингом?
В этой статье я покажу, как использовать Scikit-Learn и Набор инструментов для естественного языка для обработки, анализа и кластеризации данных Chardonnay. Используя эти методы, я надеюсь увидеть, есть ли разница в темах или темах, используемых в обзорах вин с оценками выше среднего, по сравнению с обзорами вин со средними или ниже оценками.
Импорт зависимостей и данных
Поскольку в этой статье сочетаются несколько методов анализа отзывов о Шардоне, нам нужно кое-что импортировать. Кроме того, Я провел некоторую предварительную очистку набора данных, чтобы удалить дубликаты и нули, и сохранил их в базе данных SQLite:
#import dependencies import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import re from sklearn import preprocessing, decomposition, model_selection, metrics, pipeline from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer from sklearn.cluster import KMeans import nltk from nltk.stem.wordnet import WordNetLemmatizer from nltk.corpus import stopwords import sqlite3 from sqlite3 import Error #force output to display the full description pd.set_option('display.max_colwidth', -1) #connect to database file conn = sqlite3.connect('db\wine_data.sqlite') c = conn.cursor() #create dataframe from sql query df = pd.read_sql("Select country, description, rating, price, title, variety from wine_data where variety = 'Chardonnay'", conn) #display the top 3 rows df.head(3)
Разработка и анализ функций
С самого начала я знаю, что хочу добавить пару функций. Во-первых, я добавлю столбец подсчета слов, чтобы хранить количество слов в каждом описании. Во-вторых, я добавлю двоичное поле, чтобы указать, выше ли рейтинг вина. Прежде чем я смогу добавить эти функции, мне нужно провести небольшой анализ, чтобы определить средний рейтинг.
Анализировать количество слов
Анализ количества слов может помочь вам решить, хотите ли вы сократить набор данных. Например, глядя на данные, мы видим, что минимальное количество слов для обзора вина составляет 3 слова. Если посмотреть на обзоры, содержащие менее 15 слов, средняя оценка составляет 82 из диапазона 80–100. Это говорит мне о том, что короткие обзоры могут быть связаны с более низкими оценками. Точно так же, когда я смотрю на другой конец распределения, я замечаю, что более длинные обзоры могут быть связаны с более высокими оценками.
Короткие обзоры могут быть связаны с более низкими оценками. Более длинные обзоры могут быть связаны с более высокими оценками.
#add a column for the word count df['word_count'] = df['description'].apply(lambda x: len(str(x).split(" "))) print("Word Count Median: " + str(df['word_count'].median())) print(df['word_count'].describe()) x = df['word_count'] n_bins = 95 plt.hist(x, bins=n_bins) plt.xlabel('Number of Words in Description') plt.ylabel('Frequency') plt.show()
#word counts less than 15 wc15 = df.loc[df['word_count'] < 15] print(wc15.rating.median()) print(wc15.rating.describe()) #word counts greater than 70 wc70 = df.loc[df['word_count'] > 70] print(wc70.rating.median()) print(wc70.rating.describe()) #plot the counts plt.figure(figsize=(14,4)) sns.countplot(x ='rating', data = wc70).set_title("Rating Counts")
Данные могут быть визуализированы, чтобы помочь нам увидеть взаимосвязь между количеством слов и рейтингом.
Анализ рейтингов
Поскольку я хочу посмотреть, можно ли использовать описания, чтобы определить, имеет ли вино рейтинг выше среднего, мне нужно найти средний рейтинг. Функция Pandas describe упрощает поиск статистики. Глядя на данные, мы получаем средний рейтинг 88. Добавить столбец с двоичными значениями во фрейм данных легко, используя понимание списка. Если рейтинг больше 88, в нашем новом столбце будет 1. Если рейтинг 88 или меньше, значение будет 0, так как вино не лучше среднего.
Если рейтинг больше 88, в нашем новом столбце будет 1. Если рейтинг 88 или меньше, значение будет 0, так как вино не лучше среднего.
print("Number of Unique Ratings: " + str(len(df['rating'].unique()))) print("Rating Median: " + str(df['rating'].median())) print(df['rating'].describe()) plt.figure(figsize=(14,4)) sns.countplot(x='rating', data=df).set_title("Rating Counts") plt.show() #add column to flag records with rating greater than 88 df['above_avg'] = [1 if rating > 88 else 0 for rating in df['rating']]
Обработка естественного языка
Перед кластеризацией данных я использую несколько техник НЛП, таких как удаление стоп-слов, знаков препинания и специальных символов, а также нормализация текста. После обработки текста я собираюсь векторизовать его с помощью векторизатора Scikit-Learn tf-idf.
Очистка текста
Перед кластеризацией я хочу удалить стоп-слова. Стоп-слова - это распространенные слова, такие как то и из. Удаление их из описаний позволяет выделить наиболее релевантные часто встречающиеся слова. Я смотрю на частоту слов, чтобы определить, нужно ли добавлять дополнительные слова в список игнорируемых слов. Кроме того, я очищаю описания, используя Регулярные выражения, чтобы удалить знаки препинания, теги и специальные символы, а затем лемматизирую слова, которые сводят слово к корневой форме, сохраняя при этом, что это настоящее слово. Лемматизация - это метод нормализации текста:
#create a list of stop words stop_words = set(stopwords.words("english")) #show how many words are in the list of stop words print(len(stop_words)) #179 #construct a new list to store the cleaned text clean_desc = [] for w in range(len(df.description)): desc = df['description'][w].lower() #remove punctuation desc = re.sub('[^a-zA-Z]', ' ', desc) #remove tags desc = re.sub("</?.*?>"," <> ",desc) #remove special characters and digits desc = re.sub("(\\d|\\W)+"," ",desc) split_text = desc.split() #Lemmatisation lem = WordNetLemmatizer() split_text = [lem.lemmatize(word) for word in split_text if not word in stop_words and len(word) >2] split_text = " ".join(split_text) clean_desc.append(split_text)
Векторизация текста с использованием TF-IDF
TfidfVectorizer преобразует текст в векторное пространство. Чтобы упростить концепцию, представьте, что у вас есть два предложения:
Собака белая
Кошка черная
Преобразование предложений в модель векторного пространства преобразует их таким образом, чтобы смотреть на слова во всех предложениях, а затем представлять слова в предложении числом. Например, если слово находится в предложении, это 1. Если слово отсутствует в предложении, оно отображается с 0:
Собака кошка белая черная
Собака белая = [1,1,0,1,1,0]
Кошка черная = [1,0,1,1,0,1]
TF-IDF означает термин частота документа, обратно пропорциональная частоте. Это метод взвешивания значения слова, а не его простого подсчета. Он используется для определения того, насколько важно слово для текста в документах коллекции. Эта функция полезна для поиска информации, такой как поисковые системы и интеллектуальный анализ текста. TfidfVectorizer в Scikit-Learn преобразует набор необработанных документов в матрицу функций TF-IDF. Он возвращает матрицу с помощью метода fit_transform.
#TF-IDF vectorizer tfv = TfidfVectorizer(stop_words = stop_words, ngram_range = (1,1)) #transform vec_text = tfv.fit_transform(clean_desc) #returns a list of words. words = tfv.get_feature_names()
Извлечение тем с использованием кластеризации K-средних
Кластеризация K-средних - это популярный алгоритм обучения без учителя, который можно использовать для извлечения тем путем группировки похожих обзоров и создания списка общих слов. Я собираюсь попробовать разделить данные на 21 кластер (n_clusters = 21), чтобы посмотреть, смогу ли я обнаружить темы, общие для высоких оценок, и темы, общие для низких оценок. Scikit-Learn упрощает применение k-средних.
#setup kmeans clustering kmeans = KMeans(n_clusters = 21, n_init = 17, n_jobs = -1, tol = 0.01, max_iter = 200) #fit the data kmeans.fit(vec_text) #this loop transforms the numbers back into words common_words = kmeans.cluster_centers_.argsort()[:,-1:-11:-1] for num, centroid in enumerate(common_words): print(str(num) + ' : ' + ', '.join(words[word] for word in centroid))
Визуализируйте результаты
Используя тепловые карты, я могу увидеть, как сгруппированы рейтинги. Я также могу видеть, сгруппированы ли вместе оценки выше среднего по сравнению со средними или ниже рейтингами.
#add the cluster label to the data frame df['cluster'] = kmeans.labels_ clusters = df.groupby(['cluster', 'rating']).size() fig, ax1 = plt.subplots(figsize = (26, 15)) sns.heatmap(clusters.unstack(level = 'rating'), ax = ax1, cmap = 'Reds') ax1.set_xlabel('rating').set_size(18) ax1.set_ylabel('cluster').set_size(18) clusters = df.groupby(['cluster', 'above_avg']).size() fig2, ax2 = plt.subplots(figsize = (30, 15)) sns.heatmap(clusters.unstack(level = 'above_avg'), ax = ax2, cmap="Reds") ax2.set_xlabel('Above Average Rating').set_size(18) ax2.set_ylabel('Cluster').set_size(18)
Я могу увидеть распределения, разделив фрейм данных и построив график количества кластеров!
#create dataframe of reviews not above average not_above = df.loc[df['above_avg'] == 0] not_above.describe() #create data frame of reviews above average above_avg = df.loc[df['above_avg'] == 1] above_avg.describe() #plot the counts plt.figure(figsize=(14,4)) sns.countplot(x='cluster', data=not_above).set_title("Rating Counts") plt.show() plt.figure(figsize=(14,4)) sns.countplot(x='cluster', data=above_avg).set_title("Rating Counts") plt.show()
Заключительные мысли и записная книжка
Глядя на визуализации, они показывают, что рейтинги выше среднего были в большей степени сгруппированы в 5, 6 и 12. Это означает, что слова из этих кластеров обычно использовались в обзорах вин, дававших оценки выше среднего. Требуется более глубокий взгляд на слова в кластере, поскольку трудно различить существенные различия, если смотреть только на 10 первых слов. Например, такие слова, как яблоко, аромат и вкус, встречаются в нескольких группах, что затрудняет понимание отдельных тем. Кроме того, существуют различные алгоритмы, которые могут лучше работать при моделировании темы.
Скрытое распределение Дирихле (LDA) - еще один популярный алгоритм обучения без учителя для тематического моделирования, который может превзойти K-средних. Надеюсь, у меня будет возможность изучить LDA и сравнить свои результаты.
Вы можете скачать мою записную книжку из моего репозитория на github:
Благодарю вас!
- Если вам понравилось, подписывайтесь на меня на Medium, чтобы узнать больше
- Получите ПОЛНЫЙ ДОСТУП и помогите поддержать мой контент, подписавшись
- Давайте подключимся к LinkedIn
- Анализировать данные с помощью Python? Загляните на мой сайт