• Steven Ponce
  • About
  • Data Visualizations
  • Projects
  • Resume
  • Email

On this page

  • Original
  • Makeover
  • Steps to Create this Graphic
    • 1. Load Packages & Setup
    • 2. Read in the Data
    • 3. Examine the Data
    • 4. Tidy Data
    • 5. Visualization Parameters
    • 6. Plot
    • 7. Save
    • 8. Session Info
    • 9. GitHub Repository
    • 10. References
    • 11. Custom Functions Documentation

Coachella’s lineup cuts landed on Saturday and Sunday

  • Show All Code
  • Hide All Code

  • View Source

From 2018 to 2026, Friday lost 4 artists. Saturday and Sunday lost 27 combined.

MakeoverMonday
Data Visualization
R Programming
2026
A slope chart showing that Coachella’s shrinking initial lineup contracted almost entirely on the weekend: Saturday and Sunday lost 27 acts between 2018 and 2026 while Friday lost just 4. It reframes the original’s grouped day-by-day bars as a two-point comparison that isolates where the cuts landed. Built in R with ggplot2.
Author

Steven Ponce

Published

June 8, 2026

Original

The original visualization comes from Coachella Lineup

Original visualization

Makeover

Figure 1: Slope chart of the number of artists on Coachella’s initial lineup announcement, by day, comparing 2018 and 2026. Friday stays nearly flat, slipping from 55 to 51, a loss of 4. Saturday drops from 57 to 44 and Sunday from 57 to 43, a combined weekend loss of 27. The two weekend lines, shown in rust, plunge steeply while Friday, in gray, holds close to level — the contraction concentrated on Saturday and Sunday.

Steps to Create this Graphic

1. Load Packages & Setup

Show code
```{r}
#| label: load
#| warning: false
#| message: false      
#| results: "hide"     

## 1. LOAD PACKAGES & SETUP ----
suppressPackageStartupMessages({
  if (!require("pacman")) install.packages("pacman")
  pacman::p_load(
    tidyverse, ggtext, showtext, scales, glue, janitor
)
})

### |- figure size ----
camcorder::gg_record(
  dir    = here::here("temp_plots"),
  device = "png",
  width  = 8,
  height = 8,
  units  = "in",
  dpi    = 320
)

# Source utility functions
suppressMessages(source(here::here("R/utils/fonts.R")))
source(here::here("R/utils/social_icons.R"))
source(here::here("R/utils/image_utils.R"))
source(here::here("R/themes/base_theme.R"))
```

2. Read in the Data

Show code
```{r}
#| label: read
#| include: true
#| eval: true
#| warning: false
#| 

df_raw <- read_csv(
  here::here("data/MakeoverMonday/2026/coachella lineup.csv")) |>
  clean_names()
```

3. Examine the Data

Show code
```{r}
#| label: examine
#| include: true
#| eval: true
#| results: 'hide'
#| warning: false

glimpse(df_raw)
skimr::skim_without_charts(df_raw)
```

4. Tidy Data

Show code
```{r}
#| label: tidy
#| warning: false

day_levels <- c("Friday", "Saturday", "Sunday")

# Long form, one row per day-year
df_long <- df_raw |>
  select(year, friday, saturday, sunday) |>
  pivot_longer(
    cols      = c(friday, saturday, sunday),
    names_to  = "day",
    values_to = "artists"
  ) |>
  mutate(
    day = str_to_title(day),
    day = factor(day, levels = day_levels)
  )

df_slope <- df_long |>
  filter(year %in% c(2018, 2026)) |>
  mutate(year = factor(year, levels = c(2018, 2026)))

df_change <- df_slope |>
  pivot_wider(names_from = year, values_from = artists, names_prefix = "y") |>
  mutate(
    change   = y2026 - y2018,
    pct_drop = change / y2018
  )
```

5. Visualization Parameters

