使用dplyr分组计算前序行求和时遇错误,求正确实现方式
问题:dplyr分组内前序行求和计算出错,外部逻辑正常
我在用dplyr对分组内的前序行求和时出现错误,但把逻辑搬到dplyr外面却能正常运行,想知道问题出在哪。
我有一个包含字符串起止位置的数据框,nchar是匹配模式的长度。需要为每行计算它在原始文本中的位置(模式前一位),计算逻辑是:当前行start值减1,再减去之前所有行的nchar值之和。比如第一行是4-1=3,第二行21-1-11=9,第三行50-1-(11+10)=28。
我写的dplyr代码如下:
library(dplyr) df <- data.frame( id=1, start=c(4, 21, 50), end=c(14, 30, 60), nchar=c(11,10,11) ) df %>% group_by(id) %>% mutate(pos=start[row_number()] - (1 + sum(nchar[0:(row_number() - 1)])))
运行后得到的结果和警告:
# A tibble: 3 x 5 # Groups: id [1] id start end nchar pos <dbl> <dbl> <dbl> <dbl> <dbl> 1 1 4 14 11 3 2 1 21 30 10 20 3 1 50 60 11 49 Warning message: There was 1 warning in `mutate()`. i In argument: `pos = start[row_number()] - (1 + sum(nchar[0:(row_number() - 1)]))`. i In group 1: `id = 1`. Caused by warning in `0:(row_number() - 1)`: ! numerical expression has 3 elements: only the first used
而用下面的代码能得到正确的pos列:
sapply(1:3, function(i) df$start[i] - (1 + sum(df$nchar[0:(i - 1)])))
请问怎么修改dplyr代码得到正确结果?
问题原因分析
你的dplyr代码出错的核心原因是:
sum()是向量化聚合函数,它会直接对整个分组的nchar子集求和,而非逐行计算前序行的累加和;row_number()返回的是整个分组的行号向量(此处为c(1,2,3)),导致0:(row_number()-1)生成的是多个范围的混合,R只会取第一个元素的范围,后续行的前序求和逻辑直接失效。
而sapply()是逐行迭代处理每个i,每次仅计算当前行之前的nchar和,这和dplyr中mutate的向量化执行逻辑完全不同。
修正方案
方案1:用cumsum()计算累加和(推荐,高效简洁)
利用cumsum()计算前序行的累加和,通过lag()排除当前行的数值:
df %>% group_by(id) %>% mutate( # 第一行前序和为0,后续行取到上一行的累加和 prev_nchar_sum = lag(cumsum(nchar), default = 0), pos = start - 1 - prev_nchar_sum ) %>% ungroup() # 根据需求可选是否取消分组
运行结果:
# A tibble: 3 x 6 id start end nchar prev_nchar_sum pos <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 1 4 14 11 0 3 2 1 21 30 10 11 9 3 1 50 60 11 21 28
方案2:用rowwise()逐行处理(适合复杂逻辑)
如果你的计算逻辑更复杂,cumsum无法满足,可以用rowwise()让mutate逐行执行,和sapply逻辑一致:
df %>% group_by(id) %>% rowwise() %>% mutate( pos = start - 1 - sum(nchar[0:(row_number()-1)]) ) %>% ungroup()
注意:这个方法效率低于cumsum,更适合小数据集或复杂逐行计算场景。
内容的提问来源于stack exchange,提问作者drmariod
相关产品推荐
相关产品推荐

