Publication-Ready Charts with ggplot2
Publication-ready charts require more than default ggplot2 aesthetics. Journals, conferences, and reports each have specific requirements for font sizes, dimensions, and color schemes. This guide walks you through creating professional charts that meet these standards.
Understanding Publication Requirements
Different output contexts demand different specifications:
- Journal articles: 300 DPI minimum, TIFF or PDF format, typically 85mm or 174mm width (one or two columns)
- Presentations: Vector formats preferred, larger fonts, high contrast
- Web/HTML: PNG or SVG, responsive dimensions, web-safe fonts
The key is controlling every visual element deliberately.
Setting Up Your Environment
# Install and load required packages
install.packages(c("ggplot2", "scales", "showtext", "ggthemes"))
library(ggplot2)
library(scales)
library(showtext)
library(ggthemes)
Enable higher-resolution rendering:
# Set DPI for raster outputs
options(device = "png")
Controlling Dimensions and Aspect Ratio
Publication charts typically use specific width standards:
# Standard journal widths in mm (one column / two column)
width_one_col <- 85
width_two_col <- 174
height_mm <- width_one_col / 1.618 # Golden ratio
# Convert to inches for ggplot
width_inch <- width_one_col / 25.4
height_inch <- height_mm / 25.4
Save with precise dimensions:
ggsave(
"figure1.tiff",
width = width_inch,
height = height_inch,
dpi = 300,
units = "in",
compression = "lzw"
)
Customizing Theme Elements
The theme system gives you complete control:
# Create a publication-ready theme
pub_theme <- theme_bw() +
theme(
# Text sizes (in points, matching journal requirements)
plot.title = element_text(size = 12, face = "bold", hjust = 0),
plot.subtitle = element_text(size = 10, hjust = 0),
axis.title = element_text(size = 10, face = "bold"),
axis.text = element_text(size = 9),
legend.title = element_text(size = 9, face = "bold"),
legend.text = element_text(size = 8),
legend.position = "bottom",
# Grid lines - minimal for publications
panel.grid.minor = element_blank(),
panel.grid.major = element_line(color = "gray90"),
# Remove background clutter
panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5),
strip.background = element_rect(fill = "gray95", color = "black")
)
# Apply the theme
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 2) +
labs(title = "Car Weight vs. Miles Per Gallon",
subtitle = "Data from motor trend magazine",
x = "Weight (1000 lbs)",
y = "Miles per Gallon",
color = "Cylinders") +
pub_theme
Professional Color Palettes
Avoid default rainbow scales. Use perceptually uniform palettes:
# Color-blind friendly palette
cb_palette <- c("#000000", "#E69F00", "#56B4E9", "#009E73",
"#F0E442", "#0072B2", "#D55E00", "#CC79A7")
# Viridis scale (perceptually uniform)
scale_color_viridis_d(option = "D")
# Use with your plot
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 2) +
scale_color_manual(values = cb_palette) +
pub_theme
For sequential data:
# Gradient from light to dark
scale_fill_gradient(low = "#F7FBFF", high = "#08306B")
# Or use the viridis continuous scale
scale_fill_viridis_c(option = "C")
Typography and Font Handling
Proper fonts improve readability and professionalism:
# Load Google Fonts (requires showtext)
font_add_google("Roboto", "roboto")
font_add_google("Roboto Slab", "roboto-slab")
# Set default font
theme_set(theme_bw(base_family = "roboto"))
# In your theme definition
pub_theme <- theme_bw(base_family = "roboto", base_size = 10) +
theme(...)
Exporting for Specific Formats
Different outputs require different settings:
# For journals (TIFF, 300 DPI)
ggsave(
"figure1.tiff",
plot = last_plot(),
width = 3.35,
height = 2.5,
dpi = 300,
units = "in",
compression = "lzw"
)
# For presentations (PDF, vector)
ggsave(
"figure1.pdf",
plot = last_plot(),
width = 6,
height = 4,
device = cairo_pdf
)
# For web (PNG, optimized)
ggsave(
"figure1.png",
plot = last_plot(),
width = 800,
height = 600,
units = "px",
dpi = 96
)
Building a Reusable Chart Function
Create a function for consistent styling across figures:
pub_plot <- function(data, x, y, fill = NULL,
x_label = NULL, y_label = NULL,
title = NULL, subtitle = NULL,
color_palette = NULL) {
p <- ggplot(data, aes(x = {{x}}, y = {{y}}))
if (!is.null(fill)) {
p <- p + aes(fill = {{fill}}) + geom_col()
} else {
p <- p + geom_point()
}
p <- p + labs(title = title, subtitle = subtitle,
x = x_label, y = y_label) +
pub_theme
if (!is.null(color_palette)) {
p <- p + scale_fill_manual(values = color_palette)
}
return(p)
}
# Use the function
pub_plot(mtcars, wt, mpg, fill = factor(cyl),
x_label = "Weight (1000 lbs)",
y_label = "Miles per Gallon",
title = "Weight vs. Efficiency",
color_palette = cb_palette)
Complete Example: Multi-Panel Figure
Combine multiple plots into a publication figure:
library(patchwork)
# Create individual plots
p1 <- ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() + geom_smooth(method = "lm") +
labs(title = "A") + pub_theme
p2 <- ggplot(mtcars, aes(x = factor(cyl), y = mpg)) +
geom_boxplot() +
labs(title = "B") + pub_theme
p3 <- ggplot(mtcars, aes(x = mpg)) +
geom_histogram(bins = 10) +
labs(title = "C") + pub_theme
# Combine with patchwork
combined <- (p1 | p2) / p3
# Save combined figure
ggsave(
"figure2.tiff",
plot = combined,
width = width_two_col / 25.4,
height = 4,
dpi = 300,
units = "in",
compression = "lzw"
)
Common Pitfalls to Avoid
- Tiny text: Always check font sizes at final dimensions
- Low resolution: Use 300+ DPI for print
- Rainbow colors: Use color-blind friendly palettes
- Cluttered grids: Minimal gridlines for publications
- Inconsistent styling: Create and reuse a theme function
- Wrong aspect ratios: Match journal specifications exactly
See Also
ggplot2-extensions— Extend ggplot2 with patchwork, ggrepel, and gganimatebase-r-plotting— Base R graphics and plotting alternativesinteractive-plots-plotly— Interactive visualizations with plotly