ggplot2::aes()
aes(x, y, ..., colour, fill, size, shape, alpha, linetype, linewidth) Every ggplot2 plot starts with aes(). It maps data variables to visual properties — position, colour, size, shape — so the reader can see patterns in the data rather than raw numbers. Getting comfortable with aes() is what makes ggplot2 click.
aes() uses tidy evaluation (rlang formulas, not bare strings) to capture variable names and use them as plot labels automatically.
Syntax
aes(x, y, ..., colour, fill, size, shape, alpha, linetype, linewidth)
All arguments are optional. The mappings you omit fall back to defaults or nothing.
| Argument | Visual property |
|---|---|
x, y | Position on axes |
colour | Line and point colour, fill colour for areas |
fill | Fill colour for shapes and bars |
size | Point size, line thickness |
shape | Point shape (0–25) |
alpha | Transparency (0–1) |
linetype | Line type (solid, dashed, etc.) |
linewidth | Line width |
You can map any aesthetic to any variable. aes(colour = group) colours points by group. aes(size = population) sizes points by population. The data drives the visual.
What Gets Captured
aes(x = mass, colour = sex) captures the column names mass and sex as symbols. ggplot2 converts these to strings for axis labels and legends automatically. You don’t pass strings to aes() — you pass bare column names.
ggplot(storms, aes(x = pressure, y = wind)) + geom_point()
# x-axis label becomes "pressure"
# y-axis label becomes "wind"
This is tidy evaluation in practice: the expression is captured as a quosure, not evaluated immediately.
Mapping vs Setting
aes() maps aesthetics to data. To set a constant visual property for all observations, pass the value outside aes():
# Mapped: colour varies with drat
ggplot(mtcars, aes(x = wt, y = mpg, colour = drat)) + geom_point()
# Set: all points are red
ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point(colour = "red")
# Both: points coloured by cylinder, all points size 3
ggplot(mtcars, aes(x = wt, y = mpg, colour = cyl)) + geom_point(size = 3)
Setting goes in geom_*() or other layer arguments. Mapping goes in aes().
Grouping
Grouping without a visible aesthetic uses group:
# Draw separate smoothed lines per cylinder without colour legend
ggplot(mtcars, aes(x = wt, y = mpg, group = cyl)) + geom_smooth()
Without group, geom_smooth() would draw one line across all points.
All Available Aesthetics
ggplot2 documents which aesthetics each geom supports. A quick check:
ggplot2:::.all_aesthetics
# [1] "x" "y" "alpha" "colour" "fill"
# [6] "linetype" "shape" "size" "linewidth" "stroke"
# ...etc
Not all aesthetics apply to every geom. linetype works for lines but not for points. shape works for points but not for lines.
aes_string() — String-Based Mapping
When you have column names as strings (from non-tidy contexts, or programmatic generation):
aes_string(x = "mass", y = "velocity")
Or build the call programmatically:
col_name <- "carat"
aes_string(y = col_name)
# Equivalent to aes(y = carat)
aes_string() is useful inside functions where column names come from arguments.
aes_() — Explicit Quosures
For advanced cases where you need to pass pre-built quosures:
x_var <- sym("carat")
aes_(x = !!x_var, y = price)
The !! (bang-bang) injects the symbol into the quosure. Most users don’t need this — it’s for metaprogramming with ggplot2.
Combining Mappings
You can put aesthetics in any layer. Aesthetic mappings in ggplot() apply to all layers that honour them. Aesthetic mappings in geom_*() add to or override the global mapping:
ggplot(mtcars, aes(x = wt, y = mpg)) + # global: x, y
geom_point(aes(colour = factor(cyl))) + # adds colour
geom_smooth(aes(linetype = vs), se = FALSE) # adds linetype
The legend merges aesthetics intelligently. If two layers map the same aesthetic differently, ggplot2 combines them into a single legend showing both scales.
aes() and the Pipe
When your data pipeline is long, the tidyverse pipe makes the mapping explicit:
storms %>%
filter(storm == "Amy") %>%
ggplot(aes(x = pressure, y = wind)) +
geom_line()
Rowwise Aesthetics
For some geoms, you want to show individual data points with their own row-wise aesthetics:
ggplot(economics, aes(x = date, y = psavert)) +
geom_point(aes(colour = ifelse(psavert < 0, "negative", "positive")),
size = 1.5)
This highlights rows where the personal savings rate went negative.
Column Names Become Labels
ggplot2 extracts the expression from the quosure and converts it to a string for axis labels and legend titles. aes(y = population_millions) labels the axis “population_millions”. To override, add labs(y = "Population (millions)").
See Also
- /tutorials/r-data-visualization/ggplot2-basics/ — building your first ggplot2 plot
- /reference/tidyverse/dplyr-mutate/ — create the calculated columns you map to aesthetics