R ggplot2 Quickstart: Fast Recipes for Reliable Charts
Problem
Analysts struggle to produce consistent R charts when deadlines are tight. Switching between geoms, scales, and themes without a template leads to noisy visuals and repeated fixes.
Agitate
Misplaced aesthetics, forgotten legends, and mismatched color scales create charts that mislead stakeholders. Time is wasted tweaking defaults instead of focusing on signal.
Solution
Use a compact set of ggplot2 recipes that keep aesthetics explicit, separate mappings from fixed settings, and apply a clean theme. The patterns below cover the most requested plot types and how to facet for comparisons.
Scatter: relationships and trend lines
library(ggplot2)
ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
geom_point(size = 3, alpha = 0.8) +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.8) +
labs(color = "Cylinders", x = "Weight", y = "MPG") +
theme_minimal()Line: time series or ordered categories
ggplot(economics, aes(x = date, y = unemploy)) +
geom_line(color = "#1f77b4", linewidth = 1) +
labs(x = "Date", y = "Unemployment") +
scale_x_date(date_breaks = "2 years", date_labels = "%Y") +
theme_minimal()Bar: counts or totals
ggplot(diamonds, aes(x = cut, fill = cut)) +
geom_bar(width = 0.7, color = "white") +
labs(x = "Cut", y = "Count") +
theme_minimal() +
theme(legend.position = "none")Box and violin: distribution comparisons
ggplot(iris, aes(x = Species, y = Sepal.Length, fill = Species)) +
geom_violin(alpha = 0.4, color = NA) +
geom_boxplot(width = 0.18, outlier.shape = NA, alpha = 0.8) +
labs(x = NULL, y = "Sepal Length") +
theme_minimal() +
theme(legend.position = "none")Facets for quick comparisons
ggplot(diamonds, aes(carat, price, color = cut)) +
geom_point(alpha = 0.4, size = 1.5) +
facet_wrap(~ color) +
labs(x = "Carat", y = "Price") +
theme_light()Geoms and best use cases
| Geom | Data type | Best for | Tip |
|---|---|---|---|
geom_point() | numeric vs numeric | relationships, clusters | Map color/shape to categories; add geom_smooth() for trend. |
geom_line() | ordered x, numeric y | time series, sequences | Keep one line per group with group=; avoid sorting mistakes. |
geom_bar() (count) / geom_col() (pre-agg) | categorical | counts or totals | Use position = "fill" for percentages. |
geom_boxplot() | categorical vs numeric | comparing medians/IQR | Combine with coord_flip() for long labels. |
geom_violin() | categorical vs numeric | distribution shape | Pair with boxplots to show quartiles. |
geom_histogram() | single numeric | distribution | Set bins or binwidth explicitly. |
Aesthetics vs. fixed settings
- Map data inside
aes():aes(color = species). - Set fixed values outside
aes():geom_point(color = "steelblue"). - Use
scale_*functions to format values and control palettes.
Theme and labeling checklist
- Start with
theme_minimal()ortheme_light()for clean defaults. - Add
labs(title, subtitle, caption)for context; keep axes short. - Hide redundant legends with
theme(legend.position = "none")when colors duplicate facets or labels. - For publication, adjust font sizes via
theme(text = element_text(size = 12)).
Starter template
base_plot <- ggplot(data, aes(x = x_var, y = y_var)) +
geom_point(color = "#4e79a7", alpha = 0.7) +
labs(
title = "Headline metric",
x = "X axis",
y = "Y axis",
caption = "Source: internal data"
) +
theme_minimal()Related guides
- Clean your data before plotting: R dplyr Data Wrangling Pipeline
- Heatmap-specific options: pheatmap in R
- Create base tables quickly: How to Create a Dataframe in R