rguides

ggplot2 Basics, Learn how to create data visualizations in R

What you’ll learn

This tutorial covers the key concepts and practical techniques for working with ggplot2 Basics — Learn how to create data visualizations in R. By the end, you will know how to apply the core functions in real data analysis workflows.

The grammar of graphics

ggplot2 is an R package for building data visualizations from composable components. The name “ggplot2” comes from the “Grammar of Graphics”, a system where plots are built by stacking independent layers, each controlling a different aspect of how data maps to visual elements.

The key advantage of this approach is that simple plots and complex ones use the same underlying logic. You start with a base layer, add geometry, and layer on refinements. Everything composes with the + operator.

If you have used base R’s plot() function, ggplot2 will feel different at first. Base R has a function for every chart type (plot(), hist(), barplot()). ggplot2 has one function (ggplot()) and many geom functions you stack on top. The ggplot2 approach pays off once you need to customize beyond the defaults.

Three things every plot needs

Every ggplot2 plot has three required components:

  1. Data, a data frame or tibble
  2. Aesthetic mappings, how variables map to visual properties
  3. A geom, the geometric object that draws the data

The function ggplot() sets up the first two. You add geoms with +.

# Minimal complete plot
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()

ggplot() takes a data frame as its first argument. The aes() function (short for aesthetic mapping) defines which columns map to the x-axis, y-axis, color, size, and other visual properties.

Aesthetic mappings with aes()

The aes() function is where you connect data columns to visual channels. The most common aesthetics are:

AestheticWhat it controls
xHorizontal position
yVertical position
colorPoint and line color
fillInterior fill color (bars, boxes)
sizePoint size
shapePoint shape (categorical)
alphaTransparency

A critical distinction separates aesthetics defined inside aes() from those defined outside. Inside aes(), each aesthetic maps to a data variable, so its value varies by group or observation. Outside aes(), the aesthetic gets a fixed value that applies to the entire layer.

# Inside aes() — value varies by data
ggplot(mpg, aes(x = displ, y = hwy, color = class)) + geom_point()

# Outside aes() — fixed value for all points
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(color = "navy")

Scatter plots with geom_point()

A scatter plot shows the relationship between two continuous variables. You map engine displacement to the x-axis and highway fuel economy to the y-axis, and each car in the dataset appears as a point.

ggplot(mpg, aes(x = displ, y = hwy)) + geom_point()

Adding a color aesthetic splits the points by category. Here each point’s color reflects the vehicle class:

ggplot(mpg, aes(x = displ, y = hwy, color = class)) + geom_point()

ggplot2 automatically adds a legend. No extra code required.

Line plots with geom_line()

Use geom_line() when the x-axis represents an ordered sequence, such as time:

ggplot(economics, aes(x = date, y = pop)) + geom_line()

This shows how the US population changed over time. Line plots work best when the x-axis has a meaningful order, do not use them for categorical comparisons.

Bar charts with geom_bar()

geom_bar() has two different behaviors depending on how you use it.

With only an x aesthetic, geom_bar() counts the number of rows per category and draws a bar for each count:

ggplot(mpg, aes(x = class)) + geom_bar()

ggplot2 counted the rows for you. Each bar represents one vehicle class, and its height reflects how many vehicles fall into that category. This is the default behavior of geom_bar(), no manual counting required.

You can also provide pre-aggregated data. If you already have summary values, pass y inside aes() and add stat = "identity":

ggplot(diamonds, aes(x = cut, y = carat, fill = color)) +
  geom_bar(stat = "identity")

Histograms and box plots

A histogram divides a continuous variable into bins and shows the count per bin. The choice of binwidth matters, different widths reveal different patterns in your data:

ggplot(mpg, aes(x = hwy)) +
  geom_histogram(binwidth = 2)

A box plot is better suited for comparing distributions across categories. It shows the median, quartiles, and potential outliers in a compact form:

ggplot(mpg, aes(x = drv, y = hwy)) +
  geom_boxplot()

Labels and titles

Add a complete labeling system with labs(), which handles the title, subtitle, axis labels, and caption in one call:

ggplot(mpg, aes(x = displ, y = hwy)) +
  geom_point() +
  labs(
    title = "Engine Displacement vs Highway Fuel Economy",
    subtitle = "Data from 1999 to 2008 vehicles",
    x = "Engine Displacement (L)",
    y = "Highway MPG",
    caption = "Source: fueleconomy.gov"
  )

A common mistake: vectors in ggplot()

ggplot2 requires data in a data frame or tibble. If you try passing individual vectors, it will not work:

# This will NOT work
x <- c(1, 2, 3)
y <- c(4, 5, 6)
ggplot(aes(x = x, y = y)) + geom_point()

