rbind 包含不同长度列表的数据框列

rbind a dataframe column containing list of different lengths

我有一个数据框,有一列包含不同长度的列表:

IP <- structure(list(V1 = list(l1 = c("M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M"), `l2` = c("D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M"), `l3` = c("D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", 
"D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "D", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", 
"M"))), class = "data.frame", row.names = c("1", "2", "3"))

我正在使用以下命令转换列表。这适用于较小的数据集。

output <- plyr::ldply(IP$V1, rbind)

但是当我将其应用于大型数据集(大约 >100 万)时,它会永远运行并崩溃。

有没有办法将其有效地应用于更大的数据集?

谢谢!

澄清后更新: 另一种选择是使用 stringi 中的 stri_list2matrix,速度非常快。

library(stringi)

op <- as.data.frame(stri_list2matrix(c(IP$V1), byrow = TRUE))
op$.id <- seq_along(IP$V1)

基本的 R 解决方案是使用 lapply,这也非常快(尽管在 benchmark image 中有很多可变性)。

op3 <-
  as.data.frame(transpose(setDT(lapply(
    c(IP$V1), "length<-", max(lengths(c(ok$V1)))
  ))))
op3$.id <- seq_along(IP$V1)

另一个基本的 R 解决方案是使用 sapply,它也相当快(虽然比 lapply.

慢一点)
op2 <- as.data.frame(t(sapply(c(IP$V1), "length<-", max(lengths(c(IP$V1))))))
op2$.id <- seq_along(IP$V1)

输出

# A tibble: 3 × 380
  V1    V2    V3    V4    V5    V6    V7    V8    V9    V10   V11   V12   V13   V14   V15   V16   V17   V18   V19  
  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 M     M     M     M     M     M     M     M     M     M     M     M     M     M     M     M     M     M     M    
2 D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D    
3 D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D     D    
# … with 361 more variables: V20 <chr>, V21 <chr>, V22 <chr>, V23 <chr>, V24 <chr>, V25 <chr>, V26 <chr>, V27 <chr>,
#   V28 <chr>, V29 <chr>, V30 <chr>, V31 <chr>, V32 <chr>, V33 <chr>, V34 <chr>, V35 <chr>, V36 <chr>, V37 <chr>,
#   V38 <chr>, V39 <chr>, V40 <chr>, V41 <chr>, V42 <chr>, V43 <chr>, V44 <chr>, V45 <chr>, V46 <chr>, V47 <chr>,
#   V48 <chr>, V49 <chr>, V50 <chr>, V51 <chr>, V52 <chr>, V53 <chr>, V54 <chr>, V55 <chr>, V56 <chr>, V57 <chr>,
#   V58 <chr>, V59 <chr>, V60 <chr>, V61 <chr>, V62 <chr>, V63 <chr>, V64 <chr>, V65 <chr>, V66 <chr>, V67 <chr>,
#   V68 <chr>, V69 <chr>, V70 <chr>, V71 <chr>, V72 <chr>, V73 <chr>, V74 <chr>, V75 <chr>, V76 <chr>, V77 <chr>,
#   V78 <chr>, V79 <chr>, V80 <chr>, V81 <chr>, V82 <chr>, V83 <chr>, V84 <chr>, V85 <chr>, V86 <chr>, V87 <chr>, …

基准

library (tidyverse)

bm <- microbenchmark::microbenchmark(
  r2evans = {IP$V1 <- lapply(IP$V1, `length<-`, max(lengths(IP$V1)));
  out2 <- data.frame(do.call(rbind, IP$V1));
  out2$.id <- seq_along(IP$V1)},
  RduU = {plyr::ldply(IP$V1, rbind)},
  tidyr = {IP %>%
    unnest_wider(V1, names_sep = "_")},
  stringi = {op <- as.data.frame(stri_list2matrix(c(IP$V1), byrow=TRUE)); op$.id <- seq_along(IP$V1)},
  sapply = {as.data.frame(t(sapply(c(IP$V1), "length<-", max(lengths(c(IP$V1)))))); op2$.id <- seq_along(IP$V1)},
  lapply = {op3 <- as.data.frame(transpose(setDT(lapply(c(IP$V1), "length<-", max(lengths(c(ok$V1)))))));
  op3$.id <- seq_along(IP$V1)},
  times = 100
)

microbenchmark:::autoplot(bm)

