0) Project goal + research questions


Theme D — Language as Market Access

  1. RQ-D1: Does English dominate horror film production over time?
  2. RQ-D2: Do English-language horror films receive higher investment?
  3. RQ-D3: Given the same level of investment, do English-language horror films earn higher returns?
  4. RQ-D4: Are high-return outcomes more common among English-language horror films?

Theme E - Franchise

  1. RQ-E1: Revenue–Budget joint distribution by franchise status
  2. RQ-E2: Are franchise films more favored by audiences?

Theme F — Production Outcomes & Failure Structure

  1. RQ-F1: How common are financial failures among horror films?
  2. RQ-F2: Is failure associated with production scale or design choices?
  3. RQ-F3: Does higher investment reduce the probability of failure?

Notes: Financial fields often contain zeros. In this report, we treat 0 budget/revenue/runtime as missing.

1) Libraries + data import

library(tidyverse)
library(lubridate)
library(scales)
library(stringr)
library(tidyr)
library(here)

# Advanced viz 
library(ggridges)   # ridge plots
library(ggExtra)    # marginal plots
library(plotly)     # interactive graphics
library(patchwork)  # arranging multiple plots
library(ggrepel)    # nicer labels/annotations
# Robust path: works if the file is next to the Rmd, or inside data/raw/
# data_path <- dplyr::case_when(
#   file.exists("Users/majid/Documents/3-third semester/Advance R/RVisualization-Horror-Movie-main/data/raw/horror_movies.csv") ~ "horror_movies.csv",
# 
#   TRUE ~ NA_character_
# )

# stopifnot(!is.na(data_path))
knitr::opts_knit$set(root.dir = here())
movies_raw <- read_csv(here("data", "raw", "horror_movies.csv"), show_col_types = FALSE)
head(movies_raw)
## # A tibble: 6 × 21
##    ...1      id original_title     title      original_language overview tagline
##   <dbl>   <dbl> <chr>              <chr>      <chr>             <chr>    <chr>  
## 1     1  760161 Orphan: First Kill Orphan: F… en                After e… There'…
## 2     2  760741 Beast              Beast      en                A recen… Fight …
## 3     3  882598 Smile              Smile      en                After w… Once y…
## 4     4  756999 The Black Phone    The Black… en                Finney … Never …
## 5     5  772450 Presencias         Presences  es                A man w… <NA>   
## 6     6 1014226 Sonríe             Sonríe     es                <NA>     <NA>   
## # ℹ 14 more variables: release_date <date>, poster_path <chr>,
## #   popularity <dbl>, vote_count <dbl>, vote_average <dbl>, budget <dbl>,
## #   revenue <dbl>, runtime <dbl>, status <chr>, adult <lgl>,
## #   backdrop_path <chr>, genre_names <chr>, collection <dbl>,
## #   collection_name <chr>
glimpse(movies_raw)
## Rows: 32,540
## Columns: 21
## $ ...1              <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1…
## $ id                <dbl> 760161, 760741, 882598, 756999, 772450, 1014226, 717…
## $ original_title    <chr> "Orphan: First Kill", "Beast", "Smile", "The Black P…
## $ title             <chr> "Orphan: First Kill", "Beast", "Smile", "The Black P…
## $ original_language <chr> "en", "en", "en", "en", "es", "es", "en", "en", "en"…
## $ overview          <chr> "After escaping from an Estonian psychiatric facilit…
## $ tagline           <chr> "There's always been something wrong with Esther.", …
## $ release_date      <date> 2022-07-27, 2022-08-11, 2022-09-23, 2022-06-22, 202…
## $ poster_path       <chr> "/pHkKbIRoCe7zIFvqan9LFSaQAde.jpg", "/xIGr7UHsKf0URW…
## $ popularity        <dbl> 5088.584, 2172.338, 1863.628, 1071.398, 1020.995, 93…
## $ vote_count        <dbl> 902, 584, 114, 2736, 83, 1, 125, 1684, 73, 1035, 637…
## $ vote_average      <dbl> 6.9, 7.1, 6.8, 7.9, 7.0, 1.0, 5.8, 7.0, 6.5, 6.8, 7.…
## $ budget            <dbl> 0, 0, 17000000, 18800000, 0, 0, 20000000, 68000000, …
## $ revenue           <dbl> 9572765, 56000000, 45000000, 161000000, 0, 0, 289259…
## $ runtime           <dbl> 99, 93, 115, 103, 0, 0, 88, 130, 90, 106, 98, 89, 97…
## $ status            <chr> "Released", "Released", "Released", "Released", "Rel…
## $ adult             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
## $ backdrop_path     <chr> "/5GA3vV1aWWHTSDO5eno8V5zDo8r.jpg", "/2k9tBql5GYH328…
## $ genre_names       <chr> "Horror, Thriller", "Adventure, Drama, Horror", "Hor…
## $ collection        <dbl> 760193, NA, NA, NA, NA, NA, 94899, NA, NA, 950289, N…
## $ collection_name   <chr> "Orphan Collection", NA, NA, NA, NA, NA, "Jeepers Cr…
## Details
#Sourced: https://www.kaggle.com/datasets/evangower/horror-movies/data