Show code
```{r}
#| label: params
#| include: true
#| warning: false

### |-  plot aesthetics ----
col_friday   <- "#8A817C" 
col_saturday <- "#9B2D20"  
col_sunday   <- "#C77B5A"   

day_pal <- c(
  Friday   = col_friday,
  Saturday = col_saturday,
  Sunday   = col_sunday
)

clrs <- get_theme_colors(
  palette = c(
    Friday   = col_friday,
    Saturday = col_saturday,
    Sunday   = col_sunday
  )
)

### |- label frames ----
df_left <- df_slope |>
  filter(year == "2018") |>
  mutate(label_y = case_when(
    day == "Friday" ~ artists - 0.5,
    day == "Saturday" ~ artists + 0.8,
    day == "Sunday" ~ artists - 0.8,
    TRUE ~ as.numeric(artists)
  ))

df_right <- df_slope |>
  filter(year == "2026") |>
  mutate(label_y = case_when(
    day == "Saturday" ~ artists + 0.6,
    day == "Sunday" ~ artists - 0.6,
    TRUE ~ as.numeric(artists)
  ))

### |- titles and caption ----
title_text <- glue(
  "Coachella's lineup cuts landed on<br>",
  "<span style='color:{col_saturday}'>**Saturday and Sunday**</span>"
)

subtitle_text <- glue(
  "From 2018 to 2026, Friday lost 4 artists. ",
  "Saturday and Sunday lost 27 combined."
)

caption_text <- create_mm_caption(
  mm_year     = 2026,
  mm_week     = 23,
  source_text = "Chartmetric via data.world<br>Counts cover the initial lineup announcement only"
)

### |- fonts ----
setup_fonts()
fonts <- get_font_families()

### |- plot theme ----
base_theme <- create_base_theme(clrs)

weekly_theme <- extend_weekly_theme(
  base_theme,
  theme(
    legend.position = "none",
    panel.grid = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks = element_blank(),
    axis.title = element_blank(),
    axis.text.x = element_text(face = "bold"),
    plot.title = element_markdown(size = rel(1.8), face = "bold", family = fonts$title_1, color = clrs$title),
    plot.subtitle = element_textbox_simple(
      width = unit(1, "npc"), margin = margin(t = 6, b = 14), size = rel(0.85),
      family = fonts$text, color = clrs$text
    ),
    plot.caption = element_markdown(
      hjust = 0, size = rel(0.55), lineheight = 1.15, margin = margin(t = 16),
      family = fonts$caption, color = clrs$caption
    ),
    plot.title.position = "plot",
    plot.margin = margin(t = 20, r = 60, b = 20, l = 30)
  )
)

theme_set(weekly_theme)
```

6. Plot

Show code
```{r}
#| label: plot
#| warning: false

p <- ggplot(
  df_slope,
  aes(x = year, y = artists, group = day, color = day)
) +
  # Geoms
  geom_line(linewidth = 1.1) +
  geom_point(size = 3) +
  geom_text(
    data = df_left,
    aes(y = label_y, label = glue("{day}  {artists}")),
    hjust = 1.12, fontface = "bold", size = 4.2, show.legend = FALSE
  ) +
  geom_text(
    data = df_right,
    aes(y = label_y, label = glue("{day}   {artists}")),
    hjust = -0.10, fontface = "bold", size = 4.2, show.legend = FALSE
  ) +
  # Scales
  scale_color_manual(values = day_pal) +
  scale_x_discrete(
    expand = expansion(mult = c(0.24, 0.2))
  ) +
  scale_y_continuous(expand = expansion(mult = c(0.10, 0.08))) +
  coord_cartesian(clip = "off") +
  # Labs
  labs(
    title = title_text,
    subtitle = subtitle_text,
    caption = caption_text,
    x = NULL, y = NULL
  )
```

7. Save

Show code
```{r}
#| label: save
#| warning: false

### |-  plot image ----  
save_plot(
  plot = p , 
  type = "makeovermonday", 
  year = current_year,
  week = current_week,
  width = 8, 
  height = 8
  )
```

8. Session Info

