dplyr中部分操作无需rowwise()即可按行执行的原因探究
R语言dplyr中rowwise()工作机制与逐行操作规则解析
先看你提供的示例代码及结果:
示例代码
library(dplyr) df = data.frame( group = c("a", "a", "a", "b", "b", "c"), var1 = 1:6, var2 = 7:12 ) df %>% mutate( concatNotRW = paste0(var1, "-", group), # 逐行生效 meanNotRW = mean(c(var1, var2)), # 未逐行生效 charsNotRW = strsplit(concatNotRW, "-") # 逐行生效 ) %>% rowwise() %>% mutate( concatRW = paste0(var1, "-", group), # 全部逐行生效 meanRW = mean(c(var1, var2)), charsRW = strsplit(concatRW, "-") ) -> res
运行结果
group var1 var2 concatNotRW meanNotRW charsNotRW concatRW meanRW chars <chr> <int> <int> <chr> <dbl> <list> <chr> <dbl> <list> 1 a 1 7 1-a 6.5 <chr [2]> 1-a 4 <chr [2]> 2 a 2 8 2-a 6.5 <chr [2]> 2-a 5 <chr [2]> 3 a 3 9 3-a 6.5 <chr [2]> 3-a 6 <chr [2]> 4 b 4 10 4-b 6.5 <chr [2]> 4-b 7 <chr [2]> 5 b 5 11 5-b 6.5 <chr [2]> 5-b 8 <chr [2]> 6 c 6 12 6-c 6.5 <chr [2]> 6-c 9 <chr [2]>
一、为什么paste0能逐行工作,mean却不行?
核心是函数的向量化特性和mutate()的默认处理逻辑:
paste0是向量化函数:它接收等长的向量输入,会对每个向量的对应位置元素逐一运算。比如var1是c(1,2,3,4,5,6),group是c("a","a","a","b","b","c"),paste0会自动把两个向量的第1个元素拼接、第2个元素拼接……最终得到逐行的结果。mean(c(var1, var2))的问题在于:c(var1, var2)会直接把整个var1列和var2列合并成一个长度为12的向量(c(1,2,3,4,5,6,7,8,9,10,11,12)),mean()对这个整体向量求均值得到6.5,随后mutate()会把这个单一值广播到每一行,所以所有行的meanNotRW都是6.5。strsplit同样是对向量的每个元素单独处理:输入的concatNotRW是长度为6的字符向量,strsplit会逐个拆分每个元素,返回对应长度的列表,所以看起来也是逐行生效。
二、rowwise()到底做了什么?
rowwise()会将数据框转换为按行分组的状态,此时mutate()中的所有运算都会以单一行作为独立的运算单元:
- 执行
c(var1, var2)时,不再是合并整个列向量,而是提取当前行的var1和var2值,组成一个长度为2的小向量; - 对这个小向量执行
mean(),自然得到当前行两个值的均值,也就是我们需要的逐行结果。
三、无需rowwise()的逐行操作规则
如果不想依赖rowwise(),可以用以下几种更高效的方式:
- 优先使用向量化函数:像
paste0、strsplit、if_else、case_when这类本身支持向量逐元素运算的函数,直接在mutate()中调用即可,无需额外处理。 - 使用专门的行运算函数:dplyr/base R提供了
rowMeans()、rowSums()、rowMaxs()这类专门处理行级统计的函数,效率远高于rowwise():df %>% mutate(row_mean = rowMeans(select(., var1, var2))) - 用purrr包的pmap系列函数:如果需要自定义逐行逻辑,可以用
purrr::pmap()来逐行处理多列输入:library(purrr) df %>% mutate(custom_mean = pmap_dbl(list(var1, var2), ~mean(c(..1, ..2))))
内容的提问来源于stack exchange,提问作者Lenn
相关产品推荐
相关产品推荐

