关键词:dplyr、按行计算、分组行操作、across()函数互补、
rowwise()
、
c_across()
1. 为什么需要 rowwise()
?
在实际的数据分析任务中,常常会遇到需要对同一行多个列的值进行聚合操作的情况,例如:
- 计算每行的平均值、最大值或方差
- 从多列中提取每行的 top-k 值
- 将多个字符串列拼接成一个结果字段
- 将多个评分列相加生成综合得分(composite score)
传统处理方式可能如下所示:
apply(df[, c("a","b","c")], 1, mean)
但这种方式存在明显缺陷:
apply
会将数据强制转换为矩阵,导致原始数据类型信息丢失
- 与 tidyverse 风格不兼容
- 难以嵌入到管道链式操作中,如
mutate()
和 summarise()
等语法结构
为此,dplyr 提供了
rowwise()
—— 它是
行级计算模式的开启器,专门用于解决上述问题。
2. 示例数据
以下是一个典型的学生各科成绩数据集,用于后续演示:
library(dplyr)
df <- tibble(
id = 1:5,
Math = c(80, 75, 90, 60, 88),
English = c(85, 78, 92, 65, 90),
Physics = c(78, 70, 88, 55, 95)
)
df
3. 为何不能直接使用 mutate(mean = mean(Math, English, Physics))
?
许多用户尝试通过如下代码计算三门课程的平均分:
df %>%
mutate(mean_score = mean(c(Math, English, Physics)))
然而,这种写法并不会实现逐行计算。其实际行为是:
最终输出形如:
| id | Math | English | Physics | mean_score |
| 1 | 80 | 85 | 78 | 79.6 |
| 2 | 75 | 78 | 70 | 79.6 |
| ... | ... | ... | ... | 79.6 |
这本质上是“全列整体计算 + 广播复制”,而非真正的按行聚合。
4. rowwise()
的核心功能
最基础的应用场景是:对每一行计算多列的平均值。
df %>%
rowwise() %>%
mutate(mean_score = mean(c(Math, English, Physics)))
输出示例:
| id | Math | English | Physics | mean_score |
| 1 | 80 | 85 | 78 | 81.0 |
| 2 | 75 | 78 | 70 | 74.3 |
| ... | ... | ... | ... | ... |
关键点在于:
rowwise()
明确告诉 dplyr ——
接下来的操作应以行为单位进行,而不是默认的列方向操作。
5. 更优雅的写法:结合 c_across()
为了避免手动列出所有列名,可以使用
c_across()
:
df %>%
rowwise() %>%
mutate(mean_score = mean(c_across(Math:Physics)))
其中,
c_across()
是在 rowwise 模式下的列选择工具,作用是:
- 将指定的多列组合成一个行内向量
- 便于传递给需要向量输入的函数进行处理
6. 在每行上执行多个函数
利用
rowwise()
和 mutate 结合,可同时计算每行的多种统计量:
df %>%
rowwise() %>%
mutate(
max_score = max(c_across(Math:Physics)),
min_score = min(c_across(Math:Physics))
)
7. 按行拼接非数值型字符串
对于字符型列,也可使用类似方法进行行内拼接:
df %>%
rowwise() %>%
mutate(info = paste(Math, English, Physics, sep = "-"))
输出示例:
80-85-78
8. rowwise()
与 summarise()
的组合使用
注意与
across
的区别:
rowwise()
支持在 summarise 中逐行聚合,且结果仍保留原始行数
- 只有在显式调用 ungroup 或移除 rowwise 状态后,才会解除行绑定
例如:
df %>%
rowwise() %>%
summarise(mean_score = mean(c_across(Math:Physics)))
输出仍为 5 行,而传统的
group_by
会将结果压缩为 1 行汇总值。
9. rowwise()
vs across()
功能对比
| 维度 | rowwise()
| across()
|
| 操作对象 | 行 | 列 |
| 常见任务 | 求每行的统计值 | 对多列执行相同变换 |
| 与 summarise 的关系 | 保留原始行数 | 压缩为汇总行 |
| 典型语句 | mean(c_across())
| across(cols, fun)
|
| 示例用途 | 每行平均、拼接字符串 | 对多列统一求 mean、sd、归一化等 |
简单判断标准:
- 若需对多列做相同操作 → 使用 across()
- 若需对单行跨列聚合 → 使用 rowwise()
10. 如何正确移除 rowwise()
在使用
rowwise()
后,数据框仍携带 rowwise 属性 ——
rowwise
。
如果后续希望恢复正常的列操作(如
group_by()
或
summarise()
),必须先解除该状态。
方法一:使用
ungroup()
df %>%
rowwise() %>%
summarise(mean_score = mean(c_across(Math:Physics))) %>%
ungroup()
方法二:使用
as_tibble()
重置 tibble 状态
df %>%
rowwise() %>%
summarise(mean_score = mean(c_across(Math:Physics))) %>%
as_tibble()
11. 高频应用场景汇总
| 应用场景 | 代码示例 |
| 每行求和 | mutate(total = sum(c_across(Math:Physics)))
|
| 判断某行是否存在异常值 | filter(any(c_across(Math:Physics) > 90))
|
| 计算每行 top-2 成绩的平均值 | mutate(top2 = mean(sort(c_across(Math:Physics), decreasing = TRUE)[1:2]))
|
| 将标签列按行拼接 | mutate(label = paste(c_across(Math:Physics), collapse = "_"))
|
12. 系列总结
group_by() # 分组
summarise() # 聚合
across() # 多列批量操作
rowwise() # 按行聚合处理
c_across() # rowwise专属数据抓取器