Skip to main content

Visualize Penguins with ggplot2

Build layered figures: histograms, scatterplots, and boxplots

In Coding 2 you plotted with base R’s hist(). Today you will build figures in layers with ggplot2.

The first time you click Run Code, your browser downloads R once (a few seconds). After that it is quick.

NoteThe grammar of graphs

Every ggplot2 figure has three essential pieces:

  1. data — the dataset (for example penguins)
  2. aes — aesthetic mappings: which column goes on which axis, or which color
  3. geom — the geometric object: points, boxes, bars, histogram bins

You add layers with +. We will practice all three today. The two tables below are a reference for this activity and for your assignments.

Geom Shows Needs in aes() Note
geom_histogram() Distribution of one continuous variable x bins = 30 by default; try binwidth = 100
geom_density() Smoothed outline of a histogram x
geom_bar() Count of rows in each category x (categorical) Counts for you
geom_col() Bars whose heights you already computed x, y
geom_point() Scatterplot of two continuous variables x, y
geom_jitter() Scatterplot with points nudged apart so they do not overlap x, y width = 0.2 controls the nudge
geom_boxplot() Median, quartiles, and outliers per group x (group), y
geom_violin() Full distribution shape per group x (group), y
geom_line() Values connected in order along x x, y
geom_smooth() A fitted trend line with a confidence band x, y method = "lm" for a straight line
geom_errorbar() Error bars around a summary x, ymin, ymax

Layers stack with + in the order you write them, so geom_point() + geom_smooth() draws the line on top of the points.

Aesthetic Controls Works best with Example
x, y Position on the axes Any column aes(x = flipper_length_mm, y = body_mass_g)
color Outline or point color Categories, or a continuous gradient aes(color = species)
fill Inside color of bars, boxes, and violins Categories aes(fill = island)
size Point size or line thickness Continuous aes(size = bill_depth_mm)
shape Point shape Categories (six or fewer) aes(shape = sex)
alpha Transparency, 0 to 1 Continuous, or a fixed value geom_point(alpha = 0.5)
group Which rows belong together for lines and boxes Categories aes(group = species)

Inside aes(), a setting maps a column, so each species gets its own color. Outside aes(), it sets one constant for the whole layer: geom_point(color = "blue") makes every point blue.

Helpers you will add to almost every figure: labs() for the title and axis labels, theme_minimal() for a clean background, and facet_wrap(~ species) for one panel per group.

Posit, the company that makes RStudio, publishes a one-page summary of all of this: ggplot2 cheat sheet (web page) or PDF. Keep it open while you work on assignments.

1. Load the tidyverse

Coding Assignment 2 starts the same way. Run library(tidyverse) to load ggplot2 and friends. A conflicts message afterward is expected, not an error.

NoteHint

From ?library: library(package, ...) attaches a package so its functions become available. Replace package with the name of the collection you need (ggplot2 ships inside the tidyverse bundle).

Try ?library in R for the full help page.

TipSolution
library(tidyverse)

On DataHub and in your graded assignment you will type exactly this.

2. Glimpse the penguins

glimpse() is a tidyverse way to inspect a dataset: rows, columns, and column types. Run it on penguins from the palmerpenguins package so you know which columns to map to axes and colors.

NoteHint

From ?glimpse: glimpse(x) — print a compact summary of a data frame (rows, columns, types).

Example with a built-in dataset:

glimpse(mtcars)

Apply the same function to the dataset named in the exercise prompt.

TipSolution
glimpse(penguins)

You should see 344 rows and 8 columns. species and island are <fct> (categories); measurements like body_mass_g are <dbl> (numbers).

From the glimpse() output:

  1. How many rows are there?
  2. What type is body_mass_g?
  3. Which column tells you which island each penguin was sampled on?

Answers: 344 rows; body_mass_g is <dbl>; island.

3. Histogram with ggplot2

Start a figure with ggplot(), map body_mass_g to the x-axis, and add geom_histogram(). Use the penguins dataset directly — an upgrade from Coding Activity 2’s hist(). Add a title with labs(title = "...").

NoteHint

From ?ggplot: ggplot(data = NULL, mapping = aes(), ...) — then add a layer with +.

From ?geom_histogram: bins one continuous variable.

Example:

ggplot(mtcars, aes(x = mpg)) +
  geom_histogram() +
  labs(title = "Car fuel economy")

Map the x-axis to one continuous column from penguins and add your own title.

TipSolution
ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram() +
  labs(title = "Penguin body mass")

A histogram shows how one continuous variable is distributed — how many penguins fall in each weight range. ggplot2 may warn about two rows with missing body mass; that is normal.

4. Scatterplot — two continuous variables

Build a scatterplot: flipper length on the x-axis, body mass on the y-axis. Map species to color so each species gets its own hue. Use geom_point().

NoteHint

From ?geom_point: scatterplot of two continuous variables. Map a third variable to color inside aes().

Example:

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl))) +
  geom_point()

Use flipper length and body mass on the axes and map species to color on the penguin data.

TipSolution
ggplot(penguins, aes(
  x = flipper_length_mm,
  y = body_mass_g,
  color = species
)) +
  geom_point()

Scatterplots relate two continuous measurements. Color adds a third variable — here, species — so you can see clusters.

5. Boxplot — compare groups

Coding Assignment 2 builds a boxplot one layer at a time. Here is the core pattern:

  • Put island on the x-axis, body_mass_g on the y-axis
  • Add geom_boxplot() with fill mapped to island
  • Add theme_minimal() and readable labs()
NoteHint

From ?geom_boxplot: compare a numeric variable across groups.

Example pattern:

ggplot(mtcars, aes(
  x = factor(cyl),
  y = mpg,
  fill = factor(cyl)
)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "MPG by cylinder count", x = "Cylinders", y = "MPG")

Apply the same layering — boxplot, fill, theme_minimal(), labs() — to islands and body mass.

TipSolution
ggplot(penguins, aes(
  x = island,
  y = body_mass_g,
  fill = island
)) +
  geom_boxplot() +
  theme_minimal() +
  labs(
    title = "Body mass by island",
    x = "Island",
    y = "Body mass (g)"
  )

Boxplots compare a continuous variable across groups. On your graded assignment you will add reorder() to sort groups by elevation after you join that metadata in Coding Activity 6.

6. Recap — ggplot functions you practiced

Function Role
library(tidyverse) Loads ggplot2 and related packages
glimpse() Quick look at rows, columns, and types
ggplot() + aes() Starts a figure and maps columns to axes, color, or fill
geom_histogram() Distribution of one continuous variable
geom_point() Relationship between two continuous variables
geom_boxplot() Compare a continuous variable across groups
theme_minimal() + labs() Cleans appearance and adds titles/labels

If you can explain each row in your own words, you have covered the figure-building core of Coding Assignment 2.

7. Transfer to Coding Assignment 2

On Canvas you will build figures step by step with hemoglobin measurements and population elevation data:

  1. library(tidyverse) and read your dataset
  2. Build a boxplot one layer at a time with ggplot(), geom_boxplot(), fill, theme_minimal(), and labs()

You practiced the layering here. Coding Activity 6 covers the wrangling steps — filtering, reshaping, and joining — that prepare the table for those plots.

Keep playing

No grading here. Try swapping geoms, mapping a different column to color, or predicting what a plot will look like before you run it.

Try this with a partner. One of you predicts the figure; the other runs the code. Swap roles. Saying your prediction out loud first is one of the fastest ways to learn.