Foundations of
Data Science

Spring 2026 | Data 2 (399)
Jeffrey M. Girard | Lecture 04b

Roadmap

  • Transform data by row using arrange(), filter(), and distinct()

  • Transform data by column using mutate(), select(), rename(), and relocate()

Basic wrangling verbs

  • tidyverse provides tools for wrangling tibbles
    • These functions are named after verbs
    • So if you name your objects after nouns
    • …your code becomes easier to read
Noun(noun) ❌ Verb(noun) ✔️
blender(fruit) blend(fruit)
screwdriver(screw) drive(screw)
boxcutter(box) cut(box)

Important: Saving changes

  • dplyr functions do not modify the original
    • Running a command returns a copy of the result
    • It does not change the original object!
    • This is useful in teaching but less so in practice
  • To save changes, you must use assignment <-
    • Print only: arrange(data, var)
    • Save/update: data <- arrange(data, var)

Setup

library(tidyverse)
data("starwars", package = "dplyr")
glimpse(starwars)
Rows: 87
Columns: 14
$ name       <chr> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia Or…
$ height     <int> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180, 2…
$ mass       <dbl> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, 77.…
$ hair_color <chr> "blond", NA, NA, "none", "brown", "brown, grey", "brown", N…
$ skin_color <chr> "fair", "gold", "white, blue", "white", "light", "light", "…
$ eye_color  <chr> "blue", "yellow", "red", "yellow", "brown", "blue", "blue",…
$ birth_year <dbl> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.0, …
$ sex        <chr> "male", "none", "none", "male", "female", "male", "female",…
$ gender     <chr> "masculine", "masculine", "masculine", "masculine", "femini…
$ homeworld  <chr> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan", "T…
$ species    <chr> "Human", "Droid", "Droid", "Human", "Human", "Human", "Huma…
$ films      <list> <"A New Hope", "The Empire Strikes Back", "Return of the J…
$ vehicles   <list> <"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "Imp…
$ starships  <list> <"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1",…

Rows

Row-focused verbs

  • Arrange
    • Reorders the rows according to the values in specified columns
  • Filter
    • Retains only the rows that satisfy your specified conditions
  • Distinct
    • Removes duplicate rows to retain only unique combinations of values

Arrange

Arrange alphabetically from A to Z

# Sort the rows by the name variable (ascending)
arrange(starwars, name)
# A tibble: 87 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Ackbar       180    83 none       brown mot… orange          41   male  mascu…
2 Adi Gall…    184    50 none       dark       blue            NA   fema… femin…
3 Anakin S…    188    84 blond      fair       blue            41.9 male  mascu…
4 Arvel Cr…     NA    NA brown      fair       brown           NA   male  mascu…
5 Ayla Sec…    178    55 none       blue       hazel           48   fema… femin…
6 BB8           NA    NA none       none       black           NA   none  mascu…
7 Bail Pre…    191    NA black      tan        brown           67   male  mascu…
8 Barriss …    166    50 black      yellow     blue            40   fema… femin…
# ℹ 79 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Arrange alphabetically from Z to A

# Sort the rows by the name variable (descending)
arrange(starwars, desc(name))
# A tibble: 87 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Zam Wese…    168    55 blonde     fair, gre… yellow            NA fema… femin…
2 Yoda          66    17 white      green      brown            896 male  mascu…
3 Yarael P…    264    NA none       white      yellow            NA male  mascu…
4 Wilhuff …    180    NA auburn, g… fair       blue              64 male  mascu…
5 Wicket S…     88    20 brown      brown      brown              8 male  mascu…
6 Wedge An…    170    77 brown      fair       hazel             21 male  mascu…
7 Watto        137    NA black      blue, grey yellow            NA male  mascu…
8 Wat Tamb…    193    48 none       green, gr… unknown           NA male  mascu…
# ℹ 79 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Arrange numerically

