We have spent the last two TA sessions discussing Solow Growth Model, and we all know that GDP per capita is one of the most important components of this model. In Economic growth, GDP per capita is also the major parameter of interest. In this handouts, we will work on data visualization related to GDP per capita, a measure of living standards.
“The simple graph has brought more information to the data analyst’s mind than any other devices.”– John Tukey (American Mathematician)
The handout will teach you how to visualize your data using ggplot2. R has several systems and packages for making graphs, but ggplot2 is one of the most elegant and versatile. The ggplot2 package implements the grammar of graphics, a coherent system for describing and building graphs. With ggplot2, you can do more faster by learning one system and applying it in many places.
If you’d like to learn more about the theoretical underpinnings of ggplot2 before you start, I’d recommend reading “The Layered Grammar of Graphics”, http://vita.had.co.nz/papers/layered-grammar.pdf.
The ggplot2 package is one of the many built-in packages in the large tidyverse package, a collection of packages created by Hadley Wickman to do data science in R, we mentioned in our introduction. To load tidyverse, write
library("tidyverse")
That one line of code loads the core of tidyverse; packages which you will use in almost every data analysis. If you run this code and get the error message “there is no package called ‘tidyverse’,” you will need to first install it, then run library() once again.
install.packages("tidyverse")
library("tidyverse")
You only need to install a package once, but you need to reload it every time you start a new session.
Let’s use our first graph to answer a question: Do countries with higher income levels have a longer life expectancy than countries with low income levels? You probably already have an answer, but try to make your answer precise. What does the relationship between income levels and life expectancy look like? Is it positive? Negative? Linear? Nonlinear?
You can test your answers using data from Gapminder. Gapminder is a foundation that works on making data on development broadly accessible (https://www.gapminder.org/). Gapminder has data on GDP per capita data taken from the Penn World Table (PWT), a database with information on relative levels of income, output, input, and productivity (https://www.rug.nl/ggdc/productivity/pwt/?lang=en). pwt is a greate database for research on econometric growth.
R has a custom-designed package to access the Gapminder data on GDP per capita and life expectancy. Install the gapminder package as we did above and load it by writing the following command:
library(gapminder)
Loading this package gives us access to the gapminder data frame. A data frame is a rectangular collection of observations (in the rows) and variables (in the columns). gapminder contains observations collected by the Gapminder Foundation on GDP per capita, life expectancy, and population in a selection of countries for different years. Print the data frame by writing:
gapminder
## # A tibble: 1,704 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
To learn more about gapminder, open the page by running ?gapminder in your console (because it is unnecessary in your script).
To load the packaged/built-in data into your current R session, write
gapminder<-gapminder
The <- is the notation which assigns a (new) dataset to your Environment.
In our exercise, we will start by focusing on the latest set of observations from 2007. To restrict attention to these observations, run the code
gapminder07<-filter(gapminder,year==2007)
You do not need to know data manipulation at this point. In later lectures, we will discuss how the function filter works. For now, it is enough to know that the operation above creates a data frame gapminder07 which consists all observations from 2007. You can view the data frame by simply clicking gapminder07 in Environment.
As we can see, the gapminder07 data frame is cleaned. The objective of this handout is to introduce you how to create graphs based on a cleaned dataset. The next handout is mainly about how to clean and tail a dataset into our research purposes (data manipulation).
We can create plots to show the relationship between life expectancy and GDP per capita in gapminder07. I will not tell you the graphing template right now but you will see it in a second. To create a scatter plot, run this code to put gdpPercap on the x-axis and lifeExp on the y-axis:
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp))
The plot shows a positive relationship between country income level (gdpPercap) and life expectancy (lifeExp). In other words, countries with high incomes live longer, on average.
Now let me show you components of the graphing code above. With ggplot2, you begin a plot with the function ggplot(). ggplot() creates a coordinate system that you can add layers to. The first argument of ggplot() is the dataset to use in the graph. So ggplot(data = gapminder07) creates an empty graph.
You complete your graph by adding one or more layers to ggplot(). The function geom_point() adds a layer of points to your plot, which creates a scatter plot. ggplot2 comes with many geom functions that each add a different type of layer to a plot. You can also add modifiers that change the axes, add labels, and many other useful things. The powerful property of ggplot2 lies in this possibility of building up charts step-by-step.
Each geom function in ggplot2 takes a mapping argument. This defines how variables in your dataset are mapped to visual properties. The mapping argument is always paired with aesthetic aes(), and the x and y arguments of aes() specify which variables to map to the x- and y-axes. ggplot2 looks for the mapped variable in the data argument, in this case, gapminder07.
Let’s now turn this code into a reusable template for making graphs with ggplot2. To make a graph, replace the bracketed sections in the code below with a dataset, a geom function, or a collection of mappings.
ggplot(data = <DATA>) +
<GEOM_FUNCTION>(mapping = aes(<MAPPINGS>))
The rest of this handout will show you how to complete and extend this template to make different types of graphs. We will begin with the <MAPPINGS> component and then look at different <GEOM_FUNCTION>.
We have already seen two aesthetics, x and y, in the graph above. We can also customize and add another aesthetic to our function to plot graphs of deeper insights.
The plot above shows that people in rich countries, on average, live longer than people in poor countries. However, it tells us very little about which countries belong to different categories. For example, where are the different continents on this graph? We saw in the data frame that it contains information about continents. How do we bring this information into the graph?
In ggplot2, you do this by adding an additional variable, like continent in gapminder07, to the two dimensional scatter plot by mapping it to the same aesthetic. An aesthetic is a visual property of the objects in your plot. Aesthetics can include things like the size, the shape, or the color of your points. You can display a point/line (like the one below) in different ways by changing the values of its aesthetic properties.
Take the first graph above as an example, we can remap points to the continent variable to reveal the continent of each country. You can group countries into continents by many different aesthetics properties (e.g. color, size, alpha). Write the following code to remap the colors of your points:
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp, colour = continent))
(If you prefer British English, you can use colour instead of color.)
In general, to map an aesthetic to a variable, associate the name of the aesthetic to the name of the variable inside aes(). In the example above, we used “color”, but there are other aesthetics such as shape and size that we can also map variables. Whenever we do so, ggplot2 will automatically assign a unique level of the aesthetic (here a unique color) to each unique value of the variable, a process known as scaling. In the case of continents, every unique continent (i.e., every unique level of the variable “continent”) is assigned to a unique color. Automatically, ggplot2 will also add a legend that explains which levels correspond to which values.
In the above example, we mapped continent to the color aesthetic, but we could have mapped continent to the size aesthetic in the same way. In this case, the exact size of each point would reveal its continent. However, mapping mapping to the size aesthetic is not a good idea (why?).
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp, size = continent))
## Warning: Using size for a discrete variable is not advised.
Or we could have mapped continent to the alpha aesthetic, which controls the transparency of the points, or the shape of the points (also not a good idea in this case).
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp, alpha = continent))
## Warning: Using alpha for a discrete variable is not advised.
aes()You can also set the aesthetic properties of your geom manually. For example, we can make all of the points in our plot blue:
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp), color = "blue")
In this graph, the color doesn’t convey information about a variable, but only changes the appearance of the plot. To set an aesthetic manually, set the aesthetic by name as an argument of your geom function; i.e. it goes outside of aes(). You’ll need to pick a level that makes sense for that aesthetic:
The name of a color as a character string.
The size of a point in mm.
The shape of a point.
One way to add additional variables is with aesthetics. Another way, particularly useful for categorical variables (e.g., continent, poor vs rich, rather than numerical, GDP per capita, life expectancy), is to split your plot into facets, subplots that each display one subset of the data.
To facet your plot by a single variable, use facet_wrap(). The first argument of facet_wrap() should be a formula, which you create with ~ followed by a variable name (e.g. continent). The variable that you pass to facet_wrap() should be discrete.
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp))+
facet_wrap(~continent)
To facet your plot on the combination of two variables, add facet_grid() to your plot call. The first argument of facet_grid() is also a formula. This time the formula should contain two variable names separated by a ~. To try this, we can create the Gapminder data with two different years, 1952 and 2007:
gapminder5207 <- filter(gapminder, year==1952 | year==2007)
The you can facet on two variables to create a two-by-two graphs.
ggplot(data = gapminder5207) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp))+
facet_grid(year ~ continent)
In research, it is common for us to re-scale our variables, because their are flaws with the original values or the original values do not match our research purposes.
GDP per capita is often re-scaled by log (aka log transformations). This helps our data points to be more “linear.” To do this in a ggplot, write the code:
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = log(gdpPercap), y = lifeExp))
You can draw the same graph using different codes:
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = gdpPercap, y = lifeExp))+
scale_x_continuous(trans = 'log')
Similarly, to categorize countries by the continent variable and the color aesthetic, write
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = log(gdpPercap), y = lifeExp, color=continent))
We have just look at aesthetics of ggplot2. Now let’s turn to another important component, geometric objects.
First, how are these two plots similar?
Both plots contain the same x variable, the same y variable, and both describe the same data. But the plots are not identical. Each plot uses a different visual object to represent the data. In ggplot2 syntax, we say that they use different geoms.
A geom is the geometrical object that a plot uses to represent data. People often describe plots by the type of geom that the plot uses. For example, bar charts use bar geoms (geom_bar), line charts use line geoms (geom_line), box plots use box plot geoms (geom_box), and so on. Scatter plots break the trend; they use the point geom (geom_point) as we have seen earlier. As we see above, you can use different geoms to plot the same data. The plots above use the point geom (geom_point) and the smooth geom (geom_smooth), a smooth line fitted to the data, respectively.
In other words, you can create graphs in different types, such as scatter plot, line graph, smooth graph, bar plot, and so on. The type of graph to create depends solely on your graphing purposes and research questions.
To change the geom in your plot, change the geom function that added to ggplot(). For instance, to make the two plots above, you can use this code:
# Scatter plot
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = log(gdpPercap), y = lifeExp))
# Smooth curve plot
ggplot(data = gapminder07) +
geom_smooth(mapping = aes(x = log(gdpPercap), y = lifeExp))
ggplot2 provides over 30 geoms. Every geom function in ggplot2 takes a mapping argument. That means you can assign aesthetics to geoms. However, not every aesthetic works with every geom. You could set the shape of a point, but you couldn’t set the “shape” of a line. On the other hand, you could set the line type of a line. geom_smooth() can draw a different line, with a different line type, but it is impossible to achieve with geom_point. However, some aesthetics, such as color, can be applied to nearly all geoms.
Here is another example, text plot, associated with GDP per capita and life expectancy in gapminder07. A Text plot requires us to specify the label/text that to be added on the graph.
ggplot(data = gapminder07) +
geom_text(mapping = aes(x = gdpPercap, y = lifeExp, label = country))
Similarly, we can re-scale the x-axis.
ggplot(data = gapminder07) +
geom_text(mapping = aes(x = log(gdpPercap), y = lifeExp, label = country))
Next, let’s take a look at a bar chart. Bar charts seem simple, but they are interesting because they reveal something subtle about plots. Consider a basic bar chart, as drawn with geom_bar(). The following chart displays the total number of countries in the gapminder07 dataset, grouped by continent.
ggplot(data = gapminder07) + geom_bar(mapping = aes(x = continent))
Note that the geom_bar can always only have one of x or y aesthetic since bar plots, similar to histograms and frequency plots, are designed to show the distributions of variables.
Layering different geoms together, such as geom_point and geom_smooth allows us to have two different geoms in the same graph! If this makes you excited, buckle up. In this section, we will discuss how to place multiple geoms in the same plot. Combining graphs simply means layering multiple (more than two and usually two) geoms together.
In general, ggplot works by starting from plot and adding more components using the + sign. Thus, you could continue the code using
ggplot(data = <DATA>) +
<GEOM_FUNCTION1>(mapping = aes(<MAPPINGS>)) +
<GEOM_FUNCTION2>(mapping = aes(<MAPPINGS>))
to add a new function. You will see different functions you can add below.
If the <GEOM_FUNCTION1> and <GEOM_FUNCTION2> are mapped using the identical mapping methods, i.e. <MAPPINGS1> and <MAPPINGS2> are identical to each other, there is no difference between the following template and the template above.
ggplot(data = <DATA>, mapping = aes(<MAPPINGS>)) +
<GEOM_FUNCTION1>() +
<GEOM_FUNCTION2>()
However, any tiny differences need to be specified in the <GEOM_FUNCTION> (just consider it as combing multiple layers together).
ggplot(data = <DATA>) +
<GEOM_FUNCTION1>(mapping = aes(<MAPPINGS1>)) +
<GEOM_FUNCTION2>(mapping = aes(<MAPPINGS2>))
Everything in the graphing template section can be illustrated in the following applications.
In this part, test if you can understand the code by yourselves. Once you understand them, you can create similar and even more complex graphs on your own.
ggplot(data = gapminder07) +
geom_point(mapping = aes(x = log(gdpPercap), y = lifeExp))+
geom_smooth(mapping = aes(x = log(gdpPercap), y = lifeExp))
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
ggplot(data = gapminder07,mapping = aes(x = log(gdpPercap), y = lifeExp))+geom_point()+geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
ggplot(data = gapminder07)+geom_point(mapping = aes(x = log(gdpPercap), y = lifeExp,color=continent))+geom_smooth(mapping = aes(x=log(gdpPercap),y=lifeExp),color="red",se=FALSE)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
ggplot(data = gapminder07,mapping = aes(x = log(gdpPercap), y = lifeExp))+geom_point()+geom_smooth(method="lm",se=FALSE)+facet_wrap(~continent)
## `geom_smooth()` using formula 'y ~ x'
Recreate the R code necessary to generate the following two graphs.
As you start to run R code, you’re likely to run into problems. Don’t worry — it happens to everyone. I write R code frequently but I still write code that doesn’t work!
Start by carefully comparing the code that you’re running to the code in the handout. Computer languages are extremely picky, and a misplaced character can make all the difference. For example, make sure that every ( is matched with a ) and every " is paired with another ".
One common problem when creating ggplot2 graphics is to put the + in the wrong place: it has to come at the end of the line, not the start. In other words, make sure you haven’t accidentally written code like this:
ggplot(data = gapminder07)
+ geom_point(mapping = aes(x = gdpPercap, y = lifeExp))
If you’re still stuck, try the help. You can get help about any R function by running ?function_name in the console. Don’t worry if the help doesn’t seem that helpful - instead skip down to the examples and look for code that matches what you’re trying to do.
If that doesn’t help, carefully read the error message. Sometimes the answer will be buried there! But when you’re new to R, the answer might be in the error message but you don’t yet know how to understand it. Another great tool is Google: try Googling the error message, as it’s likely someone else has had the same problem, and has gotten help online.