Data wrangling with dplyr

Authors
Affiliation

Jelmer Poelstra

Software Carpentry

Presented on

August 17, 2026

Page last modified

August 17, 2026



1 Introduction

1.1 What we’ll cover

Many of the most common data analysis tasks involve working with tabular data, i.e. data with rows and columns. In this session, you’ll learn to work with tabular data in R using the dplyr package.

For example, you’ll see how to subset, manipulate and summarize a dataset — the kinds of tasks commonly referred to as “data wrangling”. Such data processing is often essential before you can move on to data visualization (next session of this workshop!) or statistical analyses.

1.2 Setting up

Use a new script for this session, much like we did for the previous one:

  1. Open a new R script (Click the + symbol in the toolbar at the top, then click R Script)1.

  2. Save the script straight away as data-wrangling.R – you can save it anywhere you like, though it is probably best to save it in a folder specifically for this workshop.

  3. If you want the section headers as comments in your script, like in the script I am showing you in the live session, then copy-and-paste the following into your script:

    Section headers for your script (Click to expand)

    # 1 - Introduction -------------------------------------------------------------
    # 1.4 - Loading the tidyverse
    
    # 2 - The gapminder dataset ----------------------------------------------------
    
    # Challenge 1
    # The country column repeats the same country many times.
    # Scroll down in the View() tab: what makes each row unique?
    
    # 3 - arrange() ----------------------------------------------------------------
    
    # Challenge 2
    # Which line of code would you use to find the row with the highest
    # per-capita GDP?
    # A) arrange(.data = gapminder, desc(gdpPercap), year, country)
    # B) arrange(.data = gapminder, country, year, gdpPercap)
    # C) arrange(.data = gapminder, desc(gdpPercap))
    # D) arrange(.data = gapminder, gdpPercap)
    
    # 4 - filter() -----------------------------------------------------------------
    # 4.1 - Filter based on one condition
    
    # 4.2 - Filter based on multiple conditions
    
    # 5 - The pipe (|>) ------------------------------------------------------------
    
    # Challenge 3
    # The pipeline below is supposed to find the 3 countries with the lowest
    # GDP per capita in 1997, sorted from lowest to highest.
    # It has two errors. Find and fix them:
    gapminder
    filter(year = 1997) |>
      arrange(gdpPercap) |>
      head(n = 3)
    
    # Challenge 4
    # Using filter(), arrange(), and the pipe (|>), answer the following:
    # A) Which African country had the highest life expectancy in 2002?
    
    # B) What was the smallest population recorded in Asia across all years?
    
    # 6 - mutate() -----------------------------------------------------------------
    
    # Challenge 5
    # Use mutate() to create a new column gdp_billion that has the absolute GDP
    # (i.e., not relative to population size) in units of billions.
    gapminder |>
      mutate(______)
    
    # 7 - summarize() --------------------------------------------------------------
    
    # Challenge 6
    # A) Calculate the average life expectancy for each country and store the
    #    result in a new data frame.
    gapminder |>
      ______ |>
      ______
    
    # B) Use the data frame you just created to find out which country has the
    #    longest average life expectancy and which has the shortest.

1.3 The dplyr package and the tidyverse

One of R’s most powerful features is its ability to deal with tabular data. That is, data with rows and columns like those familiar from Excel spreadsheets and so on. R stores tabular data in a data structure called a “data frame”.

The dplyr package provides useful functions for manipulating data in data frames. In this session, we’ll cover the following commonly used dplyr functions:

  • arrange() to change the order of rows (i.e., to sort a data frame)
  • filter() to keep only a subset of rows
  • mutate() to manipulate columns and create new columns
  • summarize() to compute data summaries across rows (if we have time)

dplyr has many more functions, and we encourage you to explore these on your own: bonus material on this site / dplyr documentation.

dplyr, in turn, belongs to the “tidyverse”, a family of R packages for data science. Another key tidyverse package we’ll cover in today’s workshop is ggplot2 for making plots.

