LECTURE 5: Data Transformation with dplyr & Pipes
# load packages
library(tidyverse)
library(palmerpenguins)
##
## Attaching package: 'palmerpenguins'
## The following objects are masked from 'package:datasets':
##
## penguins, penguins_raw
# explore data
bind_rows(head(penguins, 4), tail(penguins, 4))
## # A tibble: 8 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <fct> <fct> <dbl> <dbl> <int> <int>
## 1 Adelie Torgersen 39.1 18.7 181 3750
## 2 Adelie Torgersen 39.5 17.4 186 3800
## 3 Adelie Torgersen 40.3 18 195 3250
## 4 Adelie Torgersen NA NA NA NA
## 5 Chinstrap Dream 43.5 18.1 202 3400
## 6 Chinstrap Dream 49.6 18.2 193 3775
## 7 Chinstrap Dream 50.8 19 210 4100
## 8 Chinstrap Dream 50.2 18.7 198 3775
## # ℹ 2 more variables: sex <fct>, year <int>
glimpse(penguins_raw)
## Rows: 344
## Columns: 17
## $ studyName <chr> "PAL0708", "PAL0708", "PAL0708", "PAL0708", "PAL…
## $ `Sample Number` <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1…
## $ Species <chr> "Adelie Penguin (Pygoscelis adeliae)", "Adelie P…
## $ Region <chr> "Anvers", "Anvers", "Anvers", "Anvers", "Anvers"…
## $ Island <chr> "Torgersen", "Torgersen", "Torgersen", "Torgerse…
## $ Stage <chr> "Adult, 1 Egg Stage", "Adult, 1 Egg Stage", "Adu…
## $ `Individual ID` <chr> "N1A1", "N1A2", "N2A1", "N2A2", "N3A1", "N3A2", …
## $ `Clutch Completion` <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "No", …
## $ `Date Egg` <date> 2007-11-11, 2007-11-11, 2007-11-16, 2007-11-16,…
## $ `Culmen Length (mm)` <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34…
## $ `Culmen Depth (mm)` <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18…
## $ `Flipper Length (mm)` <dbl> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190,…
## $ `Body Mass (g)` <dbl> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 34…
## $ Sex <chr> "MALE", "FEMALE", "FEMALE", NA, "FEMALE", "MALE"…
## $ `Delta 15 N (o/oo)` <dbl> NA, 8.94956, 8.36821, NA, 8.76651, 8.66496, 9.18…
## $ `Delta 13 C (o/oo)` <dbl> NA, -24.69454, -25.33302, NA, -25.32426, -25.298…
## $ Comments <chr> "Not enough blood for isotopes.", NA, NA, "Adult…
Revision
# Relationship between flipper length and body mass across species
ggplot(data = penguins, aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point() +
# facet_wrap(vars(species)) +
labs(title = "Penguins: Body Mass vs. Flipper Length by Species")
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_point()`).

dplyr vs ggplot2
- ggplot2 helps us answer questions with graphs
- dplyr helps us answer questions with numbers
Question → Visual Exploration (ggplot2) → Numerical Exploration
(dplyr) → Interpretation → New Questions → Etc.
dplyr functions
filter() |
Keeps rows based on conditions |
filter(penguins, species == "Adelie") |
can be used with
==,!=,<,>,& |
summarize() |
Calculates summary statistics for each group (e.g.,
mean, sum, count) |
summarize(penguins, avg_mass = mean(body_mass_g, na.rm = TRUE)) |
often with mean(), median(), n() |
group_by() |
Groups data by one or more variables (temp.) |
grouped <- group_by(penguins, species) |
often used with cat. var. and before
summarize() and filter() |
count() |
Counts the unique values of one or more variables |
count(penguins, species) |
both cat. and num. but more useful in cat.; shortcut
for group_by() + summarize(n = n()) |
mutate() |
Creates new columns or modifies existing ones |
penguins <- mutate(penguins, body_mass_kg = body_mass_g / 1000) |
make no changes to original data just adding |
1. filter()
== |
Equal to |
Is x equal to y? |
!= |
Not equal to |
Is x different from y? |
< |
Less than |
Is x less than y? |
<= |
Less than or equal to |
Is x less than or equal to y? |
> |
Greater than |
Is x greater than y? |
>= |
Greater than or equal to |
Is x greater than or equal to y? |
\| |
OR |
Either x or y must be TRUE |
& |
AND |
Both x and y must be TRUE |
& ! |
AND NOT |
x must be TRUE and y must be FALSE |
filter(penguins, species == "Adelie" | species == "Chinstrap") #A OR B
## # A tibble: 220 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <fct> <fct> <dbl> <dbl> <int> <int>
## 1 Adelie Torgersen 39.1 18.7 181 3750
## 2 Adelie Torgersen 39.5 17.4 186 3800
## 3 Adelie Torgersen 40.3 18 195 3250
## 4 Adelie Torgersen NA NA NA NA
## 5 Adelie Torgersen 36.7 19.3 193 3450
## 6 Adelie Torgersen 39.3 20.6 190 3650
## 7 Adelie Torgersen 38.9 17.8 181 3625
## 8 Adelie Torgersen 39.2 19.6 195 4675
## 9 Adelie Torgersen 34.1 18.1 193 3475
## 10 Adelie Torgersen 42 20.2 190 4250
## # ℹ 210 more rows
## # ℹ 2 more variables: sex <fct>, year <int>
filter(penguins, species == "Adelie" & species == "Chinstrap") #A AND B
## # A tibble: 0 × 8
## # ℹ 8 variables: species <fct>, island <fct>, bill_length_mm <dbl>,
## # bill_depth_mm <dbl>, flipper_length_mm <int>, body_mass_g <int>, sex <fct>,
## # year <int>
OR can also be written as %in%
filter(penguins, species %in% c("Adelie", "Chinstrap"))
## # A tibble: 220 × 8
## species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
## <fct> <fct> <dbl> <dbl> <int> <int>
## 1 Adelie Torgersen 39.1 18.7 181 3750
## 2 Adelie Torgersen 39.5 17.4 186 3800
## 3 Adelie Torgersen 40.3 18 195 3250
## 4 Adelie Torgersen NA NA NA NA
## 5 Adelie Torgersen 36.7 19.3 193 3450
## 6 Adelie Torgersen 39.3 20.6 190 3650
## 7 Adelie Torgersen 38.9 17.8 181 3625
## 8 Adelie Torgersen 39.2 19.6 195 4675
## 9 Adelie Torgersen 34.1 18.1 193 3475
## 10 Adelie Torgersen 42 20.2 190 4250
## # ℹ 210 more rows
## # ℹ 2 more variables: sex <fct>, year <int>
2. summarize()
# Example 1: only summarize
summarize(penguins,
body_mass_mean = mean(body_mass_g, na.rm = TRUE),
body_mass_sd = sd(body_mass_g, na.rm = TRUE),
n = n() # n = nrow(penguins)
)
## # A tibble: 1 × 3
## body_mass_mean body_mass_sd n
## <dbl> <dbl> <int>
## 1 4202. 802. 344
# Example 2: combine filter with summarize
penguins |>
filter(species == "Gentoo") |>
summarize(body_mass_mean = mean(body_mass_g, na.rm = TRUE),
body_mass_sd = sd(body_mass_g, na.rm = TRUE),
n = n())
## # A tibble: 1 × 3
## body_mass_mean body_mass_sd n
## <dbl> <dbl> <int>
## 1 5076. 504. 124
3. group_by()
# group by alone? not that useful
# group by and calculate mean body mass
grouped <- group_by(penguins, island, sex)
summarize(grouped, avg_mass = mean(body_mass_g, na.rm = TRUE))
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by island and sex.
## ℹ Output is grouped by island.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(island, sex))` for per-operation grouping
## (`?dplyr::dplyr_by`) instead.
## # A tibble: 9 × 3
## # Groups: island [3]
## island sex avg_mass
## <fct> <fct> <dbl>
## 1 Biscoe female 4319.
## 2 Biscoe male 5105.
## 3 Biscoe <NA> 4588.
## 4 Dream female 3446.
## 5 Dream male 3987.
## 6 Dream <NA> 2975
## 7 Torgersen female 3396.
## 8 Torgersen male 4035.
## 9 Torgersen <NA> 3681.
# group by and count
grouped <- group_by(penguins, island, sex)
count(grouped)
## # A tibble: 9 × 3
## # Groups: island, sex [9]
## island sex n
## <fct> <fct> <int>
## 1 Biscoe female 80
## 2 Biscoe male 83
## 3 Biscoe <NA> 5
## 4 Dream female 61
## 5 Dream male 62
## 6 Dream <NA> 1
## 7 Torgersen female 24
## 8 Torgersen male 23
## 9 Torgersen <NA> 5
4. count()
# total penguins by sex without count
penguins |>
group_by(sex) |>
summarize(total = n())
## # A tibble: 3 × 2
## sex total
## <fct> <int>
## 1 female 165
## 2 male 168
## 3 <NA> 11
# total penguins by sex with count
penguins |>
count(sex)
## # A tibble: 3 × 2
## sex n
## <fct> <int>
## 1 female 165
## 2 male 168
## 3 <NA> 11
5. mutate()
ncol(penguins) #8 here
## [1] 8
penguins <- mutate(penguins, body_mass_kg = body_mass_g / 1000)
ncol(penguins) #now 9
## [1] 9
Solving Questions
- Identify variables needed
filter() only keep necessary ones
group_by()
summarize()usually calculates the answer
Pipes |> or %>%
- Pipes allow you to write a sequence of operations by passing the
result of one function into the next making your code more readable and
logical
# Without Pipes
adelie <- filter(penguins, species == "Adelie")
adelie_island <- group_by(adelie, island)
adelie_avg_mass_island <- summarize(adelie_island, body_mass = mean(body_mass_g, na.rm = TRUE))
# With Pipes
adelie_avg_mass_island <- penguins |>
filter(species == "Adelie") |>
group_by(island) |>
summarize(body_mass = mean(body_mass_g, na.rm = TRUE))
Common Errors with Pipes
library(nycflights13)
data(flights)
glimpse(flights)
## Rows: 336,776
## Columns: 19
## $ year <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2…
## $ month <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ day <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ dep_time <int> 517, 533, 542, 544, 554, 554, 555, 557, 557, 558, 558, …
## $ sched_dep_time <int> 515, 529, 540, 545, 600, 558, 600, 600, 600, 600, 600, …
## $ dep_delay <dbl> 2, 4, 2, -1, -6, -4, -5, -3, -3, -2, -2, -2, -2, -2, -1…
## $ arr_time <int> 830, 850, 923, 1004, 812, 740, 913, 709, 838, 753, 849,…
## $ sched_arr_time <int> 819, 830, 850, 1022, 837, 728, 854, 723, 846, 745, 851,…
## $ arr_delay <dbl> 11, 20, 33, -18, -25, 12, 19, -14, -8, 8, -2, -3, 7, -1…
## $ carrier <chr> "UA", "UA", "AA", "B6", "DL", "UA", "B6", "EV", "B6", "…
## $ flight <int> 1545, 1714, 1141, 725, 461, 1696, 507, 5708, 79, 301, 4…
## $ tailnum <chr> "N14228", "N24211", "N619AA", "N804JB", "N668DN", "N394…
## $ origin <chr> "EWR", "LGA", "JFK", "JFK", "LGA", "EWR", "EWR", "LGA",…
## $ dest <chr> "IAH", "IAH", "MIA", "BQN", "ATL", "ORD", "FLL", "IAD",…
## $ air_time <dbl> 227, 227, 160, 183, 116, 150, 158, 53, 140, 138, 149, 1…
## $ distance <dbl> 1400, 1416, 1089, 1576, 762, 719, 1065, 229, 944, 733, …
## $ hour <dbl> 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6…
## $ minute <dbl> 15, 29, 40, 45, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0…
## $ time_hour <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01-01 0…
- Dont use
<- within the pipe, only use at the
beginning
- Each function in the pipe must be connected by
|> or
%>%
- Only need to reference the data at the beginning of the
chain, no need to repeat
- Don’t use
+ (this is fr ggplot2, not pipes), only use
|> or %>%