배열을 data.frame으로 녹이지만 한 차원을 열로 변환합니다.

StackOverflow https://stackoverflow.com//questions/22003121

  •  20-12-2019
  •  | 
  •  

문제

여러 차원의 배열을 변환하고 싶습니다(예:x, y, z;아래 'arr' 참조)를 data.frame에 추가하되 열에 차원을 유지합니다(예:z, 아래 'df2' 참조).

현재 reshape2 패키지에서 Melt와 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

그러나 5M 값을 변환하는 데 약 10초가 걸렸습니다.

  user  system elapsed 
  8.13    1.11    9.39 

더 효율적인 방법이 있나요?어떤 제안이라도 보내주셔서 감사합니다.

도움이 되었습니까?

해결책

여기 약간 다음의 조합을 사용하여 4차원 배열에 대한 보다 일반화된 솔루션을 제공합니다. aperm(...) 그리고 matrix(...).나는 이것을 더 이상 일반화할 만큼 마법사가 아닙니다.

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

그러면 두 번째 방법이 10배 이상 빠릅니다.

> 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

다른 팁

cbind(x=rep(1:1000,each=1000), 
      y=1:1000, 
      matrix(arr, ncol=5, dimnames=list(list(),dimnames(arr)$z) ) ))
.

경과 시간은 10 분의 1 초가 초과되었습니다.이것은 str ()

의 결과였습니다.
 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에 넣을 수 있다고 가정합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top