Добро пожаловать в мир лексиконов
Проводя свое исследование по анализу настроений, я познакомился с важной концепцией субъективных лексиконов. Применение лексикона - один из двух основных подходов к анализу настроений. Он включает в себя расчет тональности на основе семантической ориентации слов или фраз, встречающихся в тексте.
Доступные словари в 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)