r - 如何更快地计算排列的“交叉连接”?
问题描述
我有一个包含 2 列的 data.table。
“事件,组”
最多可以有 20 行,或者少至 1 行。
每个事件都被分类到一个给定的组中。
data.table 已经按组排序。
例如:
Events group
a 1
b 2
c 2
d 2
e 3
f 3
我需要做的是:
- 对于每个组计算其事件的所有排列。
- 计算该排列的所有交叉组合。
- 稍后对于每个“组合”,我将进一步计算。
在我的示例中,我会得到这种排列(按行显示)
a
b c d
b d c
c b d
c d b
d b c
d c b
e f
f e
最后是行的这种交叉组合:
a b c d e f
a b d c e f
a c b d e f
a c d b e f
a d b c e f
a d c b e f
a b c d f e
a b d c f e
a c b d f e
a c d b f e
a d b c f e
a d c b f e
我实现它的方式是:
library(data.table)
library(arrangements)
myDT <- data.table(ll=letters[1:6], gr=c(1,2,2,2,3,3)) #simple example
dos <- function(x,y) {
temp <- expand.grid(1:nrow(x),1:nrow(y))
cbind(x[temp[,1],], y[temp[,2],])
}
fun2 <- function(z) Reduce(dos,z)
permu <- function(xx ) { # alternative to compute the permutations
if (length(xx)==1) {
matrix(xx)
} else if (length(xx)==2) {
rbind(c(xx[1], xx[2]),c(xx[2], xx[1]))
} else {
permutations(xx)
} }
f1 <- function(x) {fun2(tapply(myDT$ll,myDT$gr, permutations))}
f2 <- function(x) {fun2(myDT[,.(.(permutations(ll))),by=gr]$V1)}
f3 <- function(x) {fun2(myDT[,.(.(permu(ll))),by=gr]$V1)}
第一种方法使用tapply。
第二种方法尝试以 data.table 的方式进行计算。第三种方法试图让小团体的计算速度更快。
我正在使用“安排”包中的排列,因为它很快。随意使用任何包(例如 RcppAlgos)或编写自己的算法。
我不介意输出是矩阵、data.table、列表、转置、使用其他容器还是以不同方式排序。
myDT <- data.table(ll=letters[1:6], gr=c(1,2,2,2,3,3))
f1() 982.05us 1.88KB 501ms
f2() 2.38ms 52.27KB 501ms
f3() 1.83ms 52.27KB 501ms
为了对其进行基准测试,我们可以使用一个更大的示例。
myDT <- data.table(ll=letters[1:15], gr=rep(1:5, times=rep(5:1))) # larger example
min median mem_alloc gc total_time
f1() 381.5ms 911ms 22.3MB 1.82s
f2() 123.5ms 185ms 22.3MB 580.22ms
f3() 99.3ms 130ms 22.3MB 505.05ms
我怎样才能更快地做到这一点?(也使用更少的内存会很好)
如果我尝试用 data.table(ll=letters[1:21], gr=rep(1:6, times=rep(6:1)) 来做它需要超过3 分钟,太长了,因为在我的实际问题中,我需要执行 100 万次计算。
解决方案
迟早你会遇到内存不足的问题data.table(ll=letters[1:21], gr=rep(1:6, times=rep(6:1)))
,你会生成 24,883,200 行 ( prod(factorial(DT[, .N, gr]$N))
)。
无论如何,如果绝对有必要生成所有排列,这里有一个选项:
library(data.table)
library(RcppAlgos)
DT <- data.table(ll=letters[1:6], gr=c(1,2,2,2,3,3))
DT <- data.table(ll=letters[1:21], gr=rep(1:6, times=rep(6:1)))
#prod(factorial(DT[, .N, gr]$N))
CJ.dt_1 <- function(...) {
Reduce(f=function(x, y) cbind(x[rep(1:nrow(x), times=nrow(y)),], y[rep(1:nrow(y), each=nrow(x)),]),
x=list(...))
} #CJ.dt_1
system.time(
ans <- do.call(CJ.dt_1, DT[, .(.(RcppAlgos::permuteGeneral(ll, .N))), gr]$V1)
)
# user system elapsed
# 16.49 4.63 21.15
推荐阅读
- oracle - oracle 查询抛出定义不明确的异常列
- mysql - 大型数据库 MYSQL 中的加载页面非常慢
- html - 网页背景图片
- python - 从列表中打印最小值(PYTHON)
- python - 如何编写代码来获取python列表中每个列表的最小值?
- docker - CMD 中的 Docker ENV
- amazon-web-services - Kinesis Streams 和 Kinesis Firehose 有什么区别?
- ios - 如何防止 WKWebView 对象崩溃?
- html - 如何实时转码 wmv 文件以在 html5 播放器中播放?
- lotus-notes - 将 Lotus Notes 中的多个字段(发件人的电子邮件地址等)导出到 Excel