【问题标题】:Find maximum from combination of two tables (for-loop too slow)从两个表的组合中查找最大值(for循环太慢)
【发布时间】:2012-04-23 21:40:14
【问题描述】:

我有一个数据表“the.data”,其中第一列表示测量仪器,其余为不同的测量数据。

instrument <- c(1,2,3,4,5,1,2,3,4,5)
hour <- c(1,1,1,1,1,2,2,2,2,2)
da <- c(12,14,11,14,10,19,15,16,13,11)
db <- c(21,23,22,29,28,26,24,27,26,22)
the.data <- data.frame(instrument,hour,da,db)

我还定义了仪器组,例如,组 1 (g1) 指的是仪器 1 和 2。

g1 <- c(1,2)
g2 <- c(4,3,1)
g3 <- c(1,5,2)
g4 <- c(2,4)
g5 <- c(5,3,1,2,6)
groups <- c("g1","g2","g3","g4","g5")

我需要找出每个组的总和在哪个时间具有每种数据类型的最大值,以及它的总和。

g1 小时 1: sum(da)=12+14=26 g1 小时 2: sum(da)=19+15=34

因此,对于 g1 和 da,答案是 2 小时和 34 小时。

我在 for 循环中使用 for 循环执行此操作,但这需要很长时间(几个小时后我中断了)。问题是.data 大约有 100.000 行长,大约有 5.000 个组,每个组包含 2-50 个仪器。

有什么好的方法可以做到这一点?

衷心感谢 Stack-overflow 的所有贡献者。

更新:现在示例中只有五个组。

/克里斯

【问题讨论】:

    标签: r plyr data.table reshape2


    【解决方案1】:

    group 循环将不得不保留,或者充其量被 lapply() 之类的东西取代。但是,hour 循环可以通过重新格式化为instrument x hour 矩阵然后只进行向量化代数来完全替换。例如:

    library(reshape2)
    
    groups = list(g1, g3)
    
    the.data.a = dcast(the.data[,1:3], instrument ~ hour)
    
    > sapply(groups, function(x) data.frame(max = max(colSums(the.data.a[x, -1])),
                                            ind = which.max(colSums(the.data.a[x, -1]))))
        [,1] [,2]
    max 34   45  
    ind 2    2   
    

    【讨论】:

    • 它有两个组运行,但对我来说有五个组会抛出一个错误。
    • @DWin 那是因为示例数据中只有仪器 1-5。其他组参考不存在的工具。
    • 感谢您快速且非常好的回答。从这里阅读,我意识到我错过了“the.data”中的一些仪器并进行了更正,但事实证明我的真实数据也缺少测量值(以小时为单位,而不是在仪器中)。
    • @Chris 太好了,很高兴它有帮助。祝你的项目好运!
    【解决方案2】:

    这是John Colby's answer 的略微修改版本,带有一些示例数据。

    set.seed(21)
    instrument <- sample(100, 1e5, TRUE)
    hour <- sample(24, 1e5, TRUE)
    da <- trunc(runif(1e5)*10)
    db <- trunc(runif(1e5)*10)
    the.data <- data.frame(instrument,hour,da,db)
    groups <- replicate(5000, sample(100, sample(50,1)))
    names(groups) <- paste("g",1:length(groups),sep="")
    
    library(reshape2)
    system.time({    
    the.data.a <- dcast(the.data[,1:3], instrument ~ hour, sum)
    out <- t(sapply(groups, function(i) {
      byHour <- colSums(the.data.a[i,-1])
      c(max(byHour), which.max(byHour))
    }))
    colnames(out) <- c("max.hour","max.sum")
    })
    # Using da as value column: use value.var to override.
    #    user  system elapsed 
    #    3.80    0.00    3.81 
    

    【讨论】:

    • 很好的例子,乔希!我一直很好奇我们能多快得到这些东西。
    【解决方案3】:

    这是使用 Hadley 的 plyrreshape2 的一种方法。首先,我们将根据仪器是否在该组中添加一些布尔值到the.data。然后我们将其融合为长格式,将不需要的行子集化,然后使用ddplydata.table 进行分组操作。

    #add boolean columns
    the.data <- transform(the.data, 
                          g1 = instrument %in% g1,
                          g2 = instrument %in% g2,
                          g3 = instrument %in% g3,
                          g4 = instrument %in% g4,
                          g5 = instrument %in% g5
                          )
    
    #load library
    library(reshape2)
    #melt into long format
    the.data.m <- melt(the.data, id.vars = 1:4)
    #subset out data that that has FALSE for the groupings
    the.data.m <- subset(the.data.m, value == TRUE)
    
    #load plyr and data.table
    library(plyr)
    library(data.table)
    
    #plyr way
    ddply(the.data.m, c("variable", "hour"), summarize, out = sum(da))
    #data.table way
    dt <- data.table(the.data.m)
    dt[, list(out = sum(da)), by = "variable, hour"]
    

    做一些基准测试看看哪个更快:

    library(rbenchmark)   
    f1 <- function() ddply(the.data.m, c("variable", "hour"), summarize, out = sum(da))
    f2 <- function() dt[, list(out = sum(da)), by = "variable, hour"]
    
    > benchmark(f1(), f2(), replications=1000, order="elapsed", columns = c("test", "elapsed", "relative"))
      test elapsed relative
    2 f2()    3.44 1.000000
    1 f1()    6.82 1.982558
    

    因此,对于此示例,data.table 大约快 2 倍。您的里程可能会有所不同。

    只是为了表明它给出了正确的值:

    > dt[, list(out = sum(da)), by = "variable, hour"]
          variable hour out
     [1,]       g1    1  26
     [2,]       g1    2  34
     [3,]       g2    1  25
     [4,]       g2    2  29
    
    ...
    

    【讨论】:

    • 我认为您的代码还没有处理 max 和 which.max 的选择。
    • @Dwin - 哦,你是对的!我之前误读/掩盖了这一点,稍后会更新。谢谢,-追逐
    【解决方案4】:

    您没有提供您的代码(或生成组的编程方式,这似乎需要组数为 5000),但这可能更有效地使用 R :

    groups <- list(g1,g2,g3,g4,g5)
    gmax <- list()
    # The "da" results
    for( gitem in seq_along(groups) ) { 
           gmax[[gitem]] <- with( subset(the.data , instrument %in% groups[[gitem]]),  
                                   tapply(da , hour, sum) ) }
    damat <- matrix(c(sapply(gmax, which.max), 
                      sapply(gmax, max)) , ncol=2)
    
    # The "db" results
    for( gitem in seq_along(groups) ) { 
           gmax[[gitem]] <- with( subset(the.data , instrument %in% groups[[gitem]]),  
                                   tapply(db , hour, sum) ) }
    dbmat <- matrix(c(sapply(gmax, which.max), 
                      sapply(gmax, max)) , ncol=2)
    
    #--------
    > damat
         [,1] [,2]
    [1,]    2   34
    [2,]    2   29
    [3,]    2   45
    [4,]    1   14
    [5,]    2   42
    > dbmat
         [,1] [,2]
    [1,]    2   50
    [2,]    2   53
    [3,]    1   72
    [4,]    1   29
    [5,]    1   73
    

    【讨论】:

      猜你喜欢
      • 2017-09-30
      • 1970-01-01
      • 1970-01-01
      • 2018-08-17
      • 2019-09-18
      • 2018-09-21
      • 2016-01-01
      • 2020-09-09
      • 1970-01-01
      相关资源
      最近更新 更多