The source codes and contents come from the E-Learning DataCamp: Sentiment Analysis in R: The Tidy Way Enjoy
Intro
- The next real-world text exploration uses tragedies and comedies by Shakespeare to show how sentiment analysis can lead to insight into differences in word use. You will learn how to transform raw text into a tidy format for further analysis.
1. To be, or not to be
- The shakespeare dataset contains three columns:
- title, the title of a Shakespearean play,
- type, the type of play, either tragedy or comedy, and
- text, a line from that play. This data frame contains the entire texts of six plays.
| library(tidytext) |
| library(dplyr) |
| load("data/shakespeare.rda") |
| glimpse(shakespeare) |
| ## Observations: 25,888 |
| ## Variables: 3 |
| ## $ title <chr> "The Tragedy of Romeo and Juliet", "The Tragedy of Romeo... |
| ## $ type <chr> "Tragedy", "Tragedy", "Tragedy", "Tragedy", "Tragedy", "... |
| ## $ text <chr> "The Complete Works of William Shakespeare", "", "The Tr... |
| |
| shakespeare %>% |
| count(title, type) |
2. Unnesting from text to word
he shakespeare dataset is not yet compatible with tidy tools. You need to first break the text into individual tokens (the process of tokenization); a token is a meaningful unit of text for analysis, in many cases, just synonymous with a single word. You also need to transform the text to a tidy data structure with one token per row. You can use tidytext’s unnest_tokens() function to accomplish all of this at once.
| library(tidytext) |
| |
| tidy_shakespeare <- shakespeare %>% |
| group_by(title) %>% |
| mutate(linenumber = row_number()) %>% |
| unnest_tokens(word, text) %>% |
| ungroup() |
| |
| tidy_shakespeare %>% |
| count(word, sort = TRUE) |
- Notice how the most common words in the data frame are words like “the”, “and”, and “i” that have no sentiments associated with them. In the next exercise, you’ll join the data with a lexicon to implement sentiment analysis.
3. Sentiment analysis of Shakespeare
After transforming the text of these Shakespearean plays to a tidy text dataset in the last exercise, the resulting data frame tidy_shakespeare is ready for sentiment analysis with such an approach. Once you have performed the sentiment analysis, you can find out how many negative and positive words each play has with just one line of code.
| shakespeare_sentiment <- tidy_shakespeare %>% |
| inner_join(get_sentiments("bing")) |
## Joining, by = "word"
| shakespeare_sentiment %>% |
| count(title, sentiment) |
- Passing two variables to count() returns the count n for each unique combination of the two variables. In this case, you have 6 plays and 2 sentiments, so count() returns 6 x 2 = 12 rows.
4. Tragedy or comedy?
- Which plays have a higher percentage of negative words? Do the tragedies have more negative words than the comedies?
| sentiment_counts <- tidy_shakespeare %>% |
| inner_join(get_sentiments("bing")) %>% |
| |
| count(title, type, sentiment) |
## Joining, by = "word"
| sentiment_counts %>% |
| group_by(title) %>% |
| mutate(total = sum(n), |
| percent = n / total) %>% |
| filter(sentiment == "negative") %>% |
| arrange(percent) |
- Looking at the percent column of your output, you can see that tragedies do in fact have a higher percentage of negative words!
5. Most common positive and negative words
- Now you can explore which specific words are driving these sentiment scores. Which are the most common positive and negative words in these plays?
| word_counts <- tidy_shakespeare %>% |
| inner_join(get_sentiments("bing")) %>% |
| count(word, sentiment) |
## Joining, by = "word"
| top_words <- word_counts %>% |
| group_by(sentiment) %>% |
| top_n(10) %>% |
| ungroup() %>% |
| mutate(word = reorder(word, n)) |
#
| |
| library(ggplot2) |
| ggplot(top_words, aes(x = word, y = n, fill = sentiment)) + |
| geom_col(show.legend = FALSE) + |
| facet_wrap(~sentiment, scales = "free") + |
| coord_flip() |

