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