You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在data.table中按组随机标记指定数量的行?

这个问题的核心是你混淆了全局行号(.I)和组内位置索引,而且当sample()返回多个值时,直接用==做逻辑判断会因为长度不匹配导致结果不符合预期。咱们一步步来解决它:

首先明确需求:每个C1分组里随机标记最多N行(如果组内行数少于N,就标记所有行)。

正确的解决方案

咱们用组内位置索引来处理,这样能保证每个分组内的逻辑判断长度完全匹配,不会出问题。

方法1:分组内直接生成标记逻辑

这是最高效的方式,直接在每个分组里生成对应行的标记:

set.seed(1)
dt = data.table(
  C1 = c("A","A","A","B","C","C","C","D","D","D"),
  C2 = c(2,1,3,1,2,3,4,5,4,5)
)

# 定义要标记的数量
N <- 2
dt[, C3 := seq_len(.N) %in% sample(.N, min(.N, N)), by = C1]

运行后查看结果:

dt
#    C1 C2    C3
# 1:  A  2  TRUE
# 2:  A  1 FALSE
# 3:  A  3  TRUE
# 4:  B  1  TRUE  # 组B只有1行,正确标记
# 5:  C  2 FALSE
# 6:  C  3  TRUE
# 7:  C  4  TRUE
# 8:  D  5  TRUE
# 9:  D  4 FALSE
#10:  D  5  TRUE

方法2:先收集选中的全局行号再标记

如果你需要单独获取选中的行号,也可以先收集所有符合条件的全局行索引,再统一标记:

selected_rows <- dt[, .(selected = .I[sample(.N, min(.N, N))]), by = C1]$selected
dt[, C3 := .I %in% selected_rows]

这个方法和方法1结果完全一致,适合需要对选中行做额外处理的场景。

为什么之前的方法失效?

咱们来拆解你之前的问题:

  1. 用.I == sample(.I, ...)的问题:当sample()返回多个值时,比如组A返回c(1,3),.I是全局行号(组A的.I是1,2,3),此时1 == c(1,3)会返回c(TRUE, FALSE),2 == c(1,3)返回c(FALSE, FALSE),3 == c(1,3)返回c(FALSE, TRUE)——最终会得到一个长度为6的逻辑向量,而组A只有3行,data.table无法正确匹配,导致结果混乱。
  2. 组B未被标记的问题:你之前用dt[, C3 := .I == sample(.I, 1), by = C1]时,组B的.I是4,但由于随机状态的影响(前面的分组已经消耗了随机数),sample(4,1)返回的不是4,所以4 == 非4得到FALSE。而用组内位置sample(.N, 1)时,组B的.N=1,sample(1,1)必然返回1,所以组内唯一行的seq_len(.N) == 1会得到TRUE,完美解决这个问题。

关键要点

  • 优先用组内位置索引(seq_len(.N))代替全局行号.I做分组内的逻辑判断,避免长度不匹配。
  • 用min(.N, N)确保当组内行数不足N时,自动标记所有行,不用额外写判断逻辑。

内容的提问来源于stack exchange,提问作者Serhii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:23:15