Code along 04
2026-09-21
By the end of today, you’ll be able to:
get_sentiments()Last week you got text into tidy format and stripped out stop words:
tidy_books <- austen_books() |> # get books
group_by(book) |> # do separately for each book
mutate(linenumber = row_number(), # add line numbers
chapter = cumsum(str_detect(text, # and chapter numbers
regex("^chapter [\\divxlc]", ignore_case = TRUE)))) |>
ungroup() |> # stop grouping by book
unnest_tokens(word, text) |> # tokenize
anti_join(stop_words) # remove stop words
head(tidy_books) # look at a few rows# A tibble: 6 × 4
book linenumber chapter word
<fct> <int> <int> <chr>
1 Sense & Sensibility 1 0 sense
2 Sense & Sensibility 1 0 sensibility
3 Sense & Sensibility 3 0 jane
4 Sense & Sensibility 3 0 austen
5 Sense & Sensibility 5 0 1811
6 Sense & Sensibility 10 1 chapter
Once words carry a pos/neg tag, you can ask questions like
We run get_sentiments("lexicon name") to load in the lexicon through the textdata package. This may prompt to confirm a download the first time they’re called in a session.
Lets look at each one first.
Liu et al. — binary: positive or negative
Mohammad & Turney — 10 categories: positive, negative, anger, anticipation, disgust, fear, joy, sadness, surprise, trust
Nielsen — numeric score. -5 (very negative) to +5 (very positive)
inner_join()This time we keep the matches instead of dropping them. So only words with sentiments are retained.
# A tibble: 6 × 5
book linenumber chapter word sentiment
<fct> <int> <int> <chr> <chr>
1 Sense & Sensibility 16 1 respectable positive
2 Sense & Sensibility 18 1 advanced positive
3 Sense & Sensibility 20 1 death negative
4 Sense & Sensibility 21 1 loss negative
5 Sense & Sensibility 25 1 comfortably positive
6 Sense & Sensibility 28 1 goodness positive
# A tibble: 297 × 2
word n
<chr> <int>
1 friend 166
2 hope 143
3 happy 125
4 love 117
5 deal 92
6 found 92
7 happiness 76
8 pretty 68
9 true 66
10 comfort 65
# ℹ 287 more rows
Look closely: “found” and “present” are in there too — not exactly overflowing with joy.
Try it yourself
Find the "trust" words in "Pride & Prejudice" using the nrc lexicon.
Answer
# A tibble: 405 × 2
word n
<chr> <int>
1 hope 121
2 father 116
3 mother 112
4 friend 104
5 happy 83
6 aunt 78
7 sir 78
8 brother 66
9 found 66
10 marriage 66
# ℹ 395 more rows
Create a new variable contribution as the frequency of word occurrence (n) times the numeric sentiment value of the word.
# A tibble: 6 × 4
word value n contribution
<chr> <dbl> <int> <dbl>
1 miss -2 26 -52
2 poor -2 11 -22
3 dear 2 9 18
4 success 2 6 12
5 pretty 1 5 5
6 affection 3 4 12
Is there a strong sentiment (repeated positive or negative words) in this chapter?
afinn_words |>
mutate(label = if_else(abs(contribution) >= 6, word, "")) |>
ggplot(aes(n, value, color = value > 0, size = abs(contribution))) +
geom_point(alpha = 0.75, show.legend = FALSE) +
geom_text(aes(label = label), check_overlap = TRUE,
show.legend = FALSE, vjust = -0.7) +
scale_y_continuous(breaks = -5:5) +
labs(x = "Count in chapter", y = "AFINN score")Count the number of times the words appear, but now the sentiment is added.
# A tibble: 6 × 3
word sentiment n
<chr> <chr> <int>
1 miss negative 26
2 poor negative 11
3 success positive 6
4 pretty positive 5
5 affection positive 4
6 lucky positive 4
Allowing us to compare the most frequent positive and negative words.
bing_words |>
group_by(sentiment) |>
slice_max(n, n = 5) |>
ungroup() |>
mutate(word = reorder_within(word, n, sentiment)) |>
ggplot(aes(n, word, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment, scales = "free_y") +
scale_y_reordered() +
labs(x = "Count in chapter", y = NULL)nrc_words |>
group_by(sentiment) |>
slice_max(n, n = 5) |>
ungroup() |>
mutate(word = reorder_within(word, n, sentiment)) |>
ggplot(aes(n, word, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~sentiment, scales = "free_y", nrow=2) +
scale_y_reordered() +
labs(x = "Count in chapter", y = NULL)Same text, different measuring tools.
# A tibble: 4 × 3
sentiment n lexicon
<chr> <int> <chr>
1 negative 4781 bing
2 positive 2005 bing
3 negative 3316 nrc
4 positive 2308 nrc
That means the “same” passage can look different before we even get to questions of context.
Try it yourself
Add "miss" to a custom stop-word list (same pattern as last week), rebuild bing_words, and see the negative counts change.
not happyvery happybarely happyhappy, but worriedsentimentrThe single sentiment() function tokenizes on sentences AND calculates a sentence level score.
Key: <element_id, sentence_id>
element_id sentence_id word_count sentiment
<int> <int> <int> <num>
1: 1 1 6 0.30618622
2: 2 1 7 -0.28347335
3: 2 2 2 -0.35355339
4: 3 1 7 0.51025204
5: 3 2 2 0.00000000
6: 4 1 7 0.05669467
7: 4 2 2 0.00000000
8: 5 1 11 -0.18844459
9: 5 2 2 -0.53033009
Add a couple data wrangling steps so we can see the sentences along with the scores.
1pp_sentences <- get_sentences(prideprejudice) |> unlist()
2p_and_p_sentences <- tibble(sentence_id = seq_along(pp_sentences),
sentence = pp_sentences)
3scores <- sentiment(p_and_p_sentences$sentence) |>
mutate(sentence_id = element_id) |>
select(sentence_id, score = sentiment)
4pp_sentence_scores <- p_and_p_sentences |>
left_join(scores, by = "sentence_id")[1] "Elizabeth lifted up her eyes in amazement, but was too much oppressed"
[2] "only one who shed tears; but she did weep from vexation and envy."
[3] "ached acutely."
[4] "most disagreeable."
[5] "sure it will be too much for Kitty."
Sentence and score shown in two steps due to word wrapping.
[1] "chosen so much more advantageously in many respects."
[2] "more pleasant aspect; but she soon saw that her friend had an excellent"
[3] "But gracious"
[4] "\"Yes, very handsome.\""
[5] "acceptance of the invitation was most ready and grateful."