如何在data.table中按分组执行更新连接操作?
按分组执行无放回抽样的data.table更新连接解决方案
这确实是data.table使用中很常见的需求——想通过更新连接,给目标表的每一行按分组从另一个表的对应组里无放回抽样取值。咱们来拆解问题,找到简洁高效的实现方式。
问题痛点分析
先看你提到的几种尝试:
- 第一种写法
df2[df1,plouf := sample(i.x),on ="ID"]:这里的i.x是整个df1的x向量,抽样是全局的,没有按ID分组,自然不符合预期。 - 第二种
df2[df1,plouf := as.numeric(sample(i.x,.N)),on ="ID",by = .EACHI]:报错的核心原因是,by=.EACHI时,.N是当前df2分组的行数,如果df2中某个ID的行数超过了df1对应ID的x值数量,无放回抽样(replace=FALSE默认)就会因为“样本量大于总体量”而报错。 - 第三种写法虽然可行,但可读性和扩展性确实太差,不推荐。
简洁高效的实现方法
我们可以分两种思路实现,既清晰又高效:
方法一:先统计抽样量,再关联赋值
# 第一步:统计df2中每个ID的行数,确定每个ID需要抽样的数量 id_counts <- df2[, .(sample_size = .N), by = ID] # 第二步:在df1中按ID分组,抽取对应数量的x值(无放回) sampled_values <- df1[id_counts, on = "ID", .(plouf = sample(x, sample_size, replace = FALSE)), by = .EACHI] # 第三步:把抽样结果赋值给df2 df2[, plouf := sampled_values[.SD, on = "ID", plouf]]
方法二:用列表直接在连接中处理(更紧凑)
如果不想单独创建统计表格,可以把df1的x按ID存为列表,再在df2中按ID匹配并抽样:
# 把df1的x按ID整理成列表 id_x_list <- df1[, .(x_list = list(x)), by = ID] # 关联后按ID分组,从对应列表中抽样对应数量的元素 df2[id_x_list, on = "ID", plouf := sample(x_list[[1]], .N, replace = FALSE), by = .EACHI]
特殊情况处理
如果df2中存在某个ID的行数多于df1对应ID的x值数量,无放回抽样必然失败。这时候你可以根据业务需求调整:
- 允许有放回抽样:把
replace=FALSE改成replace=TRUE - 智能适配抽样逻辑:比如先判断抽样量和总体量的大小,再选择抽样方式,示例:
sampled_values <- df1[id_counts, on = "ID", .(plouf = if (sample_size > length(x)) { sample(x, sample_size, replace = TRUE) } else { sample(x, sample_size, replace = FALSE) }), by = .EACHI]
验证效果
你可以用自己的示例数据测试(设置随机种子确保结果可复现):
set.seed(123) df1 <- data.table(ID = rep(letters[1:3],each = 3),x = c(runif(3,0,1),runif(3,1,2),runif(3,2,3))) df2 <- data.table(ID = c(letters[1],letters[1:5])) # 用方法二执行 id_x_list <- df1[, .(x_list = list(x)), by = ID] df2[id_x_list, on = "ID", plouf := sample(x_list[[1]], .N, replace = TRUE), by = .EACHI]
执行后df2的plouf列会对应ID从df1的x中抽样,ID为d、e的行会因为df1中没有对应数据而得到NA,符合预期。
内容的提问来源于stack exchange,提问作者denis
相关产品推荐
相关产品推荐