- Death is pretty negative and love is positive, but are there words in that list that had different connotations during Shakespeare’s time? Do you see a word that the lexicon has misidentified? - The word "wilt" was used differently in Shakespeare’s time and was not negative; the lexicon has misidentified it. For example, from Romeo and Juliet, "For thou wilt lie upon the wings of night". It is important to explore the details of how words were - cored when performing sentiment analyses.
6. Word contributions by play
- You will also practice using a different sentiment lexicon, the “afinn” lexicon in which words have a score from -5 to 5. Different lexicons take different approaches to quantifying the emotion/opinion content of words.
- Which words contribute to the overall sentiment in which plays?
| tidy_shakespeare %>% |
| count(title, word, sort = TRUE) %>% |
| inner_join(get_sentiments("afinn")) %>% |
| filter(title == "The Tragedy of Macbeth", score < 0) |
- Notice the use of words specific to Macbeth like “bloody”.
7. Calculating a contribution score
- you can calculate a relative contribution for each word in each play. This contribution can be found by multiplying the score for each word by the times it is used in each play and divided by the total words in each play.
| sentiment_contributions <- tidy_shakespeare %>% |
| count(title, word, sort = TRUE) %>% |
| inner_join(get_sentiments("afinn")) %>% |
| group_by(title) %>% |
| mutate(contribution = (n * score) / sum(n)) %>% |
| ungroup() |
## Joining, by = "word"
- Notice that “hero” shows up in your results there; that is the name of one of the characters in “Much Ado About Nothing”.
8. Alas, poor Yorick!
| sentiment_contributions %>% |
| |
| filter(title == "Hamlet, Prince of Denmark") %>% |
| |
| arrange(contribution) |
- Arrange the most positive words
| sentiment_contributions %>% |
| |
| filter(title == "The Merchant of Venice") %>% |
| |
| arrange(desc(contribution)) |
- These are definitely characteristic words for these two plays.
9. Sentiment changes through a play
- We will start by first implementing sentiment analysis using inner_join(), and then use count() with four arguments:
- title,
- type,
- an index that will section together lines of the play, and
- sentiment.
- After these lines of code, you will have the number of positive and negative words used in each index-ed section of the play. These sections will be 70 lines long in your analysis here. You want a chunk of text that is not too small (because then the sentiment changes will be very noisy) and not too big (because then you will not be able to see plot structure). In an analysis of this type you may need to experiment with what size chunks to make; sections of 70 lines works well for these plays.
| tidy_shakespeare %>% |
| inner_join(get_sentiments("bing")) %>% |
| count(title, |
| type, |
| index = linenumber %/% 70, |
| sentiment) |
- This is the first step in looking at narrative arcs.
10. Calculating net sentiment
The next steps involve spread() from the tidyr package. After these lines of code, you will have the net sentiment in each index-ed section of the play; net sentiment is the negative sentiment subtracted from the positive sentiment.
| |
| library(tidyr) |
| |
| tidy_shakespeare %>% |
| inner_join(get_sentiments("bing")) %>% |
| count(title, type, index = linenumber %/% 70, sentiment) %>% |
| spread(sentiment, n, fill = 0) %>% |
| mutate(sentiment = positive - negative) |
You are closer to plotting the sentiment through these plays.
11. Visualizing narrative arcs
you will continue to build on your manipulations of this text dataset and visualize the results of this sentiment analysis.
| library(tidyr) |
| library(ggplot2) |
| |
| tidy_shakespeare %>% |
| inner_join(get_sentiments("bing")) %>% |
| count(title, type, index = linenumber %/% 70, sentiment) %>% |
| spread(sentiment, n, fill = 0) %>% |
| mutate(sentiment = positive - negative) %>% |
| ggplot(aes(x = index, |
| y = sentiment, |
| fill = type)) + |
| geom_col() + |
| facet_wrap(~ title, scales = "free_x") |
## Joining, by = "word"

- These plots show how sentiment changes through these plays. Notice how the comedies have happier endings and more positive sentiment than the tragedies.