使用dplyr将列表重新组织为数据框

5
我会使用dplyr包的速度,将结构化列表转换为整洁的数据框。我想知道我现在发布的解决方案是否是“最先进的”,或者是否有更快的方法。
这是我开始列表的一个示例:
l = list()
l[[1]] = list(member1=c(a=rnorm(1)),member2=matrix(rnorm(3),nrow=3,ncol=1,dimnames=list(c(letters[2:4]),c("sample"))))
l[[2]] = list(member1=c(a=rnorm(1)),member2=matrix(rnorm(3),nrow=3,ncol=1,dimnames=list(c(letters[2:4]),c("sample"))))
l[[3]] = list(member1=c(a=rnorm(1)),member2=matrix(rnorm(3),nrow=3,ncol=1,dimnames=list(c(letters[2:4]),c("sample"))))

通过这个结果(为您展示玩具结构):
l
[[1]]
[[1]]$member1
    a 
0.3340196 

[[1]]$member2
 sample
b 1.0098830
c 0.6413375
d 0.9080675

[[2]]
[[2]]$member1
    a 
0.0590878 

[[2]]$member2
  sample
b  0.5585736
c -0.5936157
d -0.3985687

[[3]]
[[3]]$member1
     a 
0.06242458 

[[3]]$member2
  sample
b -0.2873391
c  0.5326067
d -1.1635551

现在我将使用一个方便的函数来重新排列数据并使用lapply遍历列表:
organizeSamples = function(x){
  member = x$member2
  output = data.frame(key=rownames(member),value=member[,1])
  return(output)
}
l_new = lapply(l, organizeSamples)

现在dplyr发挥了魔力:
samples = dplyr::bind_rows(l_new)
samples :

  key      value
1   b  1.0098830
2   c  0.6413375
3   d  0.9080675
4   b  0.5585736
5   c -0.5936157 
6   d -0.3985687
7   b -0.2873391
8   c  0.5326067
9   d -1.1635551

有一种更快、更优雅、更紧凑的方法可以用dplyr来完成吗?
3个回答

4

这里有另一种方法,使用更多的dplyr/tidyr函数和管道操作,但是我没有测试它与问题中原始方法的性能,并且是否更优雅取决于个人偏好。

library(dplyr); library(tidyr)

lapply(l, `[[`, 2) %>% 
    data.frame %>% 
    add_rownames("key") %>% 
    gather(x, value, -key) %>% 
    select(-x)

#      key      value
#1       b -1.1476570
#2       c -0.2894616
#3       d -0.2992151
#4       b  0.2522234
#5       c -0.8919211
#6       d  0.4356833
#7       b -0.2242679
#8       c  0.3773956
#9       d  0.1333364

从来没有使用过[[语法。但我一定会尝试并使用microbenchmark包在更大的列表上进行测试...顺便说一下,我喜欢管道系统! - Fabio

2

同样来自Hadleyverse,但不使用“dplyr”的另一个选择是考虑使用“reshape2”中的melt函数:

library(reshape2)
melt(l)
#         value Var1   Var2      L2 L1
# 1  -0.6264538 <NA>   <NA> member1  1
# 2   0.1836433    b sample member2  1
# 3  -0.8356286    c sample member2  1
# 4   1.5952808    d sample member2  1
# 5   0.3295078 <NA>   <NA> member1  2
# 6  -0.8204684    b sample member2  2
# 7   0.4874291    c sample member2  2
# 8   0.7383247    d sample member2  2
# 9   0.5757814 <NA>   <NA> member1  3
# 10 -0.3053884    b sample member2  3
# 11  1.5117812    c sample member2  3
# 12  0.3898432    d sample member2  3

从那里开始,您可以考虑使用 "dplyr" 进行一些清理。例如,要获得您描述的两列结果,您可以执行以下操作:

library(reshape2)
library(dplyr)

melt(l) %>%
  filter(L2 != "member1") %>%
  select(value, Var1)

(使用 set.seed(1) 创建示例数据)。


2

另一个纯tidyverse解决方案:

ll <- l %>% map_df(enframe) %>% 
    mutate(key1=map(value, rownames),
           key2=map(value, names),
           key=map2(key1, key2, ~c(.x, .y))) %>%
    select(-key1, -key2) %>%
    unnest()
ll
# A tibble: 12 × 3
      name       value   key
     <chr>       <dbl> <chr>
1  member1  2.12962812     a
2  member2 -0.87049458     b
3  member2  0.96190007     c
4  member2  0.56403433     d
5  member1 -0.41447472     a
6  member2  0.27270458     b
7  member2 -0.01384829     c
8  member2 -0.71561501     d
9  member1 -0.81835698     a
10 member2 -2.12746977     b
11 member2  0.66185843     c
12 member2  0.07878841     d

更新 我原以为您想将member1和member2的信息合并,如果只需要member2,那么就更简单了:

ll <- l %>% map_df(enframe) %>% 
    filter(name=="member2") %>%
    mutate(key=map(value, rownames)) %>%
    unnest()
ll
# A tibble: 9 × 3
     name       value   key
    <chr>       <dbl> <chr>
1 member2 -0.87049458     b
2 member2  0.96190007     c
3 member2  0.56403433     d
4 member2  0.27270458     b
5 member2 -0.01384829     c
6 member2 -0.71561501     d
7 member2 -2.12746977     b
8 member2  0.66185843     c
9 member2  0.07878841     d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接