This analysis is based on framing theory. Framing theory argues that the way an issue is presented in the media can influence how audiences understand and interpret that issue. Rather than telling people what to think, media frames encourage people to think about an issue from a particular perspective. Framing theory applies to this analysis because the focus is not on whether college sports are covered in the news, but on how college sports are covered. Specifically, the analysis compares a competition frame and a business frame. The competition frame emphasizes athletic performance, championships, rankings, coaches, and games, while the business frame emphasizes topics such as revenue, NIL deals, sponsorships, contracts, and media rights. By comparing these frames, the analysis examines which perspective receives more attention in news coverage.
U.S. news coverage of college sports will be more likely to use a competition frame than a business frame. Because sports reporting traditionally focuses on games, teams, rankings, and championships, I expect competition related coverage to appear more frequently throughout the study.
The independent variable is Competition Frame and/or Business Frame.
The dependent variable is the weekly number of news stories identified by Media Cloud.
To conduct the analysis, Media Cloud was used to collect U.S. online news stories between January 2026 and October 2026. Searches were created using keywords associated with each frame. Daily story counts were downloaded and then collected into weekly totals. The results were summarized in a table and displayed in an interactive line graph showing changes in coverage over time.
The results support the hypothesis. The graph shows that the Competition Frame received substantially more coverage than the Business Frame throughout most of the study. Weekly story counts for the competition frame were consistently higher and experienced several major spikes, particularly during March and early September. These peaks likely correspond to major college sports events and postseason competition.
The Business Frame also received news coverage but at much lower levels. Although there were increases in coverage during some weeks, the business frame rarely approached the volume of stories associated with the competition frame. This suggests that news organizations continue to focus primarily on athletic competition rather than the financial aspects of college sports. Overall, the findings indicate that college sports are more commonly framed as athletic competitions than as business enterprises, which is consistent with expectations based on framing theory.
############################################################
# 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-20"
############################################################
# STEP 4: Define Search Queries
############################################################
competition_query <-
"((NCAA OR \"college sports\" OR \"college football\" OR \"college basketball\") AND
(championship OR playoff* OR ranking* OR coach* OR
victory OR win* OR season OR game*))"
business_query <-
"((NCAA OR \"college sports\" OR \"college football\" OR \"college basketball\") AND
(revenue OR NIL OR contract* OR sponsorship* OR
money OR profit* OR \"media rights\" OR
\"television deal*\"))"
############################################################
# STEP 5: Create Topic Definitions
############################################################
topics <- c(
"Competition Frame" = competition_query,
"Business Frame" = business_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 Frames
############################################################
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 Frame
############################################################
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 =
"College Sports Framing Summary Statistics",
align = "lrrrrrr"
) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c(
"striped",
"hover",
"condensed"
)
)
Table
############################################################
# 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(
"<b>%{customdata}</b><br>",
"Week: %{x|%Y-%m-%d}<br>",
"Stories: %{text}<br>",
"<extra></extra>"
)
) |>
layout(
title =
"College Sports Coverage: Competition vs. Business Frames",
hovermode = "x unified",
xaxis = list(
title = "Week"
),
yaxis = list(
title = "Weekly Story Count",
separatethousands = TRUE
)
)
Graph