library(ggplot2)R ggplot2: introductory data visualisation
What are we going to learn?
This session is directed at R users (a beginner level is sufficient) who are new to the ggplot2 package.
During this session, you will:
- Install the most popular R data visualisation package
- Use visual data exploration to explain trends
- Learn about the 3 essential ggplot2 components
- Get familiar with the “grammar of graphics”
- Create different plots for different datasets
- Construct plots by layering components
- Refine your visualisations for effective communication
- Customise plots with colours, labels and themes
- Export plots to use them anywhere
Setting up
Let’s open Positron and get set up. (If you don’t have it installed already, follow the installation instructions.)
Everything we write today will be saved in your project. If you are using a Library computer, remember to save it on your H drive or USB stick.
New project folder
A project folder helps us keep all files related to one project in a single location. It makes it easy to share it, and to switch from one project to another.
- Select New (top left) and choose “New Folder from Template”
- Select “R Project” from the available templates
- Enter a Folder name for your project
- Choose the Location where you want to save the project, such as
Documents/RProjects, which you can create if it doesn’t exist yet - Leave the default options selected (unless instructed otherwise)
- Click “Create”
Create a script
We use a script to save our code and write more comfortably.
When creating a new R project, Positron opens a new R script in the Editor above the console. You can straight away save this script and name it something like “process.R”.
You can now write some comments (lines that start with #) to introduce the script.
Remember some of the most commonly used Positron shortcuts when writing an R script:
- execute the current command: Ctrl + EnterCtrl + Enter
- assignment operator (
<-): Alt + -Alt + -
To avoid common issues, keep in mind:
- R is case sensitive: it will tell the difference between uppercase and lowercase.
- Objects and functions have naming rules, the main ones being: no spaces; don’t start with a number.
Finding help
For any dataset or function doubts that you might have, open the documentation! In Positron, you can find it with:
- the shortcut command:
?functionname - the help function:
help(functionname) - the keyboard shortcut: press F1F1 after placing your cursor on a function name
- the popup: hover over a function name
Installing ggplot2
We first need to make sure we have the ggplot2 package available on our computer. We can execute this command in the console: install.packages("ggplot2"), or we can use the “Packages” tab:
- Open the “Packages” tab (from the blue sidebar on the left)
- Click the three-dot menu and “Install Package”
- Search for “ggplot2” and select it
- Select the latest version offered
You only need to install a package once, but you need to load it every time you start a new R session.
We can now load the package by writing this command in the script and executing it:
Introducing ggplot2
The R package ggplot2 was originally developed by Hadley Wickham with the objective of creating a grammar of graphics for categorical data (in 2007). It is based on the book The Grammar of Graphics Developed by Leland Wilkinson (first edition published in 1999).
It is now part of the group of data science packages called Tidyverse.
The components of the Grammar of Graphics
The Grammar of Graphics is based on the idea that you can build every graph from the same few components.
The components are:
- Data
- Mapping
- Statistics
- Scales
- Geometries
- Facets
- Coordinates
- Theme
In this introductory session, we will mainly focus on the data, the mapping, the statistics, the geometries and the theme.
ggplot2’s three essential components
In ggplot2, the 3 main components that we usually have to provide are:
- Where the data comes from,
- the aesthetic mappings, and
- a geometry.
For our first example, let’s use the msleep dataset (from the ggplot2 package), which contains data about mammals’ sleeping patterns.
You can find out about the dataset with
?msleep.
Let’s start with specifying where the data comes from in the ggplot() function:
ggplot(data = msleep)
This is not very interesting. We need to tell ggplot2 what we want to visualise, by mapping aesthetic elements (like our axes) to variables from the data. We want to visualise how common different conservations statuses are, so let’s associate the right variable to the x axis:
ggplot(data = msleep,
mapping = aes(x = conservation))
ggplot2 has done what we asked it to do: the conservation variable is on the x axis. But nothing is shown on the plot area, because we haven’t defined how to represent the data, with a geometry_* function:
ggplot(data = msleep,
mapping = aes(x = conservation)) +
geom_bar()
Now we have a useful plot: we can see that a lot of animals in this dataset don’t have a conservation status, and that “least concern” is the next most common value.
We can see our three essential elements in the code:
- the data comes from the
msleepobject; - the variable
conservationis mapped to the aestheticx(i.e. the x axis); - the geometry is
"bar", for “bar chart”.
Here, we don’t need to specify what variable is associated to the y axis, as the “bar” geometry automatically does a count of the different values in the conservation variable. That is what statistics are applied automatically to the data.
In ggplot2, each geometry has default statistics setting, so we often don’t need to specify which stats we want to use. We could use a stat_*() function instead of a geom_*() function, but most people start with the geometry (and let ggplot2 pick the default statistics that are applied).
Run ?geom_bar to see that it uses stat = "count" as a default, and that stat_count() does the equivalent with geom = "bar".
Horizontal bar charts
What if we prefer to use horizontal bars? We only need to switch the mapping of the variable to the y aesthetic:
ggplot(data = msleep,
mapping = aes(y = conservation)) +
geom_bar()
ggplot2 happily swaps the counts to the x axis. This is particularly helpful when long category names overlap under the x axis.
Line plots
Let’s have a look at another dataset: the economics dataset from the US. Learn more about it with ?economics, and have a peak at its structure with:
str(economics)spc_tbl_ [574 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ date : Date[1:574], format: "1967-07-01" "1967-08-01" ...
$ pce : num [1:574] 507 510 516 512 517 ...
$ pop : num [1:574] 198712 198911 199113 199311 199498 ...
$ psavert : num [1:574] 12.6 12.6 11.9 12.9 12.8 11.8 11.7 12.3 11.7 12.3 ...
$ uempmed : num [1:574] 4.5 4.7 4.6 4.9 4.7 4.8 5.1 4.5 4.1 4.6 ...
$ unemploy: num [1:574] 2944 2945 2958 3143 3066 ...
Do you think that unemployment is stable over the years? Let’s have a look with a line plot, often used to visualise time series:
ggplot(data = economics,
mapping = aes(x = date,
y = unemploy)) +
geom_line()
Let’s go through our essential elements once more:
- The
ggplot()function initialises a ggplot object. In it, we declare the input data frame and specify the set of plot aesthetics used throughout all layers of our plot; - The
aes()function groups our mappings of aesthetics to variables; - The
geom_<...>()function specifies what geometric element we want to use.
Scatterplots
Scatterplots are often used to look at the relationship between two variables. Let’s try it with a new dataset: mpg (which stands for “miles per gallon”), a dataset about fuel efficiency of different models of cars.
?mpg
str(mpg)Do you think that big engines use fuel more efficiently than small engines?
We can focus on two variables:
displ: a car’s engine size, in litres.hwy: a car’s fuel efficiency on the highway, in miles per gallon.
For the geometry, we now have use “points”:
ggplot(data = mpg,
mapping = aes(x = displ,
y = hwy)) +
geom_point()
Notice how the points seem to be aligned on a grid? That’s because the data was rounded. If we want to better visualise the density of points, we can use the “count” geometry, which makes the dots bigger when data points have the same x and y values:
ggplot(data = mpg,
mapping = aes(x = displ,
y = hwy)) +
geom_count()
Alternatively, we can avoid overlapping of points by using the “jitter” geometry, which gives the points a little shake:
ggplot(data = mpg,
mapping = aes(x = displ,
y = hwy)) +
geom_jitter()
Even though the position of the dots does not match exactly the original x and y values, it does help visualise densities better.
The plot shows a negative relationship between engine size (displ) and fuel efficiency (hwy). In other words, cars with big engines use more fuel. Does this confirm or refute your hypothesis about fuel efficiency and engine size?
However, we can see some outliers. We need to find out more about our data.
Adding aesthetics
We can highlight the “class” factor by adding a new aesthetic:
ggplot(data = mpg,
mapping = aes(x = displ,
y = hwy,
colour = class)) +
geom_jitter()
It seems that two-seaters are more fuel efficient than other cars with a similar engine size, which can be explained by the lower weight of the car. The general trend starts to make more sense!
We now know how to create a simple scatterplot, and how to visualise extra variables. But how can we better represent a correlation?
Trend lines
A trend line can be created with the geom_smooth() function:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_smooth()`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

We stopped using the argument names because we know in which order they appear: first the data, then the mapping of aesthetics. Let’s save ourselves some typing from now on!
The console shows you what function / formula was used to draw the trend line. This is important information, as there are countless ways to do that. To better understand what happens in the background, open the function’s help page and notice that the default value for the method argument is “NULL”. Read up on how it automatically picks a suitable method depending on the sample size, in the “Arguments” section.
Want a linear trend line instead? Add the argument method = "lm" to your function:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_smooth(method = "lm")`geom_smooth()` using formula = 'y ~ x'

Layering
A trend line is usually displayed on top of the scatterplot. How can we combine several layers? We can string them with the + operator:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_jitter() +
geom_smooth()`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

The order of the functions matters: the points will be drawn before the trend line, which is probably what you’re after.
The colour aesthetic
We can once again add some information to our visualisation by mapping the class variable to the colour aesthetic:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_jitter(aes(colour = class)) +
geom_smooth()`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Challenge 1 – where should aesthetics be defined?
Take the last plot we created:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_jitter(aes(colour = class)) +
geom_smooth()What would happen if you moved the colour = class aesthetic from the geometry function to the ggplot() call?
Different geometries can also have their own mappings that overwrite the defaults. If you place mappings in a geom function, ggplot2 will treat them as local mappings for the layer. It will use these mappings to extend or overwrite the global mappings for that layer only. This makes it possible to display different aesthetics in different layers.
Change a geometry’s default colour
What if you want to change the default colour of the trend line? You need to give a new default value to the geometry’s colour aesthetic:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_jitter(aes(colour = class)) +
geom_smooth(colour = "black")`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Notice how this is not happening inside an aes() function? That’s because we are not mapping a variable from the dataset to the geometry’s aesthetic! We are only replacing the default colour of this trend line, which is used when there is no mapping to the data.
If you are curious about what colour names exist in R, you can use the colours() function.
Customising labels
Variable names that are convenient to write code with are rarely suitable for a publication-ready plot. We should modify labels with the labs() function to make our plot more self-explanatory:
ggplot(mpg,
aes(x = displ,
y = hwy)) +
geom_jitter(aes(colour = class)) +
geom_smooth(colour = "black") +
labs(title = "Does engine size relate to fuel efficiency?",
x = "Displacement (L)",
y = "Fuel efficiency (mpg)",
colour = "Type of car")`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

The labs() function allows us to change
Remember that captions and titles are better sorted out in the publication itself, especially for accessibility reasons (e.g. to help with screen readers).
Saving a plot
Like your visualisation? Let’s export it to use it outside of Positron.
To keep things tidy, let’s first create a “plots” folder. Use the “Explorer” tab, or run this command in your console:
dir.create("plots")There are a few ways to manually export the plot from the Plots pane:
- Building a document or a slideshow? You can copy it straight to your clipboard with the “Copy plot to Clipboard” button, and paste it into it.
- For more options, use the “Save Plot” button.
- PNG is a good compressed raster format for graphics (prefer this one over JPEG if you can)
- If you want an image that looks sharp regardless of zoom level, and if you want to further customise your visualisation, use SVG. (Try to open an SVG file in Inkscape for example.) PDF also uses vector graphics, but is not a format designed to be further edited.
To save the last plot with a command, you can use the ggsave() function:
ggsave(filename = "plots/fuel_efficiency.png")This is great to automate the export process for each plot in your script, but ggsave() also has extra options, like setting the DPI, which is useful for getting the right resolution for a specific use. For example, to export a plot for your presentation:
ggsave(filename = "plots/fuel_efficiency.png", dpi = "screen")Saving a .svg file with requires installing the svglite package. This packages seems so work best installing in a fresh R session (Session > Restart R) from source
install.packages("svglite", type = "source"). Then load the librarylibrary(svglite)rerun your code including loading previous libraries (ggplot2etc.) and now saving a plot with a .svg extension should work!
Challenge 2 – add a variable and a smooth line
Let’s use a similar approach to what we did with the mpg dataset.
Take our previous unemployment visualisation, but represented with points this time:
ggplot(economics,
aes(x = date,
y = unemploy)) +
geom_point()How could we:
- Add a smooth line for the number of unemployed people. Are there any interesting arguments that could make the smoother more useful?
- Colour the points according to the median duration of unemployment (see
?economics)
ggplot(economics,
aes(x = date,
y = unemploy)) +
geom_point(aes(colour = uempmed)) +
geom_smooth()`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

See how the legend changes depending on the type of data mapped to the
colouraesthetic? (i.e. categorical vs continuous)
This default “trend line” is not particularly useful. We could make it follow the data more closely by using the span argument. The closer to 0, the closer to the data the smoother will be:
ggplot(economics,
aes(x = date,
y = unemploy)) +
geom_point(aes(colour = uempmed)) +
geom_smooth(span = 0.1)`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

You can now see why this is called a “smoother”: we can fit a smooth curve to data that varies a lot.
To further refine our visualisation, we could visualise the unemployment rate rather than the number of unemployed people, by calculating it straight into our code:
ggplot(economics,
aes(x = date,
y = unemploy / pop)) +
geom_point(aes(colour = uempmed)) +
geom_smooth(span = 0.1)`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

The early 1980s recession now seems to have had a more significant impact on unemployment than the Global Financial Crisis of 2007-2008.
Themes
The theme() function allows us to really get into the details of our plot’s look. However, a handful of convenient theme_*() functions make it easy to quickly apply a number of tweaks to our plot’s theme, like theme_bw():
ggplot(economics,
aes(x = date,
y = unemploy / pop)) +
geom_point(aes(colour = uempmed)) +
geom_smooth(span = 0.1) +
theme_bw()`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Try theme_minimal() as well, and if you want more options, install the ggthemes package!
These theme_*() functions also allow changing default colours across different geometries and plot elements, based on their role. For this, use the paper, ink and accent arguments:
ggplot(economics,
aes(x = date,
y = unemploy / pop)) +
geom_point(aes(colour = uempmed)) +
geom_smooth(span = 0.1) +
theme_bw(paper = "lavender", ink = "navy", accent = "hotpink")`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Play time!
Challenge 3: explore geometries
When creating a new layer, start typing geom_ and see what suggestions pop up. Are there any suggestions that sound useful or familiar to you?
Modify your plots, play around with different layers and functions, and ask questions!
Close project
Make sure you save your script before you close Positron.
Useful links
- For ggplot2:
- ggplot2 cheatsheet
- Official ggplot2 documentation
- Official ggplot2 website
- Chapter on data visualisation in the book R for Data Science
- Nicola Rennie’s book: The Art of Data Visualization with ggplot2
- From Data to Viz, a website to explore different visualisations and the code that generates them
- Selva Prabhakaran’s r-statistics.co section on ggplot2
- Coding Club’s data visualisation tutorial
- STHDA’s ggplot2 essentials
- Our compilation of general R resources