1.4 Loading the tidyverse

You should have the tidyverse already installed, but in every new R session, you need to load it before you can use it. Let’s do that now:

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

The output tells you which packages have been loaded as part of the tidyverse. (For now, you don’t have to pay attention to the “Conflicts” section.)

install.packages("tidyverse")
install.packages("gapminder")

2 The gapminder dataset

In this session and the next one, we will work with the gapminder dataset. This dataset contains statistics such as population size and life expectancy for 142 countries, at five-year intervals from 1952 to 2007.

It is available in a package of the same name. Like with the tidyverse, you should have it installed but do, as always, need to load it:

# (Unlike with the tidyverse, no output is expected when you load gapminder)
library(gapminder)

Take a look at the gapminder data frame that is now available to you:

gapminder
# A tibble: 1,704 × 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.
# ℹ 1,694 more rows

Above, the gapminder data frame is referred to as a “tibble”, which is the tidyverse’s version of a data frame, with a few small differences2.

Only the first 10 rows are printed, and the dataset has the following columns:

column name data type meaning
country factor (<fct>) Country
continent factor (<fct>) Continent that the country is in
year integer (<int>) Focal year for the stats in the next columns
lifeExp double (<dbl>) Mean life expectancy in years
pop integer (<int>) Population size
gdpPercap double (<dbl>) Per-capita GDP

Two columns are of type factor, an alternative to character commonly used for categorical data.

You can also use the View() function to look at the data frame. This will open a new tab in your editor pane with a spreadsheet-like look and feel:

View(gapminder)
# (Should display the dataset in an editor pane tab)

Challenge 1: Explore the gapminder dataset

The country column repeats the same country many times. Scroll down in the View() tab: what makes each row unique?

Each row is one country in one year. Afghanistan, for example, takes up 12 rows: one for each year from 1952 to 2007, in five-year steps.

3 arrange()

The arrange() function is like sorting functionality in Excel: it changes the order of rows based on the values in one or more columns. Entire rows are always moved together: it never reorders the values within a single column on its own, because that would scramble the data.

gapminder is currently first sorted alphabetically by country, and next by year. You may want to sort, for example, by population size instead, and can do so with arrange() as follows:

gm_arranged <- arrange(.data = gapminder, pop)

Before we take a look at the result, let’s break down the syntax of the arrange() function:

  • The first argument is the data frame to be sorted, which we specify with the .data argument.
  • The second argument is the column to sort by, which we specify without an argument name.

Like all dplyr functions, arrange() outputs a new data frame, and does not modify the input data frame. In this case, we chose to assign the output to a new object called gm_arranged.

What would have happened if we had assigned the output to gapminder? In that case, the original data frame would have been overwritten with the sorted version.

Finally, let’s look at the result of the sorting:

gm_arranged
# A tibble: 1,704 × 6
   country               continent  year lifeExp   pop gdpPercap
   <fct>                 <fct>     <int>   <dbl> <int>     <dbl>
 1 Sao Tome and Principe Africa     1952    46.5 60011      880.
 2 Sao Tome and Principe Africa     1957    48.9 61325      861.
 3 Djibouti              Africa     1952    34.8 63149     2670.
 4 Sao Tome and Principe Africa     1962    51.9 65345     1072.
 5 Sao Tome and Principe Africa     1967    54.4 70787     1385.
 6 Djibouti              Africa     1957    37.3 71851     2865.
 7 Sao Tome and Principe Africa     1972    56.5 76595     1533.
 8 Sao Tome and Principe Africa     1977    58.6 86796     1738.
 9 Djibouti              Africa     1962    39.7 89898     3021.
10 Sao Tome and Principe Africa     1982    60.4 98593     1890.
# ℹ 1,694 more rows

Sorting helps you find observations with the smallest or largest values for a certain column: above, we see that the dataset’s smallest population size is from Sao Tome and Principe in 1952.

