【问题标题】:Add a progress bar to boot function in R在R中添加进度条以启动功能
【发布时间】:2016-10-07 00:50:56
【问题描述】:

我正在尝试向 R 中的引导函数添加进度条。 我试图使示例函数尽可能简单(因此我在此示例中使用均值)。

library(boot)
v1 <- rnorm(1000)
rep_count = 1

m.boot <- function(data, indices) {
  d <- data[indices]
  setWinProgressBar(pb, rep_count)
  rep_count <- rep_count + 1
  Sys.sleep(0.01)
  mean(d, na.rm = T) 
  }

tot_rep <- 200
pb <- winProgressBar(title = "Bootstrap in progress", label = "",
                     min = 0, max = tot_rep, initial = 0, width = 300)
b <- boot(v1, m.boot, R = tot_rep)
close(pb)

引导程序正常运行,但问题是rep_count 的值在循环中没有增加,并且进度条在此过程中保持冻结状态。

如果我在引导完成后检查rep_count的值,它仍然是1。

我做错了什么?也许引导函数不是简单地将m.boot函数插入一个循环中,因此其中的变量不会增加?

谢谢。

【问题讨论】:

  • package pbapply 是一种简单的方法,可以为使用apply 系列应用函数的任何任务显示进度条。 github.com/psolymos/pbapply 。如果您可以在某种形式的apply 中使用您的m.boot,这将非常简单。

标签: r function loops progress statistics-bootstrap


【解决方案1】:

您可以使用包progress如下:

library(boot)
library(progress)

v1 <- rnorm(1000)

#add progress bar as parameter to function
m.boot <- function(data, indices, prog) {
  
  #display progress with each run of the function
  prog$tick()
  
  d <- data[indices]
  Sys.sleep(0.01)
  mean(d, na.rm = T) 
  
}

tot_rep <- 200

#initialize progress bar object
pb <- progress_bar$new(total = tot_rep + 1) 

#perform bootstrap
boot(data = v1, statistic = m.boot, R = tot_rep, prog = pb)

我还没有完全弄清楚为什么有必要将 progress_bar 的迭代次数设置为 +1 总引导复制(参数 R),但这是我自己的代码所必需的,否则会引发错误。似乎 bootstrap 函数运行的次数比您在参数 R 中指定的次数多,因此如果进度条设置为仅运行 R 次,它会认为作业在实际完成之前完成。

【讨论】:

  • 之所以必须设置 total = R+1 可能是因为 boot() 计算 R + 1 统计量 - 一个用于实际数据 (t0) 和 R 统计量 t 用于 R 引导样本。因此实际上,boot() 被调用了 R+1 次。
【解决方案2】:

pbapply 包旨在与矢量化函数一起使用。在这个问题的上下文中,有两种方法可以实现这一点:(1)按照建议编写一个包装器,它不会产生与'boot' 类相同的对象; (2) 或者,lapply(seq_len(RR), fn) 行可以写成pblapply(seq_len(RR), fn)。选项 2 可以通过在本地复制/更新 boot 函数来实现,如下例所示,或者询问包维护者 Brian Ripley 是否考虑直接或通过 pbapply 添加进度条作为依赖。

我的解决方案(由 cmets 指示的更改):

library(boot)
library(pbapply)
boot2 <- function (data, statistic, R, sim = "ordinary", stype = c("i", 
    "f", "w"), strata = rep(1, n), L = NULL, m = 0, weights = NULL, 
    ran.gen = function(d, p) d, mle = NULL, simple = FALSE, ..., 
    parallel = c("no", "multicore", "snow"), ncpus = getOption("boot.ncpus", 
        1L), cl = NULL) 
{
call <- match.call()
stype <- match.arg(stype)
if (missing(parallel)) 
    parallel <- getOption("boot.parallel", "no")
parallel <- match.arg(parallel)
have_mc <- have_snow <- FALSE
if (parallel != "no" && ncpus > 1L) {
    if (parallel == "multicore") 
        have_mc <- .Platform$OS.type != "windows"
    else if (parallel == "snow") 
        have_snow <- TRUE
    if (!have_mc && !have_snow) 
        ncpus <- 1L
    loadNamespace("parallel")
}
if (simple && (sim != "ordinary" || stype != "i" || sum(m))) {
    warning("'simple=TRUE' is only valid for 'sim=\"ordinary\", stype=\"i\", n=0', so ignored")
    simple <- FALSE
}
if (!exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)) 
    runif(1)
