Billboard Hot 100 Number Ones

ridges
music
Author

Ana Luisa Bodevan

Published

August 26, 2025

Code
############## TIDYTUESDAY WEEK 34
##############  BILBOARD HOT 100 NUMBER ONES  

############## 1. SETUP

library(pacman)

pacman :: p_load(tidyverse, dplyr, glue, scales, ggtext, showtext,
                 ggridges, ggrepel, patchwork)

billboard <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/main/data/2025/2025-08-26/billboard.csv')

############## 2. DATA ANALYSIS AND TIDYING 

df <- billboard %>% 
  select(bpm, date) %>% 
  mutate(decade = case_when(
    year(date) <= 1957 ~ "50s",
    year(date) >= 1958 & year(date) <= 1967 ~ "1960s",
    year(date) >= 1968 & year(date) <= 1977 ~ "1970s",
    year(date) >= 1978 & year(date) <= 1987 ~ "1980s",
    year(date) >= 1988 & year(date) <= 1997 ~ "1990s",
    year(date) >= 1998 & year(date) <= 2007 ~ "2000s",
    year(date) >= 2008 ~ "2010s",
    TRUE ~ "Other"
  )) # Creates decades column and classifies variable

# Create factor for decades 
df$decade <- factor(df$decade, levels = c("1960s", "1970s", "1980s", "1990s", "2000s", "2010s"))

# Calculate correlation for each decade
df %>%
  group_by(decade) %>%
  summarise(
    mean_bpm = mean(bpm, na.rm = TRUE),
    median_bpm = median(bpm, na.rm = TRUE),
    sd_bpm = sd(bpm, na.rm = TRUE),
    n = n()
  )

# Identify tempo categories
df <- df %>%
  mutate(tempo_category = case_when(
    bpm < 80 ~ "Slow Ballads",
    bpm >= 80 & bpm < 100 ~ "Mid-tempo",
    bpm >= 100 & bpm < 120 ~ "Moderate",
    bpm >= 120 & bpm < 140 ~ "Uptempo",
    bpm >= 140 ~ "Fast/Dance"
  ))

# Identify tempo trends by decade 
tempo_by_decade <- df %>%
  count(decade, tempo_category) %>%
  group_by(decade) %>%
  mutate(percentage = n/sum(n) * 100) %>% 
  drop_na(percentage, tempo_category)

# Create summary data
decade_summary <- df %>%
  group_by(decade) %>%
  summarise(mean_bpm = mean(bpm, na.rm = TRUE),
            median_bpm = median(bpm, na.rm = TRUE),
            count = n())

decade_summary <- decade_summary %>%
  mutate(label_vjust = case_when(
    decade == "1990s" ~ 1.8,   # Below the point for first 113
    decade == "2000s" ~ 1.8,   # Below the point for second 113  
    decade == "2010s" ~ -1.5,  # Above for 121
    TRUE ~ -1.5                # Above for all others
  ))          

############## 3. PLOT 

# Aesthetics 

font_add_google("Inter", "inter")
showtext_auto()

boogie_colors <- c(
  primary = "#FF6B35",      
  secondary = "#F7931E",    
  accent1 = "#C41E3A",      
  accent2 = "#2E86AB",      
  accent3 = "#A23B72",     
  accent4 = "#F18F01",      
  background = "#1A1A1D",   
  text = "#FFFFFF",         
  grid = "#404040"          
)

decade_colors <- c("1960s" = "#FF6B35", "1970s" = "#F7931E", "1980s" = "#C41E3A", 
                   "1990s" = "#A23B72", "2000s" = "#2E86AB", "2010s" = "#F18F01",
                   "2020s" = "#00B4D8")

theme_boogie <- function() {
  theme_minimal() +
    theme(
      # Background
      plot.background = element_rect(fill = boogie_colors["background"], color = NA),
      panel.background = element_rect(fill = boogie_colors["background"], color = NA),
      
      # Grid
      panel.grid.major = element_line(color = boogie_colors["grid"], linewidth = 0.3),
      panel.grid.minor = element_blank(),
      
      # Text
      text = element_text(family = "inter", color = boogie_colors["text"]),
      plot.title = element_text(size = 30, face = "bold", hjust = 0.5, 
                                margin = margin(b = 10)),
      plot.subtitle = element_text(size = 24, hjust = 0.5, 
                                   margin = margin(b = 20),
                                   color = "#CCCCCC"),
      axis.title = element_text(size = 18, face = "bold"),
      axis.text = element_text(size = 10, color = "#DDDDDD"),
      
      # Legend
      legend.background = element_rect(fill = boogie_colors["background"]),
      legend.text = element_text(color = boogie_colors["text"]),
      legend.title = element_text(color = boogie_colors["text"], face = "bold"),
      
      # Plot margins
      plot.margin = margin(20, 20, 20, 20)
    )
}

# Plot  

ggplot(df, aes(x = bpm, y = decade, fill = decade)) +
  geom_density_ridges(alpha = 0.8, scale = 0.9, color = "white", linewidth = 0.5) +
  scale_fill_manual(values = decade_colors) +
  labs(
    title = "THE BEAT EVOLUTION",
    subtitle = "How Billboard #1 hits groove through the decades",
    x = "BEATS PER MINUTE",
    y = "",
    caption = "Data: Billboard Hot 100 | Viz: @anabodevan"
  ) +
  theme_boogie() +
  theme(
    legend.position = "none",
    axis.text.y = element_text(size = 12, face = "bold"),
    plot.caption = element_text(color = "#888888", size = 9, hjust = 1)
  )

ggsave(
  filename = file.path("tidytuesday", "2025", "2025-08-26", paste0("20250826", ".png")),
  height = 7,
  width = 8,
  bg = "white",
  units = "in",
  dpi = 300
)