如何在data.table分组内高效实现指定行与其他列行的对比?
高效解决data.table大样本分组内价格对比生成哑变量问题
针对你提到的900万行级别的data.table数据,绝对不能用逐行对比或为每个Exchange创建列的方式——这种方法内存开销会指数级增长。我提供的方案基于data.table的分组聚合特性,只计算每个分组的关键统计值,再做向量级别的判断,内存占用可以忽略不计,处理速度极快。
需求回顾
我们需要按second分组完成两个哑变量的生成:
- dPRICE1:仅当
PRICE1非NA时,判断该行的PRICE1是否至少大于1个同分组内的非NA PRICE2,是则为1,否则为0;PRICE1为NA时,dPRICE1也为NA。 - dPRICE2:仅当
PRICE2非NA时,判断该行的PRICE2是否至少小于1个同分组内的非NA PRICE1,是则为1,否则为0;PRICE2为NA时,dPRICE2也为NA。
核心思路
对于每个second分组:
- 要判断
PRICE1是否至少大于一个PRICE2,等价于判断PRICE1 > 分组内PRICE2的最小值(因为最小值是分组内最小的PRICE2,只要PRICE1比最小值大,就说明存在至少一个PRICE2比它小)。 - 要判断
PRICE2是否至少小于一个PRICE1,等价于判断PRICE2 < 分组内PRICE1的最大值(因为最大值是分组内最大的PRICE1,只要PRICE2比最大值小,就说明存在至少一个PRICE1比它大)。
通过分组计算这两个统计值,再用向量操作判断每行的条件,完全避免了逐行对比的内存浪费。
代码实现
library(data.table) # 加载你的示例数据 dt <- data.table( bSIDE = c(0,0,0,0,1,1,1,1,0,0), EX = c(1,3,9,14,1,3,5,14,1,2), second=c(0,0,0,0,0,0,0,0,1,1), PRICE1=c(NA,NA,NA,NA,127.47,127.47,127.47,127.47,NA,NA), PRICE2=c(127.49,127.48,127.58,127.46,NA,NA,NA,NA,127.48,127.48) ) # 1. 按second分组,计算每个分组的PRICE2最小值和PRICE1最大值(忽略NA) dt[, `:=`( group_min_PRICE2 = min(PRICE2, na.rm = TRUE), group_max_PRICE1 = max(PRICE1, na.rm = TRUE) ), by = second] # 2. 生成dPRICE1:PRICE1非NA时判断是否大于分组最小PRICE2,否则为NA dt[, dPRICE1 := fifelse( !is.na(PRICE1), as.integer(PRICE1 > group_min_PRICE2), NA_integer_ )] # 3. 生成dPRICE2:PRICE2非NA时判断是否小于分组最大PRICE1,否则为NA dt[, dPRICE2 := fifelse( !is.na(PRICE2), as.integer(PRICE2 < group_max_PRICE1), NA_integer_ )] # 4. 可选:删除临时统计列(如果不需要保留的话) dt[, c("group_min_PRICE2", "group_max_PRICE1") := NULL]
结果验证
运行上述代码后,dPRICE2的取值完全匹配你提供的目标结果:EX=14的行PRICE2=127.46小于分组内PRICE1的最大值127.47,所以dPRICE2=1,其余非NA的PRICE2均大于该最大值,所以dPRICE2=0。
关于dPRICE1的取值差异:按需求描述,PRICE1=127.47大于分组内PRICE2的最小值127.46,所以dPRICE1应为1,但你的目标结果中是0。这可能是需求描述的细节差异(比如是否要求PRICE1大于所有PRICE2,或者是否需要按EX+second再细分分组)。如果需要按EX+second分组,只需要修改by = .(second, EX)即可。
性能说明
- 内存开销:每个分组仅存储2个数值,对于900万行数据,即使有上万个分组,内存占用也只有几十KB。
- 速度:data.table的分组聚合和向量操作都是C级别的实现,处理900万行数据只需要几秒时间。
内容的提问来源于stack exchange,提问作者Deea
相关产品推荐
相关产品推荐