#id = movie id,
#original_title = original movie title,
#title = movie title,
#original_language = Original movie language,
#overview = Description of movie,
#tagline = Tagline of movie
#release_date = release date,
#poster_path = Poster image path,
#popularity = Popularity of movie,
#vote_count = Votes for movie,
#vote_average = Vote average,
#budget = Budget for movie
#revenue = Revenue for movie
#runtime = Movie runtime
#status = Status of movie,
#adult = Adult rated,
#backdrop_path = Backdrop movie path,
#genre_names = Genres of movie,
#collection = Collection,
#collection_name = Collection name

2) Cleaning + feature engineering

movies <- movies_raw %>%
  mutate(
    release_date = as.Date(release_date),
    year = year(release_date),
    decade = floor(year / 10) * 10,
    runtime = na_if(runtime, 0),
    budget = na_if(budget, 0),
    revenue = na_if(revenue, 0),
    vote_average = na_if(vote_average, 0),
    vote_count = na_if(vote_count, 0),
    # split genre_names like "Horror, Mystery, Thriller"
    genre_list = str_split(genre_names %||% "", ",\\s*"),
    n_genres = lengths(genre_list)
  ) %>%
  filter(!is.na(year), year >= 1950, year <= max(year, na.rm = TRUE))

# Helper dataset for economics / ratings analysis
movies_fin <- movies %>%
  filter(!is.na(budget), !is.na(revenue), budget > 0, revenue > 0) %>%
  mutate(
    profit = revenue - budget,
    roi = revenue / budget,
    log_budget = log10(budget),
    log_revenue = log10(revenue),
    profit_flag = case_when(
      profit < 0 ~ "Loss",
      roi < 2 ~ "Low profit",
      roi < 5 ~ "Hit",
      TRUE ~ "Sleeper/Blockbuster"
    ) %>% factor(levels = c("Loss","Low profit","Hit","Sleeper/Blockbuster"))
  )

## movie_base 
movies_horror_base <- movies %>%
  filter(map_lgl(genre_list, ~ "Horror" %in% .x)) %>%
  mutate(
    lang_group = if_else(
      original_language == "en",
      "English", "Non-English"
    ),
    franchise = if_else(
      is.na(collection_name),
      "Standalone", "Franchise"
    )
  )

