Mastering Advanced Data Visualization: The Lollipop Chart

Step-by-Step Evolution from Basics to High-End Art

Author

Abdullah Al Shamim

Published

February 14, 2026

Notebook Introduction (Metadata)

  • Aim: Create a professional visualization of mammalian sleep habits using the msleep dataset.
  • Key Skills: Layering (Geometry), Theme Customization (Dark Mode), Annotations, and Color Gradients.
  • Concept: In this tutorial, we will learn how to transform a simple plot into “art” by adding code layer by layer.

Dataset Introduction

This dataset contains information on the sleep patterns, body weights, and brain weights of 83 mammalian species. It is an eco-physiological dataset.

Variable Dictionary

Variable Description
name Common name of the animal.
genus Taxonomic rank (Genus).
vore Dietary category (carni, herbi, omni, insecti).
order Taxonomic order.
conservation Conservation status (e.g., lc - least concern, en - endangered).
sleep_total Total sleep in 24 hours (hours).
sleep_rem REM (Rapid Eye Movement) sleep (hours).
sleep_cycle Length of sleep cycle (hours).
awake Total time awake in 24 hours (hours).
brainwt Brain weight (kilograms).
bodywt Total body weight (kilograms).

1. Environment Setup & Data Processing

First, let’s load the necessary libraries and organize our data. We will calculate the average sleep (mean_sleep) grouped by order.

Code
library(tidyverse)

1.1 Grouping & Summarizing

We use group_by(order) to categorize the animals and summarise() to calculate the average sleep for each group.

Code
# Data Preparation
sleep_data <- msleep %>% 
  group_by(order) %>% 
  summarise(mean_sleep = mean(sleep_total)) 

1.2 Factor Reordering

To make the visualization aesthetically pleasing, we use fct_reorder(). This sorts the categories from lowest to highest based on the mean sleep values.

Code
sleep_data <- sleep_data %>% 
  mutate(order = fct_reorder(order, mean_sleep)) 

2. Step-by-Step Plot Evolution

Step 1: The Basic Canvas

We start by defining the X and Y axes and adding labels.

Code
plot_step1 <- sleep_data %>% 
  ggplot(aes(order, mean_sleep)) +
  labs(title = "Average Sleep Time of Mammals by Order",
       x = "",
       y = "Hours")

plot_step1


Step 2: Styling (Custom Theme & Dark Mode)

Now we will darken the background and change text colors to create a “Neon Vibe.”

2.1 Axis Labels Style

We will tilt the X-axis labels by 45° for better readability and apply specific hex codes for color.

Code
plot_step2 <- plot_step1 +
  theme(
    axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, color = "#d580ff"),
    axis.text.y = element_text(color = "#eabfff")
  )

plot_step2

2.2 Title & Axis Title Style

We make the main title and axis titles bold and vibrant.

Code
plot_step2 <- plot_step2 +
  theme(
    axis.title.y = element_text(color = "#d580ff"),
    plot.title = element_text(color = "#d580ff", face = "bold", size = 20)
  )

plot_step2

2.3 Axis Lines & Ticks Style

We apply the neon color to the axis lines and tick marks.

Code
plot_step2 <- plot_step2 +
  theme(
    axis.line = element_line(color = "#d580ff"),
    axis.ticks = element_line(color = "#d580ff")
  )

plot_step2

2.4 Background Colors

To give the plot a “Dark Theme,” we set both panel.background and plot.background to black.

Code
plot_step2 <- plot_step2 +
  theme(
    panel.background = element_rect(fill = "black"),
    plot.background = element_rect(fill = "black")
  )

plot_step2

2.5 Cleaning Grids & Legend

To achieve a modern look, we remove all grid lines and the legend.

Code
plot_step2 <- plot_step2 +
  theme(
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  )

plot_step2


Step 3: The Lollipop Base (Reference Line & Sticks)

We add a benchmark line for the overall average sleep and connecting “sticks” for each point.

Code
plot_step3 <- plot_step2 +
  # Horizontal line for overall average sleep
  geom_hline(yintercept = mean(msleep$sleep_total), 
             color = "#d580ff", linewidth = 1) +
  # Segments to show distance from the mean line
  geom_segment(aes(x = order, 
                   y = mean(msleep$sleep_total), 
                   xend = order, 
                   yend = mean_sleep), 
               color = "#d580ff")

plot_step3


Step 4: Adding Color Gradient Points

We place points at the end of the sticks that change color based on the amount of sleep.

Code
plot_step4 <- plot_step3 +
  geom_point(aes(color = mean_sleep), size = 5) +
  scale_color_gradient(low = "purple", high = "#eabfff")

plot_step4


Step 5: The Final Touch (Annotations & Arrows)

Finally, we add an explanatory text and a curved arrow for better storytelling.

Code
final_plot <- plot_step4 +
  # Adding explanatory text
  annotate("text", x = 4, y = max(msleep$sleep_total) - 4, 
           label = "Average sleep\nfor all mammals", 
           color = "#eabfff", size = 4, fontface = "bold", hjust = 0) +
  # Adding a curved arrow pointing to the mean line
  geom_curve(aes(x = 3.7, y = max(msleep$sleep_total) - 5, 
                   xend = 1.5, yend = mean(msleep$sleep_total)), 
               color = "#eabfff", curvature = 0.5, 
               arrow = arrow(length = unit(0.07, "npc"), type = "open"))

final_plot


Key Visual Toolkit (Cheat Sheet)

  • fct_reorder(): Used to sort categories based on numeric values.
  • theme(panel.background = ...): Customizes the internal plot background.
  • geom_segment(): Creates the “sticks” for the lollipop chart.
  • **geom_curve() & annotate()**: Highlights or explains specific parts of the graph.
  • scale_color_gradient(): Shows color shifts for continuous data.

Congratulations! You have successfully transformed a simple plot into a high-end visualization.