# Sort the rows by height from shortest to tallest
arrange(starwars, height)
# A tibble: 87 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Yoda          66    17 white      green      brown            896 male  mascu…
2 Ratts Ty…     79    15 none       grey, blue unknown           NA male  mascu…
3 Wicket S…     88    20 brown      brown      brown              8 male  mascu…
4 Dud Bolt      94    45 none       blue, grey yellow            NA male  mascu…
5 R2-D2         96    32 <NA>       white, bl… red               33 none  mascu…
6 R4-P17        96    NA none       silver, r… red, blue         NA none  femin…
7 R5-D4         97    32 <NA>       white, red red               NA none  mascu…
8 Sebulba      112    40 none       grey, red  orange            NA male  mascu…
# ℹ 79 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Arrange by multiple variables

# Sort by multiple variables to break ties
arrange(starwars, hair_color, mass)
# A tibble: 87 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Mon Moth…    150  NA   auburn     fair       blue            48   fema… femin…
2 Wilhuff …    180  NA   auburn, g… fair       blue            64   male  mascu…
3 Obi-Wan …    182  77   auburn, w… fair       blue-gray       57   male  mascu…
4 Barriss …    166  50   black      yellow     blue            40   fema… femin…
5 Luminara…    170  56.2 black      yellow     blue            58   fema… femin…
6 Boba Fett    183  78.2 black      fair       brown           31.5 male  mascu…
7 Lando Ca…    177  79   black      dark       brown           31   male  mascu…
8 Jango Fe…    183  79   black      tan        brown           66   male  mascu…
# ℹ 79 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Filter

Filter by exact match

# Retain only rows where the species is exactly "Droid"
filter(starwars, species == "Droid")
# A tibble: 6 × 14
  name   height  mass hair_color skin_color  eye_color birth_year sex   gender  
  <chr>   <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr>   
1 C-3PO     167    75 <NA>       gold        yellow           112 none  masculi…
2 R2-D2      96    32 <NA>       white, blue red               33 none  masculi…
3 R5-D4      97    32 <NA>       white, red  red               NA none  masculi…
4 IG-88     200   140 none       metal       red               15 none  masculi…
5 R4-P17     96    NA none       silver, red red, blue         NA none  feminine
6 BB8        NA    NA none       none        black             NA none  masculi…
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Filter by numeric condition

# Retain only characters with a body mass greater than or equal to 100
filter(starwars, mass >= 100)
# A tibble: 10 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Darth Va…    202   136 none       white      yellow          41.9 male  mascu…
2 Owen Lars    178   120 brown, gr… light      blue            52   male  mascu…
3 Chewbacca    228   112 brown      unknown    blue           200   male  mascu…
4 Jabba De…    175  1358 <NA>       green-tan… orange         600   herm… mascu…
5 Jek Tono…    180   110 brown      fair       blue            NA   <NA>  <NA>  
6 IG-88        200   140 none       metal      red             15   none  mascu…
7 Bossk        190   113 none       green      red             53   male  mascu…
8 Dexter J…    198   102 none       brown      yellow          NA   male  mascu…
# ℹ 2 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Filter by multiple conditions (AND)

# Retain "Droid" characters AND those with mass < 50
filter(starwars, species == "Droid", mass < 50)
# A tibble: 2 × 14
  name  height  mass hair_color skin_color  eye_color birth_year sex   gender   
  <chr>  <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr>    
1 R2-D2     96    32 <NA>       white, blue red               33 none  masculine
2 R5-D4     97    32 <NA>       white, red  red               NA none  masculine
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Filter by multiple conditions (OR)

# Retain characters that have sex equal to male OR none
filter(starwars, sex == "male" | sex == "none")
# A tibble: 66 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Luke Sky…    172    77 blond      fair       blue            19   male  mascu…
2 C-3PO        167    75 <NA>       gold       yellow         112   none  mascu…
3 R2-D2         96    32 <NA>       white, bl… red             33   none  mascu…
4 Darth Va…    202   136 none       white      yellow          41.9 male  mascu…
5 Owen Lars    178   120 brown, gr… light      blue            52   male  mascu…
6 R5-D4         97    32 <NA>       white, red red             NA   none  mascu…
7 Biggs Da…    183    84 black      light      brown           24   male  mascu…
8 Obi-Wan …    182    77 auburn, w… fair       blue-gray       57   male  mascu…
# ℹ 58 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Distinct

