Подтвердить что ты не робот

Получить длинный формат данных из списка

У меня есть список списков, содержащих строки. Первая строка каждого под-списка описывает категорию, к которой принадлежат следующие строки. Я хочу получить (длинноформатный) кадр данных с одним столбцом для категории и один для контента. Как я могу получить фрейм данных в длинном формате из этого списка:

mylist <- list(
  c("A","lorem","ipsum"),
  c("B","sed", "eiusmod", "tempor" ,"inci"),
  c("C","aliq", "ex", "ea"))

> mylist
[[1]]
[1] "A"     "lorem" "ipsum"

[[2]]
[1] "B"        "sed"      "eiusmod"  "tempor"   "incidunt"

[[3]]
[1] "C"       "aliquid" "ex"      "ea" 

Он должен выглядеть как этот фрейм данных

mydf <- data.frame(cate= c("A","A","B","B","B","B","C","C","C"),
               cont= c("lorem","ipsum","sed", "eiusmod", "tempor","inci","aliq", "ex", "ea"))

> mydf
  cate    cont
1   A    lorem
2   A    ipsum
3   B      sed
4   B  eiusmod
5   B   tempor
6   B incidunt
7   C  aliquid
8   C       ex
9   C       ea

Я уже разделял категории и содержимое.

cate <- sapply(mylist, "[[",1)
cont <- sapply(mylist, "[", -(1))

Как перейти к получению mydf?

4b9b3361

Ответ 1

Мы можем использовать stack после именования элементов list для символа 'cont' с 'cape'.

setNames(stack(setNames(cont, cate))[2:1], c('cate', 'cont'))
#  cate    cont
#1    A   lorem
#2    A   ipsum
#3    B     sed
#4    B eiusmod
#5    B  tempor
#6    B    inci
#7    C    aliq
#8    C      ex
#9    C      ea

Ответ 2

Используя исходный список, а не созданные объекты разбиения, вы можете попробовать следующее:

library(data.table)
setorder(melt(as.data.table(transpose(mylist)), 
              id.vars = "V1", na.rm = TRUE), V1, variable)[]
#    V1 variable   value
# 1:  A       V2   lorem
# 2:  A       V3   ipsum
# 3:  B       V2     sed
# 4:  B       V3 eiusmod
# 5:  B       V4  tempor
# 6:  B       V5    inci
# 7:  C       V2    aliq
# 8:  C       V3      ex
# 9:  C       V4      ea

Для удовольствия вы также можете попробовать одно из следующих действий:


library(dplyr)
library(tidyr)

data_frame(id = seq_along(mylist), mylist) %>%
  unnest %>%
  group_by(id) %>%
  mutate(ind = mylist[1]) %>%
  slice(2:n())

library(purrr)
data_frame(
  value = mylist %>% map(~ .x[-1]) %>% unlist,
  ind = mylist %>% map(~ rep(.x[1], length(.x)-1)) %>% unlist
)

Обратите внимание, что вас будет раздражать тот факт, что "purrr" также имеет функцию transpose, что означает, что если у вас также загружена "data.table", вам придется привыкнуть к использованию таких вещей, как data.table::transpose или purrr::transpose, если вы используете эти функции (например, в исходном ответе). Я не тестировал, но думаю, что "data.table" по-прежнему будет самым быстрым, начиная с вашего исходного списка.

Ответ 3

Мы также можем использовать rep в сочетании с переменными, уже созданными в сообщении OP.

dat <- data.frame(cat=rep(cate, lengths(cont)),
                  cont=unlist(cont))

Итак, как было какое-то обсуждение того, что является "лучшим" ответом (если есть и тот, на который я сомневаюсь), вот некоторые контрольные показатели (в случае эффективности), основанные на списке из 100 000 векторов для обработки:

Unit: milliseconds
   expr       min        lq     mean    median       uq      max neval cld
 heroka  56.24516  67.98583 122.1209  82.35606 117.6017 391.8297    50  a 
  akrun 258.86939 283.10408 363.5425 331.50263 448.9134 578.1818    50   b
 ananda  47.72320  61.05269 132.2678  76.22913 218.8286 385.5709    50  a 

Код бенчмаркинга предполагает, что переменные cate и cont уже созданы, так как оба решения используют их.

heroka <- function(){
 data.frame(cat=rep(cate, lengths(cont)), cont=unlist(cont))
}

akrun <- function(){
  setNames(stack(setNames(cont, cate))[2:1], c('cate', 'cont'))
}

ananda <- function(){
  setorder(melt(as.data.table(transpose(mylist)), 
                id.vars = "V1", na.rm = TRUE), V1, variable)[]
}


mylist <- replicate(100000,c(sample(LETTERS[1:10],1),sample(LETTERS[1:10],sample(5))))
cate <- sapply(mylist, "[[",1)
cont <- sapply(mylist, "[", -(1))

tests <- microbenchmark(
  heroka = heroka(),
  akrun=akrun(),ananda=ananda(),
  times=50
)

Ответ 4

Еще один вариант, используя lapply

do.call(rbind, lapply(mylist, function(x) data.frame(cate = x[1], cont = x[-1])))

#  cate    cont
#1    A   lorem
#2    A   ipsum
#3    B     sed
#4    B eiusmod
#5    B  tempor
#6    B    inci
#7    C    aliq
#8    C      ex
#9    C      ea