【问题标题】:Melt large list into long format efficiently有效地将大列表融合为长格式
【发布时间】:2017-10-24 21:54:04
【问题描述】:

我收到了一个大的list,格式如下:

example <- list("12908430751", "12908453145", c("12908453145","12908472085","453145472085"), c("12908453145", "12908472085", "453145472085"), "12908453145", c("12908453145", "12908472085", "453145472085"))

example
[[1]]
[1] "12908430751"

[[2]]
[1] "12908453145"

[[3]]
[1] "12908453145"  "12908472085"  "453145472085"

[[4]]
[1] "12908453145"  "12908472085"  "453145472085"

[[5]]
[1] "12908453145"

[[6]]
[1] "12908453145"  "12908472085"  "453145472085"

虽然使用 library(reshape2); melt(example) 适用于较小的数据集,但我的实际数据需要很长时间(约 600 万个元素)。我想知道是否有更有效的方法来实现这一点。

Output
      value     L1
1   12908430751  1
2   12908453145  2
3   12908453145  3
4   12908472085  3
5  453145472085  3
6   12908453145  4
7   12908472085  4
8  453145472085  4
9   12908453145  5
10  12908453145  6
11  12908472085  6
12 453145472085  6

我发现了类似 Melt data.frame containing list to long format (efficiently) 的内容,但未能适应我的情况。

结果 example1 拥有 100 万个元素

system.time({foo <- unlist(lapply(example1, function(x) length(x)))
result <- data.frame(value = unlist(example1), 
L1 = unlist(sapply(1:length(foo), function(x) rep(x, foo[x]))))})

用户系统已过

9.63 0.10 9.73

system.time({
df <- structure(list(value = example1 , id = 1:length(example1)), .Names = 
c("value", "L1"), row.names = 1:length(example), class = "data.frame")
result1 <- setDT(df)[, .(value = unlist(value)), by = .(L1)]})

用户系统已过

1.25 0.00 1.26

system.time({result3 <- tibble(L1 = 1:length(example1), value = example1) %>% unnest()})

用户系统已过

5.99 0.00 5.98

system.time({ stack(setNames(example1, seq_along(example)))})

用户系统已过

1.08 0.00 1.08

无法让并行版本以结果结尾,但可能支持我。尽管我没有定义效率,但我还是选择了最快的方法。

【问题讨论】:

  • 这远不是最快的,但根据我的测试,stack(setNames(example, seq_along(example))) 将比melt 快一个数量级。
  • 这里的答案似乎很合适而且很快 - stackoverflow.com/questions/31551036/…
  • 而且,至少对我而言,@thelatemail 的建议比发布的任何其他解决方案(包括我自己的)都更简洁易读
  • 同意,如果@thelatemail 添加他的评论作为答案,我可以接受
  • @user6617454 - 完成

标签: r list reshape


【解决方案1】:

如果您四处挖掘,可能有更快的方法,但基本 R 有 stack 工作得非常快:

stack(setNames(example, seq_along(example)))

#         values ind
#1   12908430751   1
#2   12908453145   2
#3   12908453145   3
#4   12908472085   3
#5  453145472085   3
#6   12908453145   4
#7   12908472085   4
#8  453145472085   4
#9   12908453145   5
#10  12908453145   6
#11  12908472085   6
#12 453145472085   6

它的内部基本上是一个unlist,然后重复names(x)的每个值,对应的lengths(x)次。请参阅utils:::stack.default 阅读代码。

【讨论】:

  • data.frame(values = unlist(example), ind = rep(seq_along(example), lengths(example))) 可能更快
【解决方案2】:

使用parallel 可以毫不费力地看到改进

library(parallel)
library(dplyr)
library(reshape2)
library(data.table)  # for rleid

cl <- makeCluster(detectCores())   # automatically detect number of cores
clusterEvalQ(cl, { library(reshape2) })  # need to export package to workers

# Split your data into chunks
nchunks <- 2   # does not need to equal number of cores (can be > # of cores but should be close to number of cores)
chunks <- split(example, cut(seq_along(example), nchunks))
result <- parLapply(cl, chunks, function(i) { melt(i) })
stopCluster(cl)

# combine back into data.frame
df <- Reduce("rbind", result)
answer <- df %>%
        mutate(L1 = rleid(L1))

输出

          value L1
1   12908430751  1
2   12908453145  2
3   12908453145  3
4   12908472085  3
5  453145472085  3
6   12908453145  4
7   12908472085  4
8  453145472085  4
9   12908453145  5
10  12908453145  6
11  12908472085  6
12 453145472085  6

【讨论】:

    【解决方案3】:

    如果您乐于使用tidyverse 方法,那么创建一个tibble 怎么样,然后再使用unnest(虽然我不确定这对您的用例有多有效):

    library(tidyverse)
    
    tibble(L1 = 1:length(example), value = example) %>% unnest()
    
    #> # A tibble: 12 x 2
    #>       L1        value
    #>    <int>        <chr>
    #>  1     1  12908430751
    #>  2     2  12908453145
    #>  3     3  12908453145
    #>  4     3  12908472085
    #>  5     3 453145472085
    #>  6     4  12908453145
    #>  7     4  12908472085
    #>  8     4 453145472085
    #>  9     5  12908453145
    #> 10     6  12908453145
    #> 11     6  12908472085
    #> 12     6 453145472085
    

    【讨论】:

      【解决方案4】:

      你可能想试试这个:

      df <- structure(list(value = example , id = 1:length(example)), .Names = c("value", "L1"), 
                  row.names = 1:length(example), class = "data.frame")
      
      library(data.table)
      setDT(df)[, .(value = unlist(value)), by = .(L1)]
      
      ##     L1        value
      ##  1:  1  12908430751
      ##  2:  2  12908453145
      ##  3:  3  12908453145
      ##  4:  3  12908472085
      ##  5:  3 453145472085
      ##  6:  4  12908453145
      ##  7:  4  12908472085
      ##  8:  4 453145472085
      ##  9:  5  12908453145
      ## 10:  6  12908453145
      ## 11:  6  12908472085
      ## 12:  6 453145472085
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-16
        • 1970-01-01
        相关资源
        最近更新 更多