2016-02-12 51 views
13

我有一个包含字符串的列表列表。每个子列表的第一个字符串描述了以下字符串所属的类别。我想得到一个(长格式)数据框,其中一列用于类别,另一列用于内容。 我怎样才能长格式的数据帧从这个名单:从列表中获取长格式数据帧

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?

回答

8

在将'cont'的list元素命名为'cape'后,我们可以使用stack

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 
10

我们还可以使用rep结合已在OP的帖子中创建的变量。

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

因此,作为有关于什么是“最好的”的回答一些讨论(如果有的话甚至是一个,我怀疑),这里有一些基准测试(在情况下的性能问题),基于的100000名单向量处理:

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 

标杆代码假设变量catecont已创建的,因为这两个解决方案中使用它们。

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 
) 
13

使用你原来的列表,而不是分割对象,你创建的,你可以尝试以下方法:

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::transposepurrr::transpose如果你正在使用这些功能(就像我在原来的答案)。我还没有测试过,但我的猜测是“data.table”仍然是您的原始列表中最快的开始。

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 
+2

调用'data.frame'每个项目将是为列表大小的增加十分缓慢只是另一种选择。 – A5C1D2H2I1M1N2O1R2T1