如何用data.table高效实现条件关联后的唯一ID计数(无需全表合并)
高效统计data.table关联后的唯一ID计数(无需全表合并)
数据定义
library(data.table) table1 <- data.table(id1 = c(1324, 2324, 29, 29, 1010, 1010), type = c(1, 1, 2, 1, 1, 1), class = c("A", "A", "B", "D", "D", "A"), number = c(1, 98, 100, 100, 70, 70)) table2 <- data.table(id2 = c(1998, 1998, 2000, 2000, 2000, 2010, 2012, 2012), type = c(1, 1, 3, 1, 1, 5, 1, 1), class = c("D", "A", "D", "D", "A", "B", "A", "A"), min_number = c(34, 0, 20, 45, 5, 23, 1, 1), max_number = c(50, 100, 100, 100, 100, 9, 10, 100))
原方法的性能瓶颈
原方法先通过全表非等连接合并两表,再分组统计唯一ID,但当数据量达到数十万行时,全表合并会产生大量中间数据,导致速度急剧下降。
高效解法(避免全表合并)
核心思路是先去重减少数据量,再做精准的分组匹配统计,全程使用data.table原生操作最大化性能:
步骤1:预处理两表,去除冗余数据
先对两表去重,保留不影响统计结果的最小数据集:
# 预处理table1:保留每个(id1, type, class)的唯一number(重复number不影响关联结果) dt1_unique <- unique(table1, by = c("id1", "type", "class")) # 预处理table2:保留每个(id2, type, class, min_number, max_number)的唯一组合 dt2_unique <- unique(table2, by = c("id2", "type", "class", "min_number", "max_number"))
步骤2:统计每个class的唯一id1数量
直接从预处理后的table1分组统计,无需关联:
id1_counts <- dt1_unique[, .(n_id1 = uniqueN(id1)), by = class]
步骤3:匹配关联条件,统计唯一id2数量
仅在type和class匹配的组内做非等连接,筛选number落在区间内的记录,再统计唯一id2:
# 仅匹配type和class相同的记录,再筛选number在[min_number, max_number]的情况 matches <- dt2_unique[dt1_unique, on = c("type", "class"), allow.cartesian = TRUE][ number >= min_number & number <= max_number ] # 按class统计唯一id2数量 id2_counts <- matches[, .(n_id2 = uniqueN(id2)), by = class]
步骤4:合并结果,补全无匹配的class
将两个统计结果左连接,对没有匹配到id2的class设置n_id2为0:
result <- id1_counts[id2_counts, on = "class"] result[is.na(n_id2), n_id2 := 0]
最终结果与原方法完全一致:
> result class n_id1 n_id2 1: A 3 3 2: B 1 0 3: D 2 1
性能优势
- 去重后的数据量大幅减少,避免了全表合并产生的海量中间数据
- 全程使用data.table的高效分组和连接逻辑,比原方法中混合dplyr的方式性能更优
- 仅在必要的分组内做条件匹配,计算量远小于全表连接
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

