【问题标题】:Why are those 3 loops faster than 1 short lapply为什么这 3 个循环比 1 个短 lapply 快
【发布时间】:2019-06-21 21:58:21
【问题描述】:

在尝试对函数进行优化和基准测试时,我能够将 3 个 for 循环缩减为 1 个短 lapply 调用,但函数变慢了。

我试图理解为什么会发生这种情况,就像我预先分配 3 个具有相同长度的列表并将它们填充到 3 个不同的循环中的 3 个循环一样,这似乎没有必要且效率低下。

## Data #################
Grid = structure(list(ID = 1:81, X = c(99.99922283, 299.99922281, 499.9992228, 
                                       699.99922279, 899.99922277, 1099.99922275, 1299.99922274, 1499.99922273, 
                                       1699.99922271, 99.99922293, 299.99922291, 499.99922291, 699.99922289, 
                                       899.99922287, 1099.99922286, 1299.99922284, 1499.99922283, 1699.99922282, 
                                       99.99922303, 299.99922302, 499.99922301, 699.999223, 899.99922298, 
                                       1099.99922296, 1299.99922295, 1499.99922294, 1699.99922292, 99.99922314, 
                                       299.99922312, 499.99922311, 699.9992231, 899.99922308, 1099.99922307, 
                                       1299.99922306, 1499.99922304, 1699.99922303, 99.99922324, 299.99922323, 
                                       499.99922322, 699.9992232, 899.99922319, 1099.99922317, 1299.99922316, 
                                       1499.99922315, 1699.99922313, 99.99922335, 299.99922333, 499.99922332, 
                                       699.99922331, 899.9992233, 1099.99922328, 1299.99922327, 1499.99922325, 
                                       1699.99922324, 99.99922345, 299.99922344, 499.99922342, 699.99922341, 
                                       899.9992234, 1099.99922338, 1299.99922337, 1499.99922335, 1699.99922334, 
                                       99.99922356, 299.99922354, 499.99922353, 699.99922352, 899.9992235, 
                                       1099.99922348, 1299.99922347, 1499.99922345, 1699.99922344, 99.99922367, 
                                       299.99922365, 499.99922364, 699.99922362, 899.99922361, 1099.99922359, 
                                       1299.99922358, 1499.99922356, 1699.99922355), Y = c(1699.9975638, 
                                                                                           1699.99756369, 1699.99756357, 1699.99756347, 1699.99756336, 1699.99756325, 
                                                                                           1699.99756314, 1699.99756303, 1699.99756292, 1499.99756399, 1499.99756388, 
                                                                                           1499.99756377, 1499.99756366, 1499.99756355, 1499.99756344, 1499.99756333, 
                                                                                           1499.99756322, 1499.99756311, 1299.99756418, 1299.99756408, 1299.99756396, 
                                                                                           1299.99756386, 1299.99756375, 1299.99756363, 1299.99756353, 1299.99756342, 
                                                                                           1299.99756331, 1099.99756438, 1099.99756427, 1099.99756416, 1099.99756405, 
                                                                                           1099.99756394, 1099.99756384, 1099.99756372, 1099.99756361, 1099.99756351, 
                                                                                           899.99756457, 899.99756446, 899.99756434, 899.99756424, 899.99756414, 
                                                                                           899.99756403, 899.99756392, 899.99756381, 899.9975637, 699.99756477, 
                                                                                           699.99756466, 699.99756454, 699.99756443, 699.99756433, 699.99756422, 
                                                                                           699.99756411, 699.99756401, 699.99756389, 499.99756496, 499.99756485, 
                                                                                           499.99756474, 499.99756463, 499.99756452, 499.99756441, 499.9975643, 
                                                                                           499.9975642, 499.99756409, 299.99756516, 299.99756505, 299.99756494, 
                                                                                           299.99756483, 299.99756472, 299.99756461, 299.9975645, 299.99756439, 
                                                                                           299.99756428, 99.99756535, 99.99756524, 99.99756513, 99.99756502, 
                                                                                           99.99756491, 99.9975648, 99.99756469, 99.99756458, 99.99756448
                                       )), row.names = c("11", "12", "13", "14", "15", "16", "17", "18", 
                                                         "19", "21", "22", "23", "24", "25", "26", "27", "28", "29", "31", 
                                                         "32", "33", "34", "35", "36", "37", "38", "39", "41", "42", "43", 
                                                         "44", "45", "46", "47", "48", "49", "51", "52", "53", "54", "55", 
                                                         "56", "57", "58", "59", "61", "62", "63", "64", "65", "66", "67", 
                                                         "68", "69", "71", "72", "73", "74", "75", "76", "77", "78", "79", 
                                                         "81", "82", "83", "84", "85", "86", "87", "88", "89", "91", "92", 
                                                         "93", "94", "95", "96", "97", "98", "99"), class = "data.frame")

