在R中对受试者ID进行重新采样

6
假设我们有以下数据。
set.seed(123)
dat <- data.frame(var1=c(10,35,13,19,15,20,19), id=c(1,1,2,2,2,3,4))
(sampledIDs <- sample(min(dat$id):max(dat$id), size=3, replace=TRUE))
> [1] 2 4 2

sampledIDs是从dat$id中进行(带替换)抽样的id向量。 我需要能够在更多变量的大型数据集上使用的结果代码:

  var1 id
   13  2
   19  2
   15  2
   19  4
   13  2
   19  2
   15  2

代码dat[which(dat$id%in%sampledIDs),]不能给我想要的结果,因为这个代码的结果是:

  var1 id
    13  2
    19  2
    15  2
    19  4

在这个数据中,仅当主体为dat$id==2时,其出现次数为一次(我知道结果是什么,但不知道如何获得我想要的结果)。请问有人能帮忙吗?


编辑:感谢答案,以下是所有答案的运行时间(对于那些感兴趣的人):

                                                                 test replications elapsed relative user.self
3   dat[unlist(lapply(sampledIDs, function(x) which(x == dat$id))), ]         1000    0.67    1.000      0.64
1 dat[which(sapply(sampledIDs, "==", dat$id), arr.ind = TRUE)[, 1], ]         1000    0.67    1.000      0.67
2        do.call(rbind, split(dat, dat$id)[as.character(sampledIDs)])         1000    1.83    2.731      1.83
4                               setkey(setDT(dat), id)[J(sampledIDs)]         1000    1.33    1.985      1.33

1
提供答案分析和明确的问题陈述,将会得到+1分。 - Carl Witthoft
数据大小是多少?你之前提到过你有大量的数据。 - David Arenburg
不算真正的大数据,但比例子中的观测值/变量更多:'data.frame': 4454个观测值,15个变量。 - Giuseppe
1
你说你的数据很大...让我发布一个相对较大的数据集基准测试。4K数据集并不算大,它是微小的。 - David Arenburg
3个回答

5

对于使用 data.table 处理大数据集,这可能是最快的方法,借助二进制搜索

library(data.table)
setkey(setDT(dat), id)[J(sampledIDs)]
#    var1 id
# 1:   13  2
# 2:   19  2
# 3:   15  2
# 4:   19  4
# 5:   13  2
# 6:   19  2
# 7:   15  2

编辑: 这里有一个基准测试,针对一组不是很大的数据集(1e+05行),它可以说明哪个选项是明显的赢家。

library(data.table)
library(microbenchmark)

set.seed(123)
n <- 1e5
dat <- data.frame(var1 = sample(seq_len(100), n, replace = TRUE), id = sample(seq_len(10), n, replace = TRUE))
(sampledIDs <- sample(min(dat$id) : max(dat$id), size = 3, replace = TRUE))
dat2 <- copy(dat)

Sven1 <- function(dat) dat[unlist(lapply(sampledIDs, function(x) which(x == dat$id))), ]
Sven2 <- function(dat) dat[which(sapply(sampledIDs, "==", dat$id), arr.ind = TRUE)[ , 1], ]
flodel <- function(dat) do.call(rbind, split(dat, dat$id)[as.character(sampledIDs)])
David <- function(dat2) setkey(setDT(dat2), id)[J(sampledIDs)]

Res <- microbenchmark(Sven1(dat),
                      Sven2(dat), 
                      flodel(dat), 
                      David(dat2))
Res
# Unit: milliseconds
#        expr       min        lq    median        uq       max neval
#  Sven1(dat)  4.356151  4.817557  6.715533  7.313877 45.407768   100
#  Sven2(dat)  9.750984 12.385677 14.324671 16.655005 54.797096   100
# flodel(dat) 36.097602 39.680006 42.236017 44.314981 82.261879   100
# David(dat2)  1.813387  2.068749  2.154774  2.335442  8.665379   100

boxplot(Res)

enter image description here


举个例子,如果我们想要采样的不仅仅是3个ID,而是10个ID,那么基准测试就变得荒谬了。

(sampledIDs <- sample(min(dat$id) : max(dat$id), size = 10, replace = TRUE))
[1]  7  6 10  9  5  9  5  3  7  3
# Unit: milliseconds
#       expr        min         lq     median         uq       max neval
#  Sven1(dat)  80.124502  89.141162  97.908365 104.111738 175.40919   100
#  Sven2(dat)  99.010410 127.797966 159.404395 170.751069 209.96887   100
# flodel(dat) 129.722435 144.847505 157.737362 178.242103 232.41293   100
# David(dat2)   2.431682   2.721038   2.855103   3.057796  19.60826   100

enter image description here


3

您可以执行以下操作:

do.call(rbind, split(dat, dat$id)[as.character(sampledIDs)])

3

一种方法:

dat[unlist(lapply(sampledIDs, function(x) which(x == dat$id))), ]
#     var1 id
# 3     13  2
# 4     19  2
# 5     15  2
# 7     19  4
# 3.1   13  2
# 4.1   19  2
# 5.1   15  2

另一种方法:
dat[which(sapply(sampledIDs, "==", dat$id), arr.ind = TRUE)[ , 1], ]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接