【问题标题】:Efficiently rearranging data from list of data frames into arrays in R?有效地将数据帧列表中的数据重新排列到R中的数组中?
【发布时间】:2018-03-16 15:14:31
【问题描述】:

我有如下排列的数据:

  • 列表
  • 在列表中,每个元素都是一个数据框(50 个数据框)
  • 这些数据帧中的每一个都包含 5 行数字,有 9 个命名列(所有 50 个数据帧都有相同的 9 个名称)

我的目标是有效地重新排列这些数据,以便:

  • “第二维”(在上面的描述中为 1 到 5)成为第一维。
  • “第一个维度”(上述描述中的范围为 1 到 50)成为第二个维度。
  • 我只保留了 9 个命名列中的一部分(其余的可以丢弃),按名称选择
  • 我希望将所有数字存储在一个数组中(或者其他更高效的数据结构也可以),而不是这些低效的列表和数据框。

示例数据可以使用以下代码生成(简化为只有 2 个数据帧,每个数据帧 5 行 3 列):

example_list<-lapply(X=1:2, FUN=function(X){setNames(data.frame(X*c(1:5), -X*c(1:5), X*100*c(1:5)), c("C1", "C2", "C3"))})

这将创建以下两个数据框的列表:

> example_list[1]

  C1 C2  C3
1  1 -1 100
2  2 -2 200
3  3 -3 300
4  4 -4 400
5  5 -5 500

> example_list[2]

  C1  C2   C3
1  2  -2  200
2  4  -4  400
3  6  -6  600
4  8  -8  800
5 10 -10 1000

我的当前解决方案(带有示例数据的硬编码数字)如下所示。在这种情况下,我假设我们只关心名为“C1”和“C2”的列:

important_cols <- c("C1", "C2")
result <- array(0, c(5, 2, length(important_cols)))
for(i in 1:5){
    for(j in 1:2){
        result[i,j,] <- c(example_list[[j]][i,important_cols], recursive=T)
    }
}

它给出以下输出:

> result
, , 1

     [,1] [,2]
[1,]    1    2
[2,]    2    4
[3,]    3    6
[4,]    4    8
[5,]    5   10

, , 2

     [,1] [,2]
[1,]   -1   -2
[2,]   -2   -4
[3,]   -3   -6
[4,]   -4   -8
[5,]   -5  -10

例如,result[5,2,] = [10, -10] 对应于原始数据的 2nd 数据帧的第 5th 行(已删除第三列)。


上述解决方案有效,但我不禁怀疑应该有一个比手动实现的双重 for 循环和逐个设置所有元素更有效的解决方案一个。

【问题讨论】:

    标签: r performance dataframe


    【解决方案1】:

    您可以使用一些lapply 和purrr::transpose 来避免循环:

    # Example
    N <- 1e5
    example_list <-
      lapply(
        X = 1:2,
        FUN = function(X) {
          setNames(data.frame(X * c(1:N), -X * c(1:N), X * 100 * c(1:N)), c("C1", "C2", "C3"))
        }
      )
    
    important_cols <- c("C1", "C2")    
    
    # Your solution -> 58 seconds :O
    system.time({
      result <- array(0, c(N, 2, length(important_cols)))
      for(i in 1:N){
        for(j in 1:2){
          result[i,j,] <- c(example_list[[j]][i,important_cols], recursive=T)
        }
      }
    })
    
    # Solution with purrr::transpose -> 0 sec    
    library(magrittr)  ## for the %>%
    system.time({
      result2 <- example_list %>%
        lapply(function(df) df[important_cols]) %>%
        purrr::transpose() %>%
        sapply(function(l) do.call(cbind, l))
    })
    dim(result2) <- c(nrow(example_list[[1]]), 
                      length(example_list), 
                      length(important_cols))
    
    # Verification
    all.equal(result, result2)
    

    【讨论】:

    • 这似乎运作良好!为了完整起见,您介意在library(purrr) 中进行编辑吗(我想我也可以自己编辑它,但这是您的答案...)
    • 是的,我在这里明确使用了purrr::。如果需要,您可以加载包。
    • 啊,我明白了。最初它对我不起作用,但那是因为我还需要运行install.packages(purrr)。之后我也立即运行library(purrr),因为我认为这也是必要的。对 R 还不是很熟悉。
    • 仔细看后,实际上好像没有用。让(I, J, K) 表示原始数据的维度,K 是数据帧中重要列的数量。目标是将数据排列为(J, I, K),最好是在一个数组中(不想再处理低效的列表/数据帧索引)。你的解决方案给了我(K, J, I),外部结构仍然是一个列表
    • 对不起,但你想要的真的不清楚(“我希望所有的数字都存储在一个数组中(或者另一个更有效的数据结构也可以)”)。基本上我所拥有的与你所拥有的完全等价(使用列表而不是第三维)。如果你真的想要一个数组,只需使用 sapply 并更改结果的维度..(请参阅我的编辑)
    猜你喜欢
    • 1970-01-01
    • 2022-01-01
    • 2021-07-25
    • 2021-08-18
    • 1970-01-01
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    相关资源
    最近更新 更多