From r-skills
Expert ggplot2 data visualization in R - grammar of graphics, geoms, themes, scales, faceting, and styling. Use when user works with ggplot2, mentions "ggplot", "geom_", creates visualizations in R, asks about plot customization, "customizar theme", "customize theme", "customizar plot", themes, "facet_wrap", "facet_grid", "faceting", "facetas", "anotações", "annotations", "annotate", "adicionar anotações", "color scale", "scales", "escala de cores", "gráfico", "plot", "visualização", or data visualization best practices.
How this skill is triggered — by the user, by Claude, or both
Slash command
/r-skills:ggplot2This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Master data visualization in R using ggplot2's layered grammar of graphics. This skill provides expert guidance on creating effective, publication-ready visualizations with complete control over all visual elements.
Master data visualization in R using ggplot2's layered grammar of graphics. This skill provides expert guidance on creating effective, publication-ready visualizations with complete control over all visual elements.
Every ggplot2 visualization is built from five independent components:
Philosophy: Build plots incrementally by composing independent components, not by selecting from fixed templates. This enables infinite flexibility while mirroring analytical thinking.
ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) +
<GEOM_FUNCTION>() +
<SCALE_FUNCTIONS>() +
<COORDINATE_FUNCTION>() +
<FACET_FUNCTION>() +
<THEME_FUNCTION>()
Key Principles:
aes() maps variables to visual properties (inside geom or ggplot)aes() (e.g., colour = "blue")+ operatorEvery layer should serve one of three purposes:
❌ Wrong: aes(colour = "blue") - Maps string "blue" as data
✅ Right: colour = "blue" outside aes() - Sets fixed color
❌ Wrong: ggplot(df, aes(x = df$variable)) - Breaks plot self-containment
✅ Right: ggplot(df, aes(x = variable)) - Self-contained reference
❌ Wrong: aes(x = log(variable)) - Complex calculation in aes
✅ Right: Use dplyr::mutate() first, then map the result
❌ Wrong: Accepting default bin widths
✅ Right: Always experiment with binwidth or bins
❌ Wrong: Mapping too many aesthetics simultaneously ✅ Right: Create series of simpler plots for clarity
See references/geoms-reference.md for complete documentation.
Continuous Relationships:
geom_point() - Scatter plots (x-y relationships)geom_line() - Time series (connects in x-order)geom_path() - Connections in data ordergeom_smooth() - Add trend lines with confidence bandsDistributions:
geom_histogram() - Continuous distribution via binninggeom_freqpoly() - Frequency polygon (better for comparisons)geom_density() - Smooth density estimategeom_boxplot() - Five-number summary with outliersgeom_violin() - Distribution shape (mirrored density)Categorical Data:
geom_bar() - Count occurrences (stat = "count")geom_col() - Use pre-calculated values (stat = "identity")Text & Annotations:
geom_text() - Add text labelsgeom_label() - Text with background rectangleannotate() - Quick single annotations without data framesSee examples/plot-examples.md for complete working examples.
Universal Aesthetics (work with most geoms):
x, y - Positioncolour - Border/line colorfill - Interior color (shapes 21-25, bars, areas)alpha - Transparency (0-1)size - Size of points/linesshape - Point shape (0-25)linetype - Line pattern ("solid", "dashed", "dotted", etc.)group - Define grouping for collective geomsGeom-Specific Aesthetics:
label, hjust, vjust, angle, family, fontfacelower, upper, middle, ymin, ymaxymin, ymax (or xmin/xmax)See references/scales-reference.md for complete documentation.
scale_[aesthetic]_[type] - e.g., scale_colour_viridis_c()
Position Scales:
scale_x_continuous(limits, breaks, labels, trans, expand)
scale_x_discrete(limits, labels, expand)
scale_x_log10() # Log transformation
scale_x_date(date_breaks = "1 month", date_labels = "%b %Y")
Color Scales (accessibility-first):
# Viridis (perceptually uniform, colorblind-safe)
scale_colour_viridis_c(option = "viridis") # continuous
scale_colour_viridis_d() # discrete
# ColorBrewer
scale_colour_brewer(palette = "Set1", type = "qual") # categorical
scale_fill_distiller(palette = "Blues") # continuous
# Custom gradients
scale_colour_gradient(low = "white", high = "red")
scale_colour_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0)
scale_colour_gradientn(colours = c("red", "yellow", "green", "blue"))
# Manual
scale_colour_manual(values = c("A" = "#E41A1C", "B" = "#377EB8"))
Important: Setting scale limits discards data outside range. Use coord_cartesian(xlim, ylim) to zoom without losing data (preserves stat calculations).
See references/themes-styling.md for complete documentation.
theme_grey() # Default: grey background, white gridlines
theme_bw() # Classic dark-on-light, good for projectors
theme_minimal() # No background annotations, minimalist
theme_classic() # X/Y axis lines, no gridlines
theme_light() # Light grey lines, focuses on data
theme_dark() # Dark background, makes colors pop
theme_void() # Completely empty
All themes accept: base_size, base_family, base_line_size, base_rect_size
theme(
# Plot-level
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 12, colour = "grey50"),
plot.background = element_rect(fill = "white"),
plot.margin = margin(10, 10, 10, 10),
# Panels
panel.background = element_rect(fill = "white"),
panel.grid.major = element_line(colour = "grey90"),
panel.grid.minor = element_blank(),
# Axes
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
axis.ticks = element_line(colour = "black"),
# Legends
legend.position = "right", # or "top", "bottom", "left", "none"
legend.title = element_text(face = "bold"),
legend.background = element_rect(fill = "white", colour = "black"),
# Facets
strip.text = element_text(size = 11, face = "bold"),
strip.background = element_rect(fill = "grey80")
)
Element Functions:
element_text() - Customize textelement_rect() - Customize backgrounds/borderselement_line() - Customize lineselement_blank() - Remove elements entirelyUse for one variable with many levels, wrapped into 2D:
facet_wrap(
~ variable, # or vars(variable)
nrow = 2, ncol = 3, # grid dimensions
scales = "fixed", # or "free", "free_x", "free_y"
dir = "h", # "h" (horizontal) or "v" (vertical)
labeller = label_value
)
Use for true 2D grid with all combinations:
facet_grid(
rows ~ cols, # or rows = vars(...), cols = vars(...)
scales = "fixed", # or "free_x", "free_y", "free"
space = "fixed", # or "free_x", "free_y", "free" (panel sizing)
margins = FALSE, # add summary facets
labeller = label_value
)
When to use which:
scales = "fixed" - For cross-panel comparison (consistent axes)scales = "free" - To highlight within-panel patternscoord_cartesian(xlim, ylim, expand = TRUE) # Visual zoom (preserves data)
coord_fixed(ratio = 1) # Fixed aspect ratio
coord_flip() # Swap x and y axes
coord_polar() # Polar coordinates
coord_map() # Map projections
Critical difference: coord_cartesian() zooms visually while scale_*_continuous(limits = ...) discards data before calculations.
position_dodge(width = 0.9) # Side-by-side (bars, boxplots)
position_stack() # Stack vertically
position_fill() # Stack and normalize to 100%
position_jitter(width, height) # Add random noise (overplotting)
position_nudge(x, y) # Fixed-distance offset
labs(
title = "Main Title",
subtitle = "Subtitle text",
caption = "Data source",
x = "X-axis label",
y = "Y-axis label",
colour = "Legend title",
fill = "Fill legend title"
)
Text Annotations:
# Quick annotation without data frame
annotate("text", x = 5, y = 10, label = "Important point",
hjust = "inward", vjust = "inward")
# Data-driven annotations
geom_text(aes(label = label_var), hjust = "inward", check_overlap = TRUE)
geom_label(aes(label = label_var), nudge_y = 0.5)
Reference Lines:
geom_hline(yintercept = 0, linetype = "dashed", colour = "red")
geom_vline(xintercept = 5, linetype = "dashed")
geom_abline(intercept = 0, slope = 1)
Use {{ var }} to accept user-supplied variable names:
my_histogram <- function(data, var, bins = 30) {
ggplot(data, aes(x = {{ var }})) +
geom_histogram(bins = bins) +
theme_minimal()
}
# Usage
my_histogram(mtcars, mpg, bins = 20)
# Save components as objects
my_theme <- theme_minimal() +
theme(
plot.title = element_text(face = "bold"),
axis.text = element_text(size = 11)
)
# Use across plots
ggplot(data, aes(x, y)) + geom_point() + my_theme
scatter_with_smooth <- function(data, x_var, y_var, ...) {
ggplot(data, aes(x = {{ x_var }}, y = {{ y_var }})) +
geom_point(alpha = 0.5) +
geom_smooth(method = "lm", ...) +
theme_bw()
}
library(patchwork)
# Basic composition
p1 + p2 # Auto-arrange
p1 | p2 # Single row
p1 / p2 # Single column
(p1 | p2) / p3 # Complex layouts
# Advanced control
p1 + p2 +
plot_layout(ncol = 2, guides = "collect") +
plot_annotation(title = "Combined Analysis", tag_levels = "A")
See references/best-practices.md for comprehensive guidance.
Aesthetics:
Annotations:
hjust/vjust = "inward" for automatic alignmentinherit.aes = FALSE for self-contained annotationsPerformance:
alpha for overplotting instead of geom_jitter()geom_hex() or geom_bin2d() for dense data (>10k points)stat_summary() to aggregate before plottingReproducibility:
binwidth/bins (never rely on defaults)set.seed() before jitteringggsave() using vector formats (PDF/SVG) for publication# Basic scatter with smooth
ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = class)) +
geom_smooth(method = "lm", se = TRUE) +
scale_colour_viridis_d() +
labs(title = "Engine Size vs Highway MPG",
x = "Displacement (L)", y = "Highway MPG") +
theme_minimal()
# Histogram with facets
ggplot(mpg, aes(hwy)) +
geom_histogram(binwidth = 2, fill = "steelblue", colour = "white") +
facet_wrap(~ class, scales = "free_y") +
theme_bw()
# Boxplot with custom theme
ggplot(mpg, aes(class, hwy, fill = class)) +
geom_boxplot(show.legend = FALSE) +
scale_fill_brewer(palette = "Set2") +
coord_flip() +
theme_minimal() +
theme(panel.grid.major.y = element_blank())
See examples/plot-examples.md for comprehensive working examples.
See templates/plot-templates.md for reusable templates.
When user asks about ggplot2 visualizations:
Always provide complete, runnable code examples with proper formatting and best practices applied.
Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
npx claudepluginhub sciviews/r-claude-skills