Base R/dplyr中多条件匹配数据框:merge用法纠错与优化
问题描述
我尝试匹配nCode和grpRnk两个数据框,用下方代码得到的输出里,最右侧列的值是对的,但多出来grpRnk$Name到$subGrp这些不必要的列(第6-10列),而且原本指定的grpRnk最后一列名称没生效,反而显示成$grpRnk。想问下我是不是正确使用了merge()函数?有没有更正确或高效的多因子匹配方法?
输出结果
Name Group nmCnt seqBase subGrp grpRnk$Name $Group $nmCnt $seqBase $subGrp $grpRnk <chr> <dbl> <int> <int> <int> <chr> <dbl> <int> <int> <int> <int> 1 R 0 1 1 0 B 0 1 1 0 NA 2 R 0 2 2 0 R 0 1 1 0 NA 3 B 0 1 1 0 R 0 2 2 0 NA 4 R 0 3 3 0 R 0 3 3 0 NA 5 X 1 1 1 1 X 1 1 1 1 1 6 X 1 2 1 2 X 1 2 1 2 1
用到的代码
library(dplyr) myDF1 <- data.frame( Name = c("R","R","B","R","X","X"), Group = c(0,0,0,0,1,1) ) nCode <- myDF1 %>% group_by(Name) %>% mutate(nmCnt = row_number()) %>% ungroup() %>% mutate(seqBase = ifelse(Group == 0 | Group != lag(Group), nmCnt,0)) %>% mutate(seqBase = na_if(seqBase, 0)) %>% group_by(Name) %>% fill(seqBase) %>% mutate(seqBase = match(seqBase, unique(seqBase))) %>% ungroup() %>% mutate(subGrp = as.integer(ifelse(Group > 0, sapply(1:n(), function(x) sum(Name[1:x]==Name[x] & Group[1:x] == Group[x])),0))) grpRnk <- nCode %>% select(Name,Group,nmCnt) %>% filter(Group > 0) %>% group_by(Name,Group) %>% slice(which.min(Group)) %>% ungroup() %>% arrange(nmCnt) %>% mutate(grpRnk = dense_rank(nmCnt)) %>% select (-nmCnt) nCode %>% mutate(grpRnk = merge(nCode,grpRnk, by=c("Name","Group"), all.x=T))
问题分析与解决
1. 为什么当前merge()用错了?
你把merge()的结果直接赋值给mutate()里的grpRnk列,但merge()返回的是完整的数据框,不是单一列,所以整个合并后的框被塞进了原数据的一列中,导致出现冗余前缀列、列名混乱的问题。
2. 正确的匹配方式
方法一:直接使用merge()
不需要嵌套在mutate()里,直接合并两个数据框:
merge(nCode, grpRnk, by = c("Name", "Group"), all.x = TRUE)
这样会得到合并后的完整数据,只保留nCode的原有列和grpRnk中的grpRnk列,无冗余内容。
方法二:用dplyr的left_join()(更适配你的代码风格)
既然已经在用dplyr管道,用left_join()更连贯,效果和merge(all.x=TRUE)完全一致:
nCode %>% left_join(grpRnk, by = c("Name", "Group"))
方法三:硬要嵌套在mutate()里的写法(不推荐)
如果坚持要通过mutate()添加列,可以先合并再提取目标列,但不如前两种直接:
nCode %>% mutate(grpRnk = left_join(., grpRnk, by = c("Name", "Group")) %>% pull(grpRnk))
3. 额外优化:简化grpRnk生成代码
你生成grpRnk时,slice(which.min(Group))可以简化,因为已经按Name,Group分组且过滤了Group>0,直接用distinct或slice(1)即可:
grpRnk <- nCode %>% filter(Group > 0) %>% distinct(Name, Group, .keep_all = TRUE) %>% arrange(nmCnt) %>% mutate(grpRnk = dense_rank(nmCnt)) %>% select(Name, Group, grpRnk)
内容的提问来源于stack exchange,提问作者Village.Idyot
相关产品推荐
相关产品推荐

