Тема 12 Эмоциональная тональность

12.1 Анализ тональности

Анализ тональности текста (англ. Sentiment analysis) — задача компьютерной лингвистики, заключающаяся в определении эмоциональной окраски (тональности) текста и, в частности, в выявлении эмоциональной оценки авторов по отношению к объектам, описываемым в тексте.

В целом, задача анализа тональности текста эквивалентна задаче классификации текста, где категориями текстов могут быть тональные оценки (позитивная, негативная или нейтральная).

12.2 Подходы

Сделав большое обобщение, можно разделить существующие подходы на следующие категории:

  • подходы, основанные на правилах;
  • подходы, основанные на словарях;
  • машинное обучение с учителем;
  • машинное обучение без учителя.

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

12.3 Тезаурусы

Подходы, основанные на словарях, используют так называемые тональные словари (англ. affective lexicons) для анализа текста. В простом виде тональный словарь представляет из себя список слов со значением тональности для каждого слова.

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

Разные тезаурусы используют разные шкалы:

  • бинарную: negative / positive (-1 / 1)
  • тринарную: бинарная + 0 (neutral)
  • ранжированную: например, от -5 до 5

В некоторых случаях дополнительно вводятся различия между оценочной лексикой (“неряшливый”) и негативным фактом (“кража”) и т.п.


Загрузите библиотеки library(tidytext) и library(textdata). При помощи функции get_sentiments() выясните, какую шкалу использует каждый из лексиконов. Какой из них позволяет анализировать конкретные эмоции? Какие уникальные эмоции он содержит?

12.4 Лексиконы для русского языка

Установка пакета с лексиконами.

remotes::install_github("dmafanasyev/rulexicon")

Начало работы.

library(rulexicon)
library(tidyverse)
library(tidytext)

Русский язык входит в языков, для которых Й. Чен и С. Скиена собрали оценочную лексику (Chen and Skiena 2014). Их лексикон построен на основе графа знаний, связывающего слова на разных языках (на основе Wiktionary, Google Translate, транслитерационных ссылок и WordNet). Слова оцениваются по бинарной шкале ( -1 / 1).

set.seed(0211)
chen_skiena <- hash_sentiment_chen_skiena
sample_n(chen_skiena, 10)
##               token score
##  1:         пустошь    -1
##  2:   революционный     1
##  3:          расизм    -1
##  4:       медленный    -1
##  5:       лекарство     1
##  6:       поддержка     1
##  7: стабилизировать     1
##  8:          лисица    -1
##  9:        мягкость     1
## 10:         примесь    -1

Для русского языка в свободном доступе находится РуСентиЛекс (Создание лексикона оценочных слов русского языка РуСентилекс 2016). Он содержит около 15000 уникальных слов или фраз, среди которых оценочные слова, а также слова и выражения, не передающие оценочное отношения автора, но имеющие положительную или отрицательную ассоциацию (коннотацию). Возможные значения переменной sentiment: neutral, positive, negative, positive/negative.

set.seed(1102)
rusenti2017 <- hash_rusentilex_2017
sample_n(rusenti2017, 10)
##               token  speech.part            lemma sentiment
## 1         кривлянье         Noun        кривлянье  negative
## 2         прозевать         Verb        прозевать   neutral
## 3             пжлст Interjection            пжлст  positive
## 4         пробудить         Verb        пробудить  positive
## 5       новый смысл           NG      новый смысл  positive
## 6          скромный          Adj         скромный  positive
## 7      порадоваться         Verb     порадоваться  positive
## 8            осечка         Noun           осечка  negative
## 9  бесконтрольность         Noun бесконтрольность  negative
## 10 жалеть о прошлом           VG жалеть о прошлое  negative
##     source                       ambiguity
## 1  opinion                                
## 2     fact                         ЗЕВОТА 
## 3  opinion                                
## 4  opinion         ВСТРЯХНУТЬ, РАСШЕВЕЛИТЬ
## 5  opinion                                
## 6  opinion НЕПРИХОТЛИВЫЙ, НЕПРИТЯЗАТЕЛЬНЫЙ
## 7  feeling                                
## 8  opinion              ПРОМАХ, ОПЛОШНОСТЬ
## 9  opinion                                
## 10 feeling

При работе с этим лексиконом следует учитывать, что для отдельных слов он содержит несколько вхождений, как положительных, так и отрицательных, например:

rusenti2017 %>% 
  filter(token == "нежный") %>% 
  select(-source, -token)
