Facets, Scales, and Themes in ggplot2
Facets allow you to split data across multiple panels, scales give you precise control over how data maps to visual properties, and themes let you customize the overall appearance of your plots. Together, these three components transform basic ggplot2 visualizations into polished, publication-ready graphics.
This tutorial builds on the ggplot2 fundamentals covered in earlier tutorials. We’ll use the mpg dataset throughout to demonstrate how these features work in practice.
Facets: Creating Multi-Panel Plots
Facets are essential when you want to compare subgroups across the same visual encoding. ggplot2 provides two main facet functions: facet_wrap() and facet_grid().
Using facet_wrap()
facet_wrap() arranges panels in a grid based on a single categorical variable or a combination of variables. It’s ideal when you have one dimension of variation:
library(ggplot2)
# Create a basic scatter plot
p <- ggplot(mpg, aes(displ, hwy)) +
geom_point() +
labs(x = "Engine displacement (L)", y = "Highway MPG")
# Facet by a single variable
p + facet_wrap(~ class, nrow = 2)
This creates separate panels for each vehicle class. The nrow and ncol arguments control the layout. You can also use dir = "v" for vertical arrangement.
Using facet_grid()
facet_grid() creates a two-dimensional grid based on two variables, making it perfect for comparing across two categorical dimensions:
# Facet by two variables: drv (drive type) and cyl (cylinder count)
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_grid(drv ~ cyl) +
labs(x = "Engine displacement (L)", y = "Highway MPG")
The syntax drv ~ cyl reads as “rows ~ columns”. This generates a grid where each cell represents a unique combination of drive type and cylinder count.
Controlling Facet Labels and Scales
By default, facets share the same scales across panels. You can modify this behavior with the scales argument:
"fixed"(default): all panels share the same scale"free": scales adjust independently for each panel"free_x": x-axis scales vary; y-axis is shared"free_y": y-axis scales vary; x-axis is shared
# Allow scales to vary freely across panels
p + facet_wrap(~ class, scales = "free")
You can also control whether facet labels appear inside or outside the panels using the margins argument or by specifying strip.position.
Scales: Precise Control Over Data Mapping
Scales control how data values map to visual properties. Every aesthetic in ggplot2 has an associated scale, and customizing these gives you fine-grained control over your visualizations.
Axis Scales
Axis scale functions follow the pattern scale_<aesthetic>_<type>(). For continuous axes:
# Customize x-axis with specific limits and breaks
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
scale_x_continuous(
name = "Engine Size (liters)",
limits = c(1, 8),
breaks = seq(1, 8, by = 1),
expand = expansion(mult = 0.05)
) +
scale_y_continuous(
name = "Highway MPG",
limits = c(10, 50),
breaks = c(15, 25, 35, 45)
)
The expand argument controls the padding around the data limits, ensuring points don’t sit exactly on the axis line.
Color Scales
Color aesthetics support both continuous and discrete mappings. For discrete (categorical) variables:
# Use a custom color palette for manufacturer
ggplot(mpg, aes(displ, hwy, color = manufacturer)) +
geom_point() +
scale_color_brewer(palette = "Dark2")
The scale_color_brewer() function provides access to ColorBrewer palettes, which are designed for optimal color perception. For custom colors, use scale_color_manual():
# Define specific colors for each manufacturer
ggplot(mpg, aes(displ, hwy, color = manufacturer)) +
geom_point() +
scale_color_manual(
values = c("audi" = "#E41A1C", "ford" = "#377EB8",
"chevrolet" = "#4DAF4A", "honda" = "#984EA3")
)
For continuous color gradients, use scale_color_gradient() for a two-color gradient or scale_color_viridis() for perceptually uniform colors:
# Continuous color scale for highway mpg
ggplot(mpg, aes(displ, hwy, color = hwy)) +
geom_point(size = 3) +
scale_color_viridis_c(option = "plasma")
Legend Control
Scales also control legend appearance. The name argument overrides the default legend title, while labels customizes legend text:
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point() +
scale_color_manual(
name = "Vehicle Class",
labels = c("2seater" = "Two-seater", "compact" = "Compact",
"midsize" = "Mid-size", "minivan" = "Minivan",
"pickup" = "Pickup", "subcompact" = "Subcompact",
"suv" = "SUV"),
values = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
"#FF7F00", "#FFFF33", "#A65628")
)
Themes: Customizing Plot Appearance
Themes control the non-data aspects of your plot: backgrounds, grid lines, text formatting, and more. ggplot2 includes several built-in themes and allows complete customization.
Built-in Themes
The quickest way to change your plot’s appearance is using a complete theme:
# Various built-in themes
p <- ggplot(mpg, aes(displ, hwy, color = class)) + geom_point()
p + theme_bw() # Black and white theme
p + theme_minimal() # Minimal theme (no background)
p + theme_dark() # Dark background
p + theme_classic() # Classic theme (no grid lines)
p + theme_void() # Empty theme (only data elements)
Each theme provides a different visual style. theme_minimal() and theme_bw() are popular choices for publications.
Customizing Theme Elements
For precise control, use the theme() function with element_*() functions:
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point(size = 2) +
theme(
# Panel appearance
panel.background = element_rect(fill = "white", color = "gray80"),
panel.grid.major = element_line(color = "gray90"),
panel.grid.minor = element_blank(),
# Axis text and title
axis.title = element_text(size = 12, face = "bold"),
axis.text = element_text(color = "gray40"),
# Legend
legend.position = "bottom",
legend.title = element_text(face = "bold"),
legend.key = element_rect(fill = NA),
# Plot title
plot.title = element_text(size = 14, hjust = 0.5)
) +
labs(title = "Engine Size vs Highway Fuel Efficiency")
Key element functions include:
element_rect(): rectangles (panels, legends)element_line(): lines (grid, axes)element_text(): text (labels, titles)element_blank(): removes an element entirely
Saving Theme Customizations
If you frequently use the same theme settings, save them as an object:
# Create a custom theme
my_theme <- theme(
panel.background = element_rect(fill = "#f8f9fa"),
panel.grid.major = element_line(color = "#dee2e6"),
axis.title = element_text(size = 11),
legend.position = "bottom",
legend.background = element_rect(fill = NA)
)
# Apply to any plot
p + my_theme
You can also set a theme globally with theme_set(), affecting all subsequent plots in your session.
Bringing It All Together
Let’s create a publication-ready visualization combining facets, scales, and themes:
# Comprehensive example
ggplot(mpg, aes(displ, hwy, color = factor(cyl))) +
geom_point(size = 2, alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE) +
# Facet by drive type
facet_wrap(~ drv, nrow = 1, labeller = labeller(
drv = c("4" = "4-Wheel Drive",
"f" = "Front-Wheel Drive",
"r" = "Rear-Wheel Drive")
)) +
# Custom color scale
scale_color_brewer(name = "Cylinders", palette = "Dark2") +
# Apply custom theme
theme_minimal() +
theme(
panel.grid = element_line(color = "gray90"),
legend.position = "bottom",
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5)
) +
labs(
title = "Engine Displacement vs Highway MPG",
subtitle = "By Drive Type and Cylinder Count",
x = "Engine Displacement (L)",
y = "Highway Miles per Gallon"
)
This example demonstrates how facets create comparison panels, scales customize the color mapping and legend, and themes provide consistent visual styling.
Summary
Facets, scales, and themes are essential tools for creating professional ggplot2 visualizations:
- Facets (
facet_wrap(),facet_grid()) split data into multiple panels for comparison - Scales control how data maps to visual properties, including axis customization and color schemes
- Themes customize non-data plot elements for consistent, publication-ready styling
Master these three components, and you’ll be able to create sophisticated, polished visualizations that communicate your data effectively.