5.2 dplyr: case_when() and Distinct Rows
case_when() replaces nested if_else(). distinct() and rownames_to_column() clean identifiers.
1. case_when()
case_when() is a vectorized chain of if /
else. Each argument is condition ~ value.
Conditions are tried in order. End with TRUE ~ ... as the
default. If nothing matches, you get NA.
x <- 1:16
case_when(
x %% 35 == 0 ~ "fizz buzz",
x %% 5 == 0 ~ "fizz",
x %% 7 == 0 ~ "buzz",
TRUE ~ as.character(x)
)
## [1] "1" "2" "3" "4" "fizz" "6" "buzz" "8" "9" "fizz"
## [11] "11" "12" "13" "buzz" "fizz" "16"
Put the specific cases first. A leading TRUE ~ catches
everything and the later lines never run.
NA in x is not special. Handle it with
is.na(x) ~ ....
Inside mutate() it is the usual way to build a category
from several columns:
starwars |>
select(name:mass, gender, species) |>
mutate(
type = case_when(
height > 200 | mass > 200 ~ "large",
species == "Droid" ~ "robot",
TRUE ~ "other"
)
)
## # A tibble: 87 × 6
## name height mass gender species type
## <chr> <int> <dbl> <chr> <chr> <chr>
## 1 Luke Skywalker 172 77 masculine Human other
## 2 C-3PO 167 75 masculine Droid robot
## 3 R2-D2 96 32 masculine Droid robot
## 4 Darth Vader 202 136 masculine Human large
## 5 Leia Organa 150 49 feminine Human other
## 6 Owen Lars 178 120 masculine Human other
## 7 Beru Whitesun Lars 165 75 feminine Human other
## 8 R5-D4 97 32 masculine Droid robot
## 9 Biggs Darklighter 183 84 masculine Human other
## 10 Obi-Wan Kenobi 182 77 masculine Human other
## # ℹ 77 more rows
All right-hand sides must have the same type.
2. Row names and distinct()
Tibbles do not use row names. A matrix such as state.x77
or mtcars does. has_rownames() checks.
rownames_to_column(mtcars, var = "car") then
as_tibble() turns the names into a real column.
has_rownames(mtcars)
## [1] TRUE
mtcars |>
rownames_to_column(var = "car") |>
as_tibble()
## # A tibble: 32 × 12
## car mpg cyl disp hp drat wt qsec vs am gear carb
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Mazda RX4 21 6 160 110 3.9 2.62 16.5 0 1 4 4
## 2 Mazda RX4 … 21 6 160 110 3.9 2.88 17.0 0 1 4 4
## 3 Datsun 710 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1
## 4 Hornet 4 D… 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1
## 5 Hornet Spo… 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2
## 6 Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
## 7 Duster 360 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
## 8 Merc 240D 24.4 4 147. 62 3.69 3.19 20 1 0 4 2
## 9 Merc 230 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2
## 10 Merc 280 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4
## # ℹ 22 more rows
distinct() keeps unique rows, in the original order. You
can name columns, or use across():
starwars |>
distinct(across(contains("color"))) |>
arrange(hair_color, skin_color, eye_color)
## # A tibble: 67 × 3
## hair_color skin_color eye_color
## <chr> <chr> <chr>
## 1 auburn fair blue
## 2 auburn, grey fair blue
## 3 auburn, white fair blue-gray
## 4 black blue, grey yellow
## 5 black brown brown
## 6 black dark brown
## 7 black dark dark
## 8 black fair brown
## 9 black light brown
## 10 black tan brown
## # ℹ 57 more rows
3. Programming note (aside)
dplyr verbs use data masking
(filter(month == 1) means the column, not an object in the
console) or tidy selection
(select(starts_with("dep"))). That is why interactive work
is short. Inside a function, when the column name is stored in an
ordinary R object, you need extra tools. We will stay with typed column
names this week.
4. This week, in one place
rowwise()andc_across()for a summary across columns in one rowacross()to repeat a function on many columnscase_when()instead of nestedif_else()distinct()andrownames_to_column()when you need them
5. Practice
- Check whether
mtcarshas row names. If it does, make a tibble with acarcolumn. - Min and max of every numeric column except
birth_year, byspeciesandgender, plusn().