Передать список списков

У меня есть список, который содержит записи списка, и мне нужно транспонировать структуру. Первоначальная структура является прямоугольной, но имена в подписях не совпадают.

Вот пример:

ax <- data.frame(a=1,x=2)
ay <- data.frame(a=3,y=4)
bw <- data.frame(b=5,w=6)
bz <- data.frame(b=7,z=8)
before <- list(  a=list(x=ax, y=ay),   b=list(w=bw, z=bz))

Что я хочу:

after  <- list(w.x=list(a=ax, b=bw), y.z=list(a=ay, b=bz))

Меня не интересуют имена результирующего списка (на любом уровне).

Ясно, что это можно сделать явно:

after <- list(x.w=list(a=before$a$x, b=before$b$w), y.z=list(a=before$a$y, b=before$b$z))

но это уродливо и работает только для структуры 2x2. Какой идиоматический способ сделать это?

Ответ 1

Следующий фрагмент кода создает список с I-го элемента каждого списка в before тем:

lapply(before, "[[", i)

Теперь вам просто нужно сделать

n <- length(before[[1]]) # assuming all lists in before have the same length
lapply(1:n, function(i) lapply(before, "[[", i))

и он должен дать вам то, что вы хотите. Он не очень эффективен (перемещает каждый список много раз), и вы, вероятно, можете сделать его более эффективным, указав на текущие элементы списка, поэтому, пожалуйста, решите, достаточно ли это для вас.

Ответ 2

purrr пакет purrr делает этот процесс очень простым:

library(purrr)

before %>% transpose()

## $x
## $x$a
##   a x
## 1 1 2
## 
## $x$b
##   b w
## 1 5 6
## 
## 
## $y
## $y$a
##   a y
## 1 3 4
## 
## $y$b
##   b z
## 1 7 8

Ответ 3

Здесь другая идея - используйте тот факт, что data.table может хранить data.frame (на самом деле, учитывая ваш вопрос, возможно, вам даже не нужно работать со списками списков и просто работать с data.table):

library(data.table)

dt = as.data.table(before)
after = as.list(data.table(t(dt)))

Ответ 4

Хотя это старый вопрос, я нашел его во время поиска той же проблемы, а второй удар по google имел гораздо более элегантное решение, на мой взгляд:

list_of_lists <- list(a=list(x="ax", y="ay"), b=list(w="bw", z="bz"))
new <- do.call(rbind, list_of_lists) 

new теперь представляет собой прямоугольную структуру, странный объект: список с атрибутом измерения. Он работает с как можно большим количеством элементов, если каждый подсписчик имеет одинаковую длину. Чтобы изменить его на более распространенный R-объект, можно было бы, например, создать такую матрицу:

new.dims <- dim(new)
matrix(new,nrow = new.dims[1])

new.dims необходимо сохранить, так как функция matrix() удаляет атрибут списка. Другой путь:

new <- do.call(c, new) 
dim(new) <- new.dims

Теперь вы можете, например, преобразовать его в data.frame с as.data.frame() и разбить его на столбцы или выполнить операции с колонками. Прежде чем вы это сделаете, вы также можете изменить атрибут dim, который соответствует вашим потребностям.

Ответ 5

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

invertList = function(l){
  elemnames = NULL
  for (i in seq_along(l)){
    elemnames = c(elemnames, names(l[[i]]))
  }

  elemnames = unique(elemnames)

  res = list()
  for (i in seq_along(elemnames)){
    res[[elemnames[i]]] = list()
    for (j in seq_along(l)){
      if(exists(elemnames[i], l[[j]], inherits = F)){
        res[[i]][[names(l)[j]]] = l[[names(l)[j]]][[elemnames[i]]]
      }
    }
  }
  res
}