Unit: microseconds
    expr       min         lq        mean     median         uq       max neval
 r2evans  1503.602  1640.0915  1799.95612  1747.6035  1872.3480  3092.314   100
    RduU  1764.108  2003.0560  2150.63791  2086.5735  2232.9945  4152.803   100
   tidyr 15108.671 15938.5185 17209.04116 16487.6840 17480.8740 33108.209   100
 stringi   747.871   819.4205   875.45533   853.2315   913.2410  1569.510   100
  sapply  1056.223  1173.0940  1294.82064  1255.7130  1337.3275  2450.791   100
  lapply   939.044  1078.7225  1335.96819  1139.3605  1236.4150 13476.396   100

第一个答案: 你可以使用 data.table 因为它比 plyrtidyr.

更快
library(data.table)

setDT(IP)[, list(V1 = as.character(unlist(V1)))] %>% 
  as.data.frame()

基准

library (dplyr)

microbenchmark::microbenchmark(
  data.table = setDT(IP)[, list(V1 = as.character(unlist(V1)))] %>%
    as.data.frame(),
  tidyr = tidyr::unnest(IP, cols = c(V1)),
  plyr = plyr::ldply(IP$V1, rbind)
)


Unit: microseconds
       expr      min        lq       mean   median        uq       max neval
 data.table  588.723  679.6965  768.05463  745.360  808.5615  1465.043   100
      tidyr 2631.968 2833.8095 3269.19794 3054.737 3393.4345 12726.122   100
       plyr 1173.735 1290.8645 1379.57338 1335.448 1412.0445  2027.333   100

澄清后更新: 我们可以使用 tidyr_package:

中的 unnest_wider
library(dplyr)
library(tidyr)

IP %>% 
  unnest_wider(V1, names_sep = "_")
  V1_1  V1_2  V1_3  V1_4  V1_5  V1_6  V1_7  V1_8  V1_9  V1_10 V1_11 V1_12
  <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
1 M     M     M     M     M     M     M     M     M     M     M     M    
2 D     D     D     D     D     D     D     D     D     D     D     D    
3 D     D     D     D     D     D     D     D     D     D     D     D    
# ... with 367 more variables: V1_13 <chr>, V1_14 <chr>, V1_15 <chr>,
#   V1_16 <chr>, V1_17 <chr>, V1_18 <chr>, V1_19 <chr>, V1_20 <chr>,
#   V1_21 <chr>, V1_22 <chr>, V1_23 <chr>, V1_24 <chr>, V1_25 <chr>,
#   V1_26 <chr>, V1_27 <chr>, V1_28 <chr>, V1_29 <chr>, V1_30 <chr>,
#   V1_31 <chr>, V1_32 <chr>, V1_33 <chr>, V1_34 <chr>, V1_35 <chr>,
#   V1_36 <chr>, V1_37 <chr>, V1_38 <chr>, V1_39 <chr>, V1_40 <chr>,
#   V1_41 <chr>, V1_42 <chr>, V1_43 <chr>, V1_44 <chr>, V1_45 <chr>, ...

第一个回答: 我们可以先转换为 data.table 然后使用 data.table 代码:

library(data.table)
dt1 <- as.data.table(IP)
dt1[, .(V1 = unlist(V1)), by = setdiff(names(dt1), 'V1')]
 V1
   1:  M
   2:  M
   3:  M
   4:  M
   5:  M
  ---   
1124:  M
1125:  M
1126:  M
1127:  M
1128:  M

None 的解决方案重复了 plyr::ldply 给您的内容。即,

out1 <- plyr::ldply(IP$V1, rbind)
out1[,c(1:3, 378:380)]
#   .id 1 2  377  378  379
# 1  l1 M M    M    M    M
# 2  l2 D D <NA> <NA> <NA>
# 3  l3 D D    M    M    M

使问题复杂化的是嵌入列表的长度不同:

lengths(IP$V1)
#  l1  l2  l3 
# 379 370 379 

第一个建议的解决方案在纠正该差异后会更好(没有警告)。

IP$V1 <- lapply(IP$V1, `length<-`, max(lengths(IP$V1)))
out2 <- data.frame(do.call(rbind, IP$V1))
out2$.id <- seq_along(IP$V1)
dim(out2)
# [1]   3 380
out2[,c(1:3, 378:380)]
#    V1 V2 V3 V378 V379 .id
# l1  M  M  M    M    M   1
# l2  D  D  D <NA> <NA>   2
# l3  D  D  D    M    M   3

如果您真的希望列名只是数字(作为字符串),您可以使用 out2 <- data.frame(..., check.names = FALSE) 或手动覆盖它们。我不建议这样做,但这取决于您的需求。