tidyverse:有效地绑定列表元素

tidyverse: binding list elements efficiently

我想从下面给出的列表中绑定 data.frames 相同行数。

df1 <- data.frame(A1 = 1:10, B1 = 11:20)
df2 <- data.frame(A1 = 1:10, C1 = 21:30)
df3 <- data.frame(A2 = 1:15, B2 = 11:25, C2 = 31:45)
df4 <- data.frame(A2 = 1:15, D2 = 11:25, E2 = 51:65)
df5 <- 5

ls <- list(df1, df2, df3, df4, df5)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
bind_cols(ls[1], ls[2], .id = NULL)
#> New names:
#> * A1 -> A1...1
#> * A1 -> A1...3
#>    A1...1 B1 A1...3 C1
#> 1       1 11      1 21
#> 2       2 12      2 22
#> 3       3 13      3 23
#> 4       4 14      4 24
#> 5       5 15      5 25
#> 6       6 16      6 26
#> 7       7 17      7 27
#> 8       8 18      8 28
#> 9       9 19      9 29
#> 10     10 20     10 30
bind_cols(ls[3], ls[4], .id = NULL)
#> New names:
#> * A2 -> A2...1
#> * A2 -> A2...4
#>    A2...1 B2 C2 A2...4 D2 E2
#> 1       1 11 31      1 11 51
#> 2       2 12 32      2 12 52
#> 3       3 13 33      3 13 53
#> 4       4 14 34      4 14 54
#> 5       5 15 35      5 15 55
#> 6       6 16 36      6 16 56
#> 7       7 17 37      7 17 57
#> 8       8 18 38      8 18 58
#> 9       9 19 39      9 19 59
#> 10     10 20 40     10 20 60
#> 11     11 21 41     11 21 61
#> 12     12 22 42     12 22 62
#> 13     13 23 43     13 23 63
#> 14     14 24 44     14 24 64
#> 15     15 25 45     15 25 65

在我的实际列表中,我有大约二十 data.frames 行的不同行数。我想知道是否有更有效的方法来绑定 data.frames 相同行数而不给出列表元素的名称和索引。

也许不是最佳方法,但您可以使用循环并将具有相同列数的数据帧绑定到新数据帧中。这段代码的主要目的是检查每个数据框的维度并创建一个唯一的向量。然后在循环中,您可以使用 lapply()ls 中的数据帧进行子集化并绑定它们的列。这里的代码(考虑到小 df5 进行了更新,您可以将其作为数据框进行管理):

library(dplyr)
#Data
df1 <- data.frame(A1 = 1:10, B1 = 11:20)
df2 <- data.frame(A1 = 1:10, C1 = 21:30)
df3 <- data.frame(A2 = 1:15, B2 = 11:25, C2 = 31:45)
df4 <- data.frame(A2 = 1:15, D2 = 11:25, E2 = 51:65)
df5 <- 5
#List
ls <- list(df1, df2, df3, df4,df5)
#Index
index <- sapply(ls,function(x)dim(as.data.frame(x))[1])
m <- unique(index)
#Loop
for(i in 1:length(m))
{
  assign(paste0('df',i),do.call(bind_cols,ls[lapply(ls,function(x) dim(as.data.frame(x))[1]==m[i])==T]))
}

输出:

df1
   A1...1 B1 A1...3 C1
1       1 11      1 21
2       2 12      2 22
3       3 13      3 23
4       4 14      4 24
5       5 15      5 25
6       6 16      6 26
7       7 17      7 27
8       8 18      8 28
9       9 19      9 29
10     10 20     10 30

df2
   A2...1 B2 C2 A2...4 D2 E2
1       1 11 31      1 11 51
2       2 12 32      2 12 52
3       3 13 33      3 13 53
4       4 14 34      4 14 54
5       5 15 35      5 15 55
6       6 16 36      6 16 56
7       7 17 37      7 17 57
8       8 18 38      8 18 58
9       9 19 39      9 19 59
10     10 20 40     10 20 60
11     11 21 41     11 21 61
12     12 22 42     12 22 62
13     13 23 43     13 23 63
14     14 24 44     14 24 64
15     15 25 45     15 25 65

df3
  ...1
1    5

通过 split 更容易做到这一点。使用 gl

创建分组索引
grp <- as.integer(gl(length(ls), 2, length(ls)))

然后使用split

library(dplyr)
library(purrr)
library(stringr)
split(ls, grp) %>% # // split by the grouping index 
     map(bind_cols) %>% # // loop over the `list` and use `bind_cols`
     set_names(str_c('df', seq_along(.))) %>% # // name the `list`
     list2env(.GlobalEnv) # // create objects in global env

-输出

head(df1)
#  A1...1 B1 A1...3 C1
#1      1 11      1 21
#2      2 12      2 22
#3      3 13      3 23
#4      4 14      4 24
#5      5 15      5 25
#6      6 16      6 26

head(df2)
#  A2...1 B2 C2 A2...4 D2 E2
#1      1 11 31      1 11 51
#2      2 12 32      2 12 52
#3      3 13 33      3 13 53
#4      4 14 34      4 14 54
#5      5 15 35      5 15 55
#6      6 16 36      6 16 56

head(df3)
# A tibble: 1 x 1
#   ...1
#  <dbl>
#1     5

注意:

  1. 最好将元素保留在 list 中,而不是在全局环境中创建对象,即 list2env
  2. ls 是一个函数名,用函数名命名对象不是一个好的选择,因为它会导致错误情况