Rationale

Framing theory suggests that depending on how information is presented, it can significantly influence how it is understood by a person or audience. By emphasizing certain aspects of an issue or downplaying others, communicators, like the media or even political figures, can help shape public perception of a topic and impact how people respond, their attitudes and the choices they make.

In our post-pandemic world, news organizations like AP News continue to report on COVID-19, COVID-19 boosters and vaccines, new health guidelines, misinformation and political controversy about vaccinations.

In this assignment, I used framing theory to look at how AP News reported on COVID-19 vaccines and the political concerns and misinformation around it from January 2025 through October 2025. (Framing theory suggests that the way AP News presented its vaccine stories could influence public perception).

My results will show whether AP News coverage was more focused on science-based information (represented by the blue health line in the graph) or political controversy (represented by the red hesitancy line in the graph).

Hypothesis

Weekly APNews.com coverage volume of the most recent COVID-19 safety and guidelines (science-based information) and the misinformation and political controversies around the vaccine will differ during the first 40 weeks (January through October) of 2025.

Variables & method

The dependent variable is the weekly count of APNews.com articles about the latest COVID-19 vaccine, including both science-based coverage and coverage of controversies. This is represented on the graph as weekly count of AP News stories. The dependent variable was measured continuously during a specific timeframe, January through September 2025.

The independent variable is the topic, which is categorized as either science-based coverage (health) or political controversy (hesitancy). Key words and phrases used to identify stories about the latest COVID-19 booster that were science-based include: “booster,” “mRNA,” “CDC,” “COVID vaccine,” “Pfizer,” “Moderna,” “vaccine,” and “effective.” Key words and phrases used to identify stories about political controversy surrounding the vaccine include: “antivax,” “misinformation,” “Robert R. Kennedy Jr.,” “fear,” “Trump,” “risk,” and “efficacy.”

Eventually, I will use additional tests to assess the statistical significance of coverage volume between the science-based articles and the political controversy.

Results & discussion

The graph below compares weekly frequencies of AP News stories and news coverage of health (blue line) and hesitancy (red line) in relation to COVID-19 and the latest COVID-19 booster. Coverage begins in January 2025 through the end of September 2025, totaling the first 40 weeks of the year.

In the first half of the year, coverage of COVID-19 (health/blue line) and hesitancy regarding the latest booster (red line) was minimal.

In Week 26 there was a spike in the blue line (health), which is expected since the Centers for Disease Control and Prevention (CDC) announced the latest COVID-19 booster was available for most adults. Coverage of the latest COVID-19 vaccine was two-thirds higher, (about 67%), than stories indicating vaccine hesitancy.

In Week 36, coverage of the COVID-19 vaccine (blue line) spiked again, which coincides with the CDC announcing its latest guidelines and recommendations for the COVID-19 vaccination.

Overall, the interactive graph supports that health-related coverage focused on COVID-19, vaccines and CDC guidelines (science-based) was more prevalent than stories covering any kind of hesitancy about vaccines and updated guidelines from the CDC, at least during the first 40 weeks of 2025 (January through September), which would suggest the potential public influence would be positive.


## **Code**
This is the code that produced the figure. 


