A Complete Workflow
A realistic end-to-end ggplot2 session — from a question, through exploratory plots, to a polished final figure — using the grammar at every step.
Let us put everything together the way you will actually use it: start with a question, explore quickly, then refine one chart into something worth sharing. Throughout, notice how the grammar guides every decision.
The question
How does engine displacement relate to highway fuel efficiency, and does that relationship depend on the type of car?
We have two continuous variables (displ, hwy) and a candidate
grouping variable (class or drv). The grammar already tells us:
two continuous variables → points; a group → color or
facets.
Stage 1: a fast exploratory plot
Exploration values speed over polish. The minimal data + mapping + geom gets us looking immediately:
There is a clear negative relationship. Good — the question is worth pursuing. Next: does it depend on car type?
Stage 2: bring in the grouping variable
Try color first, since it keeps everything in one panel:
Interesting: front-wheel-drive cars (f) sit higher (more efficient).
But the three colored clouds overlap. When color gets crowded, the
grammar offers facets as the next move.
Stage 3: facet to separate the groups
Now each drivetrain's trend is unmistakable, on shared axes so the panels are comparable. The exploratory phase has answered the question. Time to make it presentable.
The exploration-to-polish pipeline
The first three stages are exploration — bare and quick. Only now, at refinement, do we spend effort on scales, labels, and theme.
Stage 4: refine into a final figure
Add the communication layer — a meaningful title, plain-language axis labels, a deliberate color scale, and a clean theme:
We dropped the legend (legend.position = "none") because the facet
strips already name each drivetrain — a small judgment call the grammar
made easy. The title now states the conclusion. This figure could go
in a report unchanged.
Saving your work
Outside the browser, ggsave() writes the last plot (or a named plot
object) to a file. The grammar's "a plot is a value" nature means you
can pass the object directly:
p <- ggplot(mpg, aes(displ, hwy)) + geom_point()
ggsave("engine-efficiency.png", plot = p, width = 8, height = 5, dpi = 300)The rhythm to internalize
Explore rough, refine once. Most of your plots will never leave the exploratory stage — they exist to answer a question for you. Spend polish effort only on the few that need to communicate to others. The grammar supports both: the same components, dialed from quick to publication-ready.
In an exploratory data analysis, why start with a bare geom_point() (no titles, colors, or theme)?
Because adding color before a theme causes errors.
Because exploratory plots cannot have titles.
Because exploration prioritizes speed of insight — a minimal data + mapping + geom answers "is there anything here?" before any effort goes into polish.
Because geom_point is the only geom allowed during exploration.
During refinement, the final figure sets legend.position = "none". Why is dropping the legend reasonable here?
Legends always clutter plots and should be removed.
The color mapping was deleted, so no legend is needed.
The plot is faceted by drivetrain, so each panel's strip label already identifies the group the legend would describe.
Themes cannot display legends.
Key takeaways
- Let the grammar guide choices: variable count and type suggest the geom; crowded color suggests facets.
- Work in stages — explore (minimal) → iterate → refine → share — and spend polish effort late, only on charts that must communicate.
- Refinement is adding the scale, labels, and theme components on top of an already-correct exploratory plot.
- A ggplot is a value: store it, grow it, and
ggsave()it.