How to Create a Pie Chart in ggplot2

ggplot2 takes an unconventional approach to pie charts. Unlike other visualization libraries that provide dedicated pie chart functions, ggplot2 requires you to build a stacked bar chart first, then...

Key Insights

  • ggplot2 doesn’t have a dedicated pie chart function—you create them by transforming stacked bar charts using coord_polar(), which converts the y-axis to polar coordinates
  • Pie charts work best for showing proportions of 3-5 categories that sum to a meaningful whole; for more categories or when precise comparison matters, use bar charts instead
  • Calculate percentage labels using mutate() before plotting, and position them carefully with geom_text() to ensure readability on smaller slices

Understanding ggplot2’s Approach to Pie Charts

ggplot2 takes an unconventional approach to pie charts. Unlike other visualization libraries that provide dedicated pie chart functions, ggplot2 requires you to build a stacked bar chart first, then transform it into a pie chart using polar coordinates. This might seem counterintuitive, but it reflects the package’s grammar-of-graphics philosophy where visualizations are built from composable layers.

Let’s start by loading the necessary libraries and creating sample data:

library(ggplot2)
library(dplyr)

# Create sample data: company market share
market_data <- data.frame(
  company = c("Company A", "Company B", "Company C", "Company D", "Company E"),
  market_share = c(35, 25, 20, 12, 8)
)

print(market_data)

This dataset represents market share percentages across five companies. The values sum to 100, making it ideal for pie chart visualization.

Building Your First Pie Chart

The key to creating pie charts in ggplot2 is understanding the transformation process. You’ll create a stacked bar chart with geom_bar(), then convert it to polar coordinates with coord_polar().

Here’s the basic approach:

# Basic pie chart
ggplot(market_data, aes(x = "", y = market_share, fill = company)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y")

Let’s break down what’s happening:

  • aes(x = "", y = market_share, fill = company): We set x to an empty string (creating a single stacked bar), map market_share to y, and use company for fill colors
  • geom_bar(stat = "identity", width = 1): Creates a bar chart with heights directly from the data (stat = "identity"), with full width to eliminate gaps
  • coord_polar(theta = "y"): Transforms the y-axis into angular coordinates, converting the stacked bar into a pie chart

The result is functional but visually cluttered with unnecessary grid lines, axes, and labels that don’t make sense for a pie chart.

Cleaning Up the Appearance

A professional pie chart needs visual refinement. Remove the coordinate system artifacts and apply custom colors:

# Define custom color palette
custom_colors <- c("#FF6B6B", "#4ECDC4", "#45B7D1", "#FFA07A", "#98D8C8")

# Clean pie chart
ggplot(market_data, aes(x = "", y = market_share, fill = company)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar(theta = "y", start = 0) +
  scale_fill_manual(values = custom_colors) +
  theme_void() +
  theme(legend.position = "right",
        legend.title = element_text(face = "bold", size = 12),
        legend.text = element_text(size = 10))

Key improvements:

  • color = "white" in geom_bar(): Adds white borders between slices for better separation
  • start = 0 in coord_polar(): Controls the starting angle of the first slice
  • scale_fill_manual(): Applies your custom color palette
  • theme_void(): Removes all background elements, axes, and grid lines
  • Custom theme adjustments: Positions and styles the legend appropriately

Adding Percentage Labels

Pie charts become significantly more useful when you add data labels. Calculate percentages and position them on each slice:

# Prepare data with percentages and label positions
market_data_labeled <- market_data %>%
  mutate(
    percentage = market_share / sum(market_share) * 100,
    label = paste0(company, "\n", round(percentage, 1), "%"),
    position = cumsum(market_share) - market_share / 2
  )

# Pie chart with labels
ggplot(market_data_labeled, aes(x = "", y = market_share, fill = company)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar(theta = "y", start = 0) +
  scale_fill_manual(values = custom_colors) +
  geom_text(aes(y = position, label = label), 
            color = "white", 
            size = 4, 
            fontface = "bold") +
  theme_void() +
  theme(legend.position = "none")  # Remove legend since labels are on slices

The mutate() chain does three things:

  1. Calculates the percentage each value represents
  2. Creates formatted labels combining company name and percentage
  3. Calculates the midpoint position of each slice for label placement

The position calculation uses cumsum() to find the cumulative sum, then subtracts half the current value to center the label in each slice.

Creating a Donut Chart

Donut charts are a modern variation that many designers prefer over traditional pie charts. They’re easier to read and look more contemporary. Creating one requires just one additional line:

# Donut chart
ggplot(market_data_labeled, aes(x = "", y = market_share, fill = company)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar(theta = "y", start = 0) +
  xlim(c(-0.5, 1.5)) +  # This creates the hole
  scale_fill_manual(values = custom_colors) +
  geom_text(aes(y = position, label = label), 
            color = "black", 
            size = 3.5) +
  theme_void() +
  theme(legend.position = "right")

The magic happens with xlim(c(-0.5, 1.5)). By extending the x-axis limits beyond the bar width, you create empty space in the center. Adjust the first value (negative number) to control the hole size—more negative values create larger holes.

You can also add a title in the center of a donut chart:

ggplot(market_data_labeled, aes(x = "", y = market_share, fill = company)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar(theta = "y", start = 0) +
  xlim(c(-0.5, 1.5)) +
  scale_fill_manual(values = custom_colors) +
  geom_text(aes(y = position, label = paste0(round(percentage, 1), "%")), 
            color = "black", 
            size = 3.5) +
  annotate("text", x = -0.5, y = 0, label = "Market\nShare", 
           size = 5, fontface = "bold") +
  theme_void() +
  theme(legend.position = "right")

When to Use Pie Charts (and When Not To)

Pie charts are controversial in data visualization. They’re effective for specific use cases but often misused. Use pie charts when:

  • You have 3-5 categories (more becomes cluttered and hard to compare)
  • You’re showing parts of a meaningful whole (percentages that sum to 100%)
  • Precise comparison isn’t critical
  • Your audience expects or prefers this format

Avoid pie charts when:

  • You have many categories (use bar charts instead)
  • Values are similar in size (humans struggle to compare angles)
  • You need precise comparisons
  • You’re showing changes over time

Here’s the same data as a bar chart for comparison:

# Alternative: horizontal bar chart
ggplot(market_data, aes(x = reorder(company, market_share), y = market_share, fill = company)) +
  geom_col(color = "white", show.legend = FALSE) +
  geom_text(aes(label = paste0(market_share, "%")), 
            hjust = -0.2, 
            size = 4) +
  scale_fill_manual(values = custom_colors) +
  coord_flip() +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
  labs(x = NULL, y = "Market Share (%)", 
       title = "Market Share by Company") +
  theme_minimal() +
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor = element_blank(),
        axis.text.x = element_text(size = 10))

This bar chart makes it easier to compare values precisely and see the ranking at a glance. The human eye is much better at comparing bar lengths than pie slice angles.

Practical Tips for Better Pie Charts

When you do create pie charts, follow these guidelines:

Order slices logically: Sort by size (largest to smallest) starting from 12 o’clock, or use a meaningful categorical order.

Limit categories: Combine small slices into an “Other” category if you have more than five segments.

Use contrasting colors: Ensure adjacent slices have sufficient contrast for colorblind accessibility.

Label directly: Place labels on slices rather than relying solely on legends when possible.

Consider donut charts: They often look cleaner and provide space for central annotations.

Pie charts have their place in data visualization, particularly in business contexts where stakeholders expect them. Just ensure you’re using them appropriately and not sacrificing clarity for familiarity. When in doubt, test both a pie chart and a bar chart with your audience to see which communicates more effectively.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.