Добро пожаловать в мир лексиконов

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

Доступные словари в R:

Функция qdap polarity (): использует лексику из hash_sentiment_huliu (U из IL @CHI Polarity (+/-) word research)

tidytext: таблица настроений, содержащая более 23 000 терминов из трех различных лексиконов субъективности со следующими соответствующими определениями:

  • NRC - слова, соответствующие 8 эмоциям, таким как «злой» или «радость» и «Поз / Отрицание».
  • BING - слова, помеченные как положительные или отрицательные.
  • AFINN - слова с оценкой от -5 до 5.

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

Вы всегда можете изменить словарный запас по своему усмотрению!

1. Определение бизнес-проблемы

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

Вы должны хорошо знать свои данные и иметь веские причины для анализа настроений, и не только потому, что это звучит круто! ;)

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

В этом блоге мы узнаем, какие объекты жилья (например, близость к ресторанам, станциям метро, ​​гигиена, безопасность и т. Д.) Обеспечивают хороший опыт аренды на основе общих отзывов клиентов.

2. Определение источников текста

Один из популярных способов извлечения отзывов клиентов с любого веб-сайта - сканирование в Интернете. Это можно сделать как на R, так и на Python. Я сохраню эту тему для следующего блога. На данный момент я выбрал набор данных из 1000 отзывов пользователей об аренде AirBnB в Бостоне.

# Upload dataset in R (available as .rds file) 
> bos_reviews <- readRDS("bos_reviews.rds", refhook = NULL)

3. Организация и очистка текста

На этом этапе мы будем использовать два разных словаря (Полярность и BING), чтобы определить оценку настроения для собранных обзоров жилья.

Простой показатель полярности на основе Qdap

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

# Check out the boston reviews polarity overall polarity score 
> bos_pol <- polarity(bos_reviews)  
> bos_pol all total.sentences total.words ave.polarity sd.polarity stan.mean.polarity all 1000 70481 0.902 0.502 1.799 
# Summary for all reviews 
> summary(bos_pol$all$polarity) 
   Min. 1st Qu. Median  Mean  3rd Qu.  Max.   NA's 
-0.9712 0.6047  0.8921 0.9022 1.2063  3.7510   1  
# Kernel density plot of polarity score for all reviews 
> ggplot(bos_pol$all, aes(x = polarity, y = ..density..)) + theme_gdocs() + geom_histogram(binwidth = 0.25, fill = "#bada55", colour = "grey60") + geom_density(size = 0.75)

Словарь BING от TidyText

Здесь мы создадим словарные данные прямо из столбца обзора под названием «комментарии».

> library(tidytext) 
> library(tidyr) 
> library(dplyr)  
# Use unnest_tokens to make text lowercase & tokenize reviews into single words. 
> tidy_reviews <- bos_reviews %>% unnest_tokens(word, comments) 
# Group by and mutate to capture original word order within each group of a corpus. 
> tidy_reviews <- tidy_reviews %>% group_by(id) %>% mutate(original_word_order = seq_along(word))  
# Quick review 
> tidy_reviews 
A tibble: 70,834 x 3 
Groups: id [1,000] 
id               word         original_word_order 
1 1               my                  1 
2 1            daughter               2 
3 1              and                  3 
4 1               i                   4 
5 1              had                  5 
6 1               a                   6 
7 1           wonderful               7 
8 1             stay                  8 
9 1             with                  9 
10 1            maura                 10 
# ... with 70,824 more rows 
# Load stopwords lexicon 
> data("stop_words") 
# Perform anti-join to remove stopwords 
> tidy_reviews_without_stopwords <- tidy_reviews %>% anti_join(stop_words)  
# Getting the correct lexicon > bing <- get_sentiments(lexicon = "bing")  
# Calculating polarity for each review > pos_neg <- tidy_reviews_without_stopwords %>% inner_join(bing) %>% count(sentiment) %>% spread(sentiment, n, fill = 0) %>% mutate(polarity = positive - negative) 
# Checking outcome 
> summary(pos_neg) 
    id              negative         positive         polarity 
Min. : 1.0       Min. : 0.0000      Min. : 0.00     Min. :-11.000 
1st Qu.: 253.0   1st Qu.: 0.0000    1st Qu.: 3.00   1st Qu.: 2.000 Median : 498.0   Median : 0.0000    Median : 4.00   Median : 4.000 Mean : 500.4     Mean : 0.6139      Mean : 4.97     Mean : 4.356 
3rd Qu.: 748.0   3rd Qu.: 1.0000    3rd Qu.: 7.00   3rd Qu.: 6.000 Max. :1000.0     Max. :14.0000      Max. :28.00     Max. : 26.000 
# Kernel density plot of Tidy Sentiment score for all reviews 
> ggplot(pos_neg, aes(x = polarity, y = ..density..)) + geom_histogram(binwidth = 0.25, fill = "#bada55", colour = "grey60") + geom_density(size = 0.75)

4. Создание корпуса на основе полярности

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

а. Разделите все 1000 комментариев на положительные и отрицательные на основе их полярности, рассчитанной на шаге 2.

# Add polarity column to the reviews
> bos_reviews_with_pol <- bos_reviews %>% mutate(polarity = bos_pol$all$polarity)

# Subset positive comments based on polarity score
> pos_comments <- bos_reviews_with_pol %>% filter(polarity > 0) %>% pull(comments)

# Subset negative comments based on polarity score
> neg_comments <- bos_reviews_with_pol %>% filter(polarity < 0) %>% pull(comments)

б. Сверните положительные и отрицательные комментарии в два больших документа.

# Paste and collapse the positive comments 
> pos_terms <- paste(pos_comments, collapse = " ")  
# Paste and collapse the negative comments  
> neg_terms <- paste(neg_comments, collapse = " ")  
# Concatenate the positive and negative terms 
> all_terms <- c(pos_terms, neg_terms)

