R语言中如何高效匹配两个data.frame的行并获取对应id2?
高效匹配两个data.frame的非循环实现方法
需求说明
现有两个data.frame:table1和table2,需要为table1的每一行匹配table2中满足以下条件的行,并提取对应的id2:
- 两个表的
class字段值相等 - 两个表的
type字段值相等 table1的number值处于table2的min_number与max_number之间(包含边界值)
原实现采用for循环,但数据量达到百万级时效率极低,因此需要高效的非循环实现方案。
示例数据
table1
table1 <- data.frame(id1 = c(1324, 1111, 2324, 2324, 1111, 1111), class = c(1, 1, 1, 2, 2, 1), type = c("A", "A", "A", "B", "C", "D"), number = c(1, 2.5, 98, 100, 80, 50))
输出:
id1 class type number 1 1324 1 A 1.0 2 1111 1 A 2.5 3 2324 1 A 98.0 4 2324 2 B 100.0 5 1111 2 C 80.0 6 1111 1 D 50.0
table2
table2 <- data.frame(id2 = c(1992, 1987, 1998, 1998, 2000, 2010, 2012), class = c(3, 3, 1, 1, 3, 1, 2), type = c("B", "C", "D", "A", "D", "D", "C"), min_number = c(0, 0, 34, 0, 20, 45, 5), max_number = c(18, 18, 50, 100, 100, 100, 100))
输出:
id2 class type min_number max_number 1 1992 3 B 0 18 2 1987 3 C 0 18 3 1998 1 D 34 50 4 1998 1 A 0 100 5 2000 3 D 20 100 6 2010 1 D 45 100 7 2012 2 C 5 100
原循环实现代码及结果
id2_list <- vector("list") for(i in 1:nrow(table1)){ id2_list[[i]] <- table2[which((table2$class == table1$class[i]) & (table2$type %in% table1$type[i]) & ((table2$min_number <= table1$number[i]) & (table2$max_number >= table1$number[i]))), ]$id2 }
输出:
[[1]] [1] 1998 [[2]] [1] 1998 [[3]] [1] 1998 [[4]] numeric(0) [[5]] [1] 2012 [[6]] [1] 1998 2010
高效非循环实现方案
方案1:使用dplyr包(适合中等规模数据)
利用dplyr的连接与分组汇总功能,避免循环:
library(dplyr) # 为table1添加行号,确保每一行都能对应结果 table1 <- table1 %>% mutate(row_num = row_number()) # 按class和type连接两个表,过滤符合条件的行,再按行号分组汇总id2为列表 matched_result <- table1 %>% left_join(table2, by = c("class", "type")) %>% filter(number >= min_number, number <= max_number) %>% group_by(row_num) %>% summarise(id2 = list(id2), .groups = "drop") # 补全无匹配的行,将空值替换为numeric(0) full_result <- table1 %>% left_join(matched_result, by = "row_num") %>% mutate(id2 = ifelse(is.na(id2), list(numeric(0)), id2)) # 提取最终的id2列表 id2_list <- full_result$id2
方案2:使用data.table包(适合百万级大数据)
data.table在处理大规模数据时性能更优,操作更高效:
library(data.table) # 转换为data.table格式 setDT(table1) setDT(table2) # 为table1添加行号 table1[, row_num := .I] # 按class和type做连接,过滤符合数值范围的行,再按行号分组汇总id2 matched_result <- table1[table2, on = .(class, type), allow.cartesian = TRUE][ number >= min_number & number <= max_number, .(id2 = list(id2)), by = row_num ] # 补全无匹配的行,替换空值为numeric(0) full_result <- table1[matched_result, on = .(row_num)] full_result[is.na(id2), id2 := list(numeric(0))] # 提取最终的id2列表 id2_list <- full_result$id2
两种方案的输出结果与原循环实现完全一致,且在百万级数据下效率会有数量级的提升。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

