Rationale

First-level agenda-setting theory predicts that issues and individuals receiving prominent media coverage are more likely to become prominent in the minds of audiences. Because media organizations have limited time, space, and resources, attention devoted to one topic or group may reduce attention available for another. As a result, issues, events, and public figures often compete with one another for prominence on the media agenda.

Drawing on first-level agenda-setting theory, this project will compare the media agenda prominence of professional male soccer players and professional female soccer players. Specifically, the analysis will compare the number of stories per week that MediaCloud’s United States National news collection published about selected internationally recognized male and female soccer players between October 5, 2025, and October 24, 2026.

The results will contribute to understanding whether sports media provide similar levels of attention to male and female athletes. Although both groups contain highly successful and widely recognized players, differences in coverage volume may reveal continuing disparities in media attention and visibility between men’s and women’s sports.

Hypothesis

Average weekly coverage for professional male soccer players is predicted to be greater than the coverage for professional female soccer players.

Variables and Method

Weekly coverage volume served as the dependent variable for the analysis. It was measured continuously as the number of stories published each week that matched a MediaCloud search query identifying either elite male soccer players or elite female soccer players.

The independent variable was player gender category, measured categorically as either male soccer players or female soccer players.

Results and Discussion

The figure and table below summarize weekly coverage volume for male and female soccer players during the period analyzed. Coverage patterns indicate how frequently each group appeared in U.S. national news and whether one group consistently received greater attention than the other.

If the summary table shows that male soccer players received more total stories and a higher average weekly story count, the findings would suggest that elite male athletes occupied a more prominent position on the media agenda than elite female athletes. Such a result would be consistent with longstanding research showing that male sports often receive greater media attention than female sports.

However, these results disproved the predicted hypothesis.

Soccer coverage male vs female

Media Cloud Topic Summary Statistics
topic Total_Stories Weekly_Minimum Weekly_Maximum Weekly_Median Weekly_Mean Weekly_SD
male soccer players 33420 133 2946 384 607.64 629.49
female soccer players 9932 70 335 174 180.58 56.71

Code

Here is the code that produced the analysis:

############################################################
# RUN ONCE TO SAVE YOUR MEDIACLOUD API KEY
############################################################

key <- "1cd41bd110f91ae494b47e6bb3eb9d3e9bd8aa12"

write(
  paste0("MEDIACLOUD_KEY=", key),
  file = "~/.Renviron",
  append = TRUE
)

readRenviron("~/.Renviron")
nzchar(Sys.getenv("MEDIACLOUD_KEY"))
## [1] TRUE
############################################################
# 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 <- "2025-10-05"
end_date   <- "2026-10-24"

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

male_soccer_query <-
  '("Lionel Messi" OR Messi OR
     "Kylian Mbappe" OR Mbappe OR
     "Erling Haaland" OR Haaland OR
     "Jude Bellingham" OR Bellingham OR
     "Cristiano Ronaldo" OR Ronaldo)'

female_soccer_query <-
  '("Alex Morgan" OR
     "Aitana Bonmati" OR Bonmati OR
     "Sophia Smith" OR
     "Trinity Rodman" OR
     "Sam Kerr" OR Kerr)'

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

topics <- c(
  "male soccer players" = male_soccer_query,
  "female soccer players" = female_soccer_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
        )
    }
  )
## Downloading: male soccer players
## Downloading: female soccer players
############################################################
# STEP 8: Examine the Downloaded Daily Data
############################################################

head(topic_data)
## # A tibble: 6 × 5
##   date       total_count count   ratio topic              
##   <date>           <int> <int>   <dbl> <chr>              
## 1 2025-10-05        7754    48 0.00619 male soccer players
## 2 2025-10-06       13770    51 0.00370 male soccer players
## 3 2025-10-07       14469    60 0.00415 male soccer players
## 4 2025-10-08       14963    85 0.00568 male soccer players
## 5 2025-10-09       14843    67 0.00451 male soccer players
## 6 2025-10-10       13131    80 0.00609 male soccer players
glimpse(topic_data)
## Rows: 770
## Columns: 5
## $ date        <date> 2025-10-05, 2025-10-06, 2025-10-07, 2025-10-08, 2025-10-0…
## $ total_count <int> 7754, 13770, 14469, 14963, 14843, 13131, 7199, 7792, 12977…
## $ count       <int> 48, 51, 60, 85, 67, 80, 72, 33, 60, 104, 77, 53, 59, 71, 6…
## $ ratio       <dbl> 0.006190353, 0.003703704, 0.004146797, 0.005680679, 0.0045…
## $ topic       <chr> "male soccer players", "male soccer players", "male soccer…
############################################################
# 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
## # A tibble: 110 × 3
##    topic                 week       stories
##    <chr>                 <date>       <int>
##  1 female soccer players 2025-10-05     262
##  2 female soccer players 2025-10-12     222
##  3 female soccer players 2025-10-19     247
##  4 female soccer players 2025-10-26     199
##  5 female soccer players 2025-11-02     209
##  6 female soccer players 2025-11-09     231
##  7 female soccer players 2025-11-16     246
##  8 female soccer players 2025-11-23     170
##  9 female soccer players 2025-11-30     230
## 10 female soccer players 2025-12-07     198
## # ℹ 100 more rows
############################################################
# 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
Media Cloud Topic Summary Statistics
topic Total_Stories Weekly_Minimum Weekly_Maximum Weekly_Median Weekly_Mean Weekly_SD
male soccer players 33420 133 2946 384 607.64 629.49
female soccer players 9932 70 335 174 180.58 56.71
############################################################
# 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