2016-03-11 2 views
4

У меня есть список больших матриц. Все эти матрицы имеют одинаковое количество строк, и я хочу «перечислить» их и связать все их столбцы вместе. Ниже приведен фрагмент кода, который я написал, но я не уверен, что это лучшее, что я могу достичь с точки зрения вычислительной эффективности.Ускоренный способ перечислить список больших матриц?

# simulate 
n <- 10 
nr <- 24 
nc <- 8000 
test <- list() 
set.seed(1234) 
for (i in 1:n){ 
    test[[i]] <- matrix(rnorm(nr*nc),nr,nc) 
} 

> system.time(res <- matrix(as.numeric(unlist(test)) ,nr,nc*n)) 
user system elapsed 
0.114 0.006 0.120 
+3

Возможно, попробуйте 'do.call (cbind, test)' – Tensibai

+0

@ Tensibai Спасибо, я попробовал, и это действительно намного быстрее. Будет обновлено мое сообщение соответственно. Любая интуиция, почему она намного быстрее? – Bayesric

+0

Меньше всего копия и литье. Я думаю, – Tensibai

ответ

6

Для работы в списке и вызвать функцию на всех объектах, do.call моя обычная первая идея, наряду с cbind здесь, чтобы связать по столбцам все объекты.

Для n=100 (с другими ответами для полноты картины):

n <- 10 
nr <- 24 
nc <- 8000 
test <- list() 
set.seed(1234) 
for (i in 1:n){ 
    test[[i]] <- matrix(rnorm(nr*nc),nr,nc) 
} 

require(data.table) 
ori <- function() { matrix(as.numeric(unlist(test)) ,nr,nc*n) } 
Tensibai <- function() { do.call(cbind,test) } 
BrodieG <- function() { `attr<-`(do.call(c, test), "dim", c(nr, nc * n)) } 
nicola <- function() { setattr(unlist(test),"dim",c(nr,nc*n)) } 

library(microbenchmark) 
microbenchmark(r1 <- ori(), 
       r2 <- Tensibai(), 
       r3 <- BrodieG(), 
       r4 <- nicola(), times=10) 

Результаты:

Unit: milliseconds 
      expr  min  lq  mean median  uq  max neval cld 
     r1 <- ori() 23.834673 24.287391 39.49451 27.066844 29.737964 93.74249 10 a 
r2 <- Tensibai() 17.416232 17.706165 18.18665 17.873083 18.192238 21.29512 10 a 
    r3 <- BrodieG() 6.009344 6.145045 21.63073 8.690869 10.323845 77.95325 10 a 
    r4 <- nicola() 5.912984 6.106273 13.52697 6.273904 6.678156 75.40914 10 a 

Что касается почему (в комментариях), @nicola сделал дать ответ об этом, есть меньше копии, чем оригинальный метод.

Все методы дает тот же результат:

> identical(r1,r2,r3,r4) 
[1] TRUE 
4

Кажется, что do.call бьет другой метод из-за копии, сделанные в ходе matrix вызова. Интересно, что вы можете избежать этой копии, используя функцию data.table::setattr, которая позволяет устанавливать атрибуты по ссылке, избегая любой копии. Я также пропустил часть as.numeric, так как это необязательно (unlist(test) уже numeric). Итак:

require(microbenchmark) 
require(data.table) 
f1<-function() setattr(unlist(test),"dim",c(nr,nc*n)) 
f2<-function() do.call(cbind,test) 
microbenchmark(res <-f1(),res2 <- f2(),times=10) 
#Unit: milliseconds 
#  expr  min  lq  mean median  uq  max neval 
# res <- f1() 4.088455 4.183504 7.540913 4.44109 4.988605 35.05378 10 
#res2 <- f2() 18.325302 18.379328 18.776834 18.66857 19.100681 19.47415 10 
identical(res,res2) 
#[1] TRUE 
4

Я думаю, у меня есть лучший. Мы можем избежать некоторых накладных расходов с cbind, так как у нас знаю все они имеют одинаковое количество строк и столбцов. Вместо этого мы используем c зная, что основной вектор характер матриц позволит нам повторно обернуть их в правильные размеры:

microbenchmark(
    x <- `attr<-`(do.call(c, test), "dim", c(nr, nc * n)), 
    y <- do.call(cbind, test) 
) 
# Unit: milliseconds 
#             expr  min  lq 
# x <- `attr<-`(do.call(c, test), "dim", c(nr, nc * n)) 4.435943 4.699006 
#        y <- do.call(cbind, test) 19.339477 19.567063 
#  mean median  uq  max neval cld 
# 12.76214 5.209938 9.095001 379.77856 100 a 
# 21.64878 20.000279 24.210848 26.02499 100 b 

identical(x, y) 
# [1] TRUE 

Если вы варьируя количество колонок вы можете, вероятно, до сих пор сделать это с некоторой осторожностью при вычислении общего количества столбцов.

+0

Это действительно удивительно быстро! Спасибо за ответ. Два вопроса: 1. Эти матрицы в моем реальном приложении имеют разное количество столбцов, этот метод по-прежнему возможен? 2. Я заметил, что ваш метод превосходит 'do.call (cbind, test)' во всех аспектах, кроме 'max' количества времени (' max' - 379.77856), почему? – Bayesric

+0

@EricWang, вы должны проверить, но пока они имеют одинаковое количество строк, и вы указываете правильное количество столбцов (т. Е. Количество столбцов для nc * n'), вы должны быть в порядке. Re time spike, я бы не стал читать много в максимуме, вероятно, столкнулся с процессом очистки системы. Я снова побежал и получил постоянную работу по всем направлениям. – BrodieG

Смежные вопросы