Chapter 3: Row & Column Operations

Published

September 18, 2026

Working with data inevitably leads to the need to filter out subsets, to sort data based on some rule and to find the unique observations. We often need to create new variables as modifications of existing ones. Some variables are often more important than others, thus we need to select them. Finally, renaming and maving variables to within view often makes working with data easier. The tools you learn here make all this possible.

They are all contained in dplyr, which is included in the tidyverse, so as usual:

Data Transformations
Row Functions Column Functions
filter mutate
arrange select
distinct rename
relocate
select

Row Functions

These functions change which rows are present in the data and keep the columns the same.

Filter observations based on conditions

Subset oberservations (rows) based on a logical condition.

Textbook link: filter

Arrange rows by value in ascending order

Textbook link: arrange

This permutes the ordering of the rows. (Note the r in arrange, r for rows.) If you provide more than one column name, each additional column will be used to break ties in the values of preceding columns. To arrange in descending order use desc().

# arrange by bill length in ascending order
penguins |> arrange(bill_len)

# arrange by bill length in descending order
penguins |> arrange(-bill_len)

# arrange by two variables
penguins |> arrange(bill_len, bill_dep)

# 
penguins |> arrange(bill_len/bill_dep)

Pipe to head() if you only want to see the first few results:

# arrange by two variables
penguins |> arrange(bill_len, bill_dep) |> head()
  species    island bill_len bill_dep flipper_len body_mass    sex year
1  Adelie     Dream     32.1     15.5         188      3050 female 2009
2  Adelie     Dream     33.1     16.1         178      2900 female 2008
3  Adelie Torgersen     33.5     19.0         190      3600 female 2008
4  Adelie     Dream     34.0     17.1         185      3400 female 2008
5  Adelie Torgersen     34.1     18.1         193      3475   <NA> 2007
6  Adelie Torgersen     34.4     18.4         184      3325 female 2007
# arrange by a mathematical combination of variables
penguins |> arrange(bill_len/bill_dep) |> head()
  species    island bill_len bill_dep flipper_len body_mass    sex year
1  Adelie Torgersen     34.6     21.1         198      4400   male 2007
2  Adelie Torgersen     33.5     19.0         190      3600 female 2008
3  Adelie Torgersen     35.1     19.4         193      4200   male 2008
4  Adelie Torgersen     37.3     20.5         199      3775   male 2009
5  Adelie Torgersen     38.6     21.2         191      3800   male 2007
6  Adelie     Dream     39.2     21.1         196      4150   male 2007

Keep distinct rows

Use distinct on several column names to find unique combinations. The .keep_all = TRUE argument is used to retain all columsns

# this keeps all unique (origin, dest) pairs
pak::pkg_install("nycflights13")
library(nycflights13)
flights |> distinct(origin,dest) 
# A tibble: 224 × 2
   origin dest 
   <chr>  <chr>
 1 EWR    IAH  
 2 LGA    IAH  
 3 JFK    MIA  
 4 JFK    BQN  
 5 LGA    ATL  
 6 EWR    ORD  
 7 EWR    FLL  
 8 LGA    IAD  
 9 JFK    MCO  
10 LGA    ORD  
# ℹ 214 more rows
# combine with other dplyr functions  
flights |> distinct(origin,dest)  |> arrange(origin)
# A tibble: 224 × 2
   origin dest 
   <chr>  <chr>
 1 EWR    IAH  
 2 EWR    ORD  
 3 EWR    FLL  
 4 EWR    SFO  
 5 EWR    LAS  
 6 EWR    PBI  
 7 EWR    MIA  
 8 EWR    ATL  
 9 EWR    PHX  
10 EWR    MSP  
# ℹ 214 more rows

Column functions

Create new variables

Textbook link: mutate

Add new variables (columns), usually via a formula involving existing ones. Use these helper functions to relocate or newly created variables.

  1. .before = 1
  2. .after = some_var_name
library(palmerpenguins)
penguins |> mutate(bill_ratio = bill_length_mm / bill_depth_mm, .after = island)
# A tibble: 344 × 9
   species island    bill_ratio bill_length_mm bill_depth_mm flipper_length_mm
   <fct>   <fct>          <dbl>          <dbl>         <dbl>             <int>
 1 Adelie  Torgersen       2.09           39.1          18.7               181
 2 Adelie  Torgersen       2.27           39.5          17.4               186
 3 Adelie  Torgersen       2.24           40.3          18                 195
 4 Adelie  Torgersen      NA              NA            NA                  NA
 5 Adelie  Torgersen       1.90           36.7          19.3               193
 6 Adelie  Torgersen       1.91           39.3          20.6               190
 7 Adelie  Torgersen       2.19           38.9          17.8               181
 8 Adelie  Torgersen       2              39.2          19.6               195
 9 Adelie  Torgersen       1.88           34.1          18.1               193
