2014-02-25 29 views
0

我想将多维数组(例如x,y,z;请参阅下面的'arr')转换为data.frame,但保留一个维在列中(例如z,见下面的'df2')。将数组融入data.frame,但将一维转换为列

目前,我在reshape2包中使用了熔化和dcast函数。

set.seed(1111) 
num <- 1000 
dim_names <- list(x = seq(num), y = seq(num), 
    z = paste0('Z', 1:5)) 
dim_arr <- as.numeric(lapply(dim_names, length)) 
arr <- array(runif(prod(dim_arr)), dim = dim_arr) 
dimnames(arr) <- dim_names 

library(reshape2) 
df <- melt(arr) 
head(df) 
system.time(df2 <- dcast(df, x + y ~ z, value.var = 'value')) 
head(df2) 

    x y  Z1  Z2  Z3  Z4   Z5 
1 1 1 0.4655026 0.8027921 0.1950717 0.0403759 0.04669389 
2 1 2 0.5156263 0.5427343 0.5799924 0.1911539 0.26069063 
3 1 3 0.2788747 0.9394142 0.9081274 0.7712205 0.68748300 
4 1 4 0.2827058 0.8001632 0.6995503 0.9913805 0.25421346 
5 1 5 0.7054767 0.8013255 0.2511769 0.6556174 0.07780849 
6 1 6 0.5576141 0.6452644 0.3362980 0.7353494 0.93147223 

然而,花了大约10秒至5转换的M值

user system elapsed 
    8.13 1.11 9.39 

是否有更有效的方法?感谢您的任何建议。

回答

1

这是略微使用aperm(...)matrix(...)的组合的4维阵列的更一般化的解决方案。我不是巫师,不能进一步概括这一点。

nx <- 2 ; ny <- 3 ; nz <- 4 ; nw <- 5 
original <- array(rnorm(nx*ny*nz*nw), dim=c(nx,ny,nz,nw), 
       dimnames=list(x=sprintf('x%s', 1:nx), y=sprintf('y%s', 1:ny), 
          z=sprintf('z%s', 1:nz), w=sprintf('w%s', 1:nw))) 

这是一个使用melt(...)dcast(...)删除所有,但最后一个维度现有的方法:

f.dcast <- function(a) dcast(melt(a), x + y + z ~ w) 

下面以aperm(...)写出来的数据作为一个向量做同样的事情一个特定的顺序,以便它作为一个格式正确的矩阵,然后cbind s与变量名称:

f.aperm <- function(a) { 
    d <- dim(a) 

    data <- matrix(as.vector(aperm(a, c(4,3,2,1))), ncol=d[4], byrow=T) 
    colnames(data) <- dimnames(a)[[4]] 

    # specify levels in the same order as the input so they don't wind up alphabetical 
    varnames <- data.frame(
    factor(rep(dimnames(a)[[1]], times=1,   each=d[2]*d[3]), levels=dimnames(a)[[1]]), 
    factor(rep(dimnames(a)[[2]], times=d[1],  each=d[3] ), levels=dimnames(a)[[2]]), 
    factor(rep(dimnames(a)[[3]], times=d[1]*d[2], each=1  ), levels=dimnames(a)[[3]]) 
) 

    names(varnames) <- names(dimnames(a))[1:3] 

    cbind(varnames, data) 
} 

他们都给予我同样的结果:

> desired <- f.dcast(original) 
> test <- f.aperm(original) 
> all.equal(desired, test) 
[1] TRUE 

第二种方法是6数组倍的速度这个尺寸:

> microbenchmark::microbenchmark(f.dcast(original), f.aperm(original)) 
Unit: milliseconds 
       expr  min  lq  mean median  uq  max neval 
f.dcast(original) 7.270208 7.343227 7.703360 7.481984 7.698812 10.392141 100 
f.aperm(original) 1.218162 1.244595 1.327204 1.259987 1.291986 4.182391 100 

如果我增加了原数组的大小:

nx <- 10 ; ny <- 20 ; nz <- 30 ; nw <- 40 

然后第二种方法快十倍以上:

> microbenchmark::microbenchmark(f.dcast(original), f.aperm(original)) 
Unit: milliseconds 
       expr  min  lq  mean median  uq  max neval 
f.dcast(original) 303.21812 385.44857 381.29150 392.11693 394.81721 472.80343 100 
f.aperm(original) 18.62788 22.25814 28.85363 23.90133 24.54939 97.96776 100 
0
cbind(x=rep(1:1000,each=1000), 
     y=1:1000, 
     matrix(arr, ncol=5, dimnames=list(list(),dimnames(arr)$z)))) 

这段时间约为十分之一秒。这是海峡的结果()

num [1:1000000, 1:7] 1 1 1 1 1 1 1 1 1 1 ... 
- attr(*, "dimnames")=List of 2 
    ..$ : NULL 
    ..$ : chr [1:7] "x" "y" "Z1" "Z2" .. 

我想你可以把在row.names,虽然它确实增加经过时间有点过一秒钟。

+0

感谢您的建议。不过,我可能需要更通用的解决方案,因为我的数组有大约10个维度。 – Bangyou

+3

发表一个缺乏足够的复杂性以使准确答案有用的例子总是一个坏主意。浪费我们的时间和你的时间。 –