##   speech.part  lemma sentiment                        ambiguity
## 1         Adj нежный  negative ХРУПКИЙ (СЛИШКОМ СЛАБЫЙ, НЕЖНЫЙ)
## 2         Adj нежный  positive                        ЛАСКОВЫЙ 
## 3         Adj нежный  positive          МЯГКИЙ, НЕЖНЫЙ НА ОЩУПЬ
## 4         Adj нежный  positive              НЕЖНЫЙ ПО ЗВУЧАНИЮ

Словарь AFINN содержит 7268 оценочных слов. Их тональность оценивается по шкале от -5 (крайне негативная) до 5 (в высшей степени положительная). Например, слово “адский” имеет оценку -5, а слово “ангельский” – +5.

set.seed(0211)
afinn <- hash_sentiment_afinn_ru
sample_n(afinn, 10)
##              token score
##  1:  экстатический   1.7
##  2:       знаковый   1.7
##  3:    счастливчик   5.0
##  4:     суматошный  -3.3
##  5:            гад  -5.0
##  6:  выразительный   5.0
##  7:     жутковатый  -5.0
##  8:   креативность   5.0
##  9:   обнадёживать   2.5
## 10: привлекательно   5.0

NRC для русского языка – это переведенная версия списка положительных и отрицательных слов Mohammad & Turney (2010)48. Таблица содержит 5179 слов с не нейтральными оценками. Бинарная шкала: -1 / 1.

set.seed(1102)
nrc <- hash_sentiment_nrc_emolex_ru
sample_n(nrc, 10)
##                   token score
##  1:        энциклопедия     1
##  2:          подходящий     1
##  3:      издевательство    -1
##  4:        пленительный     1
##  5:         утопический     1
##  6: недоброжелательство    -1
##  7:          вероломный    -1
##  8:       вирулентность    -1
##  9:        победоносный     1
## 10:    незначительность    -1

12.5 Анализ тональности с Tidy Data

(Silge and Robinson 2017), говоря об анализе эмоциональной тональности в духе tidy data, предлагают следующую иллюстрацию:

Авторы предостерегают, впрочем, что современные лексиконы могут быть не слишком информативны применительно к классической литературе (в книге анализируются романы Джейн Остин). Мы попробуем, тем не менее, подвергнуть “сентиментальному анализу” сентиментальную прозу Карамзина.

12.6 Подготовка текста

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

library(udpipe)
liza <- readLines(con = "files/karamzin_liza.txt")

russian_syntagrus <- udpipe_load_model(file = "russian-syntagrus-ud-2.5-191206.udpipe")

liza_ann <- udpipe_annotate(russian_syntagrus, liza)

liza_df <- as_tibble(liza_ann) %>% 
  select(-paragraph_id, -sentence, -xpos)

Разделим весь текст “Лизы” на отрывки по 100 слов: это позволит проверить, как меняется эмоциональная тональность произведения по мере развития сюжета.

liza_tbl <- as_tibble(liza_ann) %>% 
  filter(upos != "PUNCT") %>% 
  select(lemma) %>% 
  rename(token = lemma) %>% 
  mutate(chunk = round(((row_number() + 50) / 100), 0))

liza_tbl
## # A tibble: 5,049 × 2
##    token  chunk
##    <chr>  <dbl>
##  1 мочь       1
##  2 быть       1
##  3 никто      1
##  4 из         1
##  5 жить       1
##  6 в          1
##  7 Москва     1
##  8 не         1
##  9 знать      1
## 10 так        1
## # ℹ 5,039 more rows

В тексте чуть более 5000 слов, у нас получился 51 отрывок.

12.7 Модификация лексикона

Для анализа эмоциональной тональности возьмем лексикон AFINN, доступный в пакете rulexicon. Слово “старый” имеет в этом лексиконе отрицательную оценку, что не соответствует словоупотреблению Карамзина, и мы его удалили; слову “чувствительный” поменяли знак с минуса на плюс, поскольку для автора “Бедной Лизы” это скорее положительное качество.

Код ниже показывает, как вносятся подобные изменения:

lex <- hash_sentiment_afinn_ru

lex <- lex %>% filter(token != "старый")

lex <- lex %>% mutate_at(vars(score), ~
       case_when(token == "чувствительный" ~  1.7,
                 TRUE ~ .))

12.8 Соединение лексикона и текста

Стоп-слова, то есть слова, не несущие никакой смысловой нагрузки, нам не нужны, но удалять их отдельно нет смысла: мы соединим, при помощи функции inner_join() (см. предыдущие уроки), наш текст с одним из лексиконов, а там не будет стоп-слов. Напомню, что inner_join() работает так:

