R ggplot2 - Legend Customization

• ggplot2 provides granular control over legend appearance through `theme()`, `guides()`, and scale functions, allowing you to position, style, and organize legends to match publication requirements

Key Insights

• ggplot2 provides granular control over legend appearance through theme(), guides(), and scale functions, allowing you to position, style, and organize legends to match publication requirements • Multiple legends can be merged, removed, or customized independently using guide_legend() and guide_colorbar() with parameters for layout, spacing, and visual hierarchy • Legend titles, labels, and keys are fully customizable through scale functions and theme elements, enabling precise control over typography, colors, and positioning

Understanding ggplot2 Legend Basics

ggplot2 automatically generates legends when you map variables to aesthetics like color, fill, size, or shape. The legend system is tightly integrated with scales, making customization both powerful and sometimes complex.

library(ggplot2)
library(dplyr)

# Basic plot with automatic legend
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  labs(color = "Cylinders")

Every aesthetic mapping creates a corresponding legend. Understanding this relationship is crucial for effective customization.

Positioning and Layout Control

Legend position is controlled through theme(legend.position). You can use keywords or precise coordinates.

# Keyword positioning
p <- ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3)

# Inside plot area with coordinates (0-1 scale)
p + theme(legend.position = c(0.85, 0.85))

# Standard positions
p + theme(legend.position = "bottom")  # Also: "top", "left", "right", "none"

# Horizontal layout for bottom position
p + theme(
  legend.position = "bottom",
  legend.direction = "horizontal"
)

For multiple legends, control their arrangement with legend.box:

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), shape = factor(am))) +
  geom_point(size = 3) +
  theme(
    legend.position = "right",
    legend.box = "vertical",  # or "horizontal"
    legend.box.just = "top"
  )

Customizing Legend Appearance

The theme() function provides extensive control over legend aesthetics through legend.* parameters.

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  theme(
    legend.background = element_rect(fill = "lightgray", color = "black", linewidth = 0.5),
    legend.key = element_rect(fill = "white", color = NA),
    legend.key.size = unit(1.5, "cm"),
    legend.key.width = unit(2, "cm"),
    legend.title = element_text(size = 14, face = "bold"),
    legend.text = element_text(size = 12, color = "darkblue"),
    legend.spacing.y = unit(0.5, "cm")
  )

Remove legend background and borders for cleaner presentations:

p + theme(
  legend.background = element_blank(),
  legend.key = element_blank(),
  legend.box.background = element_blank()
)

Modifying Legend Titles and Labels

Scale functions control legend titles and labels directly:

# Customize through scale functions
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  scale_color_discrete(
    name = "Engine\nCylinders",
    labels = c("Four", "Six", "Eight")
  )

# Remove legend title
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  scale_color_discrete(name = NULL)

# Format continuous legend
ggplot(mtcars, aes(x = wt, y = mpg, color = hp)) +
  geom_point(size = 3) +
  scale_color_continuous(
    name = "Horsepower",
    breaks = seq(100, 300, 50),
    labels = paste0(seq(100, 300, 50), " HP")
  )

Working with Multiple Legends

When multiple aesthetics are mapped, you get multiple legends. Control them individually using guides():

ggplot(mtcars, aes(x = wt, y = mpg, color = hp, size = qsec)) +
  geom_point(alpha = 0.7) +
  guides(
    color = guide_colorbar(
      title = "Horsepower",
      barwidth = 1,
      barheight = 10,
      title.position = "top"
    ),
    size = guide_legend(
      title = "Quarter Mile Time",
      title.position = "top",
      nrow = 2
    )
  )

Merge legends when multiple aesthetics map to the same variable:

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), shape = factor(cyl))) +
  geom_point(size = 3) +
  scale_color_discrete(name = "Cylinders") +
  scale_shape_discrete(name = "Cylinders")  # Same name merges legends

Remove specific legends while keeping others:

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), size = hp)) +
  geom_point(alpha = 0.7) +
  guides(size = "none")  # Remove size legend only

Advanced Guide Customization

The guide_legend() and guide_colorbar() functions offer detailed control over legend presentation.

# Custom legend layout
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  guides(color = guide_legend(
    title = "Cylinders",
    nrow = 3,
    ncol = 1,
    byrow = TRUE,
    reverse = TRUE,
    title.position = "top",
    title.hjust = 0.5,
    label.position = "right",
    label.hjust = 0,
    keywidth = 2,
    keyheight = 1,
    override.aes = list(size = 5)  # Override aesthetic in legend
  ))

For continuous scales, customize color bars:

ggplot(mtcars, aes(x = wt, y = mpg, color = hp)) +
  geom_point(size = 3) +
  scale_color_gradient(low = "blue", high = "red") +
  guides(color = guide_colorbar(
    title = "Horsepower",
    barwidth = 2,
    barheight = 15,
    ticks = TRUE,
    ticks.colour = "black",
    ticks.linewidth = 1,
    frame.colour = "black",
    frame.linewidth = 0.5,
    title.position = "top",
    title.hjust = 0.5
  ))

Handling Fill and Color Legends

Distinguish between color (lines/points) and fill (areas) aesthetics:

# Both color and fill
ggplot(mtcars, aes(x = factor(cyl), y = mpg, fill = factor(cyl))) +
  geom_boxplot(aes(color = factor(cyl)), linewidth = 1) +
  scale_fill_manual(
    name = "Cylinders",
    values = c("4" = "lightblue", "6" = "lightgreen", "8" = "lightcoral")
  ) +
  scale_color_manual(
    name = "Cylinders",
    values = c("4" = "darkblue", "6" = "darkgreen", "8" = "darkred")
  )

Legend Order and Arrangement

Control the order of legend items through factor levels or manual specification:

# Reorder through factor levels
mtcars$cyl_ordered <- factor(mtcars$cyl, levels = c(8, 6, 4))

ggplot(mtcars, aes(x = wt, y = mpg, color = cyl_ordered)) +
  geom_point(size = 3) +
  scale_color_discrete(name = "Cylinders")

# Reverse legend order
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point(size = 3) +
  guides(color = guide_legend(reverse = TRUE))

Publication-Ready Legend Styling

Combine multiple customizations for professional output:

ggplot(mtcars, aes(x = wt, y = mpg, color = hp, shape = factor(am))) +
  geom_point(size = 3, alpha = 0.8) +
  scale_color_viridis_c(
    name = "Horsepower",
    option = "plasma",
    breaks = seq(100, 300, 50)
  ) +
  scale_shape_manual(
    name = "Transmission",
    values = c(16, 17),
    labels = c("Automatic", "Manual")
  ) +
  guides(
    color = guide_colorbar(
      barwidth = 1.5,
      barheight = 12,
      title.position = "top",
      title.hjust = 0.5
    ),
    shape = guide_legend(
      title.position = "top",
      title.hjust = 0.5,
      override.aes = list(size = 4)
    )
  ) +
  theme_minimal() +
  theme(
    legend.position = "right",
    legend.box = "vertical",
    legend.title = element_text(size = 11, face = "bold"),
    legend.text = element_text(size = 10),
    legend.key = element_rect(fill = "white", color = NA),
    legend.spacing.y = unit(0.3, "cm")
  )

This comprehensive approach to legend customization ensures your visualizations meet exact specifications for publications, presentations, or reports. Master these techniques to transform default ggplot2 legends into polished, professional components that enhance rather than distract from your data story.

Liked this? There's more.

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