Default sorting is from small to large, as seen above. To sort in reverse order, use the desc() (descending) helper function:

arrange(.data = gapminder, desc(pop))
# A tibble: 1,704 × 6
   country continent  year lifeExp        pop gdpPercap
   <fct>   <fct>     <int>   <dbl>      <int>     <dbl>
 1 China   Asia       2007    73.0 1318683096     4959.
 2 China   Asia       2002    72.0 1280400000     3119.
 3 China   Asia       1997    70.4 1230075000     2289.
 4 China   Asia       1992    68.7 1164970000     1656.
 5 India   Asia       2007    64.7 1110396331     2452.
 6 China   Asia       1987    67.3 1084035000     1379.
 7 India   Asia       2002    62.9 1034172547     1747.
 8 China   Asia       1982    65.5 1000281000      962.
 9 India   Asia       1997    61.8  959000000     1459.
10 China   Asia       1977    64.0  943455000      741.
# ℹ 1,694 more rows

Here, we didn’t assign the output to a new object, so it was just printed to screen. We’ll keep doing that for the remainder of the session, so we can more easily see the results of our operations.

Finally, you may want to sort by multiple columns, where ties in the first column are broken by a second column (and so on). Do this by simply listing the columns in the appropriate order:

# Sort first by continent, then by country:
arrange(.data = gapminder, continent, country)
# A tibble: 1,704 × 6
   country continent  year lifeExp      pop gdpPercap
   <fct>   <fct>     <int>   <dbl>    <int>     <dbl>
 1 Algeria Africa     1952    43.1  9279525     2449.
 2 Algeria Africa     1957    45.7 10270856     3014.
 3 Algeria Africa     1962    48.3 11000948     2551.
 4 Algeria Africa     1967    51.4 12760499     3247.
 5 Algeria Africa     1972    54.5 14760787     4183.
 6 Algeria Africa     1977    58.0 17152804     4910.
 7 Algeria Africa     1982    61.4 20033753     5745.
 8 Algeria Africa     1987    65.8 23254956     5681.
 9 Algeria Africa     1992    67.7 26298373     5023.
10 Algeria Africa     1997    69.2 29072015     4797.
# ℹ 1,694 more rows

Pay attention to the syntax: additional columns to sort by are additional arguments to the function, not part of the same argument with c(). Other dplyr functions work the same way.

Challenge 2: arrange()

One observation (row) in the gapminder dataset has the highest per-capita GDP. Which line of code would you use to find out which row that is?

Try it, and report which row (which country and year) you found.

The best answer is C. While both A and C give you the row with the highest per-capita GDP, A will also sort the entire dataset by year and country, which is not necessary to answer the question.

Most importantly, you want to sort by gdpPercap in descending order, so the row with the highest value ends up at the top:

arrange(.data = gapminder, desc(gdpPercap))
# A tibble: 1,704 × 6
   country   continent  year lifeExp     pop gdpPercap
   <fct>     <fct>     <int>   <dbl>   <int>     <dbl>
 1 Kuwait    Asia       1957    58.0  212846   113523.
 2 Kuwait    Asia       1972    67.7  841934   109348.
 3 Kuwait    Asia       1952    55.6  160000   108382.
 4 Kuwait    Asia       1962    60.5  358266    95458.
 5 Kuwait    Asia       1967    64.6  575003    80895.
 6 Kuwait    Asia       1977    69.3 1140357    59265.
 7 Norway    Europe     2007    80.2 4627926    49357.
 8 Kuwait    Asia       2007    77.6 2505559    47307.
 9 Singapore Asia       2007    80.0 4553009    47143.
10 Norway    Europe     2002    79.0 4535591    44684.
# ℹ 1,694 more rows

Kuwait in 1957 had the highest per-capita GDP in the dataset, at $113,523.

4 filter()

The filter() function outputs only those rows that satisfy one or more conditions. It is similar to filtering functionality in Excel.