# movie_finance
movies_horror_viz <- movies_horror_base %>%
  mutate(
    roi = if_else(
      !is.na(budget) & !is.na(revenue) & budget > 0,
      revenue / budget,
      NA_real_
    ),
    log_budget = if_else(
      !is.na(budget) & budget > 0,
      log10(budget),
      NA_real_
    ),
    log_revenue = if_else(
      !is.na(revenue) & revenue > 0,
      log10(revenue),
      NA_real_
    ),
    financial_observable = if_else(
      !is.na(budget) & !is.na(revenue) & budget > 0 & revenue > 0,
      "Observable", "Unobservable"
    )
  )


summary(select(movies, year, runtime, vote_average, n_genres))
##       year         runtime        vote_average       n_genres     
##  Min.   :1950   Min.   :  1.00   Min.   : 0.500   Min.   : 1.000  
##  1st Qu.:2000   1st Qu.: 26.00   1st Qu.: 4.100   1st Qu.: 1.000  
##  Median :2012   Median : 83.00   Median : 5.200   Median : 2.000  
##  Mean   :2007   Mean   : 67.69   Mean   : 5.191   Mean   : 1.991  
##  3rd Qu.:2018   3rd Qu.: 92.00   3rd Qu.: 6.100   3rd Qu.: 3.000  
##  Max.   :2022   Max.   :683.00   Max.   :10.000   Max.   :16.000  
##                 NA's   :2668     NA's   :11629
summary(select(movies_fin, budget, revenue, profit, roi))
##      budget             revenue              profit          
##  Min.   :        1   Min.   :        1   Min.   :-194775779  
##  1st Qu.:  1000000   1st Qu.:   797276   1st Qu.:   -127461  
##  Median :  5375000   Median : 11009988   Median :   2729260  
##  Mean   : 12642663   Mean   : 38559968   Mean   :  25917305  
##  3rd Qu.: 15000000   3rd Qu.: 44975559   3rd Qu.:  29052546  
##  Max.   :200000000   Max.   :701842551   Max.   : 666842551  
##       roi          
##  Min.   :   0.000  
##  1st Qu.:   0.667  
##  Median :   2.013  
##  Mean   :  26.100  
##  3rd Qu.:   4.830  
##  Max.   :6666.667

Theme D — Language as Market Access

D1) Is English a dominate language in Horror film industry?

d1_count <- movies_horror_base %>%
  count(year, lang_group)

ggplot(d1_count, aes(year, n, color = lang_group)) +
  geom_line(linewidth = 1.2) +
  scale_color_manual(
    values = c(
      "English" = "#d73027",
      "Non-English" = "#4575b4"
    ),
    name = NULL
  ) +
  labs(
    title = "Number of Horror Films Production Over Time by Language",
    subtitle = "English-language films consistently dominate horror production",
    x = NULL,
    y = "Number of films"
  ) +
  theme_minimal() +
  theme(
    legend.position = "top"
  )

Figure D1 compares an annual Horror film production shift between English and non English language over time.

What we see:

  • Within given decades, English horror films consistently outnumber non-English horror films.

  • The gap between English and non-English production begins to widen in the mid-1970s.

  • From the 1990s onward, English are often produced at roughly three times the size of horror film production of the non-English one.

By comparison, English- horror films dominate annual production volumns, and are consistently exceeding non-English films. The production gap begins to widen in the mid 1970s. This pattern give an insightful conclusion that expansion in horror film production has been concentrated in English-language markets.

D2 ) Do English-language horror films receive higher investment?

movies_horror_viz %>%
  mutate(
    decade = paste0(floor(year / 10) * 10, "s")
  ) %>%
  filter(
    !is.na(log_budget),
    log_budget > 4,
    log_budget < 8
  ) %>%
  ggplot(aes(log_budget, decade, fill = lang_group)) +
  geom_density_ridges(
    scale = 2,
    alpha = 0.55,
    color = "black",
    size = 0.25,
    position = "identity"
  ) +
  scale_fill_manual(
    values = c(
      "English" = "#d73027",
      "Non-English" = "#4575b4"
    ),
    name = NULL
  ) +
  labs(
    title = "Investment distributions across decades by language",
    subtitle = "English-language horror films tend to receive higher budgets over time",
    x = "Log10(Budget)",
    y = "Decade"
  ) +
  theme_minimal()