Find distinct values

# Find the unique homeworlds listed in the dataset
distinct(starwars, homeworld)
# A tibble: 49 × 1
  homeworld
  <chr>    
1 Tatooine 
2 Naboo    
3 Alderaan 
4 Stewjon  
5 Eriadu   
6 Kashyyyk 
7 Corellia 
8 Rodia    
# ℹ 41 more rows

Find distinct combinations

# Find unique combinations of species and sex
distinct(starwars, species, sex)
# A tibble: 41 × 2
  species        sex           
  <chr>          <chr>         
1 Human          male          
2 Droid          none          
3 Human          female        
4 Wookiee        male          
5 Rodian         male          
6 Hutt           hermaphroditic
7 <NA>           <NA>          
8 Yoda's species male          
# ℹ 33 more rows

Columns

Column-focused verbs

  • Select
    • Retains specific columns and drops the rest
  • Mutate
    • Creates new columns or modifies existing ones
  • Rename
    • Changes the names of specific columns
  • Relocate
    • Changes the positions of specific columns

Select

Select specific columns

# Retain only the name, height, and mass columns
select(starwars, name, height, mass)
# A tibble: 87 × 3
  name               height  mass
  <chr>               <int> <dbl>
1 Luke Skywalker        172    77
2 C-3PO                 167    75
3 R2-D2                  96    32
4 Darth Vader           202   136
5 Leia Organa           150    49
6 Owen Lars             178   120
7 Beru Whitesun Lars    165    75
8 R5-D4                  97    32
# ℹ 79 more rows

Drop specific columns

# Retain all columns EXCEPT films, vehicles, and starships
select(starwars, -films, -vehicles, -starships)
# A tibble: 87 × 11
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Luke Sky…    172    77 blond      fair       blue            19   male  mascu…
2 C-3PO        167    75 <NA>       gold       yellow         112   none  mascu…
3 R2-D2         96    32 <NA>       white, bl… red             33   none  mascu…
4 Darth Va…    202   136 none       white      yellow          41.9 male  mascu…
5 Leia Org…    150    49 brown      light      brown           19   fema… femin…
6 Owen Lars    178   120 brown, gr… light      blue            52   male  mascu…
7 Beru Whi…    165    75 brown      light      blue            47   fema… femin…
8 R5-D4         97    32 <NA>       white, red red             NA   none  mascu…
# ℹ 79 more rows
# ℹ 2 more variables: homeworld <chr>, species <chr>

Select all between two columns

# Retain all columns between height and gender
select(starwars, height:gender)
# A tibble: 87 × 8
  height  mass hair_color  skin_color  eye_color birth_year sex    gender   
   <int> <dbl> <chr>       <chr>       <chr>          <dbl> <chr>  <chr>    
1    172    77 blond       fair        blue            19   male   masculine
2    167    75 <NA>        gold        yellow         112   none   masculine
3     96    32 <NA>        white, blue red             33   none   masculine
4    202   136 none        white       yellow          41.9 male   masculine
5    150    49 brown       light       brown           19   female feminine 
6    178   120 brown, grey light       blue            52   male   masculine
7    165    75 brown       light       blue            47   female feminine 
8     97    32 <NA>        white, red  red             NA   none   masculine
# ℹ 79 more rows

Select using helpers

# Retain only columns that end with the phrase "color"
select(starwars, ends_with("color"))
# A tibble: 87 × 3
  hair_color  skin_color  eye_color
  <chr>       <chr>       <chr>    