4.1 Filter based on one condition

This first filter() example outputs only rows for which the life expectancy exceeds 80 years (remember, each row represents a country in a given year):

filter(.data = gapminder, lifeExp > 80)
# A tibble: 21 × 6
   country          continent  year lifeExp      pop gdpPercap
   <fct>            <fct>     <int>   <dbl>    <int>     <dbl>
 1 Australia        Oceania    2002    80.4 19546792    30688.
 2 Australia        Oceania    2007    81.2 20434176    34435.
 3 Canada           Americas   2007    80.7 33390141    36319.
 4 France           Europe     2007    80.7 61083916    30470.
 5 Hong Kong, China Asia       2002    81.5  6762476    30209.
 6 Hong Kong, China Asia       2007    82.2  6980412    39725.
 7 Iceland          Europe     2002    80.5   288030    31163.
 8 Iceland          Europe     2007    81.8   301931    36181.
 9 Israel           Asia       2007    80.7  6426679    25523.
10 Italy            Europe     2002    80.2 57926999    27968.
# ℹ 11 more rows
How many rows were output? (Click to see the answer)

21 rows were output. This is most easily seen in the first line of the output (21 x 6, i.e. 21 rows and 6 columns).

As the example above demonstrated, filter() outputs rows that satisfy the condition(s) you specify. These conditions don’t have to be based on numeric comparisons:

# Only keep rows where the value in the 'continent' column is 'Europe':
filter(.data = gapminder, continent == "Europe")
# A tibble: 360 × 6
   country continent  year lifeExp     pop gdpPercap
   <fct>   <fct>     <int>   <dbl>   <int>     <dbl>
 1 Albania Europe     1952    55.2 1282697     1601.
 2 Albania Europe     1957    59.3 1476505     1942.
 3 Albania Europe     1962    64.8 1728137     2313.
 4 Albania Europe     1967    66.2 1984060     2760.
 5 Albania Europe     1972    67.7 2263554     3313.
 6 Albania Europe     1977    68.9 2509048     3533.
 7 Albania Europe     1982    70.4 2780097     3631.
 8 Albania Europe     1987    72   3075321     3739.
 9 Albania Europe     1992    71.6 3326498     2497.
10 Albania Europe     1997    73.0 3428038     3193.
# ℹ 350 more rows

Remember to use two equals signs == to test for equality!

4.2 Filter based on multiple conditions

It’s also possible to filter based on multiple conditions. For example, you may want to see which countries in Asia had a life expectancy greater than 80 years:

filter(.data = gapminder, continent == "Asia", lifeExp > 80)
# A tibble: 6 × 6
  country          continent  year lifeExp       pop gdpPercap
  <fct>            <fct>     <int>   <dbl>     <int>     <dbl>
1 Hong Kong, China Asia       2002    81.5   6762476    30209.
2 Hong Kong, China Asia       2007    82.2   6980412    39725.
3 Israel           Asia       2007    80.7   6426679    25523.
4 Japan            Asia       1997    80.7 125956499    28817.
5 Japan            Asia       2002    82   127065841    28605.
6 Japan            Asia       2007    82.6 127467972    31656.

As in the example above, multiple conditions are by default combined using a Boolean AND. In other words, in a given row, each condition must be met to output the row.

If you want to combine conditions using a Boolean OR, where only one of the conditions needs to be met, use a | (vertical bar) between the conditions:

# Keep rows with a high life expectancy and/or a high per-capita GDP:
filter(.data = gapminder, lifeExp > 80 | gdpPercap > 100000)
# A tibble: 24 × 6
   country          continent  year lifeExp      pop gdpPercap
   <fct>            <fct>     <int>   <dbl>    <int>     <dbl>
 1 Australia        Oceania    2002    80.4 19546792    30688.
 2 Australia        Oceania    2007    81.2 20434176    34435.
 3 Canada           Americas   2007    80.7 33390141    36319.
 4 France           Europe     2007    80.7 61083916    30470.
 5 Hong Kong, China Asia       2002    81.5  6762476    30209.
 6 Hong Kong, China Asia       2007    82.2  6980412    39725.
 7 Iceland          Europe     2002    80.5   288030    31163.
 8 Iceland          Europe     2007    81.8   301931    36181.
 9 Israel           Asia       2007    80.7  6426679    25523.
