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

Reorganize list into dataframe using dplyr

我会使用 dplyr 包的速度将结构化列表转换为整洁的数据帧。 我想知道我现在发布的解决方案是 "state-of-art" 还是有更快的解决方案。

这是我的起始列表示例:

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 更快、更优雅、更紧凑?

这是另一种方法,具有更多 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

同样来自 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) 创建的示例数据)。

另一个纯粹的 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