liza_sent <- liza_tbl %>% 
  inner_join(lex)

liza_sent
## # A tibble: 461 × 3
##    token          chunk score
##    <chr>          <dbl> <dbl>
##  1 хорошо             1   5  
##  2 новый              1   1.7
##  3 приятный           1   5  
##  4 новый              1   1.7
##  5 красота            1   5  
##  6 приятный           1   5  
##  7 мрачный            1  -5  
##  8 горе               1  -5  
##  9 ужасный            1  -5  
## 10 величественный     1   3.3
## # ℹ 451 more rows

Здесь “горе” – ошибка лемматизации (“стоя на сей горе…”).

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

liza_chunk_sent <- liza_sent %>% 
  group_by(chunk) %>% 
  summarise(sum = sum(score)) %>% 
  arrange(sum)

head(liza_chunk_sent, 10)
## # A tibble: 10 × 2
##    chunk   sum
##    <dbl> <dbl>
##  1     5 -43.3
##  2    34 -35.8
##  3    31 -25  
##  4    38 -20  
##  5    42 -20  
##  6    50 -20  
##  7     3 -15.7
##  8    51 -15  
##  9     4 -14.2
## 10    46 -13.3

Довольно неожиданно, что самый негативный отрывок находится не в конце повести, ближе к трагической развязке, а почти в начале (отрывок 5, ср. отрывки 3 и 4 рядом). Представим эмоционально окрашенную лексику отрывков 3-5 в виде сравнительного облака слов:

library(reshape2)
library(wordcloud)

# добавляем новый столбец для удобства визуализации
liza_sent_class <- liza_sent %>% 
  mutate(tone = case_when( score >= 0 ~ "pos",
                           score < 0 ~ "neg"))
set.seed(0211)
liza_sent_class %>% 
  filter(chunk %in% c(3, 4, 5)) %>% 
  count(token, tone, sort = T) %>% 
  acast(token ~ tone, value.var = "n", fill = 0) %>% 
  comparison.cloud(colors = c("grey20", "grey80"),
                   max.words = 99)

Здесь видно, что негативная тональность в этой части не связана с судьбой героев: об этом говорят такие слова, как “лютый”, “враг”, “свирепый”. Рассказчик, глядя на заброшенный Симонов монастырь, вспоминает о “печальной истории” Москвы. Таким образом, с количественной точки зрения, самые мрачный фрагмент повести посвящен не судьбе бедной девушки, а “глухому стону времен”: Карамзин-историк уже переигрывает Карамзина-новелиста.

Приведем небольшой отрывок из этой части повести:

Иногда на вратах храма рассматриваю изображение чудес, в сем монастыре случившихся, там рыбы падают с неба для насыщения жителей монастыря, осажденного многочисленными врагами; тут образ богоматери обращает неприятелей в бегство. Все сие обновляет в моей памяти историю нашего отечества — печальную историю тех времен, когда свирепые татары и литовцы огнем и мечом опустошали окрестности российской столицы и когда несчастная Москва, как беззащитная вдовица, от одного бога ожидала помощи в лютых своих бедствиях.


Сохраните текст ниже и извлеките из него все радостные (или нерадостные) слова.


