library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(aes(color = "blue")) +
theme_minimal()- 1
-
The
coloraesthetic is set to βblueβ.

ggplot2ggplot2ggplot2Most plotting libraries, letβs be honest, are a bit of a black box. One command, bang, youβve got a chart. Itβs convenient, sureβa quick fix for the data visualization problem. But peek under the hood? Forget about it. And try tweaking things to get exactly what you want? Good luck with that. Theyβre built for simplicity, which all too often means a crippling lack of flexibility. And letβs face it, when youβre trying to create truly effective visualizations, customization isnβt a luxuryβitβs a necessity.
ggplot2 takes a different tack. Itβs built on the idea of a βgrammar of graphicsββthink of it as a set of Lego bricks for data visualization. Simple, modular pieces that, combined thoughtfully, can construct virtually anything you can imagine. This means youβre not trapped by pre-packaged, cookie-cutter solutions. You can actually design your charts, assembling them piece by piece, crafting them to tell precisely the story your data has to tell.
Now, thereβs a trade-off, of course. You do have to invest some time to grasp its (admittedly elegant) design principles. But trust me, the payoff is well worth the effort. Below, Iβll walk you through some of the fundamental ideas that make this approach so powerful.
In ggplot2, aesthetics are the magical connections between your data and how it looks on the plot. The aes() function is like the matchmaker that sets up these relationships. It maps variables in your data to visual properties in your plot. Thus, what you put inside aes() should be a variable in your dataset (or an expression of variables, such as log(body_mass_g)), not a fixed value you want to apply to every point.
To see this, let us again consider plotting bill_length_mm against bill_depth_mm from the penguins dataset. Suppose we want to color all points blue:
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(aes(color = "blue")) +
theme_minimal()color aesthetic is set to βblueβ.

library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(color = "blue") +
theme_minimal()color is specified outside the aes() function.

As we can see, the second option is correct. In the first tab, "blue" is not a variable in the penguins dataset; aes(color = "blue") instead maps a constant string to the color scale, so ggplot2 assigns its own default color (red/salmon, not blue) and adds a spurious legend entry labelled βblueβ. Thus, the correct way to color all points blue is to specify the color outside the aes() function as a constant.
You can map many aesthetics in ggplot2. We have already seen that:
x and y for the x- and y-axescolor for the color of the points. This is a big and very important topic. We will discuss it in more detail in Chapter 16 (Color).shape for the shape of the points. There are 25 different shapes you can choose from (link). I admit that I Google them every time I need to use them.There are some other common aesthetics you can map to points in ggplot2:
size for the size of the pointsalpha for the transparency of the points. It ranges from 0 (completely transparent) to 1 (completely opaque).library(ggplot2)
library(palmerpenguins)
ggplot(data = penguins,
aes(x = bill_length_mm, y = bill_depth_mm)
) +
geom_point(
aes(
color = species,
size = body_mass_g
),
alpha = 0.5,
shape = 21,
fill = "white"
) +
theme_minimal()species variable is mapped to the color aesthetic. Different species will have different colors.
body_mass_g variable is mapped to the size aesthetic. Different body masses will have different sizes.
alpha is the same for all points. Setting it to 0.5 makes them semi-transparent.
shape is the same for all points. Specifying 21 makes the points filled circles.
fill is the same for all points. Specifying "white" makes the insides of the circles white.

If you understand the above code, you will have a solid working knowledge of aesthetic mapping in ggplot2:
color and size aesthetics. This means that different species will have different colors and different body masses will have different sizes. Thus, you can map different variables to different aesthetics. This gives us great flexibility in how we can visualize our data.alpha, shape, and fill are set outside the aes() function, all points will have the same transparency, shape, and fill.The alpha aesthetic is incorrectly placed inside aes() because it is fixed, and size should be inside aes() because body_mass_g is a variable in the data.
alpha) based on body_mass_g, change the shape based on island, and set all points to purple.Geometric objects (geoms) are the visual representations of your data. Think of them as the artists painting your data onto the canvas. For example, we have already used geom_point() to create a scatter plot. ggplot2 offers a variety of geoms (geom_*()) to create different types of plots. As an example, we can use a different geometric object for the same data above:
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point() +
theme_minimal()geom_point() function is used to create a scatter plot.

library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_smooth() +
theme_minimal()geom_smooth() function is used to create a smooth line that fits the trend of the data.

