This expanded second-level agenda-setting code allows you to plot volume for the overall topic as well as two attributes of the topic. For example, here’s volume for “Education” (See Topic1_phrases / blue line), and the attributes “Money” (See Topic2_phrases / green line) and “Violence” (See Topic3_phrases / green line).

Code:

# ============================================
# APNews text analysis (Second-level agenda-setting theory version)
# ============================================

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

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

library(tidyverse)
library(tidytext)
library(plotly)

# ============================================
# --- Define Custom Topic Labels ---
# ============================================

# You can change these labels to anything you want.
# They will appear in the chart legend and title.
topic_labels <- list(
  Topic1 = "All education",
  Topic2 = "Violence",
  Topic3 = "Money"
)

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

FetchedData <- readRDS(url("https://github.com/drkblake/Data/raw/refs/heads/main/APNews.rds"))
saveRDS(FetchedData, file = "APNews.rds")
rm(FetchedData)

APNews <- readRDS("APNews.rds")

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

FilterTopic_phrases <- c(
  "education", "educational", "educator", "educators",
  "school", "schools", "classroom", "classrooms",
  "teacher", "teachers"
)

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

FilterTopic_pattern <- paste0("\\b", escaped_FilterTopic, "\\b", collapse = "|")

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"
    )
  )

TopicNews <- APNews %>% filter(FilterTopic == "Yes")

# ============================================
# --- Define Topic1 (Education) ---
# ============================================

Topic1_phrases <- c(
  "education", "educational", "educator", "educators",
  "school", "schools", "classroom", "classrooms",
  "teacher", "teachers"
)

Topic1_pattern <- paste0("\\b", str_replace_all(Topic1_phrases,
                                                "([\\^$.|?*+()\\[\\]{}\\\\])", "\\\\\\1"
), "\\b", collapse = "|")

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

# ============================================
# --- Define Topic2 (Shootings) ---
# ============================================

Topic2_phrases <- c(
  "shoot", "shooting", "shooter", "shot",
  "gun", "rifle", "weapon", "kill", "killed"
)

Topic2_pattern <- paste0("\\b", str_replace_all(Topic2_phrases,
                                                "([\\^$.|?*+()\\[\\]{}\\\\])", "\\\\\\1"
), "\\b", collapse = "|")

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

# ============================================
# --- Define Topic3 (Economy) ---
# ============================================

Topic3_phrases <- c(
  "fund", "funds", "funding",
  "grant", "grants", "pay",
  "salary", "salaries", "service",
  "services", "lunch", "lunches",
  "special education"
)

Topic3_pattern <- paste0("\\b", str_replace_all(Topic3_phrases,
                                                "([\\^$.|?*+()\\[\\]{}\\\\])", "\\\\\\1"
), "\\b", collapse = "|")

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

# ============================================
# --- Summarize weekly counts for all topics ---
# ============================================

Topic1_weekly <- TopicNews %>%
  filter(Topic1 == "Yes") %>%
  group_by(Week) %>%
  summarize(Count = n(), .groups = "drop") %>%
  mutate(Topic = topic_labels$Topic1)

Topic2_weekly <- TopicNews %>%
  filter(Topic2 == "Yes") %>%
  group_by(Week) %>%
  summarize(Count = n(), .groups = "drop") %>%
  mutate(Topic = topic_labels$Topic2)

Topic3_weekly <- TopicNews %>%
  filter(Topic3 == "Yes") %>%
  group_by(Week) %>%
  summarize(Count = n(), .groups = "drop") %>%
  mutate(Topic = topic_labels$Topic3)

Weekly_counts <- bind_rows(Topic1_weekly, Topic2_weekly, Topic3_weekly) %>%
  tidyr::complete(
    Topic,
    Week = full_seq(range(Week), 1),
    fill = list(Count = 0)
  ) %>%
  arrange(Topic, Week)

# ============================================
# --- Visualize the results ---
# ============================================

AS2 <- plot_ly(
  data = Weekly_counts,
  x = ~Week,
  y = ~Count,
  color = ~Topic,
  colors = c("steelblue", "seagreen", "firebrick"),
  type = "scatter",
  mode = "lines+markers",
  line = list(width = 2),
  marker = list(size = 6)
) %>%
  layout(
    title = paste(
      "Weekly Counts of",
      paste(unlist(topic_labels), collapse = ", "),
      "Stories (Filtered 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"
  )

# ============================================
# --- Display the chart ---
# ============================================

AS2