10 Italy            Europe     2002    80.2 57926999    27968.
# ℹ 14 more rows

5 The pipe (|>)

The examples so far applied a single dplyr function to a data frame. But in practice, it’s common to use several consecutive dplyr functions to wrangle a data frame into the format you want.

For example, you may want to filter rows and then sort the result. You could do that as follows:

gm_filt <- filter(.data = gapminder, lifeExp < 50)

arrange(.data = gm_filt, desc(pop))
# A tibble: 491 × 6
   country   continent  year lifeExp       pop gdpPercap
   <fct>     <fct>     <int>   <dbl>     <int>     <dbl>
 1 China     Asia       1962    44.5 665770000      488.
 2 China     Asia       1952    44   556263527      400.
 3 India     Asia       1967    47.2 506000000      701.
 4 India     Asia       1962    43.6 454000000      658.
 5 India     Asia       1957    40.2 409000000      590.
 6 India     Asia       1952    37.4 372000000      547.
 7 Nigeria   Africa     2007    46.9 135031164     2014.
 8 Indonesia Asia       1972    49.2 121282000     1111.
 9 Nigeria   Africa     2002    46.6 119901274     1615.
10 Indonesia Asia       1967    46.0 109343000      762.
# ℹ 481 more rows

And you could go on like this, successively creating new objects you use for the next step. But there is a more elegant way of doing this!

You can directly send (“pipe”) output from one function into the next function with the pipe operator |>, which is a vertical bar | followed by a greater-than sign >.

Let’s start by seeing a rephrasing of the code above, now using pipes:

gapminder |>
  filter(lifeExp < 50) |>
  arrange(desc(pop))
# A tibble: 491 × 6
   country   continent  year lifeExp       pop gdpPercap
   <fct>     <fct>     <int>   <dbl>     <int>     <dbl>
 1 China     Asia       1962    44.5 665770000      488.
 2 China     Asia       1952    44   556263527      400.
 3 India     Asia       1967    47.2 506000000      701.
 4 India     Asia       1962    43.6 454000000      658.
 5 India     Asia       1957    40.2 409000000      590.
 6 India     Asia       1952    37.4 372000000      547.
 7 Nigeria   Africa     2007    46.9 135031164     2014.
 8 Indonesia Asia       1972    49.2 121282000     1111.
 9 Nigeria   Africa     2002    46.6 119901274     1615.
10 Indonesia Asia       1967    46.0 109343000      762.
# ℹ 481 more rows

What happened here? We took the gapminder data frame, sent (“piped”) it into the filter() function, whose output in turn was piped into the arrange() function. You can think of the pipe as “then”: take gapminder, then filter, then arrange.

When using the pipe, you no longer specify the input data frame with the .data argument, because the function now gets its input data via the pipe3.

TipPipe tips

Using pipes involves less typing and, above all, is more readable than using successive assignments4.

For code readability, it is good practice to always start a new line after a pipe |>, and to keep the subsequent line(s) indented as RStudio will automatically do.

Challenge 3: Find the mistakes

The below “pipeline” is supposed to find the 3 countries with the lowest GDP per capita in 1997, sorted from lowest to highest. It has two errors. Find and fix them:

The two errors are:

  • Line 1: Missing pipe |> after gapminder
  • Line 2: Should use == (not =) to test for equality

Corrected code:

gapminder |>
  filter(year == 1997) |>
  arrange(gdpPercap) |>
  head(n = 3)
# A tibble: 3 × 6
  country          continent  year lifeExp      pop gdpPercap
  <fct>            <fct>     <int>   <dbl>    <int>     <dbl>
