Ein Array in data.frame verschmelzen, aber eine Dimension in Spalten umwandeln
Frage
Ich möchte ein Array mit mehreren Dimensionen konvertieren (z. B.x, y, z;siehe „arr“ unten) in einen data.frame, aber behalten Sie eine Dimension in den Spalten bei (z. B.z, siehe 'df2' unten).
Derzeit verwende ich die Funktionen Melt und Dcast im Paket 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
Die Konvertierung von 5 M-Werten dauerte jedoch etwa 10 s
user system elapsed
8.13 1.11 9.39
Gibt es effizientere Methoden?Vielen Dank für alle Vorschläge.
Lösung
Hier ist ein leicht allgemeinere Lösung für ein 4-dimensionales Array unter Verwendung einer Kombination von aperm(...)
Und matrix(...)
.Ich bin kein Zauberer genug, um das weiter zu verallgemeinern.
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)))
Dies ist Ihre vorhandene Methode, die verwendet wird melt(...)
Und dcast(...)
So entfernen Sie alle bis auf die letzte Dimension:
f.dcast <- function(a) dcast(melt(a), x + y + z ~ w)
Das Folgende macht dasselbe, indem es verwendet wird aperm(...)
um die Daten dann als Vektor in einer bestimmten Reihenfolge auszuschreiben, damit sie als richtig formatierte Matrix enden cbind
s mit den Variablennamen:
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)
}
Beide geben mir das gleiche Ergebnis:
> desired <- f.dcast(original)
> test <- f.aperm(original)
> all.equal(desired, test)
[1] TRUE
Die zweite Methode ist für ein Array dieser Größe sechsmal schneller:
> 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
Wenn ich die Größe des ursprünglichen Arrays vergrößere:
nx <- 10 ; ny <- 20 ; nz <- 30 ; nw <- 40
Dann ist die zweite Methode über zehnmal schneller:
> 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
Andere Tipps
verstrichene Zeit dafür war rund ein Zehntelsekunde.Dies war das Ergebnis von str ()
generasacodicetagpre.Ich nehme an, Sie könnten in Row.names eingeben, obwohl er über eine Sekunde bis ein bisschen verstrichene Zeit erhöht.