The graph displays coverage volume between the National Basketball Association (NBA) and the Women’s National Basketball Association (WNBA). Specifically, it shows weekly story counts from April 19, 2025, to October 21, 2025. The table provides a summary, including the total number of stories received by both leagues during the study period. As shown, the NBA received 80,290 stories, while the WNBA received 23,684. The data were collected from mediacloud.org.
| topic | Total_Stories | Weekly_Minimum | Weekly_Maximum | Weekly_Median | Weekly_Mean | Weekly_SD |
|---|---|---|---|---|---|---|
| NBA | 80290 | 680 | 5374 | 2526 | 2867.50 | 1166.01 |
| WNBA | 23684 | 31 | 1532 | 856 | 845.86 | 364.11 |
############################################################
# STEP 1: Install and Load Required Packages
# This step installs any missing packages and then
# loads the packages needed for the analysis.
############################################################
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 at the top of the script once,\n",
"then restart R and try again."
)
}
mc_key <- Sys.getenv("MEDIACLOUD_KEY")
############################################################
# STEP 3: Define the Date Range
# Enter the beginning and ending dates for the
# period you want to analyze.
############################################################
start_date <- "2025-04-19"
end_date <- "2025-10-21"
############################################################
# STEP 4: Define Your Topics
# Each topic consists of a topic label and a Media
# Cloud search query.
############################################################
# Agenda-setting query example:
topics <- c(
"NBA" =
"\"LeBron James\" OR \"Shai Gilgeous-Alexander\" OR NBA",
"WNBA" =
"\"A'ja Wilson\" OR \"Caitlin Clark\" OR WNBA"
)
# Framing query example:
# topics <- c(
# "Economic Frame" =
# "(immigration OR migrant) AND
# (jobs OR labor OR economy)",
#
# "Crime Frame" =
# "(immigration OR migrant) AND
# (crime OR drugs OR gang)",
#
# "Humanitarian Frame" =
# "(immigration OR migrant) AND
# (refugee OR asylum OR family)"
# )
############################################################
# STEP 5: Create a Function to Retrieve Daily
# Story Counts
# This function sends a query to Media Cloud and
# retrieves 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 6: Download Daily Counts for All Topics
# This step retrieves daily counts for every topic
# defined in the topics object.
#
# Media Cloud limits API requests, so a pause is
# inserted between queries.
############################################################
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 7: Examine the Downloaded Daily Data
# This step displays the first few rows of the
# downloaded data.
############################################################
head(topic_data)
glimpse(topic_data)
############################################################
# STEP 8: Convert Daily Counts to Weekly Counts
# This step aggregates daily story counts into
# weekly totals for easier visualization.
############################################################
weekly_counts <-
topic_data |>
mutate(
week = floor_date(
date,
unit = "week"
)
) |>
group_by(
topic,
week
) |>
summarise(
stories = sum(count),
.groups = "drop"
)
############################################################
# STEP 9: Examine Weekly Counts
# This step displays the weekly data that will be
# graphed.
############################################################
weekly_counts
############################################################
# STEP 10A: Summarize Coverage by Topic
# This step calculates descriptive statistics for
# weekly story counts and displays the results in
# a formatted table.
############################################################
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"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c(
"striped",
"hover",
"condensed"
)
)
Table
############################################################
# STEP 10B: Create an Interactive Plotly Graph
# This step creates a line graph showing weekly
# story volume for each topic.
############################################################
Graph <- plot_ly(
data = weekly_counts,
x = ~week,
y = ~stories,
color = ~topic,
colors = "Set1",
type = "scatter",
mode = "lines+markers"
) |>
layout(
title = "Weekly U.S. Coverage Volume",
xaxis = list(
title = "Week (with range slider)",
rangeslider = list(visible = TRUE)
),
yaxis = list(
title = "Weekly Story Count"
)
)
Graph
############################################################
# STEP 11: Save Weekly Counts as a CSV File
# This step exports the weekly counts to a CSV file
# that can be opened in Excel or imported into
# other software.
############################################################
write_csv(
weekly_counts,
"weekly_story_counts.csv"
)
############################################################
# STEP 12: Create a Wide-Format Version
# This step creates a spreadsheet with one row per
# week and one column for each topic.
############################################################
weekly_counts_wide <-
weekly_counts |>
pivot_wider(
names_from = topic,
values_from = stories
) |>
arrange(week)
write_csv(
weekly_counts_wide,
"weekly_story_counts_wide.csv"
)
weekly_counts_wide