TipExpand for Session Info
R version 4.5.3 (2026-03-11 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=English_United States.utf8 
[2] LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] here_1.0.2      janitor_2.2.1   glue_1.8.0      scales_1.4.0   
 [5] showtext_0.9-8  showtextdb_3.0  sysfonts_0.8.9  ggtext_0.1.2   
 [9] lubridate_1.9.5 forcats_1.0.1   stringr_1.6.0   dplyr_1.2.1    
[13] purrr_1.2.2     readr_2.2.0     tidyr_1.3.2     tibble_3.3.1   
[17] ggplot2_4.0.3   tidyverse_2.0.0 pacman_0.5.1   

loaded via a namespace (and not attached):
 [1] gtable_0.3.6       xfun_0.57          htmlwidgets_1.6.4  tzdb_0.5.0        
 [5] vctrs_0.7.3        tools_4.5.3        generics_0.1.4     curl_7.0.0        
 [9] parallel_4.5.3     gifski_1.32.0-2    pkgconfig_2.0.3    skimr_2.2.2       
[13] RColorBrewer_1.1-3 S7_0.2.1           lifecycle_1.0.5    compiler_4.5.3    
[17] farver_2.1.2       textshaping_1.0.5  repr_1.1.7         codetools_0.2-20  
[21] snakecase_0.11.1   litedown_0.9       htmltools_0.5.9    yaml_2.3.12       
[25] pillar_1.11.1      crayon_1.5.3       camcorder_0.1.0    magick_2.9.1      
[29] commonmark_2.0.0   tidyselect_1.2.1   digest_0.6.39      stringi_1.8.7     
[33] labeling_0.4.3     rsvg_2.7.0         rprojroot_2.1.1    fastmap_1.2.0     
[37] grid_4.5.3         cli_3.6.6          magrittr_2.0.5     base64enc_0.1-6   
[41] withr_3.0.2        bit64_4.6.0-1      timechange_0.4.0   rmarkdown_2.31    
[45] bit_4.6.0          otel_0.2.0         ragg_1.5.2         hms_1.1.4         
[49] evaluate_1.0.5     knitr_1.51         markdown_2.0       rlang_1.2.0       
[53] gridtext_0.1.6     Rcpp_1.1.1         xml2_1.5.2         svglite_2.2.2     
[57] rstudioapi_0.18.0  vroom_1.7.1        jsonlite_2.0.0     R6_2.6.1          
[61] systemfonts_1.3.2 

9. GitHub Repository

TipExpand for GitHub Repo

The complete code for this analysis is available in mm_2026_23.qmd.

For the full repository, click here.

10. References

TipExpand for References

Primary Data (Makeover Monday): 1. Makeover Monday 2026 Week 23: Coachella Lineup (2018–2026) 2. Original Chart: Chartmetric — Coachella Lineup, initial announcement 2018–2026 - Article context: The Numbers Behind Coachella’s Smallest Lineup in Years - Coverage: initial lineup poster only; artists per day (Friday, Saturday, Sunday) and total, across seven editions (2018, 2019, 2022–2026; 2020–21 not held) Source Data: 3. Chartmetric — How Music Charts - Coverage: artist counts compiled from Coachella’s initial lineup announcement posters, 2018–2026 - Unit: per-day artist counts (Friday, Saturday, Sunday) and an edition total 4. MakeoverMonday 2026 W23 — data.world - CSV file: coachella lineup.csv; 7 rows × 5 columns (year, friday, saturday, sunday, total) Note: Counts reflect the initial lineup announcement poster only — later additions, set-time fill-ins, and separately programmed stages (Do LaB, Heineken House) are excluded, matching the original chart’s own scope. The 2020 and 2021 editions were not held (pandemic) and are absent from the series, not recorded as zero. The slopegraph compares the first year of the record (2018) with the latest (2026); the day-level asymmetry holds whether anchored to 2018, to the 2022 peak, or across the full seven-edition trajectory. “Saturday and Sunday lost 27 combined” is the sum of Saturday’s drop (57→44, −13) and Sunday’s (57→43, −14); Friday fell 55→51 (−4). Per-day counts sum to the edition total exactly in 2018–2019 but run one short from 2022 onward — the difference is the unattached fourth-headliner slot introduced post-pandemic, so the three days lost 31 acts combined while the overall initial lineup fell 30 (169→139). No causal relationship is asserted: the chart describes where the contraction landed across days, not why, and “cuts” refers to fewer artists booked on the initial poster.

