使用dplyr和purrr将列表中每个子列表的第i个元素合并在一起

4

我有一个相同结构的列表,如下:

    test1 <- list(first = data.frame(col1 = c(1,2), col2 = c(3,4)), 
                  second = data.frame(COL1 = c(100,200), COL2 = c(300, 400)))

    test2 <- list(first = data.frame(col1 = c(5,6), col2 = c(7,8)), 
                  second = data.frame(COL1 = c(500,600), COL2 = c(700,800)))

    orig.list <- list(test1, test2)

我希望:

  1. 将每个嵌套列表的第一个元素绑定在一起,将每个嵌套列表的第二个元素绑定在一起,以此类推。
  2. 重新组合结果元素为单个列表,并与第一个列表具有相同的结构。

我可以通过逐个元素轻松完成此操作:

    firsts <- orig.list %>% purr::map(1) %>% dplyr::bind_rows()
    seconds <- orig.list %>% purr::map(2) %>% dplyr::bind_rows()

    new.list <- list(first = firsts, second = seconds)

然而,对于n个列表元素,这需要我:

  1. 知道每个列表中元素的数量。
  2. 知道元素的名称和顺序,以便能够使用正确的名称和顺序重新创建新列表。
  3. 重复复制并粘贴同一行代码。

我正在寻找如何更通用地应用purrr:map(或其他tidyverse函数)来组合一个列表的所有元素,并保留元素的名称和顺序。

2个回答

4
在你展示的最简单情况下,你可以使用 pmap 并行遍历列表,使用 bind_rows 组合各个数据框:
library(tidyverse)
pmap(orig.list, bind_rows)

#$first
#  col1 col2
#1    1    3
#2    2    4
#3    5    7
#4    6    8

#$second
#  COL1 COL2
#1  100  300
#2  200  400
#3  500  700
#4  600  800

identical(pmap(orig.list, bind_rows), new.list)
# [1] TRUE

为了让这个更加通用,即处理元素数量和每个子列表中名称的顺序可以变化的情况,您可以使用:

map(map_df(orig.list, ~ as.data.frame(map(.x, ~ unname(nest(.))))), bind_rows)

即,您将每个子列表嵌套为数据帧,并让bind_rows为您检查名称。

测试用例

使用相同的test1,交换test2中元素的顺序:

test2 <- list(second = data.frame(COL1 = c(500,600), COL2 = c(700,800)),
              first = data.frame(col1 = c(5,6), col2 = c(7,8)))

orig.list1 <- list(test1, test2)

map(map_df(orig.list1, ~ as.data.frame(map(.x, ~ unname(nest(.))))), bind_rows)

提供:

#$first
#  col1 col2
#1    1    3
#2    2    4
#3    5    7
#4    6    8

#$second
#  COL1 COL2
#1  100  300
#2  200  400
#3  500  700
#4  600  800

现在从test2中删除一个元素:
test2 <- list(first = data.frame(col1 = c(5,6), col2 = c(7,8)))
orig.list2 <- list(test1, test2)

map(map_df(orig.list2, ~ as.data.frame(map(.x, ~ unname(nest(.))))), bind_rows)

提供:

#$first
#  col1 col2
#1    1    3
#2    2    4
#3    5    7
#4    6    8

#$second
#  COL1 COL2
#1  100  300
#2  200  400

如果列表元素之一不是数据框,有没有办法使其工作?test1 <- list(first = data.frame(col1 = c(1,2), col2 = c(3,4)), second = seq(101:200))test2 <- list(second = seq(1:100), first = data.frame(col1 = c(5,6), col2 = c(7,8))) - undefined

3

You want purrr::transpose :

library(purrr)
library(dplyr)
transpose(orig.list) %>% map(bind_rows)

# $first
# col1 col2
# 1    1    3
# 2    2    4
# 3    5    7
# 4    6    8
# 
# $second
# COL1 COL2
# 1  100  300
# 2  200  400
# 3  500  700
# 4  600  800

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