Figure D2 present investment distribution across English and non-English horror film production. Using a log 10 budget scale.

  • Production budgets for horror films increase over time for both English and non-English films, as reflected by a rightward shift in budget distributions across decades.

  • Across most decades, English-language horror films tend to exhibit higher budget distributions than non-English horror films.

  • The divergence between English and non-English budget distributions becomes more pronounced from the 1980s onward, with the English-language distribution shifting further to the right.

  • Non-English horror films remain relatively more concentrated within lower-budget ranges throughout the observed period.

Overall, the figure suggests a persistent difference in investment scale between English and non-English horror films, with English-language productions generally operating at higher budget levels. The widen separation in budget distributions over time highlights an increasing divergence in production scale rather than a uniform shift affecting both groups equally.

D3a ) Given investment, English films convert better?

movies_horror_viz %>%
  filter(financial_observable == "Observable") %>%
  mutate(
    budget_bin = cut(
      log_budget,
      breaks = seq(4, 8, by = 0.5),
      include.lowest = TRUE
    )
  ) %>%
  group_by(budget_bin, lang_group) %>%
  summarise(
    med = median(roi),
    q25 = quantile(roi, 0.25),
    q75 = quantile(roi, 0.75),
    n = n(),
    .groups = "drop"
  ) %>%
  filter(n >= 10) %>%
  ggplot(aes(budget_bin, med, color = lang_group)) +
  geom_point(position = position_dodge(0.5), size = 3) +
  geom_errorbar(
    aes(ymin = q25, ymax = q75),
    position = position_dodge(0.5),
    width = 0.2
  ) +
  scale_y_log10() +
  labs(
    title = "Median ROI by investment level and language",
    subtitle = "Based on financially observable horror films",
    x = "Log10 budget (binned)",
    y = "ROI (log scale)",
    color = NULL
  ) +
  theme_minimal()

Figure D3a presents median ROI and interquartile ranges across budget levels, comparing English and non-English horror films.

  • Across most investment levels, English-language horror films exhibit higher median ROI than non-English films.

  • The difference in median ROI is most pronounced at low to mid investment levels.

  • For both language groups, median ROI tends to decline as investment levels increase.

  • Despite this downward trend, English-language films maintain higher median ROI at comparable budget tiers.

Overall, the figure suggests that while higher-budget horror films are associated with lower median ROI for both language groups, English-language productions consistently achieve higher returns at comparable investment levels.

D3b ) High-return regions are more densely occupied by English films?

movies_horror_viz %>%
  filter(financial_observable == "Observable") %>%
  mutate(
    log_roi = log10(roi)
  ) %>%
  filter(
    !is.na(log_budget),
    !is.na(log_roi),
    log_roi > -1,
    log_roi < 2
  ) %>%
  ggplot(aes(log_budget, log_roi)) +
  geom_bin2d(bins = 30) +
  facet_wrap(~ lang_group) +
  scale_fill_viridis_c(trans = "log10") +
  labs(
    title = "Investment–return density by language",
    subtitle = "Heatmaps show where high-return outcomes concentrate",
    x = "Log10 Budget",
    y = "Log10 ROI",
    fill = "Count"
  ) +
  theme_minimal()

Figure D3b visualizes the joint distribution of investment and return on investment for English and non-English horror films using a two-dimensional density heatmap (log10 scale).

  • For English-language horror films, higher-density regions are primarily concentrated at mid to high investment levels with moderate to high ROI values.

  • English-language films exhibit a more clearly defined high-density region, indicating a greater clustering of observations within a specific investment–return range.

  • Non-English horror films display a more dispersed distribution across investment and ROI levels, with fewer observations forming dense clusters.

  • High-ROI observations among non-English films appear more sporadic and less concentrated across investment levels.

