배열을 data.frame으로 녹이지만 한 차원을 열로 변환합니다.
문제
여러 차원의 배열을 변환하고 싶습니다(예: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에 넣을 수 있다고 가정합니다.
제휴하지 않습니다 StackOverflow