c. Создайте матрицу терминологических документов (TDM), взвешенную по частоте документов (TFIDF).

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

# Pipe a VectorSource Corpus 
> all_corpus <- all_terms %>% VectorSource() %>% VCorpus()  
# Simple TFIDF TDM 
> all_tdm <- TermDocumentMatrix(all_corpus, control = list( weighting = weightTfIdf, removePunctuation = TRUE, stopwords = stopwords(kind = "en")))  
# Examine the TDM  
> all_tdm <> Non-/sparse entries: 4348/5582 Sparsity: 56% Maximal term length: 372 Weighting: term frequency - inverse document frequency (normalized) (tf-idf) 
# Matrix 
> all_tdm_m <- as.matrix(all_tdm)  
# Column names 
> colnames(all_tdm_m) <- c("positive", "negative")

5. Извлечение компонентов

На этом этапе мы извлечем ключевые особенности корпуса из TDM, что приведет к положительным и отрицательным отзывам.

# Top positive words 
> order_by_pos <- order(all_tdm_m[, 1], decreasing = TRUE)  
# Review top 10 positive words 
> all_tdm_m[order_by_pos, ] %>% head(n = 10) 
DocsTerms       positive     negative 
walk           0.004565669       0 
definitely     0.004180255       0 
staying        0.003735547       0 
city           0.003290839       0 
wonderful      0.003112956       0 
restaurants    0.003053661       0 
highly         0.002964720       0 
station        0.002697895       0 
enjoyed        0.002431070       0 
subway         0.002401423       0  
# Top negative words 
> order_by_neg <- order(all_tdm_m[, 2], decreasing = TRUE)  
# Review top 10 negative words 
> all_tdm_m[order_by_neg, ] %>% head(n = 10) 
DocsTerms       positive      negative 
condition           0        0.002162942 
demand              0        0.001441961 
disappointed        0        0.001441961 
dumpsters           0        0.001441961 
hygiene             0        0.001441961 
inform              0        0.001441961 
nasty               0        0.001441961 
safety              0        0.001441961 
shouldve            0        0.001441961 
sounds              0        0.001441961

6. Анализ функций

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

а. Облако сравнения

> comparison.cloud(all_tdm_m, max.words = 20, colors = c("darkblue","darkred"))

б. Масштабируемое облако сравнения

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

# Scale/center & append 
> bos_reviews$scaled_polarity <- scale(bos_pol$all$polarity)  
# Subset positive comments 
> pos_comments <- subset(bos_reviews$comments, bos_reviews$scaled_polarity > 0)  
# Subset negative comments 
> neg_comments <- subset(bos_reviews$comments, bos_reviews$scaled_polarity < 0)  
# Paste and collapse the positive comments 
> pos_terms <- paste(pos_comments, collapse = " ")  
# Paste and collapse the negative comments 
> neg_terms <- paste(neg_comments, collapse = " ")  
# Organize 
> all_terms<- c(pos_terms, neg_terms)  
# VCorpus 
> all_corpus <- VCorpus(VectorSource(all_terms)) 
# TDM 
> all_tdm <- TermDocumentMatrix( all_corpus, control = list( weighting = weightTfIdf, removePunctuation = TRUE, stopwords = stopwords(kind = "en")))  
# Column names 
> all_tdm_m <- as.matrix(all_tdm) 
> colnames(all_tdm_m) <- c("positive", "negative")  
# Comparison cloud 
> comparison.cloud(all_tdm_m, max.words = 100, colors = c("darkblue", "darkred"))

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

# Create effort 
> effort <- tidy_reviews_without_stopwords %>% count(id) 
# Inner join 
> pos_neg_with_effort <- inner_join(pos_neg, effort) 
# Review 
> pos_neg_with_effort  
A tibble: 953 x 5 Groups: id [?] 
id      negative     positive      polarity     n 
1 1         0            4             4        26 
2 2         0            3             3        27 
3 3         0            3             3        16 
4 4         0            6             6        32 
5 5         0            2             2         8 
6 6         0            3             3        21 
7 7         0            5             5        18 
8 8         0            2             2        10 
9 9         0            4             4        12 
10 10       1           15            14        46 
# ... with 943 more rows  
# Add pol 
> pos_neg_pol <- pos_neg_with_effort %>% mutate(pol = ifelse(polarity >= 0, "Positive", "Negative"))  
# Plot 
> ggplot(pos_neg_pol, aes(polarity,n, color = pol)) + geom_point(alpha = 0.25) + geom_smooth (method = "lm", se = FALSE) + ggtitle("Relationship between word effort & polarity")

7. Выводы по чертежам.

Неудивительно, что самые популярные положительные термины включали:

  • ходить
  • хорошо оборудованный
  • рестораны
  • метро
  • станции

В отличие от этого, верхние отрицательные термины включали:

  • автоматическая публикация
  • мусорные контейнеры
  • грязный
  • гигиена
  • безопасность
  • звуки

Кроме того, видно, что автор приложит больше усилий, чтобы написать более сильный положительный или отрицательный отзыв. Как это правда!

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

Большое спасибо за чтение!

При написании этого блога я хотел поделиться основными концепциями анализа настроений со всеми вами, начинающими исследователями данных, но мы НЕ МОЖЕМ останавливаться на достигнутом! Думаю, я готов к более сложному проекту по выявлению эмоций для своего портфолио: D Как насчет вас?

Делитесь своими идеями / отзывами в комментариях ниже.

Вы также можете связаться со мной в Linkedin.

Продолжайте кодировать. Ваше здоровье!

(Впервые опубликовано на сайте www.datacritics.com)