11. Custom Functions Documentation

Note📦 Custom Helper Functions

This analysis uses custom functions from my personal module library for efficiency and consistency across projects.

Functions Used:

  • fonts.R: setup_fonts(), get_font_families() - Font management with showtext
  • social_icons.R: create_social_caption() - Generates formatted social media captions
  • image_utils.R: save_plot() - Consistent plot saving with naming conventions
  • base_theme.R: create_base_theme(), extend_weekly_theme(), get_theme_colors() - Custom ggplot2 themes

Why custom functions?
These utilities standardize theming, fonts, and output across all my data visualizations. The core analysis (data tidying and visualization logic) uses only standard tidyverse packages.

Source Code:
View all custom functions → GitHub: R/utils

Back to top

Citation

BibTeX citation:
@online{ponce2026,
  author = {Ponce, Steven},
  title = {Coachella’s Lineup Cuts Landed on {Saturday} and {Sunday}},
  date = {2026-06-08},
  url = {https://stevenponce.netlify.app/data_visualizations/MakeoverMonday/2026/mm_2026_W23.html},
  langid = {en}
}
For attribution, please cite this work as:
Ponce, Steven. 2026. “Coachella’s Lineup Cuts Landed on Saturday and Sunday.” June 8. https://stevenponce.netlify.app/data_visualizations/MakeoverMonday/2026/mm_2026_W23.html.
Source Code
---
title: "Coachella's lineup cuts landed on Saturday and Sunday"
subtitle: "From 2018 to 2026, Friday lost 4 artists. Saturday and Sunday lost 27 combined."
description: "A slope chart showing that Coachella's shrinking initial lineup contracted almost entirely on the weekend: Saturday and Sunday lost 27 acts between 2018 and 2026 while Friday lost just 4. It reframes the original's grouped day-by-day bars as a two-point comparison that isolates where the cuts landed. Built in R with ggplot2."
date: "2026-06-08"
author:
  - name: "Steven Ponce"
    url: "https://stevenponce.netlify.app"
citation:
  url: "https://stevenponce.netlify.app/data_visualizations/MakeoverMonday/2026/mm_2026_W23.html"
categories: ["MakeoverMonday", "Data Visualization", "R Programming", "2026"]
tags: [
  "makeover-monday",
  "data-visualization",
  "ggplot2",
  "rstats",
  "slopegraph",
  "slope-chart",
  "coachella",
  "music-festival",
  "live-music",
  "direct-labeling",
  "ggtext",
  "2026"
]
image: "thumbnails/mm_2026_23.png"
format:
  html:
    toc: true
    toc-depth: 5
    code-link: true
    code-fold: true
    code-tools: true
    code-summary: "Show code"
    self-contained: true
    theme: 
      light: [flatly, assets/styling/custom_styles.scss]
      dark: [darkly, assets/styling/custom_styles_dark.scss]
editor_options: 
  chunk_output_type: inline
execute: 
  freeze: true
  cache: true
  error: false
  message: false
  warning: false
  eval: true
---

```{r}
#| label: setup-links
#| include: false

# CENTRALIZED LINK MANAGEMENT

## Project-specific info 
current_year <- 2026
current_week <- 23
project_file <- "mm_2026_23.qmd"
project_image <- "mm_2026_23.png"

## Data Sources
data_main <- "https://data.world/makeovermonday/2026w23"
data_secondary <- "https://data.world/makeovermonday/2026w23"

## Repository Links  
repo_main <- "https://github.com/poncest/personal-website/"
repo_file <- paste0("https://github.com/poncest/personal-website/blob/master/data_visualizations/MakeoverMonday/", current_year, "/", project_file)

## External Resources/Images
chart_original <- "https://raw.githubusercontent.com/poncest/MakeoverMonday/refs/heads/master/2026/Week_22/original_chart.png"

## Organization/Platform Links
org_primary <- "https://hmc.chartmetric.com/the-numbers-behind-coachellas-smallest-lineup-in-years/"
org_secondary <- "https://hmc.chartmetric.com/the-numbers-behind-coachellas-smallest-lineup-in-years/"

# Helper function to create markdown links
create_link <- function(text, url) {
  paste0("[", text, "](", url, ")")
}

# Helper function for citation-style links
create_citation_link <- function(text, url, title = NULL) {
  if (is.null(title)) {
    paste0("[", text, "](", url, ")")
  } else {
    paste0("[", text, "](", url, ' "', title, '")')
  }
}
```

### Original

The original visualization comes from `r create_link("Coachella Lineup", data_secondary)`

![Original visualization](https://raw.githubusercontent.com/poncest/MakeoverMonday/refs/heads/master/2026/Week_23/original_chart.png)

### Makeover

![Slope chart of the number of artists on Coachella's initial lineup announcement, by day, comparing 2018 and 2026. Friday stays nearly flat, slipping from 55 to 51, a loss of 4. Saturday drops from 57 to 44 and Sunday from 57 to 43, a combined weekend loss of 27. The two weekend lines, shown in rust, plunge steeply while Friday, in gray, holds close to level — the contraction concentrated on Saturday and Sunday. ](mm_2026_23.png){#fig-1}

### [**Steps to Create this Graphic**]{.mark}

#### [1. Load Packages & Setup]{.smallcaps}

```{r}
#| label: load
#| warning: false
#| message: false      
#| results: "hide"     

## 1. LOAD PACKAGES & SETUP ----
suppressPackageStartupMessages({
  if (!require("pacman")) install.packages("pacman")
  pacman::p_load(
    tidyverse, ggtext, showtext, scales, glue, janitor
)
})

### |- figure size ----
camcorder::gg_record(
  dir    = here::here("temp_plots"),
  device = "png",
  width  = 8,
  height = 8,
  units  = "in",
  dpi    = 320
)

# Source utility functions
suppressMessages(source(here::here("R/utils/fonts.R")))
source(here::here("R/utils/social_icons.R"))
source(here::here("R/utils/image_utils.R"))
source(here::here("R/themes/base_theme.R"))
```

#### [2. Read in the Data]{.smallcaps}

```{r}
#| label: read
#| include: true
#| eval: true
#| warning: false
#| 

df_raw <- read_csv(
  here::here("data/MakeoverMonday/2026/coachella lineup.csv")) |>
  clean_names()
```

#### [3. Examine the Data]{.smallcaps}

```{r}
#| label: examine
#| include: true
#| eval: true
#| results: 'hide'
#| warning: false

glimpse(df_raw)
skimr::skim_without_charts(df_raw)
```

#### [4. Tidy Data]{.smallcaps}

```{r}
#| label: tidy
#| warning: false

day_levels <- c("Friday", "Saturday", "Sunday")

# Long form, one row per day-year
df_long <- df_raw |>
  select(year, friday, saturday, sunday) |>
  pivot_longer(
    cols      = c(friday, saturday, sunday),
    names_to  = "day",
    values_to = "artists"
  ) |>
  mutate(
    day = str_to_title(day),
    day = factor(day, levels = day_levels)
  )

df_slope <- df_long |>
  filter(year %in% c(2018, 2026)) |>
  mutate(year = factor(year, levels = c(2018, 2026)))

df_change <- df_slope |>
  pivot_wider(names_from = year, values_from = artists, names_prefix = "y") |>
  mutate(
    change   = y2026 - y2018,
    pct_drop = change / y2018
  )


```

#### [5. Visualization Parameters]{.smallcaps}

```{r}
#| label: params
#| include: true
#| warning: false

### |-  plot aesthetics ----
col_friday   <- "#8A817C" 
col_saturday <- "#9B2D20"  
col_sunday   <- "#C77B5A"   

day_pal <- c(
  Friday   = col_friday,
  Saturday = col_saturday,
  Sunday   = col_sunday
)

clrs <- get_theme_colors(
  palette = c(
    Friday   = col_friday,
    Saturday = col_saturday,
    Sunday   = col_sunday
  )
)

### |- label frames ----
df_left <- df_slope |>
  filter(year == "2018") |>
  mutate(label_y = case_when(
    day == "Friday" ~ artists - 0.5,
    day == "Saturday" ~ artists + 0.8,
    day == "Sunday" ~ artists - 0.8,
    TRUE ~ as.numeric(artists)
  ))

df_right <- df_slope |>
  filter(year == "2026") |>
  mutate(label_y = case_when(
    day == "Saturday" ~ artists + 0.6,
    day == "Sunday" ~ artists - 0.6,
    TRUE ~ as.numeric(artists)
  ))

### |- titles and caption ----
title_text <- glue(
  "Coachella's lineup cuts landed on<br>",
  "<span style='color:{col_saturday}'>**Saturday and Sunday**</span>"
)

subtitle_text <- glue(
  "From 2018 to 2026, Friday lost 4 artists. ",
  "Saturday and Sunday lost 27 combined."
)

caption_text <- create_mm_caption(
  mm_year     = 2026,
  mm_week     = 23,
  source_text = "Chartmetric via data.world<br>Counts cover the initial lineup announcement only"
)

### |- fonts ----
setup_fonts()
fonts <- get_font_families()

### |- plot theme ----
base_theme <- create_base_theme(clrs)

weekly_theme <- extend_weekly_theme(
  base_theme,
  theme(
    legend.position = "none",
    panel.grid = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks = element_blank(),
    axis.title = element_blank(),
    axis.text.x = element_text(face = "bold"),
    plot.title = element_markdown(size = rel(1.8), face = "bold", family = fonts$title_1, color = clrs$title),
    plot.subtitle = element_textbox_simple(
      width = unit(1, "npc"), margin = margin(t = 6, b = 14), size = rel(0.85),
      family = fonts$text, color = clrs$text
    ),
    plot.caption = element_markdown(
      hjust = 0, size = rel(0.55), lineheight = 1.15, margin = margin(t = 16),
      family = fonts$caption, color = clrs$caption
    ),
    plot.title.position = "plot",
    plot.margin = margin(t = 20, r = 60, b = 20, l = 30)
  )
)

theme_set(weekly_theme)
```

#### [6. Plot]{.smallcaps}

```{r}
#| label: plot
#| warning: false

p <- ggplot(
  df_slope,
  aes(x = year, y = artists, group = day, color = day)
) +
  # Geoms
  geom_line(linewidth = 1.1) +
  geom_point(size = 3) +
  geom_text(
    data = df_left,
    aes(y = label_y, label = glue("{day}  {artists}")),
    hjust = 1.12, fontface = "bold", size = 4.2, show.legend = FALSE
  ) +
  geom_text(
    data = df_right,
    aes(y = label_y, label = glue("{day}   {artists}")),
    hjust = -0.10, fontface = "bold", size = 4.2, show.legend = FALSE
  ) +
  # Scales
  scale_color_manual(values = day_pal) +
  scale_x_discrete(
    expand = expansion(mult = c(0.24, 0.2))
  ) +
  scale_y_continuous(expand = expansion(mult = c(0.10, 0.08))) +
  coord_cartesian(clip = "off") +
  # Labs
  labs(
    title = title_text,
    subtitle = subtitle_text,
    caption = caption_text,
    x = NULL, y = NULL
  )

```

#### [7. Save]{.smallcaps}

```{r}
#| label: save
#| warning: false

### |-  plot image ----  
save_plot(
  plot = p , 
  type = "makeovermonday", 
  year = current_year,
  week = current_week,
  width = 8, 
  height = 8
  )
```

#### [8. Session Info]{.smallcaps}

::: {.callout-tip collapse="true"}
##### Expand for Session Info

```{r, echo = FALSE}
#| eval: true
#| warning: false

sessionInfo()
```
:::

#### [9. GitHub Repository]{.smallcaps}

::: {.callout-tip collapse="true"}
##### Expand for GitHub Repo

The complete code for this analysis is available in `r create_link(project_file, repo_file)`.

For the full repository, `r create_link("click here", repo_main)`.
:::

#### [10. References]{.smallcaps}
::: {.callout-tip collapse="true"}
##### Expand for References
**Primary Data (Makeover Monday):**
1. Makeover Monday `r current_year` Week `r current_week`: `r create_link("Coachella Lineup (2018–2026)", data_main)`
2. Original Chart: `r create_link("Chartmetric — Coachella Lineup, initial announcement 2018–2026", "https://hmc.chartmetric.com/the-numbers-behind-coachellas-smallest-lineup-in-years/")`
   - Article context: `r create_link("The Numbers Behind Coachella's Smallest Lineup in Years", "https://hmc.chartmetric.com/the-numbers-behind-coachellas-smallest-lineup-in-years/")`
   - Coverage: initial lineup poster only; artists per day (Friday, Saturday, Sunday) and total, across seven editions (2018, 2019, 2022–2026; 2020–21 not held)
**Source Data:**
3. `r create_link("Chartmetric — How Music Charts", "https://hmc.chartmetric.com")`
   - Coverage: artist counts compiled from Coachella's initial lineup announcement posters, 2018–2026
   - Unit: per-day artist counts (Friday, Saturday, Sunday) and an edition total
4. `r create_link("MakeoverMonday 2026 W23 — data.world", "https://data.world/makeovermonday/2026w23")`
   - CSV file: coachella lineup.csv; 7 rows × 5 columns (year, friday, saturday, sunday, total)
**Note:** Counts reflect the *initial* lineup announcement poster only — later additions, set-time fill-ins, and separately programmed stages (Do LaB, Heineken House) are excluded, matching the original chart's own scope. The 2020 and 2021 editions were not held (pandemic) and are absent from the series, not recorded as zero. The slopegraph compares the first year of the record (2018) with the latest (2026); the day-level asymmetry holds whether anchored to 2018, to the 2022 peak, or across the full seven-edition trajectory. "Saturday and Sunday lost 27 combined" is the sum of Saturday's drop (57→44, −13) and Sunday's (57→43, −14); Friday fell 55→51 (−4). Per-day counts sum to the edition total exactly in 2018–2019 but run one short from 2022 onward — the difference is the unattached fourth-headliner slot introduced post-pandemic, so the three days lost 31 acts combined while the overall initial lineup fell 30 (169→139). No causal relationship is asserted: the chart describes *where* the contraction landed across days, not why, and "cuts" refers to fewer artists booked on the initial poster.
:::


#### [11. Custom Functions Documentation]{.smallcaps}

::: {.callout-note collapse="true"}
##### 📦 Custom Helper Functions

This analysis uses custom functions from my personal module library for efficiency and consistency across projects.

**Functions Used:**

-   **`fonts.R`**: `setup_fonts()`, `get_font_families()` - Font management with showtext
-   **`social_icons.R`**: `create_social_caption()` - Generates formatted social media captions
-   **`image_utils.R`**: `save_plot()` - Consistent plot saving with naming conventions
-   **`base_theme.R`**: `create_base_theme()`, `extend_weekly_theme()`, `get_theme_colors()` - Custom ggplot2 themes

**Why custom functions?**\
These utilities standardize theming, fonts, and output across all my data visualizations. The core analysis (data tidying and visualization logic) uses only standard tidyverse packages.

**Source Code:**\
View all custom functions → [GitHub: R/utils](https://github.com/poncest/personal-website/tree/master/R)
:::

© 2024 Steven Ponce

Source Issues