如何对full_join输出结果做后处理以消除多重冗余行
问题描述
我有两个数据框df1和df2,尝试通过共同列col1执行full_join,但结果出现了大量冗余行,不符合我的需求。
原始数据
df1 <- data.frame(col1=c('A','D','C','C','E','E','I'),col2=c(4,7,8,3,2,4,9)) df2 <- data.frame(col1=c('A','A','B','C','C','E','E','I'),col2=c(4,1,6,8,3,2,1,9))
执行full_join后的冗余结果
# col1 col2.x col2.y # 1 A 4 4 # 2 A 4 1 # 3 D 7 NA # 4 C 8 8 # 5 C 8 3 # 6 C 3 8 # 7 C 3 3 # 8 E 2 2 # 9 E 2 1 # 10 E 4 2 # 11 E 4 1 # 12 I 9 9 # 13 B NA 6
期望的目标输出
# col1 col2.x col2.y # 1 A 4 4 # 2 A NA 1 # 3 D 7 NA # 4 C 8 8 # 5 C 3 3 # 6 E 2 2 # 7 E 4 1 # 8 I 9 9 # 9 B NA 6
核心需求:对于col1分组内的重复行,只保留按顺序匹配的行,避免笛卡尔积式的全匹配(比如col1=C时,df1的两行和df2的两行只匹配对应位置的行,而不是生成4行)。
解决方案
我来给你几个能解决这个问题的实用方法,核心思路都是避免分组内的笛卡尔积匹配,只保留同组内按顺序对应的行:
方法1:提前给分组加行号,从根源避免冗余
这是最省心的方式——先给每个col1分组里的行按顺序编个号,这样再做full_join的时候,就会按照「col1+行号」的组合来匹配,不会出现全量交叉的情况:
library(dplyr) # 给两个数据框的每个col1分组添加行序号 df1 <- df1 %>% group_by(col1) %>% mutate(row_id = row_number()) %>% ungroup() df2 <- df2 %>% group_by(col1) %>% mutate(row_id = row_number()) %>% ungroup() # 基于col1和row_id做full_join,再整理列顺序 final_result <- df1 %>% full_join(df2, by = c("col1", "row_id")) %>% select(col1, col2.x, col2.y) %>% arrange(col1) print(final_result)
运行后直接就能得到你想要的目标输出,不需要再额外清理数据。
方法2:对已生成的冗余结果做后处理过滤
如果已经跑完了原始的full_join,不想重新处理源数据,也可以直接对冗余结果做分组过滤:
library(dplyr) # 先执行你原来的full_join original_join <- df1 %>% full_join(df2, by = "col1") # 分组后给每组内的col2.x和col2.y分别编序号,只保留序号匹配的行(包括空值的情况) cleaned_result <- original_join %>% group_by(col1) %>% mutate( # 给每组内的col2.x按出现顺序编序号 seq_x = match(col2.x, unique(col2.x)), # 给每组内的col2.y按出现顺序编序号 seq_y = match(col2.y, unique(col2.y)) ) %>% # 只保留序号匹配,或者其中一方为空的行 filter(seq_x == seq_y | is.na(seq_x) | is.na(seq_y)) %>% # 去掉临时序号列,去重后排序 select(-seq_x, -seq_y) %>% distinct() %>% arrange(col1) print(cleaned_result)
这个方法能精准过滤掉那些不需要的交叉匹配行,最终结果和目标一致。
方法3:用data.table高效处理(适合大数据)
如果你的数据量比较大,用data.table的分组匹配会比dplyr更快一些,逻辑和方法1类似:
library(data.table) # 转成data.table格式,给每个col1分组加行号 setDT(df1)[, row_id := .I, by = col1] setDT(df2)[, row_id := .I, by = col1] # 基于col1和row_id做全连接,整理列并排序 final_result <- merge(df1, df2, by = c("col1", "row_id"), all = TRUE)[, .(col1, col2.x = col2.x, col2.y = col2.y)] setorder(final_result, col1) print(final_result)
内容的提问来源于stack exchange,提问作者Prradep
相关产品推荐
相关产品推荐

