为何dpplyr分组函数添加max后出现异常结果?
dplyr分组计算异常:max函数误用导致的结果错误
问题背景
运行以下dplyr代码时,GroupRank列能得到预期分组结果;但将GroupRank = if_else(as.character(Group) == "0", ElementCnt, min(GroupRank))修改为GroupRank = max(1L,if_else( as.character(Group) == "0", ElementCnt, min(GroupRank)))后,Group为0的行GroupRank均变为3,与预期不符。
初始代码及预期结果
library(dplyr) myData <- data.frame( Element = c("A","A","B","A","C","C"), Group = c(0,0,0,0,1,1) ) myDataGroups <- myData %>% mutate(origOrder = row_number()) %>% group_by(Element) %>% mutate(ElementCnt = row_number()) %>% ungroup() %>% mutate(Group = factor(Group, unique(Group))) %>% arrange(Group) %>% mutate(groupCt = cumsum(Group != lag(Group, 1, Group[[1]])) - 1L) %>% group_by(Group) %>% mutate(GroupRank = ElementCnt - max(0L,groupCt), GroupRank = if_else(as.character(Group) == "0", ElementCnt, min(GroupRank)) )%>% ungroup() %>% arrange(origOrder) myDataGroups
预期输出:
# A tibble: 6 x 6 Element Group origOrder ElementCnt groupCt GroupRank <chr> <fct> <int> <int> <int> <int> 1 A 0 1 1 -1 1 2 A 0 2 2 -1 2 3 B 0 3 1 -1 1 4 A 0 4 3 -1 3 5 C 1 5 1 0 1 6 C 1 6 2 0 1
修改后代码的异常结果
修改后的关键行:
GroupRank = max(1L,if_else( as.character(Group) == "0", ElementCnt, min(GroupRank)))
异常输出:
Element Group origOrder ElementCnt groupCt GroupRank <chr> <fct> <int> <int> <int> <int> 1 A 0 1 1 -1 3 2 A 0 2 2 -1 3 3 B 0 3 1 -1 3 4 A 0 4 3 -1 3 5 C 1 5 1 0 1 6 C 1 6 2 0 1
错误原因
问题出在max()函数的使用方式:
- 在dplyr的
group_by上下文里,max()是对整个分组的向量计算全局最大值,而非逐行进行元素级比较。 - 对于Group=0的分组,
if_else(as.character(Group) == "0", ElementCnt, min(GroupRank))返回的是该组的ElementCnt向量c(1,2,1,3),max(1L, 这个向量)会直接取整个向量的最大值3,然后将这个值广播到组内所有行,导致所有Group=0的行GroupRank都变成3。
解决方法
要实现逐行取最大值的需求,需要使用元素级别的最大值函数pmax(),它会对输入的向量逐元素比较并返回对应位置的最大值,而非全局最大。
修改后的关键代码行:
GroupRank = pmax(1L, if_else(as.character(Group) == "0", ElementCnt, min(GroupRank)))
修正后的完整代码及输出
library(dplyr) myData <- data.frame( Element = c("A","A","B","A","C","C"), Group = c(0,0,0,0,1,1) ) myDataGroups <- myData %>% mutate(origOrder = row_number()) %>% group_by(Element) %>% mutate(ElementCnt = row_number()) %>% ungroup() %>% mutate(Group = factor(Group, unique(Group))) %>% arrange(Group) %>% mutate(groupCt = cumsum(Group != lag(Group, 1, Group[[1]])) - 1L) %>% group_by(Group) %>% mutate(GroupRank = ElementCnt - max(0L,groupCt), GroupRank = pmax(1L, if_else(as.character(Group) == "0", ElementCnt, min(GroupRank))) )%>% ungroup() %>% arrange(origOrder) myDataGroups
输出结果(符合预期):
# A tibble: 6 x 6 Element Group origOrder ElementCnt groupCt GroupRank <chr> <fct> <int> <int> <int> <int> 1 A 0 1 1 -1 1 2 A 0 2 2 -1 2 3 B 0 3 1 -1 1 4 A 0 4 3 -1 3 5 C 1 5 1 0 1 6 C 1 6 2 0 1
内容的提问来源于stack exchange,提问作者Curious Jorge - user9788072
相关产品推荐
相关产品推荐

