10 A Assignment

Author

Antik Salimullah

Jane Austen Sentiment Analysis

library(janeaustenr)
library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(stringr)
library(tidytext)
library(tidyr)
library(textdata)

tidy_books <- austen_books() %>%
  group_by(book) %>%
  mutate(
    linenumber = row_number(),
    chapter = cumsum(str_detect(text, 
                                regex("^chapter [\\divxlc]", 
                                      ignore_case = TRUE)))) %>%
  ungroup() %>%
  unnest_tokens(word, text)
nrc_joy <- get_sentiments("nrc") %>% 
  filter(sentiment == "joy")

tidy_books %>%
  filter(book == "Emma") %>%
  inner_join(nrc_joy) %>%
  count(word, sort = TRUE)
Joining with `by = join_by(word)`
# A tibble: 301 × 2
   word          n
   <chr>     <int>
 1 good        359
 2 friend      166
 3 hope        143
 4 happy       125
 5 love        117
 6 deal         92
 7 found        92
 8 present      89
 9 kind         82
10 happiness    76
# ℹ 291 more rows
library(tidyr)

jane_austen_sentiment <- tidy_books %>%
  inner_join(get_sentiments("bing")) %>%
  count(book, index = linenumber %/% 80, sentiment) %>%
  pivot_wider(names_from = sentiment, values_from = n, values_fill = 0) %>% 
  mutate(sentiment = positive - negative)
Joining with `by = join_by(word)`
Warning in inner_join(., get_sentiments("bing")): Detected an unexpected many-to-many relationship between `x` and `y`.
ℹ Row 435434 of `x` matches multiple rows in `y`.
ℹ Row 5051 of `y` matches multiple rows in `x`.
ℹ If a many-to-many relationship is expected, set `relationship =
  "many-to-many"` to silence this warning.
library(ggplot2)

ggplot(jane_austen_sentiment, aes(index, sentiment, fill = book)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~book, ncol = 2, scales = "free_x")

Citation for the above code

Silge, Julia, and David Robinson. 2017. Text Mining with R: A Tidy Approach. O’Reilly Media.

Original code: https://www.tidytextmining.com/sentiment

Load Giants ESPN Articles

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.1     ✔ readr     2.2.0
✔ lubridate 1.9.5     ✔ tibble    3.3.1
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(tidytext)

giants_talent <- read_lines("Jaxson_Dart_all_angles .txt")
giants_injury <- read_lines("dart_knee_surgery.txt")

Tokenize the Articles

giants_text <- tibble(
  article = c(
    rep("Talent", length(giants_talent)),
    rep("Injury", length(giants_injury))
  ),
  text = c(giants_talent, giants_injury)
)

giants_tidy <- giants_text %>%
  unnest_tokens(word, text)

Identify Positive and Negative words

giants_bing <- giants_tidy %>%
  inner_join(get_sentiments("bing"), by = "word")

giants_bing %>%
  count(article, sentiment)
# A tibble: 4 × 3
  article sentiment     n
  <chr>   <chr>     <int>
1 Injury  negative     26
2 Injury  positive     33
3 Talent  negative     23
4 Talent  positive     80

Sentiment Metrics

giants_sentiment <- giants_bing %>%
  count(article, sentiment) %>%
  pivot_wider(
    names_from = sentiment,
    values_from = n,
    values_fill = 0
  ) %>%
  mutate(sentiment_score = positive - negative)

giants_sentiment
# A tibble: 2 × 4
  article negative positive sentiment_score
  <chr>      <int>    <int>           <int>
1 Injury        26       33               7
2 Talent        23       80              57

Visualize

library(ggplot2)

ggplot(giants_sentiment,
       aes(x = article, y = sentiment_score, fill = article)) +
  geom_col() +
  labs(
    title = "Sentiment Analysis of ESPN Giants Articles",
    x = "Article",
    y = "Sentiment Score (Positive - Negative)"
  ) +
  theme_minimal() +
  guides(fill = "none")

Using Vader (lexicon) for Sentiment

library(vader)

giants_vader <- vader_df(c(
  paste(giants_talent, collapse = " "),
  paste(giants_injury, collapse = " ")
))

giants_vader <- giants_vader %>%
  mutate(article = c("Talent", "Injury")) %>%
  select(article, compound, pos, neg, neu)

giants_vader
  article compound   pos   neg   neu
1  Talent    1.000 0.154 0.033 0.813
2  Injury    0.972 0.105 0.088 0.807

Comparing Vader and Bing

sentiment_comparison <- giants_sentiment %>%
  select(article, bing_score = sentiment_score) %>%
  left_join(
    giants_vader %>%
      mutate(vader_net = pos - neg) %>%
      select(article, compound, vader_net),
    by = "article"
  )

sentiment_comparison
# A tibble: 2 × 4
  article bing_score compound vader_net
  <chr>        <int>    <dbl>     <dbl>
1 Injury           7    0.972     0.017
2 Talent          57    1         0.121

Discussion and Findings

The sentiment analysis shows that both ESPN articles contain more positive than negative language. Using the Bing lexicon, the article discussing Jaxson Dart’s talent received a sentiment score of +57, compared to +7 for the article discussing his injury.

VADER produced similar findings, with positive-minus-negative scores of 0.121 for the talent article and 0.017 for the injury article. Both methods suggest that the talent article contains more positive language.

One limitation is that the articles may differ in length, which affects raw Bing scores. Additionally, sentiment analysis does not always capture the full context of sports journalism.