Agenda-setting theory says that the issues the news media cover the most tend to become the issues people see as most important. McCombs and Shaw (1972) first tested this idea during the 1968 presidential election in Chapel Hill, North Carolina. Their work built on an earlier point from Cohen (1963): the media may not always tell people what to think, but they are very good at telling people what to think about.
An important part of this theory is that the media agenda has limited space. News outlets only have so many reporters, so much airtime, and so much room on a homepage. Because of this, issues compete with each other for attention. Zhu (1992) called this the “zero-sum” side of agenda-setting. When one issue gets more coverage, another issue may get less. At other times, two issues may trade places, or they may stay at about the same level.
Using first-level agenda-setting theory, my project will compare how much attention the media gave to two related but different criminal justice issues: police reform and violent crime. Both issues deal with public safety, and both are regular parts of the national political conversation, including in 2026, a midterm election year. However, they look at public safety from different sides. Police reform coverage focuses on how law enforcement behaves, including misconduct, use of force, accountability, and oversight. Violent crime coverage focuses on the threat of crime itself, including shootings, homicides, and assaults. Because these issues can push public attention in opposite directions, toward holding police accountable or toward supporting stronger policing, they are a good example of how competing issues share space on the media agenda.
Specifically, this analysis will compare the number of stories per week that MediaCloud.org’s United States - National news collection gathered about each issue, from the week starting Jan. 4, 2026, through the week ending Oct. 24, 2026. The results will help show whether attention to one side of the public safety debate comes at the expense of the other.
Average weekly coverage volume of police reform and violent crime differed during the first 10 months of 2026.
The dependent variable is weekly coverage volume. It is measured as the number of stories published each week that contain the search terms in each issue’s MediaCloud.org query.
The independent variable is issue. It has two categories, “Police Reform” and “Violent Crime,” and each category is defined by keywords that are likely to appear in stories about that issue.
Police reform stories were defined as stories matching this MediaCloud query:
reform_query <-
"((police OR policing OR officer*) AND
(reform OR accountability OR misconduct OR
brutality OR \"use of force\" OR oversight OR
\"body camera\" OR \"police violence\"))"
Violent crime stories were defined as stories matching this MediaCloud query:
crime_query <-
"((homicide* OR murder* OR shooting* OR
\"violent crime\" OR assault*) AND
(suspect* OR victim* OR killed OR
wounded OR charged))"
Some stories, such as those about police shootings, may match both queries and be counted under both issues.
Later in the project, I will use a paired-samples t-test to see whether the difference in weekly coverage between the two issues is statistically significant. A paired test fits this design because each week gives one count for each issue.
The graph and table below show each issue’s weekly coverage volume during the time period studied.
| topic | Total_Stories | Weekly_Minimum | Weekly_Maximum | Weekly_Median | Weekly_Mean | Weekly_SD |
|---|---|---|---|---|---|---|
| Violent Crime | 133,843 | 2,177 | 5,354 | 3,117.0 | 3,186.74 | 596.94 |
| Police Reform | 45,757 | 661 | 1,941 | 1,087.5 | 1,089.45 | 269.96 |
Across the 42 weeks studied, police reform got more coverage than violent crime in 0 weeks, and violent crime got more coverage in the other 42. On average, violent crime received more coverage than police reform. Police reform averaged 1,089 stories per week (median = 1,088), while violent crime averaged 3,187 stories per week (median = 3,117). The median, which is less affected by unusually busy weeks, was higher for violent crime.
Police reform coverage peaked the week of January 25, 2026, with 1,941 stories. Violent crime coverage peaked the week of January 25, 2026, with 5,354 stories. The standard deviations in the table show how steady each issue’s coverage was. A larger standard deviation compared with the mean means coverage jumped up and down around certain events instead of staying steady from week to week.
One possible reason for the differences is the kind of news each issue produces. Violent crime creates a steady flow of stories almost every day, which may keep its coverage consistently high. Police reform coverage, on the other hand, often depends on specific events, such as a viral video of a police encounter, a major court ruling, or a new state or federal policy. Events like these can cause sudden spikes followed by quieter periods. Agenda-setting theory also suggests that when one issue surges, the other may lose ground. For example, a high-profile misconduct case could briefly push crime coverage down, while a wave of shootings could push reform coverage aside. Since 2026 is a midterm election year, candidates’ messages about public safety may also have affected how much attention each issue received, especially in the weeks before the November election.
To test these ideas, I will need to look at the stories published during the peak weeks to see which events caused the spikes. I will also run the paired-samples t-test to find out whether the difference in average weekly coverage is large enough to be statistically significant and support the hypothesis.
Cohen, B. C. (1963). The press and foreign policy. Princeton University Press.
McCombs, M. E., & Shaw, D. L. (1972). The agenda-setting function of mass media. Public Opinion Quarterly, 36(2), 176–187.
Zhu, J.-H. (1992). Issue competition and attention distraction: A zero-sum theory of agenda-setting. Journalism Quarterly, 69(4), 825–836.
Here is the code that produced the analysis.
############################################################
# STEP 1: Install and Load Required Packages
############################################################
required_packages <- c(
"tidyverse",
"httr2",
"lubridate",
"plotly",
"knitr",
"kableExtra"
)
for (pkg in required_packages) {
if (!requireNamespace(pkg, quietly = TRUE)) {
install.packages(pkg)
}
}
library(tidyverse)
library(httr2)
library(lubridate)
library(plotly)
library(knitr)
library(kableExtra)
############################################################
# STEP 2: Verify Media Cloud API Key
############################################################
if (!nzchar(Sys.getenv("MEDIACLOUD_KEY"))) {
stop(
"No Media Cloud API key was found.\n",
"Run the setup section once,\n",
"then restart R and try again."
)
}
mc_key <- Sys.getenv("MEDIACLOUD_KEY")
############################################################
# STEP 3: Define the Date Range
############################################################
start_date <- "2026-01-04"
end_date <- "2026-10-24"
############################################################
# STEP 4: Define Search Queries
############################################################
reform_query <-
"((police OR policing OR officer*) AND
(reform OR accountability OR misconduct OR
brutality OR \"use of force\" OR oversight OR
\"body camera\" OR \"police violence\"))"
crime_query <-
"((homicide* OR murder* OR shooting* OR
\"violent crime\" OR assault*) AND
(suspect* OR victim* OR killed OR
wounded OR charged))"
############################################################
# STEP 5: Create Topic Definitions
############################################################
topics <- c(
"Police Reform" = reform_query,
"Violent Crime" = crime_query
)
############################################################
# STEP 6: Create a Function to Retrieve Daily
# Story Counts
############################################################
mc_counts_over_time <- function(
query,
start_date,
end_date,
collection_id = 34412234
) {
resp <-
request(
"https://search.mediacloud.org/api/search/count-over-time"
) |>
req_headers(
Authorization = paste("Token", mc_key)
) |>
req_url_query(
q = query,
start = start_date,
end = end_date,
platform = "onlinenews-mediacloud",
cs = collection_id
) |>
req_perform()
results <- resp |>
resp_body_json()
map_dfr(
results$count_over_time$counts,
as_tibble
) |>
mutate(
date = as.Date(date)
)
}
############################################################
# STEP 7: Download Daily Counts for All Topics
############################################################
topic_data <-
imap_dfr(
topics,
function(search_string, topic_name) {
message(
"Downloading: ",
topic_name
)
results <-
mc_counts_over_time(
query = search_string,
start_date = start_date,
end_date = end_date
)
Sys.sleep(35)
results |>
mutate(
topic = topic_name
)
}
)
############################################################
# STEP 8: Examine the Downloaded Daily Data
############################################################
head(topic_data)
glimpse(topic_data)
############################################################
# STEP 9: Convert Daily Counts to Weekly Counts
############################################################
weekly_counts <-
topic_data |>
mutate(
week = floor_date(
date,
unit = "week"
)
) |>
group_by(
topic,
week
) |>
summarise(
stories = sum(count),
.groups = "drop"
)
############################################################
# STEP 10: Examine Weekly Counts
############################################################
weekly_counts
############################################################
# STEP 11: Summarize Coverage by Topic
############################################################
topic_summary <-
weekly_counts |>
group_by(topic) |>
summarise(
Total_Stories = sum(stories),
Weekly_Minimum = min(stories),
Weekly_Maximum = max(stories),
Weekly_Median = median(stories),
Weekly_Mean = round(
mean(stories),
2
),
Weekly_SD = round(
sd(stories),
2
),
.groups = "drop"
) |>
arrange(
desc(Total_Stories)
)
Table <-
topic_summary |>
kbl(
caption =
"Media Cloud Topic Summary Statistics",
align = "lrrrrrr",
format.args = list(big.mark = ",")
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c(
"striped",
"hover",
"condensed"
)
)
############################################################
# STEP 12: Create an Interactive Plotly Graph
############################################################
weekly_counts <- weekly_counts |>
mutate(
stories_formatted = formatC(
stories,
format = "d",
big.mark = ","
)
)
Graph <- plot_ly(
data = weekly_counts,
x = ~week,
y = ~stories,
color = ~topic,
colors = "Set1",
type = "scatter",
mode = "lines+markers",
customdata = ~topic,
text = ~stories_formatted,
hovertemplate =
paste(
"%{customdata}<br>",
"Week: %{x|%Y-%m-%d}<br>",
"Stories: %{text}",
"<extra></extra>"
)
) |>
layout(
title =
"Weekly U.S. Coverage Volume",
hovermode = "x unified",
xaxis = list(
title = "Week"
),
yaxis = list(
title = "Weekly Story Count",
separatethousands = TRUE
)
)
############################################################
# STEP 13: Calculate Values Reported in the Text
############################################################
ref <- topic_summary |> filter(topic == "Police Reform")
cri <- topic_summary |> filter(topic == "Violent Crime")
fmt <- function(x) formatC(x, format = "f", digits = 0, big.mark = ",")
higher_mean <- if (ref$Weekly_Mean > cri$Weekly_Mean) "police reform" else "violent crime"
lower_mean <- if (ref$Weekly_Mean > cri$Weekly_Mean) "violent crime" else "police reform"
higher_median <- if (ref$Weekly_Median > cri$Weekly_Median) "police reform" else "violent crime"
weeks_ref_ahead <-
weekly_counts |>
select(topic, week, stories) |>
pivot_wider(names_from = topic, values_from = stories) |>
summarise(n = sum(`Police Reform` > `Violent Crime`, na.rm = TRUE)) |>
pull(n)
total_weeks <- n_distinct(weekly_counts$week)
peak_ref <- weekly_counts |>
filter(topic == "Police Reform") |>
slice_max(stories, n = 1, with_ties = FALSE)
peak_cri <- weekly_counts |>
filter(topic == "Violent Crime") |>
slice_max(stories, n = 1, with_ties = FALSE)
Graph
Table