Chapter 15: ggplot2
Shih Chien University
2026-08-03
Hadley Wickham’s ggplot2 became one of R’s most popular packages — not just for pretty output, but because it speaks a language for describing plots: the grammar of graphics. You describe what you want to show, not how to draw it. The toy data (lattice’s, with renamed variables):
We specified an x, a y, and a data set — never the words “scatter plot.” (qplot = quick plot; deprecated in recent ggplot2 but still working — the full ggplot() style follows shortly.)
Like lattice, ggplot2 functions return an object; printing draws it. Inside functions and scripts: print it yourself.
data: a, b, c [10x3]
mapping: x = ~a, y = ~b
faceting: <empty>
-----------------------------------
geom_point: na.rm = FALSE
stat_identity: na.rm = FALSE
position_identity
The summary lays the plot’s anatomy bare: the data, the mapping (x = a, y = b), the faceting, and the layer (geom_point + stat_identity + identity position).
Panels (lattice’s conditioning) are facets, specified by formula:


(Hadley’s preferred spelling of the first: qplot(x=a, y=b, data=d) + facet_wrap(~c).)
Group-by-color is just another mapping — same data, one panel:
With only x supplied, qplot defaults to a histogram; ask for another geom by name:
Drawing a chart means doing many things at once; the grammar names each one, and ggplot2’s functions mirror the names:
geom_point, geom_bar, …);coord_cartesian), polar (coord_polar), map projections (coord_map);stat_bin for histograms, stat_boxplot…;summary of any plot object reports every component — your map when tuning a chart.
Real, complicated data: US Medicare cost and outcome measures, packaged three ways — outcome.of.care.measures.national (national mortality/readmission rates for heart attack, heart failure, pneumonia), medicare.payments (average payment per hospital × 70 conditions; 140,722 records), medicare.payments.by.state:
Condition Measure Rate
1 Heart Attack Mortality 15.9
2 Heart Failure Mortality 11.3
3 Pneumonia Mortality 11.9
4 Heart Attack Readmission 19.8
5 Heart Failure Readmission 24.8
6 Pneumonia Readmission 18.4
One categorical x, so a univariate plot with bar heights from weight=Rate; facet by Measure, fill by Measure:
Drop the facets, set position="dodge" to place bars side by side. The book wrote qplot(..., position="dodge"), but modern ggplot2 has removed qplot’s position argument — position adjustments now belong to the layer, so we write it the full-grammar way:
Both work — differently: faceting invites comparing conditions within each measure; dodging invites comparing measures within each condition. The display is an argument.
Question: do hospitals treating more cases charge Medicare more or less? Restrict to the three heart-failure diagnosis groups and plot payment vs. case count, colored by diagnosis:
temp <- tempfile(fileext = ".rda")
download.file("https://raw.githubusercontent.com/cran/nutshell/master/data/medicare.payments.rda",
temp, mode = "wb")
load(temp)
heart.failure <- c("Heart failure and shock w/o CC/MCC",
"Heart failure and shock w MCC",
"Heart failure and shock w CC")
payment.plot <- qplot(x=Number.Of.Cases, y=Medicare.Average.Payment,
data=subset(medicare.payments, Diagnosis.Related.Group %in% heart.failure),
color=Diagnosis.Related.Group)
payment.plotUnreadable — everything clumps left. (Small counts are censored by HIPAA.)
Recipe: log x-scale, semi-opaque points (alpha=I(1/10)), a smoothing line, trimmed y. The elegant route adds components with +:
payment.plot.alpha <- qplot(x=Number.Of.Cases, y=Medicare.Average.Payment,
data=subset(medicare.payments, Diagnosis.Related.Group %in% heart.failure),
color=Diagnosis.Related.Group, alpha=I(1/10), ylim=c(0, 20000))
payment.plot.scaled <- payment.plot.alpha + scale_x_log10() + geom_smooth()
payment.plot.scaledsummary of this object now shows two geom/stat/position triples — one per layer — with the alpha aesthetic threaded through.
Costs rise with volume — geography, perhaps? State-level data, with State levels reordered by payment (sorting the levels, not the rows) before a dot plot:
temp <- tempfile(fileext = ".rda")
download.file("https://raw.githubusercontent.com/cran/nutshell/master/data/medicare.payments.by.state.rda",
temp, mode = "wb")
load(temp)
medicare.payments.by.state.hf <- subset(medicare.payments.by.state,
Diagnosis.Related.Group %in% heart.failure)
medicare.payments.by.state.hf$State <- with(medicare.payments.by.state.hf,
reorder(State, Medicare.Average.Payment.Maximum, mean))
qplot(x=Medicare.Average.Payment.Maximum, y=State,
data=medicare.payments.by.state.hf,
color=Diagnosis.Related.Group)Top of the list: Northern Mariana Islands, Alaska, Virgin Islands — remote and expensive — then NY, MD, CA: high cost of living and big hospitals. Cheapest: Puerto Rico, American Samoa. Cost and volume are both correlated with geography.
Are adjacent states similar? Merge payments with state polygons (maps package) and fill by payment — geom="polygon" plus a repositioned legend:
library(maps)
states <- map_data("state")
state.name.map <- data.frame(abb=state.abb, region=tolower(state.name),
stringsAsFactors=FALSE)
states <- merge(states, state.name.map, by="region")
toplot <- merge(states, medicare.payments.by.state,
by.x="abb", by.y="State")
toplot <- toplot[order(toplot$order), ]
qplot(long, lat,
data=subset(toplot,
Diagnosis.Related.Group=="Heart failure and shock w/o CC/MCC"),
group=group, fill=Medicare.Average.Payment.Maximum,
geom="polygon") +
theme(legend.position="bottom", legend.direction="vertical")(The book wrote opts(...) — long since replaced by theme().)
| Argument | Description | Default |
|---|---|---|
x, y, data |
values and (optional) data frame | |
facets |
facet formula: one-sided → facet_wrap, two-sided → facet_grid |
NULL |
geom |
geoms as character vector | "auto": point (x,y) / histogram (x) |
stat / position |
statistics / position adjustments (book era — removed in ggplot2 2.0) | list(NULL) |
xlim, ylim, log |
limits; log = ""/"x"/"y"/"xy" |
|
main, xlab, ylab, asp |
titles, labels, aspect | deparsed expressions |
... |
other aesthetics passed to layers |
Beyond qplot: create the object with ggplot(data, mapping=aes(...)), then add layers and options with +:
data: a, b, c [10x3]
mapping: x = ~a, y = ~b
faceting: <empty>
-----------------------------------
geom_point: na.rm = FALSE
stat_identity: na.rm = FALSE
position_identity
aes(x, y, ...) declares the mappings (relatives: aes_string, aes_auto in the book’s era).ggplot(...) has no layers — printing it once errored (“No layers in plot”); modern ggplot2 draws an empty panel.layer("point") added a layer by short-name; modern layer() needs explicit geom/stat/position — in practice, use geom_*.geom_abline line by slope+intercept |
geom_errorbar(h) error bars |
geom_polygon polygons |
geom_area area plot |
geom_freqpoly frequency polygon |
geom_quantile quantile-regression lines |
geom_bar bars |
geom_hex hexagonal binning |
geom_rect rectangles |
geom_bin2d 2-D bin heatmap |
geom_histogram histogram |
geom_ribbon ribbons (à la Minard) |
geom_blank nothing |
geom_hline / geom_vline h/v lines |
geom_rug rug |
geom_boxplot box plots |
geom_jitter jittered points |
geom_segment segments |
geom_contour contours |
geom_line / geom_path lines/paths |
geom_smooth smoothed mean |
geom_crossbar boxless boxes |
geom_linerange vertical intervals |
geom_step steps |
geom_density(2d) densities |
geom_point / geom_pointrange |
geom_text text |
geom_tile tiles |
bin, bin2d, binhex, boxplot, contour, density, density2d, function, identity, qq, quantile, smooth, spoke, sum, summary, unique, plus line helpers abline/hline/vline (modern ggplot2 folds these into geoms).alpha, brewer (ColorBrewer), continuous, date/datetime, discrete, gradient/gradient2/gradientn, grey, hue, identity, linetype, manual, shape, size (today spelled e.g. scale_color_brewer, scale_x_continuous).cartesian, equal, flip, map, polar, trans.facet_grid (grid layout) and facet_wrap (wrapped 1-D list).dodge, fill, identity, jitter, stack.Tip
Course note on API drift. Since the book: opts() → theme(); qplot is soft-deprecated in favor of ggplot() + geom_*(); scales are written scale_<aesthetic>_<type>. The grammar itself — data, mapping, geom, stat, scale, coord, facet, position — is unchanged, which is exactly the point of having a grammar.
Copyright. These slides are adapted from R in a Nutshell: A Desktop Quick Reference (2nd ed.) by Joseph Adler, O’Reilly Media. All rights reserved by the original author and publisher.
Non-commercial use only. These materials are strictly for educational purposes and may not be used for commercial gain.
Attribution. Any reproduction, distribution, or use of these materials must properly credit the original source.
R in a Nutshell: A Desktop Quick Reference