10 Adelie  Torgersen       2.08           42            20.2               190
# ℹ 334 more rows
# ℹ 3 more variables: body_mass_g <int>, sex <fct>, year <int>

Select

The function select() is used to choose which columns you want to keep. You can use the helper functions starts_with(), ends_with(), contains(), or num_range() to select columns based on their names. You can also use where() to select columns based on their type (numeric, factor, character).

This is useful if you have too many columns and you with choose which columns you wish to view.

Textbook link: select

Select tips & helper functions

  1. use : to select a range
penguins |> select(species:bill_depth_mm) |> head()
# A tibble: 6 × 4
  species island    bill_length_mm bill_depth_mm
  <fct>   <fct>              <dbl>         <dbl>
1 Adelie  Torgersen           39.1          18.7
2 Adelie  Torgersen           39.5          17.4
3 Adelie  Torgersen           40.3          18  
4 Adelie  Torgersen           NA            NA  
5 Adelie  Torgersen           36.7          19.3
6 Adelie  Torgersen           39.3          20.6
penguins |> select(1:3) |> head()
# A tibble: 6 × 3
  species island    bill_length_mm
  <fct>   <fct>              <dbl>
1 Adelie  Torgersen           39.1
2 Adelie  Torgersen           39.5
3 Adelie  Torgersen           40.3
4 Adelie  Torgersen           NA  
5 Adelie  Torgersen           36.7
6 Adelie  Torgersen           39.3
  1. use ! to exclude
penguins |> select(-c(species, island)) |> head()
# A tibble: 6 × 6
  bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex     year
           <dbl>         <dbl>             <int>       <int> <fct>  <int>
1           39.1          18.7               181        3750 male    2007
2           39.5          17.4               186        3800 female  2007
3           40.3          18                 195        3250 female  2007
4           NA            NA                  NA          NA <NA>    2007
5           36.7          19.3               193        3450 female  2007
6           39.3          20.6               190        3650 male    2007
  1. use where with is.factor, or is.numeric, or is.character

Example

# Select all numeric columns
penguins |> select(where(is.numeric)) |> head()
# A tibble: 6 × 5
  bill_length_mm bill_depth_mm flipper_length_mm body_mass_g  year
           <dbl>         <dbl>             <int>       <int> <int>
1           39.1          18.7               181        3750  2007
2           39.5          17.4               186        3800  2007
3           40.3          18                 195        3250  2007
4           NA            NA                  NA          NA  2007
5           36.7          19.3               193        3450  2007
6           39.3          20.6               190        3650  2007
# general rule of thumb: use `where()` inside `select()`, `mutate(across(...))` or `pick()`
# and apply where() to is.numeric, is.factor, or is.character 
  1. starts_with("abc"): matches names that begin with “abc”.

Example

# Select columns that start with "bill"
penguins |> select(starts_with("bill")) |> head()
# A tibble: 6 × 2
  bill_length_mm bill_depth_mm
           <dbl>         <dbl>
1           39.1          18.7
2           39.5          17.4
3           40.3          18  
4           NA            NA  
5           36.7          19.3
6           39.3          20.6
# general rule of thumb: use `starts_with()`, `ends_with()`, `contains()`, or `num_range()` to select columns based on their names
# you do not combine these with `where()`, they are used directly inside `select()`
  1. ends_with("xyz"): matches names that end with “xyz”.
  1. contains("ijk"): matches names that contain “ijk”.
  2. num_range("x", 1:3): matches x1, x2 and x3.

Example

# select a range of columns
df <- penguins |> mutate(
  b1 = round(bill_length_mm),
  b2 = bill_depth_mm^2,
  b3 = bill_length_mm + bill_depth_mm,
  .before = 1)

df |> select(num_range("b", c(1,3))) |> head()
# A tibble: 6 × 2
     b1    b3
  <dbl> <dbl>
1    39  57.8
2    40  56.9
3    40  58.3
4    NA  NA  
5    37  56  
6    39  59.9
  1. To drop colummns: select(data,-c(this_col,that_col))

Example

penguins |> select(-c(species, island)) |> head()
# A tibble: 6 × 6
  bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex     year
           <dbl>         <dbl>             <int>       <int> <fct>  <int>
1           39.1          18.7               181        3750 male    2007
2           39.5          17.4               186        3800 female  2007
3           40.3          18                 195        3250 female  2007
4           NA            NA                  NA          NA <NA>    2007
5           36.7          19.3               193        3450 female  2007
6           39.3          20.6               190        3650 male    2007
  1. use regular expressions: select(matches("^b.*mm"))

Note: ^b.*mm is a regular expression that matches a b followed by anything . any number times * followed by mm. See regex101.com

