【问题标题】:How to interrupt a function within a loop after a certain time in R?如何在R中的一段时间后中断循环内的函数?
【发布时间】:2020-07-21 21:13:24
【问题描述】:

我通过 R 中的 for 循环多次运行算法。我的循环非常基本,看起来像这样。

iter <- 5 #number of iterations
result <- list()

for (i in 1:iter) {
fit <- algorithm() #this is an example function that starts the algorithm
result[[i]] <- print(fit)
}

问题在于每次运行的运行时间差异很大。有的跑只需要 10 分钟,有的则需要一个多小时。但是,我知道较长的运行时间是由于算法由于初始值而存在问题,并且这些运行的结果无论如何都会是错误的。

所以,我现在正在寻找一种解决方案,即 (1) 在1000 秒,(2) 继续 for 循环,(3) 为每个中断添加一个额外的迭代。所以,最后,我想要运行时间少于 1000 秒的五次运行的结果。

有人有想法吗?这在技术上是否可行?提前致谢!

【问题讨论】:

    标签: r


    【解决方案1】:

    我认为您可以为此使用setTimeLimit

    快速演示:

    setTimeLimit(elapsed = 2)
    Sys.sleep(999)
    # Error in Sys.sleep(999) : reached elapsed time limit
    setTimeLimit(elapsed = Inf)
    

    (请务必注意,当您不再希望中断时,您应该返回时间限制设置。)

    我的“复杂算法”会随机休眠。那些随机长度是

    set.seed(42)
    sleeps <- sample(10, size=5)
    sleeps
    # [1]  1  5 10  8  2
    

    我将设置一个 6 秒的任意限制,超过此限制睡眠将被中断,我们将不会获得任何返回值。这应该会中断第三个和第四个元素。

    iter <- 5
    result <- list()
    for (i in seq_len(iter)) {
      result[[i]] <- tryCatch({
        setTimeLimit(elapsed = 6)
        Sys.sleep(sleeps[[i]])
        setTimeLimit(elapsed = Inf)
         c(iter = i, slp = sleeps[[i]])
      }, error = function(e) NULL)
    }
    result
    # [[1]]
    # iter  slp 
    #    1    1 
    # [[2]]
    # iter  slp 
    #    2    5 
    # [[3]]
    # NULL
    # [[4]]
    # NULL
    # [[5]]
    # iter  slp 
    #    5    2 
    

    如果你有不同的“睡眠”并且你最终得到一个比你需要的更短的对象,只需附加它:

    result <- c(result, vector("list", 5 - length(result)))
    

    我会稍微改进一下,有几件事:

    • 以这种方式填充result时,我更喜欢lapply而不是for循环;和
    • 由于复杂的算法可能由于其他原因而失败,如果我的睡眠提前失败,那么时间限制将不会被重置,所以我将使用on.exit,它确保在其外壳退出时调用一个函数,无论是由于错误与否。
    result <- lapply(seq_len(iter), function(i) {
      setTimeLimit(elapsed = 6)
      on.exit(setTimeLimit(elapsed = Inf), add = TRUE)
      tryCatch({
        Sys.sleep(sleeps[i])
        c(iter = i, slp = sleeps[i])
      }, error = function(e) NULL)  
    })
    result
    # [[1]]
    # iter  slp 
    #    1    1 
    # [[2]]
    # iter  slp 
    #    2    5 
    # [[3]]
    # NULL
    # [[4]]
    # NULL
    # [[5]]
    # iter  slp 
    #    5    2 
    

    在这种情况下,result 的长度为 5,因为 lapply 每次迭代都会返回一些内容。 (lapply 的使用对于 R 来说是惯用的,它的效率通常在 applymap-like 方法中,不像其他语言通过文字 for 循环实现真正的速度。)

    (顺便说一句:除了on.exit 逻辑,我也可以使用tryCatch(..., finally=setTimeLimit(elapsed=Inf))。)

    on.exit 逻辑的替代方法是在要限制的执行块 中使用setTimeLimit(.., transient=TRUE)。这将使这段代码

    result <- lapply(seq_len(iter), function(i) {
      tryCatch({
        setTimeLimit(elapsed = 6, transient = TRUE)
        Sys.sleep(sleeps[i])
        c(iter = i, slp = sleeps[i])
      },
      error = function(e) NULL)
    })
    

    这样做的一个好处是,无论受限代码块是否成功/中断,一旦完成,限制就会立即解除,因此无意中将其留在原位的风险较小。

    【讨论】:

    • 感谢您的解决方案!这已经对我有很大帮助了!但是还有一种方法可以在每次超过时间限制时向循环或 lapply 添加额外的迭代?在您的示例中, lapply 方法会产生一个长度为 5 的对象,但如果每次超过时间限制时我都可以额外运行一次算法,那就太好了,所以结果将是一个长度为 5 的对象,实际上是 5结果而不是 NULL
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    • 2021-11-09
    • 2017-07-07
    • 2023-04-04
    • 2021-12-21
    • 1970-01-01
    相关资源
    最近更新 更多