1 Congo, Dem. Rep. Africa     1997    42.6 47798986      312.
2 Myanmar          Asia       1997    60.3 43247867      415 
3 Burundi          Africa     1997    45.3  6121610      463.

Challenge 4: Find the extremes

Using filter(), arrange(), and the pipe (|>), answer the following questions about the gapminder dataset:

  1. Which African country had the highest life expectancy in 2002?

We’ll use the same head() trick as in the exercise above to see only the top country, but this is not strictly necessary:

gapminder |>
  filter(continent == "Africa", year == 2002) |>
  arrange(desc(lifeExp)) |>
  head(n = 1)
# A tibble: 1 × 6
  country continent  year lifeExp    pop gdpPercap
  <fct>   <fct>     <int>   <dbl>  <int>     <dbl>
1 Reunion Africa     2002    75.7 743981     6316.
  1. What was the smallest population recorded in Asia across all years?
gapminder |>
  filter(continent == "Asia") |>
  arrange(pop) |>
  head(n = 1)
# A tibble: 1 × 6
  country continent  year lifeExp    pop gdpPercap
  <fct>   <fct>     <int>   <dbl>  <int>     <dbl>
1 Bahrain Asia       1952    50.9 120447     9867.

6 mutate()

So far, we’ve focused on functions that subset and reorganize data frames. But we haven’t yet seen how to change the data or compute derived data. This can be done with the mutate() function.

For example, to create a new column with population sizes in millions:

# Create a new column 'pop_million' by dividing 'pop' by a million:
gapminder |>
  mutate(pop_million = pop / 10^6)
# A tibble: 1,704 × 7
   country     continent  year lifeExp      pop gdpPercap pop_million
   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>       <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.        8.43
 2 Afghanistan Asia       1957    30.3  9240934      821.        9.24
 3 Afghanistan Asia       1962    32.0 10267083      853.       10.3 
 4 Afghanistan Asia       1967    34.0 11537966      836.       11.5 
 5 Afghanistan Asia       1972    36.1 13079460      740.       13.1 
 6 Afghanistan Asia       1977    38.4 14880372      786.       14.9 
 7 Afghanistan Asia       1982    39.9 12881816      978.       12.9 
 8 Afghanistan Asia       1987    40.8 13867957      852.       13.9 
 9 Afghanistan Asia       1992    41.7 16317921      649.       16.3 
10 Afghanistan Asia       1997    41.8 22227415      635.       22.2 
# ℹ 1,694 more rows

To modify an existing column rather than adding a new one, simply “assign back to the same name”:

# Change the unit of the 'pop' column:
gapminder |>
  mutate(pop = pop / 10^6)
# A tibble: 1,704 × 6
   country     continent  year lifeExp   pop gdpPercap
   <fct>       <fct>     <int>   <dbl> <dbl>     <dbl>
 1 Afghanistan Asia       1952    28.8  8.43      779.
 2 Afghanistan Asia       1957    30.3  9.24      821.
 3 Afghanistan Asia       1962    32.0 10.3       853.
 4 Afghanistan Asia       1967    34.0 11.5       836.
 5 Afghanistan Asia       1972    36.1 13.1       740.
 6 Afghanistan Asia       1977    38.4 14.9       786.
 7 Afghanistan Asia       1982    39.9 12.9       978.
 8 Afghanistan Asia       1987    40.8 13.9       852.
 9 Afghanistan Asia       1992    41.7 16.3       649.
10 Afghanistan Asia       1997    41.8 22.2       635.
# ℹ 1,694 more rows

Challenge 5: Absolute GDP

Use mutate() to create a new column gdp_billion that has the absolute GDP (i.e., not relative to population size) in units of billions.

gapminder |>
  mutate(gdp_billion = gdpPercap * ______ / ______)