High-return outcomes are more densely concentrated among English-language horror films, particularly at mid-to-high investment levels, while non-English films show fewer and more scattered high-return cases.

Conclusion

This analysis highlights differences in production scale, investment levels, and return patterns between English and non-English horror films. English-language horror films consistently dominate production volume and operate at higher budget levels over time.

At comparable investment levels, English-language films tend to exhibit higher median returns, particularly within low to mid budget ranges. Correspondingly, higher-ROI observations are more densely clustered among English-language films, while non-English films display more dispersed return patterns. Overall, these findings suggest a persistent association between language and observed production and return structures in the horror film industry, without implying causal mechanisms underlying these differences.

Theme E — Franchise Protection

E1) Revenue–Budget joint distribution by franchise status

p <- movies_horror_viz %>%
  filter(financial_observable == "Observable") %>%
  filter(
    log_budget > 4, log_budget < 8,
    log_revenue > 4, log_revenue < 9
  ) %>%
  ggplot(aes(log_budget, log_revenue, color = franchise)) +
  geom_point(alpha = 0.2, size = 1) +
  scale_color_manual(
    values = c(
      "Franchise" = "#4575b4",
      "Standalone" = "#d73027"
    )
  ) +
  labs(
    title = "Revenue–budget outcomes by franchise status",
    subtitle = "Joint outcomes among financially observable horror films",
    x = "Budget (log10 USD)",
    y = "Revenue (log10 USD)",
    color = NULL
  ) +
  theme_minimal()

ggMarginal(
  p,
  type = "density",
  groupColour = TRUE,
  groupFill = TRUE,
  alpha = 0.4
)

  • Franchise films are more heavily concentrated at higher budget levels.

  • At comparable budget levels, franchise films tend to achieve higher revenues on average than standalone films.

  • Standalone films exhibit greater dispersion in revenue outcomes, particularly at mid-range budgets, reflecting higher variability and risk.

  • The revenue distribution for franchise films is more right-shifted, suggesting a higher probability of strong box office performance.

  • Budget distributions for franchise and standalone films overlap substantially, but their revenue outcomes diverge, especially at higher budgets.

The joint distribution shows that franchise films are more concentrated at higher budget levels and tend to achieve higher revenues than standalone films at comparable investment levels. While budget distributions overlap substantially between the two groups, franchise films exhibit a more right-shifted revenue distribution with comparatively lower dispersion in outcomes. Overall, these patterns indicate systematic differences in revenue distributions associated with franchise status, without implying causal mechanisms or performance guarantees.

E2) Are franchise films more favored by audiences?

movies_horror_base %>%
  filter(!is.na(vote_average), vote_average > 0) %>%
  ggplot(aes(vote_average, color = franchise)) +
  stat_ecdf(size = 1) +
  scale_color_manual(
    values = c(
      "Franchise" = "#4575b4",
      "Standalone" = "#d73027"
    )
  ) +
  labs(
    title = "Cumulative distribution of audience ratings",
    subtitle = "Franchise horror films tend to receive higher audience ratings",
    x = "Average Rating",
    y = "Cumulative proportion",
    color = NULL
  ) +
  theme_minimal()

  • Franchise films are less likely to receive low or mediocre ratings, as indicated by a lower cumulative proportion in the mid-range rating interval.

  • For any given rating threshold, franchise films show a higher probability of achieving ratings above that level, suggesting more consistent audience approval compared to standalone films.

  • Differences between franchise and standalone films are most pronounced in common rating ranges rather than at extreme values, indicating a distributional shift rather than isolated outliers.

Franchise status is associated with a right-shifted audience rating distribution, indicating that franchise horror films tend to receive higher ratings across much of the distribution. Differences between franchise and standalone films are most evident in common rating ranges rather than at extreme values, consistent with a general distributional shift rather than isolated high-rated cases.

Conclusion

