在data.table中对每个组内的随机行进行采样

时间:2013-04-29 22:18:02

标签: r data.table

您如何使用data.table有效地获取数据框中每个组内的行样本?

DT = data.table(a = sample(1:2), b = sample(1:1000,20))
DT
    a   b
 1: 2 562
 2: 1 183
 3: 2 180
 4: 1 874
 5: 2 533
 6: 1  21
 7: 2  57
 8: 1  20
 9: 2  39
10: 1 948
11: 2 799
12: 1 893
13: 2 993
14: 1  69
15: 2 906
16: 1 347
17: 2 969
18: 1 130
19: 2 118
20: 1 732

我想的是:DT[ , sample(??, 3), by = a]会为每个“a”返回三行样本(返回行的顺序并不重要):

    a   b
 1: 2 180
 2: 2  57
 3: 2 799
 4: 1  69
 5: 1 347
 6: 1 732

5 个答案:

答案 0 :(得分:40)

也许是这样的?

> DT[,.SD[sample(.N, min(3,.N))],by = a]
   a   b
1: 1 744
2: 1 497
3: 1 167
4: 2 888
5: 2 950
6: 2 343

(感谢Josh的纠正,下面。)

答案 1 :(得分:8)

我相信朱兰的答案可以进一步推广。详细信息在这里(How do you sample groups in a data.table with a caveat),但我相信这个解决方案可以解释没有“3”行的样本。

当尝试从小于“x”公共值的行中“x”次采样时,当前解决方案将会出错。在下面的例子中,x = 3。它考虑到了这个警告。 (由nrussell完成的解决方案)

set.seed(123)
##
DT <- data.table(
  a=c(1,1,1,1:15,1,1), 
  b=sample(1:1000,20))
##
R> DT[,.SD[sample(.N,min(.N,3))],by = a]
     a   b
 1:  1 288
 2:  1 881
 3:  1 409
 4:  2 937
 5:  3  46
 6:  4 525
 7:  5 887
 8:  6 548
 9:  7 453
10:  8 948
11:  9 449
12: 10 670
13: 11 566
14: 12 102
15: 13 993
16: 14 243
17: 15  42

答案 2 :(得分:2)

this answer by David Arenburg的启发,另一种避免.SD分配的方法是对群组进行抽样,然后使用.EACHI

重新加入原始数据
DT[ DT[, sample(.N, 3), by=a], b[i.V1], on="a", by=.EACHI]

#    a  V1
# 1: 2  42
# 2: 2 498
# 3: 2 179
# 4: 1 469
# 5: 1  93
# 6: 1 898

DT[, sample(.N, 3), by=a]行为每个组提供样本

#         a V1
# 1:      1  9
# 2:      1  3
# 3:      1  2
# 4:      2  4
# 5:      2  9
# ---          

因此我们可以使用V1向我们提供与之对应的b

答案 3 :(得分:0)

Stratified sampling&gt; 过采样

size=don[y==1,.(strata=length(iden)),by=.(y,x)] # count of iden by strata   
table(don$x,don$y) 

don<-merge(don,size[,.(y,strata)],by="x") #merge strata values  
don_strata=don[,.SD[sample(.N,strata)],by=.(y,x)]

答案 4 :(得分:0)

影响这个问题的答案有两个微妙的考虑因素,乔希·奥布赖恩(Josh O'Brien)和瓦伦丁(Valentin)在评论中提到了这些因素。首先是通过.SD进行的子集效率非常低,最好直接对.I进行采样(请参见下面的基准)。

如果我们从.I进行采样,第二个考虑因素是,在sample(.I, size = 1).I > 1时调用length(.I) = 1会导致意外行为。在这种情况下,sample()的行为就像我们叫sample(1:.I, size = 1)一样,这肯定不是我们想要的。正如Valentin所指出的,在这种情况下,最好使用结构.I[sample(.N, size = 1)]

作为基准,我们构建了一个简单的1,000 x 1数据表,并按组随机抽样。即使data.table这么小,.I方法也大约快20倍。

library(microbenchmark)
library(data.table)

set.seed(1L)
DT <- data.table(id = sample(1e3, 1e3, replace = TRUE))

microbenchmark(
  `.I` = DT[DT[, .I[sample(.N, 1)], by = id][[2]]],
  `.SD` = DT[, .SD[sample(.N, 1)], by = id]
)
#> Unit: milliseconds
#>  expr       min        lq     mean    median        uq       max neval
#>    .I  2.396166  2.588275  3.22504  2.794152  3.118135  19.73236   100
#>   .SD 55.798177 59.152000 63.72131 61.213650 64.205399 102.26781   100

reprex package(v0.3.0)于2020-12-02创建