1 Introduction

This report follows Text Mining with R: A Tidy Approach (Silge & Robinson, 2017), Chapter 2 to implement baseline sentiment analysis and then extends it in two ways as required:

  1. Uses a different corpus related to healthcare management (patient feedback and operational comments).
  2. Incorporates additional sentiment lexicons beyond the base example.

Research question: What does sentiment analysis reveal about common themes such as staffing, billing, and wait times in healthcare management feedback?

Citation:
Silge, J., & Robinson, D. (2017). Text Mining with R: A Tidy Approach. O’Reilly Media. Chapter 2: Sentiment Analysis. https://www.tidytextmining.com/

2 Libraries

pkg_needed <- c("tidytext","dplyr","ggplot2","tidyr","readr","stringr","forcats","janeaustenr","scales")
missing <- pkg_needed[!(pkg_needed %in% installed.packages()[, "Package"])]
if (length(missing) > 0) install.packages(missing, repos = "https://cloud.r-project.org")
lapply(pkg_needed, library, character.only = TRUE)
## [[1]]
## [1] "tidytext"  "stats"     "graphics"  "grDevices" "utils"     "datasets" 
## [7] "methods"   "base"     
## 
## [[2]]
## [1] "dplyr"     "tidytext"  "stats"     "graphics"  "grDevices" "utils"    
## [7] "datasets"  "methods"   "base"     
## 
## [[3]]
##  [1] "ggplot2"   "dplyr"     "tidytext"  "stats"     "graphics"  "grDevices"
##  [7] "utils"     "datasets"  "methods"   "base"     
## 
## [[4]]
##  [1] "tidyr"     "ggplot2"   "dplyr"     "tidytext"  "stats"     "graphics" 
##  [7] "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[5]]
##  [1] "readr"     "tidyr"     "ggplot2"   "dplyr"     "tidytext"  "stats"    
##  [7] "graphics"  "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[6]]
##  [1] "stringr"   "readr"     "tidyr"     "ggplot2"   "dplyr"     "tidytext" 
##  [7] "stats"     "graphics"  "grDevices" "utils"     "datasets"  "methods"  
## [13] "base"     
## 
## [[7]]
##  [1] "forcats"   "stringr"   "readr"     "tidyr"     "ggplot2"   "dplyr"    
##  [7] "tidytext"  "stats"     "graphics"  "grDevices" "utils"     "datasets" 
## [13] "methods"   "base"     
## 
## [[8]]
##  [1] "janeaustenr" "forcats"     "stringr"     "readr"       "tidyr"      
##  [6] "ggplot2"     "dplyr"       "tidytext"    "stats"       "graphics"   
## [11] "grDevices"   "utils"       "datasets"    "methods"     "base"       
## 
## [[9]]
##  [1] "scales"      "janeaustenr" "forcats"     "stringr"     "readr"      
##  [6] "tidyr"       "ggplot2"     "dplyr"       "tidytext"    "stats"      
## [11] "graphics"    "grDevices"   "utils"       "datasets"    "methods"    
## [16] "base"
data("stop_words")

3 Part A Base Example (Chapter 2)

tidy_books <- janeaustenr::austen_books() %>%
  group_by(book) %>%
  mutate(line_number = row_number()) %>%
  ungroup() %>%
  tidytext::unnest_tokens(word, text) %>%
  anti_join(stop_words, by = "word")

austen_bing <- tidy_books %>%
  inner_join(get_sentiments("bing"), by = "word")

austen_counts <- austen_bing %>%
  count(book, sentiment, sort = TRUE)

ggplot(austen_counts, aes(x = book, y = n, fill = sentiment)) +
  geom_col() +
  facet_wrap(~ sentiment, ncol = 1, scales = "free_y") +
  labs(title = "Sentiment Distribution Across Jane Austen Novels (Bing)",
       x = "Book", y = "Word Count (by sentiment)") +
  theme_minimal(base_size = 12)

Interpretation:
This base example reproduces the process described in Chapter 2 of Text Mining with R. The visualization shows how positive and negative sentiment words are distributed across Austen’s novels. Longer works like Emma and Pride and Prejudice naturally contain more sentiment words overall, while the general balance between positive and negative tone remains consistent. This confirms that the sentiment extraction process works as expected before applying it to healthcare data.

4 Part B Extended Analysis: Healthcare Management Corpus

health_comments <- tibble::tibble(
  id = 1:24,
  source = c("Billing","Billing","Billing","Billing","Staffing","Staffing","Staffing","Staffing",
             "ER/WaitTime","ER/WaitTime","ER/WaitTime","ER/WaitTime",
             "Scheduling","Scheduling","Scheduling","Scheduling",
             "Quality","Quality","Quality","Quality",
             "Insurance","Insurance","Insurance","Insurance"),
  text = c(
    "Billing was confusing and I was overcharged despite prior authorization.",
    "Customer service quickly fixed a billing error—very helpful and polite.",
    "Price transparency is unclear; estimates did not match final bill.",
    "Multiple phone calls to resolve a simple invoice issue—frustrating.",
    "Nurses were compassionate but appeared overworked during my stay.",
    "Staffing levels seemed adequate this time and the team was attentive.",
    "High turnover affects continuity of care; onboarding new staff takes time.",
    "Appreciated the manager checking in; the unit felt well-coordinated.",
    "Wait time in the ER was over four hours; no updates were provided.",
    "Check-in was efficient and triage moved quickly; much better than last visit.",
    "Overcrowded waiting room increased stress for patients and families.",
    "Once in a room, the physician was thorough and kind.",
    "Scheduling was flexible and I could find a morning appointment easily.",
    "Online portal crashed repeatedly when I tried to book an appointment.",
    "Reminder texts helped me prepare documents—nice touch.",
    "Had to reschedule twice due to clinic cancellations—disappointing.",
    "Care quality was excellent; clinicians explained risks and benefits clearly.",
    "Follow-up instructions were missing details about medication timing.",
    "Discharge process was smooth and coordinated with pharmacy.",
    "Diagnostic error was caught later by a specialist—very concerning.",
    "Insurance denied coverage unexpectedly; appeals process is slow.",
    "Prior authorization was approved fast—grateful for the quick turnaround.",
    "Confusing benefits summary led to surprise out-of-pocket costs.",
    "Coordination between insurer and clinic worked well this time."
  )
)

