Sciogliere una matrice in dati. Frame ma convertendo una dimensione in colonne
Domanda
Vorrei convertire un array con più dimensioni (ad es. x, y, z; vedi 'arr' sotto) in un dato. Franco, ma mantenere una dimensione nelle colonne (ad es. Z, vedere 'DF2' sotto).
Attualmente, uso le funzioni di fusione e dCast nel pacchetto RESHAPE2.
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
.
Tuttavia, ci sono voluti circa 10 s per convertire 5 valori M
user system elapsed
8.13 1.11 9.39
.
Ci sono metodi più efficienti?Grazie per qualsiasi suggerimento.
Soluzione
Ecco un leggermente una soluzione più generalizzata per un array di 4 dimensioni utilizzando una combinazione di aperm(...)
e matrix(...)
.Non sono anche un mago da generalizzare ulteriormente.
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)))
.
Questo è il tuo metodo esistente che utilizza melt(...)
e dcast(...)
per rimuovere tutti tranne l'ultima dimensione:
f.dcast <- function(a) dcast(melt(a), x + y + z ~ w)
.
Di seguito è la stessa cosa utilizzando aperm(...)
per scrivere i dati come vettore in un determinato ordine in modo che si snoda come una matrice correttamente formattata, quindi cbind
con i nomi delle variabili:
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)
}
.
Entrambi mi danno lo stesso risultato:
> desired <- f.dcast(original)
> test <- f.aperm(original)
> all.equal(desired, test)
[1] TRUE
.
Il secondo metodo è 6 volte più veloce per un array questa taglia:
> 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
.
Se aumento delle dimensioni dell'array originale:
nx <- 10 ; ny <- 20 ; nz <- 30 ; nw <- 40
.
Quindi il secondo metodo è più veloce di oltre dieci volte:
> 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
. Altri suggerimenti
cbind(x=rep(1:1000,each=1000),
y=1:1000,
matrix(arr, ncol=5, dimnames=list(list(),dimnames(arr)$z) ) ))
.
tempo trascorso per quello era circa un decimo di secondo.Questo è stato il risultato di 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" ..
.
Suppongo che tu possa mettere in row.names, anche se aumenta il tempo trascorso a un po 'sopra un secondo.