How to Save Plots in ggplot2
Saving plots programmatically isn't just about getting images out of R—it's fundamental to reproducible research and professional data science workflows. When you save plots through RStudio's export...
Key Insights
- Use
ggsave()as your default method for saving ggplot2 plots—it automatically detects format from the filename extension and intelligently sizes your output. - Vector formats (PDF, SVG) are essential for publications and scalable graphics, while high-DPI PNG files work best for presentations and web content.
- Control dimensions explicitly with width, height, and DPI parameters rather than relying on defaults, and always save plots to variables for reproducible batch operations.
Introduction to Saving ggplot2 Plots
Saving plots programmatically isn’t just about getting images out of R—it’s fundamental to reproducible research and professional data science workflows. When you save plots through RStudio’s export button, you’re introducing manual steps that can’t be version controlled, automated, or reliably reproduced. The ggsave() function from ggplot2 solves this by providing a consistent, scriptable interface for exporting publication-quality graphics.
While R offers base graphics functions like png(), pdf(), and dev.off(), ggsave() is purpose-built for ggplot2 objects and handles most edge cases automatically. It detects output format from your filename, applies sensible defaults for dimensions and resolution, and works seamlessly with the ggplot2 object model.
Basic Plot Saving with ggsave()
The simplest ggsave() call requires only a filename. The function automatically detects the format from the extension and saves the most recently created plot:
library(ggplot2)
library(dplyr)
# Create a basic scatter plot
plot <- ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 3) +
labs(title = "Fuel Efficiency vs. Weight",
x = "Weight (1000 lbs)",
y = "Miles per Gallon",
color = "Cylinders") +
theme_minimal()
print(plot)
# Save as PNG (default 7x7 inches at 300 DPI)
ggsave("fuel_efficiency.png")
# Save as PDF (vector format, scales perfectly)
ggsave("fuel_efficiency.pdf")
The beauty of this approach is that ggsave() examines your filename extension and automatically selects the appropriate graphics device. You don’t need to manually open and close devices or remember device-specific parameters.
Controlling Plot Dimensions and Resolution
Default dimensions rarely match your needs. For presentations, you might want widescreen aspect ratios. For journals, you’ll need specific column widths. The width, height, units, and dpi parameters give you complete control:
# Create a more complex plot
plot <- ggplot(diamonds %>% sample_n(1000),
aes(x = carat, y = price, color = cut)) +
geom_point(alpha = 0.6) +
scale_y_continuous(labels = scales::dollar) +
labs(title = "Diamond Prices by Carat and Cut Quality",
x = "Carat", y = "Price", color = "Cut") +
theme_minimal(base_size = 12)
# Thumbnail for web (small, 72 DPI screen resolution)
ggsave("diamond_thumb.png", plot,
width = 4, height = 3, units = "in", dpi = 72)
# Standard presentation slide (widescreen)
ggsave("diamond_slide.png", plot,
width = 10, height = 5.625, units = "in", dpi = 150)
# High-resolution poster (A4 landscape, print quality)
ggsave("diamond_poster.png", plot,
width = 297, height = 210, units = "mm", dpi = 300)
# Journal submission (2-column width, 600 DPI)
ggsave("diamond_journal.tiff", plot,
width = 86, height = 86, units = "mm", dpi = 600)
For screen display, 72-150 DPI is sufficient. For print publications, use 300 DPI minimum. Academic journals often require 600 DPI for line art and 300 DPI for photographs. Units can be “in” (inches), “cm”, “mm”, or “px” (pixels).
File Format Options and Use Cases
Choosing the right format is critical. Vector formats scale infinitely without quality loss, while raster formats have fixed resolutions but smaller file sizes for complex plots.
Vector formats (PDF, SVG, EPS) store mathematical descriptions of shapes rather than pixels. They’re perfect for:
- Journal publications that will be professionally printed
- Plots that need to scale (posters, presentations at unknown resolutions)
- Graphics with text that must remain crisp
Raster formats (PNG, JPEG, TIFF) store pixel grids. Use them for:
- Web content and HTML reports (PNG with transparency)
- Photographs or plots with thousands of points (where vector files become huge)
- Quick previews and thumbnails
# Complex plot with many points
complex_plot <- ggplot(diamonds, aes(x = carat, y = price)) +
geom_point(alpha = 0.1, size = 0.5) +
theme_minimal()
# Vector format: large file but infinitely scalable
ggsave("complex_vector.pdf", complex_plot,
width = 8, height = 6, units = "in")
# Raster format: smaller file, fixed resolution
ggsave("complex_raster.png", complex_plot,
width = 8, height = 6, units = "in", dpi = 300)
# Check file sizes
file.info(c("complex_vector.pdf", "complex_raster.png"))$size / 1024
# PDF might be 2-5 MB, PNG around 500-800 KB
For plots with thousands of semi-transparent points, PNG often produces smaller files than PDF. For simple line plots and bar charts, PDF is usually smaller and always more flexible.
Advanced Techniques
Real workflows require saving multiple related plots or working with plots created earlier in your script. Always save plots to variables for maximum flexibility:
# Batch save multiple plots for different categories
output_dir <- "plots"
dir.create(output_dir, showWarnings = FALSE)
# Create and save plots for each cylinder count
for (cyl_count in unique(mtcars$cyl)) {
# Filter data
data_subset <- mtcars %>% filter(cyl == cyl_count)
# Create plot
p <- ggplot(data_subset, aes(x = wt, y = mpg)) +
geom_point(size = 3, color = "steelblue") +
geom_smooth(method = "lm", se = FALSE, color = "red") +
labs(title = paste(cyl_count, "Cylinder Vehicles"),
x = "Weight (1000 lbs)", y = "MPG") +
theme_minimal()
# Save with descriptive filename
filename <- file.path(output_dir, paste0("mpg_", cyl_count, "cyl.png"))
ggsave(filename, p, width = 6, height = 4, dpi = 300)
}
For better font rendering and special character support, use Cairo graphics devices:
# Enhanced rendering with Cairo
ggsave("plot_cairo.png", plot,
width = 8, height = 6, dpi = 300,
device = "png", type = "cairo")
# Cairo also works for PDF
ggsave("plot_cairo.pdf", plot,
width = 8, height = 6,
device = cairo_pdf)
Cairo handles anti-aliasing better and provides superior Unicode support, which matters if your plots include non-ASCII characters or mathematical symbols.
Troubleshooting Common Issues
The most common problem is cut-off labels or legends. This happens when plot elements extend beyond the default plot boundaries:
# Problem: long labels get cut off
problem_plot <- ggplot(mtcars, aes(x = rownames(mtcars), y = mpg)) +
geom_col() +
coord_flip() +
labs(title = "Miles Per Gallon by Vehicle Model",
x = "", y = "MPG") +
theme_minimal()
# This will cut off car names
ggsave("cutoff_problem.png", problem_plot, width = 6, height = 8)
# Solution: increase width or adjust plot margins
fixed_plot <- problem_plot +
theme(plot.margin = margin(10, 10, 10, 40, "pt"))
ggsave("cutoff_fixed.png", fixed_plot, width = 8, height = 8)
For memory issues with large plots, save in chunks or reduce point counts:
# For huge datasets, sample before plotting
if (nrow(data) > 10000) {
plot_data <- data %>% sample_n(10000)
} else {
plot_data <- data
}
Best Practices and Workflow Tips
Organize your plot outputs systematically:
# Create dated output directory
output_dir <- paste0("plots_", Sys.Date())
dir.create(output_dir, showWarnings = FALSE)
# Use descriptive, version-controlled names
ggsave(file.path(output_dir, "01_exploratory_scatter.png"), plot1)
ggsave(file.path(output_dir, "02_regression_results.png"), plot2)
Recommended settings by use case:
| Use Case | Format | Width × Height | DPI | Units |
|---|---|---|---|---|
| Web/Blog | PNG | 800 × 600 | 96 | px |
| Presentation | PNG | 10 × 5.625 | 150 | in |
| Journal (1-col) | 86 × 86 | 300 | mm | |
| Journal (2-col) | 178 × 120 | 300 | mm | |
| Poster | 36 × 24 | 150 | in | |
| Social Media | PNG | 1200 × 675 | 96 | px |
Always save publication plots as both high-res raster (for immediate use) and vector (for future flexibility):
# Save both formats for important plots
ggsave("figure1.png", plot, width = 7, height = 5, dpi = 300)
ggsave("figure1.pdf", plot, width = 7, height = 5)
The investment in proper plot saving pays dividends when reviewers request format changes, you need to resize for different venues, or you’re reproducing analyses months later. Make ggsave() calls explicit in your scripts, commit them to version control, and your future self will thank you.