seed <- get(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
n <- NROW(data)
if ((n == 0) || is.null(n)) 
    stop("no data in call to 'boot'")
temp.str <- strata
strata <- tapply(seq_len(n), as.numeric(strata))
t0 <- if (sim != "parametric") {
    if ((sim == "antithetic") && is.null(L)) 
        L <- empinf(data = data, statistic = statistic, stype = stype, 
            strata = strata, ...)
    if (sim != "ordinary") 
        m <- 0
    else if (any(m < 0)) 
        stop("negative value of 'm' supplied")
    if ((length(m) != 1L) && (length(m) != length(table(strata)))) 
        stop("length of 'm' incompatible with 'strata'")
    if ((sim == "ordinary") || (sim == "balanced")) {
        if (isMatrix(weights) && (nrow(weights) != length(R))) 
            stop("dimensions of 'R' and 'weights' do not match")
    }
    else weights <- NULL
    if (!is.null(weights)) 
        weights <- t(apply(matrix(weights, n, length(R), 
            byrow = TRUE), 2L, normalize, strata))
    if (!simple) 
        i <- index.array(n, R, sim, strata, m, L, weights)
    original <- if (stype == "f") 
        rep(1, n)
    else if (stype == "w") {
        ns <- tabulate(strata)[strata]
        1/ns
    }
    else seq_len(n)
    t0 <- if (sum(m) > 0L) 
        statistic(data, original, rep(1, sum(m)), ...)
    else statistic(data, original, ...)
    rm(original)
    t0
}
else statistic(data, ...)
pred.i <- NULL
fn <- if (sim == "parametric") {
    ran.gen
    data
    mle
    function(r) {
        dd <- ran.gen(data, mle)
        statistic(dd, ...)
    }
}
else {
    if (!simple && ncol(i) > n) {
        pred.i <- as.matrix(i[, (n + 1L):ncol(i)])
        i <- i[, seq_len(n)]
    }
    if (stype %in% c("f", "w")) {
        f <- freq.array(i)
        rm(i)
        if (stype == "w") 
            f <- f/ns
        if (sum(m) == 0L) 
            function(r) statistic(data, f[r, ], ...)
        else function(r) statistic(data, f[r, ], pred.i[r, 
            ], ...)
    }
    else if (sum(m) > 0L) 
        function(r) statistic(data, i[r, ], pred.i[r, ], 
            ...)
    else if (simple) 
        function(r) statistic(data, index.array(n, 1, sim, 
            strata, m, L, weights), ...)
    else function(r) statistic(data, i[r, ], ...)
}
RR <- sum(R)
res <- if (ncpus > 1L && (have_mc || have_snow)) {
    if (have_mc) {
        parallel::mclapply(seq_len(RR), fn, mc.cores = ncpus)
    }
    else if (have_snow) {
        list(...)
        if (is.null(cl)) {
            cl <- parallel::makePSOCKcluster(rep("localhost", 
              ncpus))
            if (RNGkind()[1L] == "L'Ecuyer-CMRG") 
              parallel::clusterSetRNGStream(cl)
            res <- parallel::parLapply(cl, seq_len(RR), fn)
            parallel::stopCluster(cl)
            res
        }
        else parallel::parLapply(cl, seq_len(RR), fn)
    }
}
else pblapply(seq_len(RR), fn) #### changed !!!
t.star <- matrix(, RR, length(t0))
for (r in seq_len(RR)) t.star[r, ] <- res[[r]]
if (is.null(weights)) 
    weights <- 1/tabulate(strata)[strata]
boot.return(sim, t0, t.star, temp.str, R, data, statistic, 
    stype, call, seed, L, m, pred.i, weights, ran.gen, mle)
}
## Functions not exported by boot
isMatrix <- boot:::isMatrix
index.array <- boot:::index.array
boot.return <- boot:::boot.return
## Now the example
m.boot <- function(data, indices) {
  d <- data[indices]
  mean(d, na.rm = T) 
}
tot_rep <- 200
v1 <- rnorm(1000)
b <- boot2(v1, m.boot, R = tot_rep)

