Расплавить массив в данные. Справо, но преобразование одно измерение в столбцы

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

  •  20-12-2019
  •  | 
  •  

Вопрос

Я хотел бы преобразовать массив с несколькими размерами (например, x, y, z; см. «ARR» ниже) в данные. Справка, но держите размер в столбцах (например, Z, см. «DF2» ниже)Отказ

В настоящее время я использую плавленые и DCAC, функции в пакете 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
.

Тем не менее, потребовалось около 10 с, чтобы преобразовать 5 м значений

  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 с именами переменной:

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
.

Другие советы

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" ..
.

Я полагаю, вы можете положить в ряд. Имеется, что это увеличивается, прошедшее время немного на секунду.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top