01使用 dplyr 选择、筛选与抽取数据

使用 select、relocate、filter、slice 和 distinct 控制数据框的列、行与重复记录。

2026-03-08
RTidyversedplyrData Wrangling
本章目录 · 8

数据整理经常围绕两个方向展开:对列决定保留哪些变量和如何排列,对行决定保留哪些观测。dplyr 为这些任务提供了职责明确的函数。

library(dplyr)

df <- tibble(
  id = 1:8,
  group = c("A", "B", "A", "B", "C", "C", "A", "B"),
  score = c(95, 81, 88, 60, 72, NA, 87, 100),
  score_2 = c(92, 79, 90, 65, 75, 70, 85, 98),
  label = c("pos", "neg", "pos", "neg", "pos", "neg", "neg", "pos")
)

select():选择或排除列

df |>
  select(id, group, score)

列名可以按范围和规则选择:

df |> select(id:score)
df |> select(starts_with("score"))
df |> select(ends_with("_2"))
df |> select(contains("core"))
df |> select(matches("^s"))
df |> select(where(is.numeric))

排除列:

df |> select(-label)
df |> select(-starts_with("score"))

常用 tidyselect 辅助函数包括:

辅助函数 选择规则
starts_with() 指定前缀
ends_with() 指定后缀
contains() 包含固定文本
matches() 匹配正则表达式
num_range() 带连续编号的列名
where() 列本身满足类型判断
all_of() 严格使用字符向量中的列名
any_of() 忽略字符向量中不存在的列名

编程时,列名存放在字符向量中应使用 all_of()any_of()

needed <- c("id", "group", "score")
df |> select(all_of(needed))

relocate():只调整列顺序

relocate() 不增删列,只移动位置:

df |> relocate(score)
df |> relocate(score, .after = id)
df |> relocate(starts_with("score"), .before = group)
df |> relocate(where(is.character), .after = last_col())

需要选择并重新排列一部分列时使用 select();需要保留全部列、只调整位置时使用 relocate()

filter():按条件筛选行

df |> filter(group == "A")
df |> filter(group == "A", score >= 90)
df |> filter(group == "A" & score >= 90)
df |> filter(group == "A" | score >= 90)

逗号连接的多个条件等价于逻辑与。类别集合使用 %in%

target_ids <- c(1, 3, 5)
df |> filter(id %in% target_ids)

区间可以使用两个比较,或 between()

df |> filter(score >= 80, score <= 95)
df |> filter(between(score, 80, 95))

筛选缺失值

df |> filter(is.na(score))
df |> filter(!is.na(score))

涉及 NA 的比较会得到 NA 而非 TRUEfilter() 只保留条件明确为 TRUE 的行。因此 score != NA 不能用于排除缺失。

slice_*():按位置或顺序抽取行

filter() 根据行的值判断;slice_*() 根据位置、极值或抽样规则选择行。

df |> slice(1:3)
df |> slice_head(n = 3)
df |> slice_tail(n = 2)
df |> slice_min(order_by = score, n = 2)
df |> slice_max(order_by = score, n = 2)
df |> slice_sample(prop = 0.25)

极值存在并列时,默认可能返回超过 n 行。只需要固定数量时显式设置:

df |>
  slice_max(order_by = score, n = 1, with_ties = FALSE)

抽样结果需要复现时先设置随机种子:

set.seed(42)
df |> slice_sample(n = 3)

组内抽取

df |>
  group_by(group) |>
  slice_max(order_by = score, n = 1, with_ties = FALSE) |>
  ungroup()

也可以使用局部分组:

df |>
  slice_max(
    order_by = score,
    n = 1,
    with_ties = FALSE,
    by = group
  )

组内“最高一行”必须先决定并列如何处理,以及缺失是否参与。

distinct():识别唯一组合

完全相同的行去重:

df |> distinct()

查看变量的唯一组合:

df |> distinct(group, label)

按键去重并保留整行:

df |> distinct(id, .keep_all = TRUE)

distinct() 默认保留第一次出现的记录。若业务规则是保留最新或最高记录,应先明确排序:

records <- tibble(
  id = c(1, 1, 2, 2),
  date = as.Date(c("2025-01-01", "2025-02-01", "2025-01-10", "2025-01-20")),
  value = c(8, 10, 6, 9)
)

records |>
  arrange(id, desc(date)) |>
  distinct(id, .keep_all = TRUE)

去重规则应能用字段表达。只写“删除重复值”而不说明判定键和保留规则,会让数据清理不可复现。

一条完整流水线

result <- df |>
  filter(!is.na(score), group %in% c("A", "B")) |>
  select(id, group, starts_with("score"), label) |>
  slice_max(
    order_by = score,
    n = 1,
    with_ties = FALSE,
    by = group
  ) |>
  relocate(group, id)

把列选择、行条件、抽取规则和最终排列分开写,管道会比一个复杂下标表达式更容易核查。