【讨论】:

  • 这是一个不错的解决方案,但我不认为包的维护者有兴趣在本地添加任何进度条,因为这将不可避免地降低boot 的性能功能。不过,这可能是一个优雅的解决方案,使用 boot 函数的副本,就像您所做的那样!
【解决方案3】:

增加的rep_count是一个局部变量,每次函数调用后都会丢失。在下一次迭代中,函数再次从全局环境中获取rep_count,即其值为1。

你可以使用&lt;&lt;-:

rep_count <<- rep_count + 1

这分配给在函数外部的搜索路径上首先找到的rep_count。当然,通常不建议使用&lt;&lt;-,因为应该避免函数的副作用,但这里有一个合法的用例。但是,您可能应该将整个内容包装在一个函数中,以避免对全局环境产生副作用。

可能会有更好的解决方案...

【讨论】:

  • 从程序员的角度来看,我认为这是更正确的方式。但由于我在编程方面的能力不足,我想我会坚持下面提供的解决方案。非常感谢!
  • 你不需要高级编程技能来改变这一行代码。
  • 你是对的,我只是喜欢使用别人的函数,因为我认为他们的编程比我的好。例如,能够使用pbapply 包增加了在几秒钟内更改进度条样式和类型的能力。我正在尝试找到一种将pbapplybootstrap 一起使用的方法。但也许有人会在我之前找到解决方案!
  • 如果要使用apply类型的函数,则不能使用boot。您必须编写自己的引导程序(这并不难,而且可能具有教育意义)。
【解决方案4】:

我想我找到了一个可能的解决方案。这将@Roland 的答案与pbapply 包的便利性结合在一起,使用它的函数startpb()closepb() 等。

library(boot)
library(pbapply)

v1 <- rnorm(1000)
rep_count = 1
tot_rep = 200

m.boot <- function(data, indices) {
  d <- data[indices]
  setpb(pb, rep_count)
  rep_count <<- rep_count + 1
  Sys.sleep(0.01)                #Just to slow down the process
  mean(d, na.rm = T) 
}

pb <- startpb(min = 0, max = tot_rep)
b <- boot(v1, m.boot, R = tot_rep)
closepb(pb)
rep_count = 1

如前所述,将所有内容包装在一个函数中可以避免与rep_count 变量混淆。

【讨论】:

    【解决方案5】:

    dplyr 包中的进度条运行良好:

    library(dplyr)
    library(boot)
    
    v1 <- rnorm(1000)
    
    m.boot <- function(data, indices) {
      d <- data[indices]
      p$tick()$print()  # update progress bar
      Sys.sleep(0.01)
      mean(d, na.rm = T) 
    }
    
    tot_rep <- 200
    p <- progress_estimated(tot_rep+1)  # init progress bar
    b <- boot(v1, m.boot, R = tot_rep)
    

    【讨论】:

      【解决方案6】:

      你可以使用包pbapply

      library(boot)
      library(pbapply)
      v1 <- rnorm(1000)
      rep_count = 1
      
      # your m.boot function ....
      m.boot <- function(data, indices) {
                                         d <- data[indices]
                                         mean(d, na.rm = T) 
                                         }
      
      # ... wraped in `bootfunc`
      bootfunc <- function(x) { boot(x, m.boot, R = 200) }
      
      # apply function to v1 , returning progress bar
      pblapply(v1, bootfunc)
      
      # > b <- pblapply(v1, bootfunc)
      # >   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% Elapsed time: 02s
      

      【讨论】:

      • 我有问题。该函数多次运行bootstrap函数,得到的b对象不是单个bootstrap对象,而是1000个bootstrap对象的向量。我认为 pbapply 可能不适用于此功能。
      • 确实@fzara,我正在考虑这个问题,我会回来解决这个问题。
      • 非常感谢!同时我找到了一个解决方法,希望它对你也有用。
      猜你喜欢
      • 1970-01-01
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-10
      • 1970-01-01
      • 2011-05-18
      相关资源
      最近更新 更多