In the two plots above, the two tabs share the same data and axes, but use different geometric objects. The first tab uses geom_point() to create a scatter plot, while the second uses geom_smooth() to create a smooth line that fits the trend of the data.
You can use more than one geometric object in a plot. For example, you can add a smooth line and points to the plot. Notice that the order of adding layers matters. The last layer will be drawn on top of the previous layers. To see this:
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point() +
geom_smooth() +
theme_minimal()geom_point() function is used to create a scatter plot.
geom_smooth() function is used to create a smooth line that fits the trend of the data.

The smooth line is on top of the points.
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_smooth() +
geom_point() +
theme_minimal()
Now the black points are on top of the smooth line.
We want to explore the relationship between body_mass_g and flipper_length_mm. Please plot the data with both the points and a smooth trend line. What preliminary conclusions can you draw from the plot?
The relationship is positive and roughly linear: heavier penguins tend to have longer flippers.
This is not something typically taught at length in a typical ggplot2 tutorial. I finally learned it from experience, after scratching my head many times over mysterious code.
To show this, we introduce another aesthetic mapping: group. It is a central aesthetic that is used to group data. For example, in the figure above, it is quite annoying that the smooth line describes the average trend across species, when we are often only interested in trends within species. We can group the data by species so that the smooth line is fitted to each species separately. This matters: pooled across species, bill length and bill depth are negatively related, but within each species the trend is positive (an instance of Simpsonβs paradox).
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point() +
geom_smooth(aes(group = species)) +
theme_minimal()species variable is mapped to the group aesthetic. This means that the smooth line will be fitted to each species separately.

But the figure itself is still not very informative because it is difficult to tell which species is which. We can add a color aesthetic:
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(aes(color = species)) +
geom_smooth(aes(group = species)) +
theme_minimal()
(A note: once color = species is mapped, ggplot2 already groups by that discrete variable, so a separate group = species is redundant here. We keep group explicitly to show what it does on its own.)
However, it is annoying that the color only applies to the points, but not to the smooth line. There are two ways to fix this:
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(aes(color = species)) +
geom_smooth(aes(group = species, color = species)) +
theme_minimal()species variable is mapped to the color aesthetic for the points.
species variable is mapped to the color aesthetic for the smooth line.

library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm,
group = species,
color = species
)
) +
geom_point() +
geom_smooth() +
theme_minimal()species variable is mapped to the group aesthetic globally.
species variable is mapped to the color aesthetic globally.

These two approaches produce the same result! In brief, what is included within the ggplot() function is applied to all geometric objects, while what is included within the geom_*() functions is only applied to that specific geometric object. A best practice is, whenever you are unsure, to include the aesthetic mapping in the geom_*() function.
To help you better understand, we can write quite cumbersome code to achieve the same results:
library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins
) +
geom_point(
aes(
x = bill_length_mm,
y = bill_depth_mm,
group = species,
color = species
)
) +
geom_smooth(
aes(
x = bill_length_mm,
y = bill_depth_mm,
group = species,
color = species
)
) +
theme_minimal()
As we can see, even x and y do not have to be included in the ggplot() function. They can be specified directly in the geom_*() functions.
This exercise aims to show you a common pitfall in using ggplot2 with global vs. local assignment.
island, with points colored by species. Run the following code and see what the warnings are.colour aesthetic was dropped during statistical transformation.geom_smooth() inherited group = species from the global aes() in ggplot(). That explicit group takes precedence over the grouping that color = island would imply, so the fit is per species and color = island cannot be applied.group and color into the individual geoms (or override group locally).ggplot2 works this time, this is a common pitfall that can lead to unexpected results with more complex figures.library(ggplot2)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(aes(color = species)) +
geom_smooth(aes(group = island)) +
theme_minimal()
This is slightly more advanced, but it is good to know that the data argument can be set globally or locally. For example, suppose we want to plot points for all species, but add a trend line only for the Gentoo species.
library(tidyverse)
library(palmerpenguins)
ggplot(
data = penguins,
aes(
x = bill_length_mm,
y = bill_depth_mm
)
) +
geom_point(aes(color = species)) +
geom_smooth(data = filter(penguins, species == "Gentoo")) +
theme_minimal()
We will explain what the filter() function does when we cover data wrangling. For now, just know that it is used to subset the data for a specific species. Note that the smooth line is blue (the default) rather than colored by species, because color is mapped only inside geom_point().