4.1 dplyr: Rows

DATA 412/612 · Week 4 · PDF · Source (.qmd)

filter(), slice(), and arrange() choose and order rows. Use nycflights13::flights.

1. Why dplyr

Real tables are messy: missing values, extra rows, names that do not match, numbers stored as text. dplyr is the tidyverse grammar for one table. The verbs have the same first argument, a data frame, so they pipe.

library(tidyverse)
library(nycflights13)
data("flights")

Install nycflights13 once in the console. flights is every flight out of New York City in 2013. ?flights.

A data frame is a list of atomic vectors of the same length: columns can differ in type; rows are the observational units. A tibble is the tidyverse data frame. We use the two words as synonyms.

The usual verbs, by what they touch:

  • rows: filter(), slice(), arrange()
  • columns: select(), rename(), mutate(), relocate()
  • groups: group_by(), summarize()

A first pipeline. Average scheduled departure time, by carrier, in the second half of the year:

flights |>
  filter(month >= 7) |>
  group_by(carrier) |>
  summarize(mean_dep = mean(dep_time, na.rm = TRUE))
## # A tibble: 16 × 2
##    carrier mean_dep
##    <chr>      <dbl>
##  1 9E         1467.
##  2 AA         1297.
##  3 AS         1304.
##  4 B6         1380.
##  5 DL         1350.
##  6 EV         1363.
##  7 F9         1466.
##  8 FL         1405.
##  9 HA          958.
## 10 MQ         1391.
## 11 OO         1744.
## 12 UA         1326.
## 13 US         1230.
## 14 VX         1273.
## 15 WN         1279.
## 16 YV         1557.

The Base R version is flights[flights$month >= 7, ] then aggregate(). The pipe is easier to read and to debug: comment out a line, or insert View().


2. filter() keeps rows by value

filter() keeps rows where a condition is TRUE. Note 2.1 already compared it to subset().

flights |>
  filter(month == 1)
## # A tibble: 27,004 × 19
##     year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##    <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
##  1  2013     1     1      517            515         2      830            819
##  2  2013     1     1      533            529         4      850            830
##  3  2013     1     1      542            540         2      923            850
##  4  2013     1     1      544            545        -1     1004           1022
##  5  2013     1     1      554            600        -6      812            837
##  6  2013     1     1      554            558        -4      740            728
##  7  2013     1     1      555            600        -5      913            854
##  8  2013     1     1      557            600        -3      709            723
##  9  2013     1     1      557            600        -3      838            846
## 10  2013     1     1      558            600        -2      753            745
## # ℹ 26,994 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

Several conditions separated by commas are and. Write & and | when the logic is mixed. xor() is exclusive or.

flights |>
  filter(month == 1, origin == "JFK")
## # A tibble: 9,161 × 19
##     year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##    <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
##  1  2013     1     1      542            540         2      923            850
##  2  2013     1     1      544            545        -1     1004           1022
##  3  2013     1     1      557            600        -3      838            846
##  4  2013     1     1      558            600        -2      849            851
##  5  2013     1     1      558            600        -2      853            856
##  6  2013     1     1      558            600        -2      924            917
##  7  2013     1     1      559            559         0      702            706
##  8  2013     1     1      606            610        -4      837            845
##  9  2013     1     1      611            600        11      945            931
## 10  2013     1     1      613            610         3      925            921
## # ℹ 9,151 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>
flights |>
  filter((month == 1 & origin == "LGA") | (month == 12 & origin == "JFK"))
## # A tibble: 17,096 × 19
##     year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##    <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
##  1  2013     1     1      533            529         4      850            830
##  2  2013     1     1      554            600        -6      812            837
##  3  2013     1     1      557            600        -3      709            723
##  4  2013     1     1      558            600        -2      753            745
##  5  2013     1     1      559            600        -1      941            910
##  6  2013     1     1      600            600         0      851            858
##  7  2013     1     1      600            600         0      837            825
##  8  2013     1     1      602            610        -8      812            820
##  9  2013     1     1      602            605        -3      821            805
## 10  2013     1     1      623            610        13      920            915
## # ℹ 17,086 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

Unknown levels: unique(flights$origin), or levels(as.factor(flights$origin)).

filter() drops rows where the condition is NA. Ask for them with is.na(). NA == NA is NA, not TRUE.

dfdat <- data.frame(x = c(1, NA, 2), y = c(2, 4, 1))
dfdat |> filter(x == 1)
##   x y
## 1 1 2
dfdat |> filter(x == 1 | is.na(x))
##    x y
## 1  1 2
## 2 NA 4

For doubles, == is strict. Use dplyr::near():

sqrt(2)^2 == 2
## [1] FALSE
near(sqrt(2)^2, 2)
## [1] TRUE

Integers can use ==.