You need to do two things, which can be combined into a single line:

  1. Make the GDP absolute by multiplying by the population size: gdpPercap * pop
  2. Change the unit of the absolute GDP to billions by dividing by a billion: / 10^9
gapminder |>
  mutate(gdp_billion = gdpPercap * pop / 10^9)
# A tibble: 1,704 × 7
   country     continent  year lifeExp      pop gdpPercap gdp_billion
   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>       <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.        6.57
 2 Afghanistan Asia       1957    30.3  9240934      821.        7.59
 3 Afghanistan Asia       1962    32.0 10267083      853.        8.76
 4 Afghanistan Asia       1967    34.0 11537966      836.        9.65
 5 Afghanistan Asia       1972    36.1 13079460      740.        9.68
 6 Afghanistan Asia       1977    38.4 14880372      786.       11.7 
 7 Afghanistan Asia       1982    39.9 12881816      978.       12.6 
 8 Afghanistan Asia       1987    40.8 13867957      852.       11.8 
 9 Afghanistan Asia       1992    41.7 16317921      649.       10.6 
10 Afghanistan Asia       1997    41.8 22227415      635.       14.1 
# ℹ 1,694 more rows

7 summarize() (If we have time)

The final dplyr function we’ll cover is summarize(), which computes summaries of your data across rows. For example, to calculate the mean GDP across the entire dataset:

# The syntax is similar to 'mutate': <new-column> = <operation>
gapminder |>
  summarize(mean_gdp = mean(gdpPercap))
# A tibble: 1 × 1
  mean_gdp
     <dbl>
1    7215.

The output is still a data frame, but unlike with the previous dplyr functions, its rows are not the rows of the input: summarize() “collapses” the data down, here to a single number.

summarize() becomes really powerful in combination with group_by(), which lets you compute groupwise stats. For example, to get the mean GDP separately for each continent:

gapminder |>
  group_by(continent) |>
  summarize(mean_gdp = mean(gdpPercap))
# A tibble: 5 × 2
  continent mean_gdp
  <fct>        <dbl>
1 Africa       2194.
2 Americas     7136.
3 Asia         7902.
4 Europe      14469.
5 Oceania     18622.

group_by() implicitly splits a data frame into groups of rows: here, one group for observations from each continent. After that, operations like in summarize() will happen separately for each group, which is how we ended up with per-continent means.

Challenge 6: Mean life expectancy

  1. Calculate the average life expectancy for each country and store the result in a new data frame.
lifeExp_bycountry <- gapminder |>
  group_by(______) |>
  summarize(mean_lifeExp = ______)

First, create a data frame with the mean life expectancy by country:

lifeExp_bycountry <- gapminder |>
  group_by(country) |>
  summarize(mean_lifeExp = mean(lifeExp))
  1. Use the data frame you just created to find out which country has the longest average life expectancy and which has the shortest.

Sort the data frame twice, once in each direction, to see the countries with the shortest and the longest life expectancy. You could optionally pipe into head() to only see the top n, here top 1:

lifeExp_bycountry |>
  arrange(mean_lifeExp) |>
  head(n = 1)
# A tibble: 1 × 2
  country      mean_lifeExp
  <fct>               <dbl>
1 Sierra Leone         36.8
lifeExp_bycountry |>
  arrange(desc(mean_lifeExp)) |>
  head(n = 1)
# A tibble: 1 × 2
  country mean_lifeExp
  <fct>          <dbl>
1 Iceland         76.5

So, Sierra Leone has the shortest average life expectancy (36.8 years), and Iceland has the longest average life expectancy (76.5 years).


Back to top

Footnotes

  1. Or click File => New file => R Script.↩︎

  2. The main difference is the nicer default printing behavior of tibbles: e.g. the data types of columns are shown, and only a limited number of rows are printed.↩︎

  3. Specifically, the input goes to the function’s first argument by default.↩︎

  4. Using pipes also avoids cluttering your environment with intermediate objects, which saves computer memory.↩︎