【问题标题】:Convert list of vectors to data frame将向量列表转换为数据框
【发布时间】:2017-09-25 12:35:39
【问题描述】:

我正在尝试将向量列表(本质上是多维数组)转换为数据框,但每次尝试都会得到意想不到的结果。

我的目标是实例化一个空白列表,用包含有关循环迭代信息的向量将其填充到 for 循环中,然后在完成后将其转换为数据框。

> vectorList <- list()
> for(i in  1:5){
+     vectorList[[i]] <- c("number" = i, "square root" = sqrt(i))
+ }
> vectorList

输出:

> [[1]]
>      number square root 
>           1           1 
> 
> [[2]]
>      number square root 
>    2.000000    1.414214 
> 
> [[3]]
>      number square root 
>    3.000000    1.732051 
> 
> [[4]]
>      number square root 
>           4           2 
> 
> [[5]]
>      number square root 
>    5.000000    2.236068

现在我希望它成为一个对 2 个变量进行 5 次观察的数据框,但尝试从“vectorList”创建一个数据框

numbers <- data.frame(vectorList)

导致 5 个变量的 2 次观察。

奇怪的是,它甚至不会被 reshape2 强制(我知道这将是一个糟糕的工作,但我尝试过)。

任何人有任何见解?

【问题讨论】:

  • 只是关于您的方法的一般说明:如果可以避免的话,您不应该在 for 循环中增加这样的列表。当您在列表末尾添加某些内容时,R 必须复制整个列表。这对于小型案例来说很好,但如果您的列表很大(并且在您的案例中它变得越来越大),这可能会非常低效。
  • 对于您的数据构建,您可以像这样使用lapplyvectorList &lt;- lapply(1:5, function(x) c(x, sqrt(x)))

标签: r list vector dataframe


【解决方案1】:

你可以使用:

as.data.frame(do.call(rbind, vectorList))

或者:

library(data.table)
rbindlist(lapply(vectorList, as.data.frame.list))

或者:

library(dplyr)
bind_rows(lapply(vectorList, as.data.frame.list))

【讨论】:

  • 第一个返回警告:Warning message: In (function (..., deparse.level = 1) : number of columns of result is not a multiple of vector length (arg 3) 第二个和第三个返回错误:Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, : arguments imply differing number of rows: 1, 0
  • @PM0087 它对我来说非常好用。您是否使用了问题中的数据?
【解决方案2】:

还有Reduce:

Reduce(rbind, vectorList)

    # number square root
# init      1    1.000000
          # 2    1.414214
          # 3    1.732051
          # 4    2.000000
          # 5    2.236068

【讨论】:

  • 请注意,Reduce(rbind, vectorList) 返回一个矩阵,因此您需要将其包装在 data.frame 中以返回一个 data.frame 对象。
【解决方案3】:

我知道的最快和最有效的方法是使用data.table::transpose 函数(如果你的向量的长度是低维的):

as.data.frame(data.table::transpose(vectorList), col.names = names(vectorList[[1]]))

但是,您需要手动设置列名,因为 data.table::transpose 会删除它们。还有一个purrr::transpose 函数不会删除列名,但它似乎更慢。 下面是一个小基准,包括其他用户的建议:

vectorList = lapply(1:1000, function(i) (c("number" = i, "square root" = sqrt(i))))
bench = microbenchmark::microbenchmark(
  dplyr = dplyr::bind_rows(lapply(vectorList, as.data.frame.list)),
  rbindlist = data.table::rbindlist(lapply(vectorList, as.data.frame.list)),
  Reduce = Reduce(rbind, vectorList),
  transpose_datatable = as.data.frame(data.table::transpose(vectorList), col.names = names(vectorList[[1]])),
  transpose_purrr = data.table::as.data.table(purrr::transpose(vectorList)),
  do.call = as.data.frame(do.call(rbind, vectorList)),
  times = 10)
bench
# Unit: microseconds
#                 expr        min         lq        mean      median         uq        max neval cld
#                dplyr 286963.036 292850.136 320345.1137 310159.7380 341654.619 385399.851    10   b
#            rbindlist 285830.750 289935.336 306120.7257 309581.1895 318131.031 324217.413    10   b
#               Reduce   8573.474   9073.649  12114.5559   9632.1120  11153.511  33446.353    10  a 
#  transpose_datatable    372.572    424.165    500.8845    479.4990    532.076    701.822    10  a 
#      transpose_purrr    539.953    590.365    672.9531    671.1025    718.757    911.343    10  a 
#              do.call    452.915    537.591    562.9144    570.0825    592.334    641.958    10  a 

# now use bigger list and disregard the slowest
vectorList = lapply(1:100000, function(i) (c("number" = i, "square root" = sqrt(i))))
bench.big = microbenchmark::microbenchmark(
  transpose_datatable = as.data.frame(data.table::transpose(vectorList), col.names = names(vectorList[[1]])),
  transpose_purrr = data.table::as.data.table(purrr::transpose(vectorList)),
  do.call = as.data.frame(do.call(rbind, vectorList)),
  times = 10)
bench.big
# Unit: milliseconds
#                 expr       min        lq       mean     median         uq       max neval cld
#  transpose_datatable  3.470901   4.59531   4.551515   4.708932   4.873755   4.91235    10 a  
#      transpose_purrr 61.007574  62.06936  68.634732  65.949067  67.477948  97.39748    10  b 
#              do.call 97.680252 102.04674 115.669540 104.983596 138.193644 151.30886    10   c

【讨论】:

    【解决方案4】:

    使用purrr 的替代解决方案:

    purrr::map_dfr( vectorList, as.list )
    # # A tibble: 5 x 2
    #   number `square root`
    #    <dbl>         <dbl>
    # 1      1          1   
    # 2      2          1.41
    # 3      3          1.73
    # 4      4          2   
    # 5      5          2.24
    

    代码有效地将每个向量转换为一个列表,并将结果逐行连接到一个公共数据帧中。

    【讨论】:

    • tidyverse 方法(dplyr::bind_rows()purrr::map_dfr())的优点在于它们可以处理具有不同长度的列表元素,以及按元素顺序不同的命名向量.例如在将xml2::xml_attrs() 的输出转换为矩形数据时非常有用。
    猜你喜欢
    • 1970-01-01
    • 2015-04-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多