3. slice() keeps rows by position

flights |> slice(5:10)
## # A tibble: 6 × 19
##    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
## 1  2013     1     1      554            600        -6      812            837
## 2  2013     1     1      554            558        -4      740            728
## 3  2013     1     1      555            600        -5      913            854
## 4  2013     1     1      557            600        -3      709            723
## 5  2013     1     1      557            600        -3      838            846
## 6  2013     1     1      558            600        -2      753            745
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>
flights |> slice(c(1, 4, 6))
## # A tibble: 3 × 19
##    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
## 1  2013     1     1      517            515         2      830            819
## 2  2013     1     1      544            545        -1     1004           1022
## 3  2013     1     1      554            558        -4      740            728
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>
flights |> slice(10:n())     # n() is the number of rows
## # A tibble: 336,767 × 19
##     year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##    <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
##  1  2013     1     1      558            600        -2      753            745
##  2  2013     1     1      558            600        -2      849            851
##  3  2013     1     1      558            600        -2      853            856
##  4  2013     1     1      558            600        -2      924            917
##  5  2013     1     1      558            600        -2      923            937
##  6  2013     1     1      559            600        -1      941            910
##  7  2013     1     1      559            559         0      702            706
##  8  2013     1     1      559            600        -1      854            902
##  9  2013     1     1      600            600         0      851            858
## 10  2013     1     1      600            600         0      837            825
## # ℹ 336,757 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

Helpers:

  • slice_head(n = 3), slice_tail(n = 3)
  • slice_sample(n = 200) or slice_sample(prop = 0.1)
  • slice_min(arr_delay, n = 5), slice_max(arr_delay, n = 5)

Drop NAs in the ordering column first. Ties can return more rows than n. with_ties = FALSE cuts at n using the current row order.

flights |>
  filter(!is.na(arr_delay)) |>
  slice_max(arr_delay, n = 5)
## # A tibble: 5 × 19
##    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
## 1  2013     1     9      641            900      1301     1242           1530
## 2  2013     6    15     1432           1935      1137     1607           2120
## 3  2013     1    10     1121           1635      1126     1239           1810
## 4  2013     9    20     1139           1845      1014     1457           2210
## 5  2013     7    22      845           1600      1005     1044           1815
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

set.seed() before slice_sample() if you want a repeatable draw. Two samples of the “same” size (prop = 0.01 vs n = 3367) are still two draws.


4. arrange() sorts rows

Default is ascending. desc(x) or -x (numerics) reverses. Extra arguments break ties. Missing values go last, even under desc().

flights |> arrange(dep_delay)
## # A tibble: 336,776 × 19
##     year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##    <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
##  1  2013    12     7     2040           2123       -43       40           2352
##  2  2013     2     3     2022           2055       -33     2240           2338
##  3  2013    11    10     1408           1440       -32     1549           1559
##  4  2013     1    11     1900           1930       -30     2233           2243
##  5  2013     1    29     1703           1730       -27     1947           1957
##  6  2013     8     9      729            755       -26     1002            955
##  7  2013    10    23     1907           1932       -25     2143           2143
##  8  2013     3    30     2030           2055       -25     2213           2250
##  9  2013     3     2     1431           1455       -24     1601           1631
## 10  2013     5     5      934            958       -24     1225           1309
## # ℹ 336,766 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>
flights |> arrange(desc(dep_delay), arr_delay)
## # A tibble: 336,776 × 19
##     year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
##    <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
##  1  2013     1     9      641            900      1301     1242           1530
##  2  2013     6    15     1432           1935      1137     1607           2120
##  3  2013     1    10     1121           1635      1126     1239           1810
##  4  2013     9    20     1139           1845      1014     1457           2210
##  5  2013     7    22      845           1600      1005     1044           1815
##  6  2013     4    10     1100           1900       960     1342           2211
##  7  2013     3    17     2321            810       911      135           1020
##  8  2013     6    27      959           1900       899     1236           2226
##  9  2013     7    22     2257            759       898      121           1026
## 10  2013    12     5      756           1700       896     1058           2020
## # ℹ 336,766 more rows
## # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>,
## #   tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>,
## #   hour <dbl>, minute <dbl>, time_hour <dttm>

Character order follows the locale.


5. Practice

  1. Flights in odd months, or on even days of even months (%%).
  2. Flights that went to Houston (IAH or HOU), on UA, AA, or DL, in July–September, arrived more than 15 minutes late, and did not leave late.
  3. Rows 10, 100, 1000, 10000, and 100000.
  4. The last 10 of the first 30 Newark (EWR) flights.
  5. Longest distance, ties broken by air_time.
  6. The 50 longest air_times, then the 10 smallest arr_delays among those. Repeat with air_time < 300. What does with_ties = FALSE change?