Rationale

Agenda-setting theory explains that media attention shapes public attention. According to political scientist Bernard Cohen (1963), it does not necessarily tell people what to think, but instead what to think about. More specifically, first-level agenda-setting theory explains that certain topics or issues can compete to gain salience. The key to why certain topics are more important than others is an individual’s need for orientation. If they perceive something as having high relevance, they will pay more attention to that topic depending on their degree of uncertainty. This is why many issues and events compete. Each topic can have varying degrees of relevance for each individual at different points in time. While this theory can ascertain which world issues and events are more prevalent in the public mind, it may also be used to explain why certain sports are covered more than others at certain time periods. 

Using first-level agenda-setting theory, my project will compare the media influence of the four major sports leagues in the United States: the NFL, NBA, MLB, and NHL. Specifically, the analysis will cover weekly story volume in MediaCloud.org’s National news archive for each sport over the last ten months, from the week of January 10th, 2026, to the week of October 17th, 2026. The results will explain how these sports can be prominent in the public mind at different points in time.

Hypothesis

The average weekly coverage volume of each major sport will differ during the first ten months of 2026.

Variables & Method

The dependent variable in the analysis is a continuous measure of the number of stories per week, which includes terms from multiple MediaCloud.org queries indicating coverage for one sport or the other. The independent variable is “sport.” It is a categorical measure identified as one of the four analyzed sports: NFL, NBA, MLB, or NHL.

Stories about each sport were operationalized as stories matching each respective MediaCloud query:

NFL_query <-
  "(NFL OR
    \"National Football League\" OR
    \"Super Bowl\" OR
    \"NFL Draft\" OR
    \"Pro Bowl\")"
NBA_query <-
  "(NBA OR
    \"National Basketball Association\" OR
    \"NBA Finals\" OR
    \"NBA Draft\" OR
    \"NBA All-Star Game\")"
MLB_query <-
  "(MLB OR
    \"Major League Baseball\" OR
    \"World Series\" OR
    \"MLB All-Star Game\" OR
    \"MLB Draft\")"
NHL_query <-
  "(NHL OR
    \"National Hockey League\" OR
    \"Stanley Cup\" OR
    \"NHL All-Star Game\" OR
    \"NHL Draft\")"

I will conduct a one-way ANOVA test to assess the statistical significance of the difference between the coverage volume of each sport.

Results & Discussion

Below is a graph and figure that summarize the weekly coverage volume of each sport over the last ten months. On average, the NFL has the highest story count with 4,367 stories. The MLB and NBA both have just over 3,000 weekly stories, and the NHL has just under 1,500. The NFL has multiple spikes over the given time period, particularly on the weeks of February 8th, March 8th, and April 19th. The NBA overtakes the NFL in media coverage from starting on the week of of April 26th; the MLB overtakes the NFL a week later on May 3rd. For the NBA, its coverage drops off starting after the week of June 21st. The NFL overtakes all other sports on the week of August 9th. Unfortunately, the NHL could only overtake two sports in different time periods: the MLB from the week of January 11th to the 25th, and the NBA in the week of September 20th.

One explanation as to why certain sports are more prevalent than others is the events that occur. Particularly, events like the beginning of a season, playoffs, a championship game/series, or a draft may draw more coverage to that particular sport. It may also draw attention away from sports that are in their regular season or offseason. The current data supports the hypothesis; however, a one-way ANOVA test will need to be conducted to see if the variation is statistically significant.

Code

Below is the code used to conduct 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-17"

############################################################
# STEP 4: Define Search Queries
############################################################

NFL_query <-
  "(NFL OR
    \"National Football League\" OR
    \"Super Bowl\" OR
    \"NFL Draft\" OR
    \"Pro Bowl\")"


NBA_query <-
  "(NBA OR
    \"National Basketball Association\" OR
    \"NBA Finals\" OR
    \"NBA Draft\" OR
    \"NBA All-Star Game\")"

MLB_query <-
  "(MLB OR
    \"Major League Baseball\" OR
    \"World Series\" OR
    \"MLB All-Star Game\" OR
    \"MLB Draft\")"

NHL_query <-
  "(NHL OR
    \"National Hockey League\" OR
    \"Stanley Cup\" OR
    \"NHL All-Star Game\" OR
    \"NHL Draft\")"



############################################################
# STEP 5: Create Topic Definitions
############################################################

topics <- c(
  "NFL" = NFL_query,
  "NBA" = NBA_query,
  "MLB" = MLB_query,
  "NHL" = NHL_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"
  ) |>
  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(
      "%{customdata}
",
      "Week: %{x|%Y-%m-%d}
",
      "Stories: %{text}
",
      ""
    )
) |>
  layout(
    title =
      "Weekly U.S. Coverage Volume",
    
    hovermode = "x unified",
    
    xaxis = list(
      title = "Week"
    ),
    
    yaxis = list(
      title = "Weekly Story Count",
      separatethousands = TRUE
    )
  )

Graph