How where() works

  • You pass a function (like is.numeric, is.factor, or is.character) into where().
  • It evaluates that function on every column in your data frame.
  • It keeps only the columns where the function returns TRUE

Common Use Cases

You typically use where() inside select(), mutate(across(...)) or pick()

Select all numeric columns

df %>% select(where(is.numeric))
# A tibble: 344 × 8
      b1    b2    b3 bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <dbl> <dbl> <dbl>          <dbl>         <dbl>             <int>       <int>
 1    39  350.  57.8           39.1          18.7               181        3750
 2    40  303.  56.9           39.5          17.4               186        3800
 3    40  324   58.3           40.3          18                 195        3250
 4    NA   NA   NA             NA            NA                  NA          NA
 5    37  372.  56             36.7          19.3               193        3450
 6    39  424.  59.9           39.3          20.6               190        3650
 7    39  317.  56.7           38.9          17.8               181        3625
 8    39  384.  58.8           39.2          19.6               195        4675
 9    34  328.  52.2           34.1          18.1               193        3475
10    42  408.  62.2           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 1 more variable: year <int>

Apply a transformation to all factor columns

df %>% mutate(across(where(is.factor), as.character))
# A tibble: 344 × 11
      b1    b2    b3 species island    bill_length_mm bill_depth_mm
   <dbl> <dbl> <dbl> <chr>   <chr>              <dbl>         <dbl>
 1    39  350.  57.8 Adelie  Torgersen           39.1          18.7
 2    40  303.  56.9 Adelie  Torgersen           39.5          17.4
 3    40  324   58.3 Adelie  Torgersen           40.3          18  
 4    NA   NA   NA   Adelie  Torgersen           NA            NA  
 5    37  372.  56   Adelie  Torgersen           36.7          19.3
 6    39  424.  59.9 Adelie  Torgersen           39.3          20.6
 7    39  317.  56.7 Adelie  Torgersen           38.9          17.8
 8    39  384.  58.8 Adelie  Torgersen           39.2          19.6
 9    34  328.  52.2 Adelie  Torgersen           34.1          18.1
10    42  408.  62.2 Adelie  Torgersen           42            20.2
# ℹ 334 more rows
# ℹ 4 more variables: flipper_length_mm <int>, body_mass_g <int>, sex <chr>,
#   year <int>

Use custom conditions with formula shortcuts

df %>% select(where(~ is.numeric(.x) & mean(.x, na.rm = TRUE) > 10))
# A tibble: 344 × 8
      b1    b2    b3 bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <dbl> <dbl> <dbl>          <dbl>         <dbl>             <int>       <int>
 1    39  350.  57.8           39.1          18.7               181        3750
 2    40  303.  56.9           39.5          17.4               186        3800
 3    40  324   58.3           40.3          18                 195        3250
 4    NA   NA   NA             NA            NA                  NA          NA
 5    37  372.  56             36.7          19.3               193        3450
 6    39  424.  59.9           39.3          20.6               190        3650
 7    39  317.  56.7           38.9          17.8               181        3625
 8    39  384.  58.8           39.2          19.6               195        4675
 9    34  328.  52.2           34.1          18.1               193        3475
10    42  408.  62.2           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 1 more variable: year <int>

Make sure you know how to:

Use select() and these helper functions to create various subsets of penguins data.

rename

Explicitly rename variables. Do so in bulk with janitor::clean_names

penguins |> rename(bill_length = bill_length_mm, bill_depth = bill_depth_mm)
# A tibble: 344 × 8
   species island    bill_length bill_depth flipper_length_mm body_mass_g sex   
   <fct>   <fct>           <dbl>      <dbl>             <int>       <int> <fct> 
 1 Adelie  Torgersen        39.1       18.7               181        3750 male  
 2 Adelie  Torgersen        39.5       17.4               186        3800 female
 3 Adelie  Torgersen        40.3       18                 195        3250 female
 4 Adelie  Torgersen        NA         NA                  NA          NA <NA>  
 5 Adelie  Torgersen        36.7       19.3               193        3450 female
 6 Adelie  Torgersen        39.3       20.6               190        3650 male  
 7 Adelie  Torgersen        38.9       17.8               181        3625 female
 8 Adelie  Torgersen        39.2       19.6               195        4675 male  
 9 Adelie  Torgersen        34.1       18.1               193        3475 <NA>  
10 Adelie  Torgersen        42         20.2               190        4250 <NA>  
# ℹ 334 more rows
# ℹ 1 more variable: year <int>

relocate

Permute the ordering of columns (notice the c in relocate, c for columns)

You can also achieve this with select()

relocate(penguins, sex, year)
# versus
select(penguins, sex,year, everything())

Assignment-4