1 blond       fair        blue     
2 <NA>        gold        yellow   
3 <NA>        white, blue red      
4 none        white       yellow   
5 brown       light       brown    
6 brown, grey light       blue     
7 brown       light       blue     
8 <NA>        white, red  red      
# ℹ 79 more rows

See also: starts_with(), contains(), matches(), and where()

Mutate

Mutate new columns

# Create a new column and add all the way to the right
mutate(starwars, height_m = height / 100)
# A tibble: 87 × 15
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Luke Sky…    172    77 blond      fair       blue            19   male  mascu…
2 C-3PO        167    75 <NA>       gold       yellow         112   none  mascu…
3 R2-D2         96    32 <NA>       white, bl… red             33   none  mascu…
4 Darth Va…    202   136 none       white      yellow          41.9 male  mascu…
5 Leia Org…    150    49 brown      light      brown           19   fema… femin…
6 Owen Lars    178   120 brown, gr… light      blue            52   male  mascu…
7 Beru Whi…    165    75 brown      light      blue            47   fema… femin…
8 R5-D4         97    32 <NA>       white, red red             NA   none  mascu…
# ℹ 79 more rows
# ℹ 6 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>, height_m <dbl>

Mutate existing columns

# Modify the height variable "in place"
mutate(starwars, height = height * 10)
# A tibble: 87 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <dbl> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Luke Sky…   1720    77 blond      fair       blue            19   male  mascu…
2 C-3PO       1670    75 <NA>       gold       yellow         112   none  mascu…
3 R2-D2        960    32 <NA>       white, bl… red             33   none  mascu…
4 Darth Va…   2020   136 none       white      yellow          41.9 male  mascu…
5 Leia Org…   1500    49 brown      light      brown           19   fema… femin…
6 Owen Lars   1780   120 brown, gr… light      blue            52   male  mascu…
7 Beru Whi…   1650    75 brown      light      blue            47   fema… femin…
8 R5-D4        970    32 <NA>       white, red red             NA   none  mascu…
# ℹ 79 more rows
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Rename and Relocate

Rename columns

# Change the name of 'homeworld' to 'planet' (new = old)
rename(starwars, planet = homeworld)
# A tibble: 87 × 14
  name      height  mass hair_color skin_color eye_color birth_year sex   gender
  <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
1 Luke Sky…    172    77 blond      fair       blue            19   male  mascu…
2 C-3PO        167    75 <NA>       gold       yellow         112   none  mascu…
3 R2-D2         96    32 <NA>       white, bl… red             33   none  mascu…
4 Darth Va…    202   136 none       white      yellow          41.9 male  mascu…
5 Leia Org…    150    49 brown      light      brown           19   fema… femin…
6 Owen Lars    178   120 brown, gr… light      blue            52   male  mascu…
7 Beru Whi…    165    75 brown      light      blue            47   fema… femin…
8 R5-D4         97    32 <NA>       white, red red             NA   none  mascu…
# ℹ 79 more rows
# ℹ 5 more variables: planet <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>

Relocate columns

# Move the 'species' column to the very first position
relocate(starwars, species, .before = name)
# A tibble: 87 × 14
  species name     height  mass hair_color skin_color eye_color birth_year sex  
  <chr>   <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr>
1 Human   Luke Sk…    172    77 blond      fair       blue            19   male 
2 Droid   C-3PO       167    75 <NA>       gold       yellow         112   none 
3 Droid   R2-D2        96    32 <NA>       white, bl… red             33   none 
4 Human   Darth V…    202   136 none       white      yellow          41.9 male 
5 Human   Leia Or…    150    49 brown      light      brown           19   fema…
6 Human   Owen La…    178   120 brown, gr… light      blue            52   male 
7 Human   Beru Wh…    165    75 brown      light      blue            47   fema…
8 Droid   R5-D4        97    32 <NA>       white, red red             NA   none 
# ℹ 79 more rows
# ℹ 5 more variables: gender <chr>, homeworld <chr>, films <list>,
#   vehicles <list>, starships <list>

Can also use .after and position numbers, e.g., .after = 1