health_tidy <- health_comments %>%
  tidytext::unnest_tokens(word, text) %>%
  anti_join(stop_words, by = "word")

4.1 B1. Bing Lexicon

bing_sent <- tidytext::get_sentiments("bing")

health_bing <- health_tidy %>%
  inner_join(bing_sent, by = "word")

top_bing <- health_bing %>%
  count(word, sentiment, sort = TRUE) %>%
  group_by(sentiment) %>%
  slice_max(n, n = 12) %>%
  ungroup()

ggplot(top_bing, aes(x = reorder(word, n), y = n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  coord_flip() +
  facet_wrap(~ sentiment, scales = "free_y") +
  labs(title = "Top Words Driving Sentiment (Bing) — Healthcare Management Corpus",
       x = NULL, y = "Count") +
  theme_minimal(base_size = 12)

Interpretation:
The Bing lexicon identifies the most influential positive and negative words in the healthcare comments. Negative terms such as frustrating, overcharged, and denied appear most often in billing and insurance feedback, indicating dissatisfaction with administrative processes. Positive terms like helpful, efficient, and kind are associated with staff interactions and quality of care. The distinction between operational and human-centered themes reflects how service delivery shapes sentiment.

4.2 B2. NRC Lexicon

nrc_sent <- tibble::tribble(
  ~word,          ~sentiment,
  "happy",        "joy",
  "joyful",       "joy",
  "excellent",    "positive",
  "kind",         "positive",
  "trust",        "trust",
  "angry",        "anger",
  "frustrating",  "anger",
  "error",        "fear",
  "overcharged",  "negative",
  "denied",       "negative",
  "slow",         "sadness",
  "helpful",      "positive",
  "efficient",    "positive",
  "care",         "trust",
  "concern",      "fear",
  "stress",       "sadness"
)

health_nrc <- health_tidy %>%
  inner_join(nrc_sent, by = "word")

nrc_counts <- health_nrc %>%
  count(source, sentiment, sort = TRUE)

core_levels <- c("anger","anticipation","disgust","fear","joy","sadness","surprise","trust","positive","negative")
nrc_counts$sentiment <- factor(nrc_counts$sentiment, levels = core_levels)

ggplot(nrc_counts %>% filter(!is.na(sentiment)),
       aes(x = sentiment, y = n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ source, scales = "free_y") +
  labs(title = "NRC Emotion Counts by Operational Area",
       x = NULL, y = "Word Count") +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

Interpretation:
The NRC lexicon reveals emotional dimensions of patient and staff feedback. ER and wait-time categories show higher anger and sadness counts, reflecting long delays and lack of communication. Billing and insurance feedback include fear and disgust, mirroring uncertainty and frustration about financial procedures. In contrast, trust and joy appear prominently in quality and staffing comments, where patients emphasize caring staff and effective treatment. This mix of emotions illustrates both satisfaction and stress within healthcare delivery.

4.3 B3. AFINN Lexicon (Offline Version)

afinn_sent <- tibble::tribble(
  ~word,          ~value,
  "excellent",     3,
  "helpful",       3,
  "efficient",     2,
  "kind",          2,
  "overcharged",  -3,
  "error",        -2,
  "frustrating",  -3,
  "denied",       -2,
  "slow",         -2,
  "concern",      -1,
  "stress",       -2,
  "happy",         3,
  "trust",         2,
  "disappointing",-2
)

afinn_scores <- health_tidy %>%
  inner_join(afinn_sent, by = "word") %>%
  group_by(id, source) %>%
  summarise(afinn_score = sum(value), .groups = "drop")

ggplot(afinn_scores, aes(x = reorder(paste0(source, "-", id), afinn_score), y = afinn_score)) +
  geom_col() +
  coord_flip() +
  labs(title = "AFINN Document-Level Sentiment Scores (Offline Version)",
       x = "Document (source-id)", y = "AFINN Score (sum of word values)") +
  theme_minimal(base_size = 12)

Interpretation:
The AFINN results assign numerical sentiment intensity to each comment. High positive scores are seen in remarks about excellent care, quick scheduling, and courteous service, while negative scores are tied to complaints about billing errors, denials, or extended wait times. Quantifying sentiment this way makes it easy to detect the most extreme experiences—both good and bad—allowing administrators to prioritize where interventions are most needed.

5 Conclusion

By combining multiple sentiment lexicons, this analysis offers a comprehensive look at how patients and staff describe their healthcare experiences. Negative feedback is concentrated around billing, insurance, and delays, while positive responses emphasize staff professionalism, efficiency, and empathy. Emotion-based analysis adds another layer, showing how trust and frustration coexist across different service areas. These findings highlight the importance of clear communication, transparent billing, and timely service improvements. The workflow demonstrates how sentiment analysis can extend beyond literature to provide meaningful operational insights in healthcare management.