mut2 = sapply(1:100, function(i) sample(c(0,1), size = nrow(Grid), replace = T))

## Functions #################
## Triple For loop
getRects          <- function(trimtonOut, Grid){
  len1 <- dim(trimtonOut)[2]
  childli = childnew = rectidli = vector("list", len1);
  for (i in 1:len1) {
    childli[[i]] <- trimtonOut[,i]
  }
  for (u in 1:len1){
    rectidli[[u]] <- which(childli[[u]]==1, arr.ind = T)
  }
  for (z in 1:len1) {
    childnew[[z]] <- Grid[rectidli[[z]],];
  }
  return(childnew)
}
## Shortest Lapply
getRects1          <- function(trimtonOut, Grid){
  lapply(1:dim(trimtonOut)[2], function(i) {
    Grid[which(trimtonOut[,i]==1, arr.ind = T),]
  })
}
## Shorter Lapply
getRects2          <- function(trimtonOut, Grid){
  lapply(1:dim(trimtonOut)[2], function(i) {
    tmp = which(trimtonOut[,i]==1, arr.ind = T)
    Grid[tmp,]
  })
}
## Longest Lapply
getRects3          <- function(trimtonOut, Grid){
  lapply(1:dim(trimtonOut)[2], function(i) {
    tmp = trimtonOut[,i]
    tmp1 = which(tmp==1, arr.ind = T)
    Grid[tmp1,]
  })
}


## Execute and Compare #################
getRectV  <- getRects(mut2, Grid)
getRectV1 <- getRects1(mut2, Grid)
getRectV2 <- getRects2(mut2, Grid)
getRectV3 <- getRects3(mut2, Grid)

identical(getRectV,getRectV1)
identical(getRectV,getRectV2)
identical(getRectV,getRectV3)

## Benchmark #################
library(microbenchmark)
# mut2 = sapply(1:400, function(i) sample(c(0,1), size = nrow(Grid), replace = T))
mc = microbenchmark(
                    loop = getRects(mut2, Grid),
                    lap1 = getRects1(mut2, Grid),
                    lap2 = getRects2(mut2, Grid),
                    lap3 = getRects3(mut2, Grid)
)
mc

【问题讨论】:

    标签: r for-loop lapply microbenchmark


    【解决方案1】:

    你确定这些时差有那么大吗?

    library(microbenchmark)
    # mut2 = sapply(1:400, function(i) sample(c(0,1), size = nrow(Grid), replace = T))
    mc = microbenchmark(
      loop = getRects(mut2, Grid),
      lap1 = getRects1(mut2, Grid),
      lap2 = getRects2(mut2, Grid),
      lap3 = getRects3(mut2, Grid)
    )
    mc
    #> Unit: milliseconds
    #>  expr      min       lq     mean   median       uq       max neval
    #>  loop 2.651485 2.699166 3.195301 2.756171 3.136741  8.010173   100
    #>  lap1 2.755571 2.828128 3.098850 2.877806 3.012487  7.427598   100
    #>  lap2 2.737105 2.808924 3.118260 2.863221 2.939996 13.706736   100
    #>  lap3 2.719101 2.787040 3.191893 2.852963 3.004811  8.490867   100
    

    【讨论】:

    • 它们并不重要。您还可以通过取消注释 microbenchmark 之前的行来增加mut2,您会看到三重 for 循环始终是“赢家”,并且差异会增加。虽然它可能并不重要,但我试图理解为什么,因为这对我来说似乎不合逻辑。
    • 如果算法复杂度相同,那么您将看到与某些函数调用开销相关的微小差异...您能否更明确地说明您认为不合逻辑的内容?
    • 除了 *apply 函数总是比 for 循环更受欢迎。好吧,我必须创建 3 个长度相同的列表,并用 3 个单独的循环填充它们,而不是只用 1 个 lapply 调用创建 1 个列表。
    • 来自 Hadley Wickham 的 Advanced R:泛函的常见用途是替代 for 循环。 For 循环在 R 中名声不好。它们以速度慢着称(尽管这种名声只是部分正确,有关更多详细信息,请参阅适当的修改)。但是 for 循环的真正缺点是它们的表现力不是很强。 在您的情况下,lapply 的优势在于表现力几乎与显式循环版本相同。这本身就是一个很好的理由。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 2019-07-17
    • 1970-01-01
    • 2013-08-05
    • 2014-03-21
    • 1970-01-01
    • 2022-01-12
    相关资源
    最近更新 更多