dplyr: mutate, summarise, and group_by
Prerequisites: dplyr: filter, select, and arrange
These verbs create new columns and summaries — and group_by makes them
work per group, which answers most everyday data questions.
mutate — add or change columns
library(dplyr, warn.conflicts = FALSE)
staff <- tibble(
name = c("Ada", "Linus", "Grace", "Alan", "Margaret"),
team = c("Data", "Infra", "Data", "Research", "Infra"),
salary = c(72000, 65000, 91000, 58000, 83000),
years = c(5, 3, 12, 2, 8)
)
staff |>
mutate(
monthly = round(salary / 12),
level = if_else(years >= 5, "senior", "junior")
) |>
select(name, monthly, level)
# A tibble: 5 × 3
name monthly level
<chr> <dbl> <chr>
1 Ada 6000 senior
2 Linus 5417 junior
3 Grace 7583 senior
4 Alan 4833 junior
5 Margaret 6917 senior
if_else() is dplyr's stricter ifelse(). For several conditions, use
case_when():
library(dplyr, warn.conflicts = FALSE)
tibble(score = c(95, 84, 72, 55)) |>
mutate(grade = case_when(
score >= 90 ~ "A",
score >= 80 ~ "B",
score >= 70 ~ "C",
.default = "F"
))
# A tibble: 4 × 2
score grade
<dbl> <chr>
1 95 A
2 84 B
3 72 C
4 55 F
summarise — reduce to one row
library(dplyr, warn.conflicts = FALSE)
staff <- tibble(
team = c("Data", "Infra", "Data", "Research", "Infra"),
salary = c(72000, 65000, 91000, 58000, 83000)
)
staff |> summarise(people = n(), avg_salary = mean(salary))
# A tibble: 1 × 2
people avg_salary
<int> <dbl>
1 5 73800
group_by — summaries per group
library(dplyr, warn.conflicts = FALSE)
staff <- tibble(
team = c("Data", "Infra", "Data", "Research", "Infra"),
salary = c(72000, 65000, 91000, 58000, 83000)
)
staff |>
group_by(team) |>
summarise(people = n(), avg_salary = mean(salary), top = max(salary)) |>
arrange(desc(avg_salary))
# A tibble: 3 × 4
team people avg_salary top
<chr> <int> <dbl> <dbl>
1 Data 2 81500 91000
2 Infra 2 74000 83000
3 Research 1 58000 58000
"Split the data into groups, summarise each group, combine the results" — this pattern answers questions like average sales per region, number of customers per month, or highest score per class.
count(team) is a handy shortcut for group_by(team) |> summarise(n = n()).
Common mistakes
- Forgetting
na.rm = TRUEinsidesummarise, so one missing value makes the whole group's meanNA. - Leaving data grouped after
group_bywithmutate— later operations stay grouped. Use.by = teaminside a single verb (summarise(..., .by = team)) or callungroup().
Practice
Using the staff data from the previous lesson, find the total salary
cost and the average years of experience for each team.
Show solution
library(dplyr, warn.conflicts = FALSE)
staff <- tibble(
team = c("Data", "Infra", "Data", "Research", "Infra"),
salary = c(72000, 65000, 91000, 58000, 83000),
years = c(5, 3, 12, 2, 8)
)
staff |>
summarise(total_salary = sum(salary), avg_years = mean(years), .by = team)
# A tibble: 3 × 3
team total_salary avg_years
<chr> <dbl> <dbl>
1 Data 163000 8.5
2 Infra 148000 5.5
3 Research 58000 2