如何从每个因素的 data.table 行中抽取随机子样本?

How to take a random subsample from rows of a data.table per factor?

我正在尝试创建一个大数据集的子样本,其中每个因素都有不同的 n。我希望能够非常快地执行此操作,因为我已经执行了数十万次。你能帮我用 data.tables 优化这个过程吗?

我现在做的是将 "rownumber" rn 添加到 data.table,使用各种 n 对其进行采样,然后基于它进行子集。我认为一定有更聪明的方法来做到这一点,但我似乎无法弄清楚。

# generate data.table
DT <- data.table(rn = 1:100, factor = letters[1:3],
  value = rnorm(100, c(1, 5, 10)))

# subset based on "row number" with various numbers per category
subsetrn <- DT[, .(rn = sample(rn,
  if (factor == "a") 12
  else if (factor == "b") 20
  else if (factor == "c") 5
  else NULL, replace = TRUE)),
  by = factor]

# subset
ss <- DT[rn %in% subsetrn[, rn]]

编辑:我见过这种从 data.table 中快速采样的方法,但它不是按因子进行的: 我还看到了如何对每个因素进行等量处理的技巧,但不是对每个因素进行不同量处理的技巧。

EDIT2:我一直在研究@akron 的解决方案,但仍然遇到问题:

如果我有一个因子为 0,则有问题:

# generate data.table
DT <- data.table(factor = letters[1:4],
  value = rnorm(300, c(1, 5, 10)))

# subset based on "row number" with various numbers per category subsetrn

# index data table with numbers
id <- data.table(factor = letters[1:4], val = c(12, 20, 5, 0))
# map our DT onto it, then subsample by the new val
ssid <- DT[id, on = .(factor)][, sample(.I, val[1], replace = TRUE), factor]
# subset
ss <- DT[ssid[, V1]]
count(ss[, factor])
##   x freq
## 1 a   10
## 2 b   12
## 3 c    5
## 4 d   10

# this is wrong! It only works if I do it like this
ss <- DT[id, on = .(factor)][ssid[, V1]]

我希望能够获得 ssid,这样我就可以 DT[ssid[, V1]](或 DT[ssid]),这样我就可以通过引用来做所有事情,而不是制作 DT 的本地副本.在我的应用程序中,这一切都包含在一个函数中,该函数当前复制 DT 的一小部分 50k 次,耗时约 25 分钟。该函数对子集执行一些计算,然后返回输出。这很慢,我想弄清楚是否可以通过某种方式引用它。

这个问题现在可能变得有点太具体了 ;-)。

我们可以加入 key/value 数据集并使用 .Isample

DT[DT[data.table(factor = letters[1:3], val = c(12, 20, 5)), 
      on = .(factor)][, sample(.I, val[1], replace = TRUE), factor]$V1]

如果我们把它分成几部分-

data.table(factor = letters[1:3], val = c(12, 20, 5))

是一个 key/value data.table,通过加入 on 'factor` 将 'val' 作为原始数据集的列。

第二步,我们做joining

DT[data.table(factor = letters[1:3], val = c(12, 20, 5)), 
      on = .(factor)]

现在,我们 sample 行索引,按 'factor' 分组,指定 size 作为 'val' 的第一个元素,提取行索引列 $V1 并使用它来对原始数据集进行子集化。即

DT[....$V1]