The analysis reveal differences in audience reception and revenue distributions between franchise and standalone horror films. At comparable budget levels, franchise films tend to exhibit higher median audience ratings and less dispersed revenue outcomes. While budget distributions overlap substantially across the two groups, franchise films show more concentrated patterns in both audience reception and box office performance.

Overall, these findings indicate an association between franchise status and greater outcome stability within the observed data, without implying causal mechanisms or protective effects. Rather than guaranteeing exceptional success, franchise films appear to occupy narrower and more predictable ranges of outcomes in an otherwise volatile production environment.

Theme F - Production Outcomes & Failure Structure

F1) Where does financial failure tend to occur?

movies_horror_viz %>%
  filter(financial_observable == "Observable") %>%
  mutate(
    profit = revenue - budget,
    failure = profit < 0,
    budget_bin = cut(
      log_budget,
      breaks = seq(4, 8, by = 0.5)
    ),
    runtime_bin = cut(
      runtime,
      breaks = seq(60, 180, by = 15)
    )
  ) %>%
  group_by(budget_bin, runtime_bin) %>%
  summarise(
    failure_rate = mean(failure),
    n = n(),
    .groups = "drop"
  ) %>%
  filter(n >= 15) %>%
  ggplot(aes(budget_bin, runtime_bin, fill = failure_rate)) +
  geom_tile(color = "white") +
  scale_fill_viridis_c(
    labels = scales::percent,
    name = "Failure rate"
  ) +
  labs(
    title = "Failure risk across budget and runtime",
    subtitle = "Probability of financial loss among observable horror films (cells with ≥15 films)",
    x = "Budget (log10, binned)",
    y = "Runtime (minutes)"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

This heatmap displays the estimated failure rate across combinations of production budget (log10-binned) and runtime (minutes), with lighter colors indicating higher proportions of financially unsuccessful films. Bins containing at least 15 films.

  • Films with mid-range budgets (log10 ≈ 6–7) and runtimes between approximately 75 and 105 minutes tend to exhibit higher failure rates relative to other budget–runtime combinations.

  • Within shorter runtime ranges (75–90 minutes), failure rates vary substantially across budget levels, indicating notable heterogeneity in outcomes across investment scales.

  • Higher-budget films (log10 ≥ 7.5) generally appear to be associated with lower or more moderate failure rates across observed runtime categories, although variation remains within individual bins.

Overall, the results suggest that financial risk in film production is associated with the joint configuration of budget and runtime rather than either factor considered in isolation.

Mid-budget films appear to be associated with relatively higher failure rates across several runtime ranges. In contrast, higher-budget and longer films tend to cluster in regions characterized by lower or more moderate failure rates, although substantial variation remains within individual budget–runtime combinations.

F2) How do successful and failed films differ in their investemt-return patterns?

movies_horror_viz %>%
  filter(financial_observable == "Observable") %>%
  mutate(
    profit = revenue - budget,
    outcome = if_else(profit < 0, "Failure", "Success"),
    log_roi = log10(roi)
  ) %>%
  filter(log_roi > -1.5, log_roi < 2.5) %>%
  ggplot(aes(log_budget, log_roi, color = outcome)) +
  geom_point(
    alpha = 0.15,
    size = 0.8
  ) +
  geom_density_2d(
    linewidth = 0.9
  ) +
  scale_color_manual(
    values = c(
      "Failure" = "#d73027",
      "Success" = "#4575b4"
    )
  ) +
  labs(
    title = "Investment–return structure by financial outcome",
    subtitle = "Density contours reveal distinct regions for success and failure (financially observable horror films)",
    x = "Log10 Budget",
    y = "Log10 ROI",
    color = NULL
  ) +
  theme_minimal()

