我有两个data.table; 我想从匹配键的那些中随机地分配一个元素到另一个.我现在这样做的方式很慢.
让我们具体一点; 这是一些示例数据:
dt1<-data.table(id=sample(letters[1:5],500,replace=T),var1=rnorm(500),key="id")
dt2<-data.table(id=c(rep("a",4),rep("b",8),rep("c",2),rep("d",5),rep("e",7)),
place=paste(sample(c("Park","Pool","Rec Center","Library"),
26,replace=T),
sample(26)),key="id")
Run Code Online (Sandbox Code Playgroud)
我想为每个观察添加两个随机选择的places dt1,但places必须匹配id.
这就是我现在正在做的事情:
get_place<-function(xx) sapply(xx,function(x) dt2[.(x),sample(place,1)])
dt1[,paste0("place",1:2):=list(get_place(id),get_place(id))]
Run Code Online (Sandbox Code Playgroud)
这有效,但它很慢 - 在我的电脑上运行需要66秒,基本上是一个eon.
一个问题似乎是我似乎无法正确利用键控:
类似的东西dt2[.(dt1$id),mult="random"]是完美的,但似乎不可能.
有什么建议?
一个简单的答案
dt2[.(dt1),as.list(c(
place=sample(place,size=2,replace=TRUE)
)),by=.EACHI,allow.cartesian=TRUE]
Run Code Online (Sandbox Code Playgroud)
这种方法很简单,并且说明了data.table像笛卡尔连接这样的特征by=.EACHI,但是非常慢,因为对于dt1它的每一行(i)样本和(ii)将结果强制转换为列表.
一个更快的答案
nsamp <- 2
dt3 <- dt2[.(unique(dt1$id)),list(i0=.I[1]-1L,.N),by=.EACHI]
dt1[.(dt3),paste0("place",1:nsamp):=
replicate(nsamp,dt2$place[i0+sample(N,.N,replace=TRUE)],simplify=FALSE)
,by=.EACHI]
Run Code Online (Sandbox Code Playgroud)
使用replicatewith simplify=FALSE(同样在@bgoldst的答案中)最有意义:
data.table创建新列时所需的格式.replicate 是重复模拟的标准R函数.基准.我们应该看看不同的几个功能,而不是dt1随着我们的进行修改:
# candidate functions
frank2 <- function(){
dt3 <- dt2[.(unique(dt1$id)),list(i0=.I[1]-1L,.N),by=.EACHI]
dt1[.(dt3),
replicate(nsamp,dt2$place[i0+sample(N,.N,replace=TRUE)],simplify=FALSE)
,by=.EACHI]
}
david2 <- function(){
indx <- dt1[,.N, id]
sim <- dt2[.(indx),
replicate(2,sample(place,size=N,replace=TRUE),simplify=FALSE)
,by=.EACHI]
dt1[, sim[,-1,with=FALSE]]
}
bgoldst<-function(){
dt1[,
replicate(2,ave(id,id,FUN=function(x)
sample(dt2$place[dt2$id==x[1]],length(x),replace=T)),simplify=F)
]
}
# simulation
size <- 1e6
nids <- 1e3
npls <- 2:15
dt1 <- data.table(id=sample(1:nids,size=size,replace=TRUE),var1=rnorm(size),key="id")
dt2 <- unique(dt1)[,list(place=sample(letters,sample(npls,1),replace=TRUE)),by=id]
# benchmarking
res <- microbenchmark(frank2(),david2(),bgoldst(),times=10)
print(res,order="cld",unit="relative")
Run Code Online (Sandbox Code Playgroud)
这使
Unit: relative
expr min lq mean median uq max neval cld
bgoldst() 8.246783 8.280276 7.090995 7.142832 6.579406 5.692655 10 b
frank2() 1.042862 1.107311 1.074722 1.152977 1.092632 0.931651 10 a
david2() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10 a
Run Code Online (Sandbox Code Playgroud)
如果我们切换参数......
# new simulation
size <- 1e4
nids <- 10
npls <- 1e6:2e6
dt1 <- data.table(id=sample(1:nids,size=size,replace=TRUE),var1=rnorm(size),key="id")
dt2 <- unique(dt1)[,list(place=sample(letters,sample(npls,1),replace=TRUE)),by=id]
# new benchmarking
res <- microbenchmark(frank2(),david2(),times=10)
print(res,order="cld",unit="relative")
Run Code Online (Sandbox Code Playgroud)
我们看
Unit: relative
expr min lq mean median uq max neval cld
david2() 3.3008 3.2842 3.274905 3.286772 3.280362 3.10868 10 b
frank2() 1.0000 1.0000 1.000000 1.000000 1.000000 1.00000 10 a
Run Code Online (Sandbox Code Playgroud)
正如人们所预料的,哪种方式速度更快-崩溃dt1的david2或折叠dt2的frank2-取决于有多少信息被倒塌的压缩.