03使用 dplyr 完成分组操作

使用 group_by、summarise、mutate、filter、count、ungroup 和 .by 完成持久或一次性的分组计算。

2026-03-05
RTidyversedplyrGroupingsummarise
本章目录 · 9

分组不会修改数据值,而是改变后续动词的计算范围。同一个 mean(score) 在未分组数据中返回全局均值,在分组数据中则对每组分别计算。

library(dplyr)

scores <- tibble(
  group = c("A", "A", "B", "B", "C"),
  sex = c("F", "M", "F", "F", "M"),
  score = c(10, 12, 15, 14, 13)
)

group_by() 建立分组状态

grouped <- scores |>
  group_by(group)

group_vars(grouped)

group_by() 返回带分组元数据的表,后续 summarise()mutate()filter()slice_*() 会识别该状态。

summarise():每组压缩为汇总行

scores |>
  group_by(group) |>
  summarise(
    n = n(),
    mean_score = mean(score),
    sd_score = sd(score),
    .groups = "drop"
  )

多个变量共同定义组:

scores |>
  group_by(group, sex) |>
  summarise(
    mean_score = mean(score),
    .groups = "drop"
  )

.groups 控制结果是否继续分组:

结果
"drop" 移除全部分组
"drop_last" 移除最后一层
"keep" 保留全部分组
"rowwise" 结果按行分组

如果后续不再需要分组,显式使用 "drop" 最容易预测。

组内 mutate()

mutate() 保留原行数,并把每组统计量写回各行:

scores |>
  group_by(group) |>
  mutate(
    group_mean = mean(score),
    centered = score - group_mean,
    rank_in_group = min_rank(desc(score))
  ) |>
  ungroup()

这适合组内中心化、标准化、比例和排名。

组内 filter()

scores |>
  group_by(group) |>
  filter(score >= mean(score)) |>
  ungroup()

条件中的 mean(score) 按组计算。若分组状态被意外保留,后面的筛选也会继续按组执行。

ungroup() 清除分组状态

scores |>
  group_by(group) |>
  mutate(rank_in_group = min_rank(desc(score))) |>
  ungroup() |>
  mutate(overall_rank = min_rank(desc(score)))

ungroup() 只删除分组元数据,不删除列或行。需要只移除部分分组时,可以指定变量:

scores |>
  group_by(group, sex) |>
  ungroup(sex)

.by:一次性的局部分组

只在一个动词中需要分组时,可以把分组规则写在 .by 中:

scores |>
  summarise(
    n = n(),
    mean_score = mean(score),
    .by = group
  )
scores |>
  mutate(group_mean = mean(score), .by = group)
scores |>
  filter(score >= mean(score), .by = group)

多列局部分组:

scores |>
  summarise(
    mean_score = mean(score),
    .by = c(group, sex)
  )

.by 不会让分组状态流入后续管道,适合一次性计算;group_by() 适合多个连续步骤共享同一分组。

count():分组计数的快捷方式

scores |> count(group)
scores |> count(group, sex)
scores |> count(group, sort = TRUE, name = "frequency")

它相当于分组后计算 n(),但表达计数意图更直接。

加权计数对权重列求和:

sales <- tibble(
  category = c("A", "A", "B"),
  units = c(10, 15, 8)
)

sales |> count(category, wt = units, name = "units")

加权结果不是行数,列名应反映实际含义。

从计数到比例

scores |>
  count(group, sex) |>
  mutate(proportion = n / sum(n), .by = group)

这里的分母是每个 group 的总数。需要总体比例时不要使用 .by。比例表最常见的错误就是分母层级不清。

选择持久分组还是局部分组

  • 连续多个步骤都依赖同一分组:group_by(),结束后 ungroup()
  • 单个动词中的临时分组:.by
  • 只做频数统计:count()
  • 输出表不应继续带分组:在 summarise() 中设置 .groups = "drop"

分组是数据对象的状态。让它在代码中明确开始、明确结束,可以避免后续步骤在错误的计算范围中运行。