векторизация и распараллеливание дезагрегации списка

Вот некоторый код, который генерирует список data.frames, а затем преобразует этот исходный список в новый список, где каждый элемент списка представляет собой список строк каждого фрейма данных.

Например.
- l1 имеет длину 10, и каждый элемент представляет собой data.frame с 1000 строками.
- l2 — это список длиной 1000 (nrow(l1[[k]])), а каждый элемент — это list длины 10 (length(l1)), содержащий векторы-строки из элементов l1

l1 <- vector("list", length= 10)
set.seed(65L)
for (i in 1:10) {
  l1[[i]] <- data.frame(matrix(rnorm(10000),ncol=10))
}

l2 <- vector(mode="list", length= nrow(l1[[1]]))
for (i in 1:nrow(l1[[1]])) {
  l2[[i]] <- lapply(l1, function(l) return(unlist(l[i,])))
}

Изменить Чтобы прояснить, как l1 относится к l2, вот код, не зависящий от языка.

for (j in 1:length(l1) {
  for (i in 1:nrow(l1[[1]]) { # where nrow(l1[[1]]) == nrow(l1[[k]]) k= 2,...,10
    l2[[i]][[j]] <- l1[[j]][i,]
  }
}

Как мне ускорить создание l2 с помощью векторизации или распараллеливания? Проблема, с которой я столкнулся, заключается в том, что parallel::parLapplyLB разбивает списки; однако я не хочу разбивать список l1, я хочу разделить строки в каждом элементе l1. Промежуточное решение векторизовало бы мой текущий подход, используя некоторую функцию *apply для замены цикла for. Это, очевидно, может быть распространено и на параллельное решение.

Если я решу это самостоятельно до того, как будет найдено приемлемое решение, я опубликую свой ответ здесь.


person Alex W    schedule 11.03.2016    source источник
comment
Не назначайте сначала глобальную среду, добавляйте в список, а затем удаляйте из глобальной среды. Назначьте прямо в список (в вашем цикле for l1[[i]] <- ...) или просто l1 = replicate(10, data.frame(matrix(rnorm(10000), ncol = 10)), simplify = F)   -  person Gregor Thomas    schedule 11.03.2016
comment
Ваши настоящие кадры данных с одинаковым количеством столбцов?   -  person Tensibai    schedule 11.03.2016
comment
@Tensibai гарантирует, что каждый элемент l1 будет иметь одинаковое количество строк, но не одинаковое количество столбцов.   -  person Alex W    schedule 11.03.2016
comment
Я не совсем понимаю, что вы пытаетесь получить в конце, это транспозиция каждого df в l1?   -  person Tensibai    schedule 11.03.2016
comment
@Tensibai Я разбиваю каждый элемент l1 на составляющие его строки. Таким образом, вместо списка длиной 10, где каждый элемент имеет 1000 строк, у меня есть список длиной 1000, где каждый элемент представляет собой список длиной 10.   -  person Alex W    schedule 11.03.2016
comment
Правильно, просто комментарий, а не ответ.   -  person Gregor Thomas    schedule 11.03.2016
comment
@ Алекс, чего я не понял, так это того, что находится в списке длиной 10, 10 векторов из 1000 значений из столбцов l1 df?   -  person Tensibai    schedule 11.03.2016
comment
Хорошо, понял, каждая запись l2 представляет собой список одного и того же индекса строки для каждого df в l1... то есть: l2[[2]][[3]] <- l1[[3]][2,] (правильно?)   -  person Tensibai    schedule 11.03.2016
comment
@Tensibai Да, ваш последний комментарий. могу уточнить в посте   -  person Alex W    schedule 11.03.2016


Ответы (1)


Я бы полностью сломал структуру и перестроил второй список через split. Этот подход требует гораздо больше памяти, чем исходный, но, по крайней мере, для данного примера он> в 10 раз быстрее:

sgibb <- function(x) {
  ## get the lengths of all data.frames (equal to `sapply(x, ncol)`)
  n <- lengths(x)
  ## destroy the list structure
  y <- unlist(x, use.names = FALSE)
  ## generate row indices (stores the information which row the element in y
  ## belongs to)
  rowIndices <- unlist(lapply(n, rep.int, x=1L:nrow(x[[1L]])))
  ## split y first by rows
  ## and subsequently loop over these lists to split by columns
  lapply(split(y, rowIndices), split, f=rep.int(seq_along(n), n))
}

alex <- function(x) {
  l2 <- vector(mode="list", length= nrow(x[[1]]))
  for (i in 1:nrow(x[[1]])) {
    l2[[i]] <- lapply(x, function(l) return(unlist(l[i,])))
  }
  l2
}

## check.attributes is need because the names differ
all.equal(alex(l1), sgibb(l1), check.attributes=FALSE)

library(rbenchmark)
benchmark(alex(l1), sgibb(l1), order = "relative", replications = 10)
#       test replications elapsed relative user.self sys.self user.child sys.child
#2 sgibb(l1)           10   0.808    1.000     0.808        0          0         0
#1  alex(l1)           10  11.970   14.814    11.972        0          0         0
person sgibb    schedule 11.03.2016
comment
Очень признателен! Я получаю улучшение в 8,57 раз на реальном тестовом наборе данных. - person Alex W; 11.03.2016