``` r
# ============================================
# APNews text analysis (Framing Theory version)
# ============================================

# ============================================
# --- Load required libraries ---
# ============================================

if (!require("tidyverse")) install.packages("tidyverse")
if (!require("tidytext")) install.packages("tidytext")

library(tidyverse)
library(tidytext)

# ============================================
# --- Load the APNews data ---
# ============================================

# Read the data from the web
FetchedData <- readRDS(url("https://github.com/drkblake/Data/raw/refs/heads/main/APNews.rds"))
# Save the data on your computer
saveRDS(FetchedData, file = "APNews.rds")
# remove the downloaded data from the environment
rm (FetchedData)

APNews <- readRDS("APNews.rds")

# ============================================
# --- Define and apply FilterTopic ---
# ============================================

# --- Define FilterTopic phrases --- CHANGE THESE KEY WORDS
FilterTopic_phrases <- c(
  "COVID",
  "COVID-19",
  "coronavirus",
  "pandemic",
  "virus",
  "vaccine",
  "vaccination",
  "SARS-CoV-2"
)

# --- Escape regex special characters ---
escaped_FilterTopic <- str_replace_all(
  FilterTopic_phrases,
  "([\\^$.|?*+()\\[\\]{}\\\\])",
  "\\\\\\1"
)

# --- Build whole-word/phrase regex pattern ---
FilterTopic_pattern <- paste0("\\b", escaped_FilterTopic, "\\b", collapse = "|")

# --- Flag stories matching the FilterTopic ---
APNews <- APNews %>%
  mutate(
    Full.Text.clean = str_squish(Full.Text),
    FilterTopic = if_else(
      str_detect(Full.Text.clean, regex(FilterTopic_pattern, ignore_case = TRUE)),
      "Yes",
      "No"
    )
  )

# --- Create a TopicNews data frame consisting only of FilterTopic stories ---
TopicNews <- APNews %>%
  filter(FilterTopic == "Yes")

# ============================================
# --- Flag Topic1-related stories (within TopicNews) ---
# ============================================

# --- Define Topic1 phrases --- PICK WORDS THAT GO ALONG WITH THE TOPIC
phrases <- c(
  "antivax",
  "misinformation",
  "Robert R. Kennedy Jr.",
  "fear",
  "Trump",
  "risk",
  "efficacy"
)

# --- Escape regex special characters ---
escaped_phrases <- str_replace_all(
  phrases,
  "([\\^$.|?*+()\\[\\]{}\\\\])",
  "\\\\\\1"
)

# --- Build pattern and apply matching ---
pattern <- paste0("\\b", escaped_phrases, "\\b", collapse = "|")

TopicNews <- TopicNews %>%
  mutate(
    Topic1 = if_else(
      str_detect(Full.Text.clean, regex(pattern, ignore_case = TRUE)),
      "Yes",
      "No"
    )
  )

# ============================================
# --- Flag Topic2-related stories (within TopicNews) ---
# ============================================

# --- Define Topic2 phrases ---
phrases <- c(
  "booster",
  "mRNA",
  "CDC",
  "effective",
  "Covid vaccine",
  "Pfizer", 
  "Moderna",
  "vaccine"
)

# --- Escape regex special characters ---
escaped_phrases <- str_replace_all(
  phrases,
  "([\\^$.|?*+()\\[\\]{}\\\\])",
  "\\\\\\1"
)

# --- Build pattern and apply matching ---
pattern <- paste0("\\b", escaped_phrases, "\\b", collapse = "|")

TopicNews <- TopicNews %>%
  mutate(
    Topic2 = if_else(
      str_detect(Full.Text.clean, regex(pattern, ignore_case = TRUE)),
      "Yes",
      "No"
    )
  )

# ============================================
# --- Visualize weekly counts of Topic1- and Topic2-related stories ---
# ============================================

if (!require("plotly")) install.packages("plotly")
library(plotly)

# --- Summarize weekly counts for Topic1 = "Yes" ---
Topic1_weekly <- TopicNews %>%
  filter(Topic1 == "Yes") %>%
  group_by(Week) %>%
  summarize(Count = n(), .groups = "drop") %>%
  mutate(Topic = "Hesitancy") # Note custom Topic1 label

# --- Summarize weekly counts for Topic2 = "Yes" ---
Topic2_weekly <- TopicNews %>%
  filter(Topic2 == "Yes") %>%
  group_by(Week) %>%
  summarize(Count = n(), .groups = "drop") %>%
  mutate(Topic = "Health") # Note custom Topic2 label

# --- Combine both summaries into one data frame ---
Weekly_counts <- bind_rows(Topic2_weekly, Topic1_weekly)

# --- Fill in missing combinations with zero counts ---
Weekly_counts <- Weekly_counts %>%
  tidyr::complete(
    Topic,
    Week = full_seq(range(Week), 1),  # generate all week numbers
    fill = list(Count = 0)
  ) %>%
  arrange(Topic, Week)

# --- Create interactive plotly line chart ---
FR1 <- plot_ly(
  data = Weekly_counts,
  x = ~Week,
  y = ~Count,
  color = ~Topic,
  colors = c("steelblue", "firebrick"),
  type = "scatter",
  mode = "lines+markers",
  line = list(width = 2),
  marker = list(size = 6)
) %>%
  layout(
    title = "Weekly Counts of Topic1- and Topic2-Related Stories within the FilterTopic Dataset",
    xaxis = list(
      title = "Week Number (starting with Week 1 of 2025)",
      dtick = 1
    ),
    yaxis = list(title = "Number of Articles"),
    legend = list(title = list(text = "Topic")),
    hovermode = "x unified"
  )

# ============================================
# --- Show the chart ---
# ============================================

FR1