This figure illustrates the relationship between production budget (log10 USD) and return on investment (ROI, log10), separated by financial outcome. Kernel density are used to highlight the concentration of successful and failed films, allowing comparison of their investment–return structures.

  • Successful films cluster in regions with positive ROI, primarily at moderate to high budget levels (log10 ≈ 6–7.5).

  • Failed films are concentrated in areas with negative ROI, even within similar budget ranges, indicating that higher investment does not guarantee success.

  • There is a clear vertical separation between the density regions associated with successful and failed films, reflecting distinct ROI distributions across financial outcomes.

Conclusion

This project examines patterns of risk and return in the horror film industry by comparing films across language groups, franchise status, and production outcomes. Rather than evaluating individual titles, the analysis focuses on how differences in production scale and market positioning are reflected in investment levels, audience ratings, and financial results.

The results indicate consistent differences between English-language and non-English horror films. English-language productions account for a larger share of total output, operate at higher budget levels, and tend to exhibit higher returns at comparable levels of investment. High-ROI observations are also more densely clustered among English-language films, reflecting differences in the distribution of financial outcomes rather than isolated successes.

Franchise status is similarly associated with distinct outcome patterns. Franchise films generally show less dispersion in both audience ratings and revenue outcomes compared to standalone films. At the same time, analysis of financial losses suggests that production risk varies across combinations of budget and runtime, rather than being determined by investment scale alone.

Overall, the findings highlight systematic variation in production and outcome structures within the horror film industry. Differences in language and franchise affiliation are associated with more concentrated and predictable patterns of audience reception and financial performance, while substantial variability remains across individual films.

7) Reproducibility

sessionInfo()
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-apple-darwin20
## Running under: macOS 15.7.2
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Europe/Warsaw
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] ggrepel_0.9.6   patchwork_1.3.2 plotly_4.10.4   ggExtra_0.11.0 
##  [5] ggridges_0.5.7  here_1.0.1      scales_1.4.0    lubridate_1.9.3
##  [9] forcats_1.0.0   stringr_1.5.1   dplyr_1.1.4     purrr_1.2.1    
## [13] readr_2.1.5     tidyr_1.3.1     tibble_3.2.1    ggplot2_4.0.1  
## [17] tidyverse_2.0.0
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       xfun_0.54          bslib_0.8.0        htmlwidgets_1.6.4 
##  [5] tzdb_0.5.0         vctrs_0.6.5        tools_4.4.1        generics_0.1.3    
##  [9] parallel_4.4.1     fansi_1.0.6        highr_0.11         pkgconfig_2.0.3   
## [13] data.table_1.16.2  RColorBrewer_1.1-3 S7_0.2.0           lifecycle_1.0.4   
## [17] compiler_4.4.1     farver_2.1.2       httpuv_1.6.15      htmltools_0.5.8.1 
## [21] sass_0.4.9         yaml_2.3.10        lazyeval_0.2.2     crayon_1.5.3      
## [25] pillar_1.9.0       later_1.3.2        jquerylib_0.1.4    MASS_7.3-65       
## [29] cachem_1.1.0       mime_0.12          tidyselect_1.2.1   digest_0.6.37     
## [33] stringi_1.8.4      labeling_0.4.3     rprojroot_2.0.4    fastmap_1.2.0     
## [37] grid_4.4.1         cli_3.6.5          magrittr_2.0.4     dichromat_2.0-0.1 
## [41] utf8_1.2.4         withr_3.0.2        promises_1.3.2     bit64_4.5.2       
## [45] timechange_0.3.0   rmarkdown_2.28     httr_1.4.7         bit_4.5.0         
## [49] hms_1.1.3          shiny_1.10.0       evaluate_1.0.1     knitr_1.48        
## [53] miniUI_0.1.1.1     viridisLite_0.4.2  rlang_1.1.6        isoband_0.2.7     
## [57] Rcpp_1.1.0         xtable_1.8-4       glue_1.8.0         rstudioapi_0.16.0 
## [61] vroom_1.6.5        jsonlite_1.8.9     R6_2.5.1