# You need to wrap them in a data frame first
ggplot(data.frame(x, y), aes(x = x, y = y)) + geom_point()

This error trips up a lot of people coming from base R, where you could plot vectors directly.

Piping data into ggplot()

The tidyverse pipe %>% lets you chain data transformations directly into your visualization. You filter or mutate your data first, then pipe the result straight into ggplot():

library(dplyr)
library(ggplot2)

mpg %>%
  filter(class == "compact") %>%
  ggplot(aes(x = displ, y = hwy, color = drv)) +
  geom_point(size = 3)

The key insight is that ggplot() receives the tibble from the pipe. Data preparation and visualization live in one readable chain.

Building block by block

Start with ggplot(data) to create an empty canvas. Add global aesthetics with aes(x = col1, y = col2), or include them in the ggplot() call: ggplot(data, aes(x, y)). Add a geom layer: + geom_point() adds points, + geom_line() adds lines, + geom_bar(stat = "identity") adds bars with pre-computed heights.

Common geoms

geom_point(), scatter plots for two continuous variables. geom_line(), time series or connected ordered points. geom_bar(), counts (no y needed with default stat = "count"). geom_histogram(bins = 30), distribution of a continuous variable. geom_boxplot(), distribution summary by group. geom_smooth(), adds a trend line. Each geom can have its own aes() that overrides the global aesthetics.

Color and size

Map color to a variable for distinct colors per category: aes(color = species). Map size to a variable for bubble charts: aes(size = population). Set a fixed value outside aes(): geom_point(color = "steelblue", size = 3). scale_color_brewer(palette = "Set2") applies a ColorBrewer palette. scale_color_viridis_d() applies the viridis colorblind-safe palette for discrete variables.

Labels and themes

labs(title = "My Plot", x = "X axis", y = "Y axis", color = "Group") sets all labels at once. theme_minimal(), theme_bw(), theme_classic() apply built-in themes. Layer theme() calls for specific adjustments: theme(legend.position = "bottom") moves the legend, theme(axis.text.x = element_text(angle = 45, hjust = 1)) rotates x-axis labels.

The grammar of graphics foundation

ggplot2’s design follows the Grammar of Graphics, a theoretical framework for describing visualizations as combinations of independent components. Rather than having separate functions for bar charts, scatter plots, and line charts, ggplot2 has one core system where the type of visualization emerges from the combination of a geometry, a data mapping, and optional statistical transformation. A bar chart and a scatter plot use the same framework with different geom choices.

This grammar produces a system where learning the components, data, aesthetics, geometries, facets, scales, coordinates, themes, gives you the ability to describe and build almost any visualization. Adding a new aesthetic mapping, changing a scale, or replacing one geometry with another are incremental modifications to the same grammar rather than switches between fundamentally different tools.

Building up layers

ggplot2 plots are constructed by adding layers. Each layer specifies data, an aesthetic mapping, and a geometry. Multiple layers can use different data and different mappings in the same plot. A common pattern is a geom_point layer for raw data points and a geom_smooth layer for a fitted trend line — both in the same figure, sharing x and y axes but drawing from different specifications.

The plus sign is the layer addition operator in ggplot2. It does not add values — it adds graphical layers to the plot object. The order of layers matters for rendering: later layers draw on top of earlier ones. For a plot with both a shaded background and data points, add the background geometry before the point geometry so the points appear on top.

Statistical transformations

Most geometries apply implicit statistical transformations. A histogram bins continuous data before drawing rectangles. A smooth line fits a model to the data and draws the fitted curve with a confidence band. A bar chart counts observations per category. These transformations are the stat component of the grammar and can be customized or bypassed.

For plots where the data is already summarized — a data frame of category counts ready to be drawn as bars — use stat = “identity” to tell the geometry to use the data values directly without further transformation. Forgetting stat = “identity” when the data is pre-summarized causes the geometry to attempt a counting transformation on already-counted data, producing incorrect results.

Conclusion

ggplot2 builds visualizations by combining data, aesthetic mappings, and geometric layers through a consistent + operator interface. The three foundational pieces are your data frame, the aes() mappings that connect variables to visual properties, and the geom_ functions that determine how those aesthetics appear.

Once you understand this layered approach, you can create anything from quick exploratory charts to polished publication graphics. The same mental model scales from a simple scatter plot to a multi-layer faceted figure.

To move beyond the basics, explore how to layer multiple geoms on the same plot, how facets split your data into subplots, and how themes control the non-data elements of your charts.

Next steps

Now that you understand ggplot2 basics — learn how to create data visualizations in r, explore these related topics to deepen your knowledge and apply these techniques in more complex scenarios.

See also