bear <- tibble (text = "The bear puts both arms around the tree above her
And draws it down as if it were a lover
And its choke cherries lips to kiss good-bye,
Then lets it snap back upright in the sky.
Her next step rocks a boulder on the wall
(She's making her cross-country in the fall).
Her great weight creaks the barbed-wire in its staples
As she flings over and off down through the maples,
Leaving on one wire moth a lock of hair.
Such is the uncaged progress of the bear.
The world has room to make a bear feel free;
The universe seems cramped to you and me.
Man acts more like the poor bear in a cage
That all day fights a nervous inward rage - 
His mood rejecting all his mind suggests.
He paces back and forth and never rests
The toe-nail click and shuffle of his feet,
The telescope at one end of his beat -
And at the other end the microscope,
Two instruments of nearly equal hope,
And in conjunction giving quite a spread.
Or if he rests from scientific tread,
'Tis only to sit back and sway his head
Through ninety odd degrees of arc, it seems,
Between two metaphysical extremes.
He sits back on his fundamental butt
With lifted snout and eyes (if any) shut,
(he almost looks religious but he's not),
And back and forth he sways from cheek to cheek,
At one extreme agreeing with one Greek -
At the other agreeing with another Greek
Which may be thought, but only so to speak.
A baggy figure, equally pathetic
When sedentary and when peripatetic.")


Разделите стихотворение выше на предложения. Найдите самое позитивное и самое негативное предложение в тексте. Как влияет на результат использование различных лексиконов? Какое негативное слово встречается чаще всего?


12.9 Тональность на оси времени

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

liza_chunk_sent <- liza_chunk_sent %>% 
  mutate(tone = case_when( sum >= 0 ~ "pos",
                           sum < 0 ~ "neg"))

library(ggplot2)
ggplot(liza_chunk_sent, aes(chunk, sum, fill = tone)) +
  geom_col(show.legend = F)

График получился весьма осмысленным. Мы уже сказали выше про отрывки 3-4. Дальше немного скорби в отрывке 8 посвящено покойному отцу Лизы. В 11-м отрывке отразилась тревога матери за судьбу дочери: “коварно”, “обидеть”, “дурной” вносят вклад в настроение этого фрагмента. Это достаточно характерно для сентиментальной прозы с ее противопоставлением пороков городской жизни и пасторальных добродетелей.

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

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

Еще два минимума: отрывки 31 и 34. В первом из них Лиза встревожена вестью о возможном замужестве с сыном крестьянина. Отрывок 34 – это падение Лизы:

Грозно шумела буря, дождь лился из черных облаков — казалось, что натура сетовала о потерянной Лизиной невинности.

На графике видно, что это место гораздо более эмоционально, чем эпизод самоубийства Лизы: именно после знаменитых карамзинских многоточий и тире события устремляются к трагическому финалу. О самой смерти девушки Карамзин говорит, конечно, с грустью, но без надрыва: “Тут она бросилась в воду”.

38, 39, 42 – Эраст отправляется на войну. Все, как это принято у Карамзина, плачут, что зафиксировал и наш график.

Наконец, в отрывках 49-51 доминирует тема смерти:

library(RColorBrewer)
pal <- RColorBrewer::brewer.pal(5, "Dark2")

liza_sent_class %>% 
  filter(chunk %in% c(49:51)) %>% 
  filter(tone == "neg") %>% 
  count(token, sort = T) %>% 
  with(wordcloud(token, n, max.words = 100, colors = pal))

Следует отметить, что часть этих слов относится не к самой девушке, а к ее матери.

12.10 Для других языков

Для языков, которые используют латиницу, в R есть отличный пакет под названием syuzhet. Его разработал известный цифровой литературовед Мэтью Джокерс49. Название пакета, как говорит его разработчик, подсмотрено у русских формалистов Виктора Шкловского и Владимира Проппа. Возможности и ограничения этого пакета обсуждались в специальной литературе50.

Для анализа возьмем стихотворение Роберта Фроста “Медведи” (перевод).

library(syuzhet)

bear_vec <- bear$text

Пакет позволяет разделить текст на предложения:

sent_vec <- get_sentences(bear_vec)
sent_vec[1:2]
## [[1]]
##  [1] "The bear puts both arms around the tree above her\nAnd draws it down as if it were a lover\nAnd its choke cherries lips to kiss good-bye,\nThen lets it snap back upright in the sky."                                                                                                                        
##  [2] "Her next step rocks a boulder on the wall\n(She's making her cross-country in the fall)."                                                                                                                                                                                                                     
##  [3] "Her great weight creaks the barbed-wire in its staples\nAs she flings over and off down through the maples,\nLeaving on one wire moth a lock of hair."                                                                                                                                                        
##  [4] "Such is the uncaged progress of the bear."                                                                                                                                                                                                                                                                    
##  [5] "The world has room to make a bear feel free;\nThe universe seems cramped to you and me."                                                                                                                                                                                                                      
##  [6] "Man acts more like the poor bear in a cage\nThat all day fights a nervous inward rage - \nHis mood rejecting all his mind suggests."                                                                                                                                                                          
##  [7] "He paces back and forth and never rests\nThe toe-nail click and shuffle of his feet,\nThe telescope at one end of his beat -\nAnd at the other end the microscope,\nTwo instruments of nearly equal hope,\nAnd in conjunction giving quite a spread."                                                         
##  [8] "Or if he rests from scientific tread,\n'Tis only to sit back and sway his head\nThrough ninety odd degrees of arc, it seems,\nBetween two metaphysical extremes."                                                                                                                                             
##  [9] "He sits back on his fundamental butt\nWith lifted snout and eyes (if any) shut,\n(he almost looks religious but he's not),\nAnd back and forth he sways from cheek to cheek,\nAt one extreme agreeing with one Greek -\nAt the other agreeing with another Greek\nWhich may be thought, but only so to speak."
## [10] "A baggy figure, equally pathetic\nWhen sedentary and when peripatetic."                                                                                                                                                                                                                                       
## 
## [[2]]
## NULL

Воспользуемся регулярными выражениями, чтобы удалить переносы строк:

library(stringr)
sent_vec <- str_replace_all(sent_vec, "\\n", " ")
## Warning in stri_replace_all_regex(string, pattern,
## fix_replacement(replacement), : argument is not an atomic vector;
## coercing
sent_vec[1:2]
## [1] "c(\"The bear puts both arms around the tree above her\\nAnd draws it down as if it were a lover\\nAnd its choke cherries lips to kiss good-bye,\\nThen lets it snap back upright in the sky.\", \"Her next step rocks a boulder on the wall\\n(She's making her cross-country in the fall).\", \"Her great weight creaks the barbed-wire in its staples\\nAs she flings over and off down through the maples,\\nLeaving on one wire moth a lock of hair.\", \"Such is the uncaged progress of the bear.\", \"The world has room to make a bear feel free;\\nThe universe seems cramped to you and me.\",  \"Man acts more like the poor bear in a cage\\nThat all day fights a nervous inward rage - \\nHis mood rejecting all his mind suggests.\", \"He paces back and forth and never rests\\nThe toe-nail click and shuffle of his feet,\\nThe telescope at one end of his beat -\\nAnd at the other end the microscope,\\nTwo instruments of nearly equal hope,\\nAnd in conjunction giving quite a spread.\", \"Or if he rests from scientific tread,\\n'Tis only to sit back and sway his head\\nThrough ninety odd degrees of arc, it seems,\\nBetween two metaphysical extremes.\",  \"He sits back on his fundamental butt\\nWith lifted snout and eyes (if any) shut,\\n(he almost looks religious but he's not),\\nAnd back and forth he sways from cheek to cheek,\\nAt one extreme agreeing with one Greek -\\nAt the other agreeing with another Greek\\nWhich may be thought, but only so to speak.\", \"A baggy figure, equally pathetic\\nWhen sedentary and when peripatetic.\")"
## [2] NA

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

Функция get_sentiment принимает в качестве аргументов вектор и метод (о методах можно подробнее узнать, вызвав помощь).

bing_vector <- get_sentiment(sent_vec, method="bing")

bing_vector
## [1] -5

Как видно, не очень радостное стихотворение. Взглянем на самое мрачное предложение:

sent_vec[which.min(bing_vector)]
## [1] "c(\"The bear puts both arms around the tree above her\\nAnd draws it down as if it were a lover\\nAnd its choke cherries lips to kiss good-bye,\\nThen lets it snap back upright in the sky.\", \"Her next step rocks a boulder on the wall\\n(She's making her cross-country in the fall).\", \"Her great weight creaks the barbed-wire in its staples\\nAs she flings over and off down through the maples,\\nLeaving on one wire moth a lock of hair.\", \"Such is the uncaged progress of the bear.\", \"The world has room to make a bear feel free;\\nThe universe seems cramped to you and me.\",  \"Man acts more like the poor bear in a cage\\nThat all day fights a nervous inward rage - \\nHis mood rejecting all his mind suggests.\", \"He paces back and forth and never rests\\nThe toe-nail click and shuffle of his feet,\\nThe telescope at one end of his beat -\\nAnd at the other end the microscope,\\nTwo instruments of nearly equal hope,\\nAnd in conjunction giving quite a spread.\", \"Or if he rests from scientific tread,\\n'Tis only to sit back and sway his head\\nThrough ninety odd degrees of arc, it seems,\\nBetween two metaphysical extremes.\",  \"He sits back on his fundamental butt\\nWith lifted snout and eyes (if any) shut,\\n(he almost looks religious but he's not),\\nAnd back and forth he sways from cheek to cheek,\\nAt one extreme agreeing with one Greek -\\nAt the other agreeing with another Greek\\nWhich may be thought, but only so to speak.\", \"A baggy figure, equally pathetic\\nWhen sedentary and when peripatetic.\")"

Результат применения различных методов может несколько отличаться.

afinn_vector <- get_sentiment(sent_vec, method="afinn")

afinn_vector
## [1] 2
sent_vec[which.min(afinn_vector)]
## [1] "c(\"The bear puts both arms around the tree above her\\nAnd draws it down as if it were a lover\\nAnd its choke cherries lips to kiss good-bye,\\nThen lets it snap back upright in the sky.\", \"Her next step rocks a boulder on the wall\\n(She's making her cross-country in the fall).\", \"Her great weight creaks the barbed-wire in its staples\\nAs she flings over and off down through the maples,\\nLeaving on one wire moth a lock of hair.\", \"Such is the uncaged progress of the bear.\", \"The world has room to make a bear feel free;\\nThe universe seems cramped to you and me.\",  \"Man acts more like the poor bear in a cage\\nThat all day fights a nervous inward rage - \\nHis mood rejecting all his mind suggests.\", \"He paces back and forth and never rests\\nThe toe-nail click and shuffle of his feet,\\nThe telescope at one end of his beat -\\nAnd at the other end the microscope,\\nTwo instruments of nearly equal hope,\\nAnd in conjunction giving quite a spread.\", \"Or if he rests from scientific tread,\\n'Tis only to sit back and sway his head\\nThrough ninety odd degrees of arc, it seems,\\nBetween two metaphysical extremes.\",  \"He sits back on his fundamental butt\\nWith lifted snout and eyes (if any) shut,\\n(he almost looks religious but he's not),\\nAnd back and forth he sways from cheek to cheek,\\nAt one extreme agreeing with one Greek -\\nAt the other agreeing with another Greek\\nWhich may be thought, but only so to speak.\", \"A baggy figure, equally pathetic\\nWhen sedentary and when peripatetic.\")"

Но в нашем случае методы согласны: сравнение человека с беспокойным медведем в клетке – эмоциональный минимум стихотворения.

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

sum(afinn_vector)
## [1] 2
mean(afinn_vector)
## [1] 2
summary(afinn_vector)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       2       2       2       2       2       2

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

Результат можно передать функции plot():

plot(afinn_vector, 
     type="l", 
     main="Example Plot Trajectory",
     xlab = "Narrative Time",
     ylab= "Emotional Valence")
abline(h = 0, col = "red", lty = 2)

О других способах визуализировать результат можно узнать из виньетки к пакету.


Постройте сравнительное облако слов (негативная и позитивная лексика) для стихотворения Фроста.


12.11 Виды эмоций

Лексикон NRC дает возможность позволяет не просто оценить эмоциональную валентность, но и выявить конкретные эмоции:

nrc_data <- get_nrc_sentiment(sent_vec)

nrc_data
##   anger anticipation disgust fear joy sadness surprise trust
## 1     5           10       3    4   7       6        5    10
##   negative positive
## 1       10       14

Выбрать конкретные эмоции можно так (но результат в нашем случае не очень осмысленный):

joy_items <- which(nrc_data$joy > 0)
sent_vec[joy_items]

На графике это можно отразить при помощи базовой barplot:

barplot(
  sort(colSums(prop.table(nrc_data[, 1:8]))), 
  horiz = TRUE, 
  cex.names = 0.7, 
  las = 1, 
  main = "Emotions in Sample text", xlab="Percentage"
  )

Сложно осуждать машину за не вполне верно считанную тональность: Фрост – сложный автор. Для отзывов на Tripadvisor это может сработать лучше.

12.12 Sentimentr

В заключение еще несколько фокусов.

library(sentimentr)
bear %>% 
  get_sentences() %>% 
  sentiment_by(by=c('sentence_id'))
##     sentence_id word_count sd ave_sentiment
##  1:           1         38 NA    0.25955427
##  2:           2         17 NA   -0.06063391
##  3:           3         29 NA   -0.09284767
##  4:           4          8 NA    0.26516504
##  5:           5         18 NA    0.00000000
##  6:           6         25 NA   -0.57000000
##  7:           7         45 NA    0.14161764
##  8:           8         28 NA    0.06614378
##  9:           9         55 NA    0.46519791
## 10:          10         10 NA   -0.23717082
bear %>% 
  get_sentences() %>% 
  sentiment_by(by=c('sentence_id')) %>% 
  highlight(file = "polarity.html")


Литература

Chen, Y., and S. Skiena. 2014. “Building Sentiment Lexicons for All Major Languages.” Proceedings of 52nd Annual Meeting of the Association for Computational Linguistics, 383–89.
Silge, Julia, and David Robinson. 2017. Text Mining with r. O’Reilly. http://www.tidytextmining.com.
Создание лексикона оценочных слов русского языка РуСентилекс.” 2016. Труды Конференции OSTIS-2016, 377–82.