Body Mass Distributions by Penguin Species - Ridgeline Density Plots
This plot uses ridgeline curves to compare the body mass distribution of Adelie, Gentoo, and Chinstrap penguins. It blends smooth density estimates with individual data points, using a color gradient to emphasize mass values and highlight species-specific differences in central tendency and spread.
#TidyTuesday
{ggridges}
Images
{magick}
Author
Aditya Dahiya
Published
April 20, 2025
About the Data
The penguins dataset is included in base R starting from version 4.5.0, and is available via the datasets package. It provides measurements of adult penguins across three species—Adélie, Chinstrap, and Gentoo—found on three islands in the Palmer Archipelago, Antarctica. Key variables include flipper length, body mass, bill dimensions, sex, and the year of observation. This dataset is a curated subset of a more extensive penguins_raw dataset, which also contains information on nesting behavior and blood isotope ratios. Originally used by Gorman et al. (2014) to explore sexual dimorphism in penguins, the dataset gained broader popularity through the palmerpenguins package as a user-friendly alternative to the classic iris dataset. Recent efforts by Kaye et al. (2025) have integrated this data directly into base R, along with reproducible scripts and documentation.
Figure 1: This graphic displays the distribution of body mass (in grams) across three species of penguins — Adelie, Gentoo, and Chinstrap — using ridgeline plots. Each ridge represents a smoothed density curve for a species, overlaid with individual data points and colored using a gradient based on body mass. The plot highlights differences in central tendency and spread, with Gentoo penguins generally being heavier, followed by Chinstrap and Adelie. Gradient shading emphasizes the mass range, while quantile lines indicate medians. This visualization effectively combines density estimation with raw data using the powerful {ggridges} extension to ggplot2.
How I made this graphic?
This ridgeline plot leverages the {ggridges} package to visualize body_mass distributions across three penguin species, using geom_density_ridges_gradient to apply a color gradient based on the underlying density. Statistical aesthetics such as stat(x) enable dynamic fill mapping, while quantile_lines = TRUE, quantiles = 2 highlights median density along each ridge (Vignette). Raw observations are overlaid via jittered points using position_points_jitter() for clarity, ensuring individual data points are visible beneath each curve. Manual species labels are added with geom_text(). A continuous palette from the {paletteer} package is applied with scale_fill_paletteer_c(), squishing values outside the specified limits for a polished color scale.
Loading required libraries
Code
pacman::p_load( tidyverse, # All tidy tools: data wrangling scales, # Nice Scales for ggplot2 fontawesome, # Icons display in ggplot2 ggtext, # Markdown text support for ggplot2 showtext, # Display fonts in ggplot2 colorspace, # Lighten and Darken colours magick, # Download images and edit them ggimage, # Display images in ggplot2 pactchwork, # Composing Plots ggridges # for ridgeline density plots)penguins <- penguins |>as_tibble()
Visualization Parameters
Code
# Font for titlesfont_add_google("Yaldevi",family ="title_font") # Font for the captionfont_add_google("Barlow Condensed",family ="caption_font") # Font for plot textfont_add_google("Fira Code",family ="body_font") showtext_auto()# cols4all::c4a_gui()# Pick a colour paletter that is Colour-Blind friendly, fair and # has a good contrast ratio with whitemypal <- paletteer::paletteer_d("ltc::trio3")mypal <-c("#FF7502", "#C55CC9", "#0F6F74")# A base Colourbg_col <-"white"seecolor::print_color(bg_col)# Colour for highlighted texttext_hil <-"grey30"seecolor::print_color(text_hil)# Colour for the texttext_col <-"grey30"seecolor::print_color(text_col)line_col <-"grey30"# Define Base Text Sizebts <-90# Caption stuff for the plotsysfonts::font_add(family ="Font Awesome 6 Brands",regular = here::here("docs", "Font Awesome 6 Brands-Regular-400.otf"))github <-""github_username <-"aditya-dahiya"xtwitter <-""xtwitter_username <-"@adityadahiyaias"social_caption_1 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{github};</span> <span style='color: {text_hil}'>{github_username} </span>")social_caption_2 <- glue::glue("<span style='font-family:\"Font Awesome 6 Brands\";'>{xtwitter};</span> <span style='color: {text_hil}'>{xtwitter_username}</span>")plot_caption <-paste0("**Data:** {palmerpenguins}, base R", " | **Code:** ", social_caption_1, " | **Graphics:** ", social_caption_2 )rm(github, github_username, xtwitter, xtwitter_username, social_caption_1, social_caption_2)# Add text to plot-------------------------------------------------plot_title <-"Body Mass Distributions\nby Penguin Species"plot_subtitle <-"This ridgeline plot, powered by <b style='color:black'>{ggridges}</b> and its<b style='color:black'>geom_density_ridges_gradient()</b>,<br>overlays smoothed density curves with vibrant gradient fills. It layers jittered<br>raw measurements via <b style='color:black'>position_points_jitter()</b> beneath each ridge. The graphic<br>contrasts body mass distributions of <b style='color:#FF7502'>Adelie</b>, <b style='color:#0F6F74'>Gentoo</b>, and <b style='color:#C55CC9'>Chinstrap</b>."
# Saving a thumbnaillibrary(magick)# Saving a thumbnail for the webpageimage_read(here::here("data_vizs", "tidy_palmerpenguins_2.png")) |>image_resize(geometry ="x400") |>image_write( here::here("data_vizs", "thumbnails", "tidy_palmerpenguins_2.png" ) )
Session Info
Code
pacman::p_load( tidyverse, # All tidy tools: data wrangling scales, # Nice Scales for ggplot2 fontawesome, # Icons display in ggplot2 ggtext, # Markdown text support for ggplot2 showtext, # Display fonts in ggplot2 colorspace, # Lighten and Darken colours magick, # Download images and edit them ggimage, # Display images in ggplot2 pactchwork, # Composing Plots ggridges # for ridgeline density plots)sessioninfo::session_info()$packages |>as_tibble() |>select(package, version = loadedversion, date, source) |>arrange(package) |> janitor::clean_names(case ="title" ) |> gt::gt() |> gt::opt_interactive(use_search =TRUE ) |> gtExtras::gt_theme_espn()
Table 1: R Packages and their versions used in the creation of this page and graphics