【问题标题】:How to take a random subsample from rows of a data.table per factor?如何从每个因素的 data.table 行中获取随机子样本?
【发布时间】:2017-10-18 11:55:50
【问题描述】:

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

我现在要做的是在 data.table 中添加一个“行号”rn,使用各种 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 中快速采样的方法,但它不是按因素来做的:https://stackoverflow.com/a/33201094/5252333 我还看到了如何对等量的每个因素执行此操作的技巧,但不是针对不同数量的每个因素。

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 分钟。该函数对子集执行一些计算,然后返回输出。这很慢,我想知道是否可以通过某种方式通过引用来做到这一点。

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

【问题讨论】:

  • 试试DT[data.table(factor = letters[1:3], val = c(12, 20, 5)), on = .(factor)][, sample(DT$rn, val[1], replace = TRUE), factor]
  • @akrun: 返回一个带有factor 和采样的rn 的data.table,所以在那之后我仍然需要对DT 进行子集化。所以它是所有 if (else) 语句的一个很好的替代品,但我想知道这整个事情是否可以一步完成,所以我根本不需要 rn
  • 不需要rn。您可以使用.I 代替rn
  • 这两个选项之间的差异显然已经很明显了:在获得rn 10k 次的两种方法上使用微基准测试,我的 if/else 方法和您的 @987654337 的平均值为 95.43 纳秒@ 方法 31.47 纳秒。

标签: r data.table row sample


【解决方案1】:

我们可以加入一个键/值数据集并使用.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))

是一个键/值data.table,通过加入on“因子”来获取“val”作为原始数据集上的列。

第二步,我们进行连接

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

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

DT[....$V1]

【讨论】:

  • 您能否解释一下这是如何工作的以及on = 的作用?另外,您知道预先设置密钥是否可以以某种方式提高子集的速度吗?编辑:在on 上找到documentation^pdfthis vignette。不过,我们将不胜感激。
  • @Japhir 添加了更多描述。在这种情况下,设置密钥可能不会提高速度。但是,您可以尝试使用基准测试
  • 我刚刚注意到我显然有一个特殊情况,如果您不为 DT 中的每个因素分配多个观察值,它就不起作用。为了解决这个问题,我不得不DT[indexDT, on = .(factor)][...$V1]
  • @Japhir 对不起,我没有得到这个特殊情况。你是说那行数少了?
  • 不,对于 mwe 来说,就像它所在的数据表中的因子包含 a 和 b,但没有 c。我现在想我应该将 c 的 val(在我的实际情况下还有许多其他的)设置为 0。这样可以更优雅地解决问题。
猜你喜欢
  • 2019-12-28
  • 2013-04-23
  • 2022-01-11
  • 2015-08-22
  • 2018-10-24
  • 1970-01-01
  • 2022-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多