使用“创建进度条”时如何创建进度条;foreach();R中的函数?

使用“创建进度条”时如何创建进度条;foreach();R中的函数?,r,foreach,progress-bar,R,Foreach,Progress Bar,有一些关于如何在R程序中为循环创建计数器的信息性文章。但是,在使用带有“foreach()”的并行版本时,如何创建类似的函数?在循环之前,使用Sys.time()保存开始时间。循环行、列或其他你知道总数的东西。然后,在循环中,您可以计算到目前为止运行的时间(请参见difftime)、完成百分比、速度和估计剩余时间。每个进程都可以使用消息功能打印这些进度行。您将得到如下输出 1/1000 complete @ 1 items/s, ETA: 00:00:45 2/1000 complete @ 1

有一些关于如何在R程序中为循环创建计数器的信息性文章。但是,在使用带有“foreach()”的并行版本时,如何创建类似的函数?

在循环之前,使用
Sys.time()
保存开始时间。循环行、列或其他你知道总数的东西。然后,在循环中,您可以计算到目前为止运行的时间(请参见
difftime
)、完成百分比、速度和估计剩余时间。每个进程都可以使用
消息
功能打印这些进度行。您将得到如下输出

1/1000 complete @ 1 items/s, ETA: 00:00:45
2/1000 complete @ 1 items/s, ETA: 00:00:44

显然,循环顺序将极大地影响这项工作的效果。不知道如何使用
foreach
,但是使用
multicore
mclappy
您将使用
mc.preschedule=FALSE
获得良好的结果,这意味着随着前面项目的完成,项目将按顺序逐个分配给进程。

以下代码将在R中为控制结构生成一个很好的进度条。它还将通过替换为所需的进度条对象来使用图形进度条

# Gives us the foreach control structure.
library(foreach)
# Gives us the progress bar object.
library(utils)
# Some number of iterations to process.
n <- 10000
# Create the progress bar.
pb <- txtProgressBar(min = 1, max = n, style=3)
# The foreach loop we are monitoring. This foreach loop will log2 all 
# the values from 1 to n and then sum the result. 
k <- foreach(i = icount(n), .final=sum, .combine=c) %do% {
    setTxtProgressBar(pb, i)
    log2(i)
}
# Close the progress bar.
close(pb)
#为我们提供了foreach控制结构。
图书馆(foreach)
#为我们提供进度条对象。
图书馆(utils)
#要处理的迭代次数。

n此代码是的修改版本,即使将
%dopar%
与并行后端一起使用,也会生成一个进度条:

#Load Libraries
library(foreach)
library(utils)
library(iterators)
library(doParallel)
library(snow)

#Choose number of iterations
n <- 1000

#Progress combine function
f <- function(){
  pb <- txtProgressBar(min=1, max=n-1,style=3)
  count <- 0
  function(...) {
    count <<- count + length(list(...)) - 1
    setTxtProgressBar(pb,count)
    Sys.sleep(0.01)
    flush.console()
    c(...)
  }
}

#Start a cluster
cl <- makeCluster(4, type='SOCK')
registerDoParallel(cl)

# Run the loop in parallel
k <- foreach(i = icount(n), .final=sum, .combine=f()) %dopar% {
  log2(i)
}

head(k)

#Stop the cluster
stopCluster(cl)
#加载库
图书馆(foreach)
图书馆(utils)
库(迭代器)
图书馆(双平行)
图书馆(雪)
#选择迭代次数
n编辑:在对doSNOW软件包进行修改后,当使用
%dopar%
时,显示一个漂亮的进度条变得非常简单,并且可以在Linux、Windows和OS X上运行

doSNOW
现在通过
.options.snow
参数正式支持进度条

library(doSNOW)
cl <- makeCluster(2)
registerDoSNOW(cl)
iterations <- 100
pb <- txtProgressBar(max = iterations, style = 3)
progress <- function(n) setTxtProgressBar(pb, n)
opts <- list(progress = progress)
result <- foreach(i = 1:iterations, .combine = rbind, 
                  .options.snow = opts) %dopar%
{
    s <- summary(rnorm(1e6))[3]
    return(s)
}
close(pb)
stopCluster(cl) 

是进度条的外观。这看起来有点奇怪,因为每个进度条都会打印一个新的进度条,而且工作人员可能会有一点滞后,这会导致进度条偶尔来回移动

这现在可以通过
并行
软件包实现。使用OSX 10.11上的R3.2.3进行测试,在RStudio内部运行,使用
“PSOCK”
类型集群

library(doParallel)

# default cluster type on my machine is "PSOCK", YMMV with other types
cl <- parallel::makeCluster(4, outfile = "")
registerDoParallel(cl)

n <- 10000
pb <- txtProgressBar(0, n, style = 2)

invisible(foreach(i = icount(n)) %dopar% {
    setTxtProgressBar(pb, i)
})

stopCluster(cl)
库(双并行)
#我的机器上的默认集群类型是“PSOCK”,YMMV和其他类型

cl这段代码使用
doMC
后端,并使用
R
中的优秀软件包,实现了一个跟踪并行化
foreach
循环的进度条。它假定由
numCores
指定的所有内核所做的工作量大致相等

library(foreach)
library(doMC)
library(progress)

iterations <- 100
numCores <- 8

registerDoMC(cores=numCores)

pbTracker <- function(pb,i,numCores) {
    if (i %% numCores == 0) {
        pb$tick()
    }
}

pb <- progress_bar$new(
  format <- " progress [:bar] :percent eta: :eta",
  total <- iterations / numCores, clear = FALSE, width= 60)


output = foreach(i=1:iterations) %dopar% {
    pbTracker(pb,i,numCores)
    Sys.sleep(1/20)
}
库(foreach)
图书馆(doMC)
图书馆(进度)

迭代您还可以使用
progress
包来实现这一点

# loading parallel and doSNOW package and creating cluster ----------------
library(parallel)
library(doSNOW)

numCores<-detectCores()
cl <- makeCluster(numCores)
registerDoSNOW(cl)

# progress bar ------------------------------------------------------------
library(progress)

iterations <- 100                               # used for the foreach loop  

pb <- progress_bar$new(
  format = "letter = :letter [:bar] :elapsed | eta: :eta",
  total = iterations,    # 100 
  width = 60)

progress_letter <- rep(LETTERS[1:10], 10)  # token reported in progress bar

# allowing progress bar to be used in foreach -----------------------------
progress <- function(n){
  pb$tick(tokens = list(letter = progress_letter[n]))
} 

opts <- list(progress = progress)

# foreach loop ------------------------------------------------------------
library(foreach)

foreach(i = 1:iterations, .combine = rbind, .options.snow = opts) %dopar% {
  summary(rnorm(1e6))[3]
}

stopCluster(cl) 

#加载并行和doSNOW包并创建集群----------------
图书馆(平行)
图书馆(doSNOW)


你知道如何接受堆栈溢出的答案吗?如果没有,请阅读FAQ并回顾您之前的问题。Paralleler博客中有一个
foreach
的例子,我认为值得一读:)嗯,这很奇怪。在实际计算完成后,我的函数似乎可以一次性更新进度条……此方法可能仅适用于doRedis后端。我将不得不研究如何使它与doParallel后端一起工作。它与doParallel不能很好地工作,因为doParallel仅在返回所有结果后调用combine函数,因为它是通过调用parallel clusterApplyLB函数实现的。此技术仅适用于动态调用联合函数的后端,如doRedis、doMPI、doNWS和(已失效?)doSMP。@Steve Weston感谢您的澄清。这对我来说很有意义,现在我明白了为什么我的函数在doRedis上工作,但不是doParallel。你可以尝试刷新控制台。。。未经测试。此答案没有解决与并行化相关的OP问题,%dopar%您是在使用某种全局计数器,还是依赖正在循环的索引(
i
)?@C8H10N4O2:循环的索引。使用
mclappy
时,它会在
mc.preschedule=FALSE
时产生很好的效果,有时是错误的,但通常与默认值非常接近(通常更快)
mc.preschedule=TRUE
。建议的改进(我认为它与您的想法非常接近,不需要单独回答):基本上,使用
cat
每次迭代在
tempfile
中写一个换行符,然后计算换行符的数量(我使用
wc
,因为我在Linux上,但是还有其他针对Windows的解决方案),并使用它来更新进度条。这样做的好处是它是单调递增的。缺点是你必须在每次迭代中读取一个文件——不知道这有多慢。谢谢@MichaelChirico的建议,但现在有了一种“官方”的方法。我已经更新了答案。我似乎无法从函数中实现此功能。软件包doSNOW现在已被取代。Windows 10上的R 3.2.2似乎没有生成任何包含此代码的进度条。。。这是针对>=3.2.3的吗?@IainS我宁愿将差异归因于操作系统的不一致性,而不是R版本。这似乎偶尔会下降。它可能无法处理迭代的异步性质(i=15可能在i=10之前完成)。如果您实际上注册了多个内核,这是行不通的。上面的示例似乎与我的MacBook Pro 2017,R v上的一样有效。3.5.1. 我相信上面一个与并行相关的包可以防止多个内核在循环中的实际工作很小的情况下启动。试着在循环中加入一些更费劲的东西——它应该会起作用。但是上面提到的甚至没有注册核心?我认为它实际上并没有把任务分出去。要清楚的是,上述工作对我来说,b
# loading parallel and doSNOW package and creating cluster ----------------
library(parallel)
library(doSNOW)

numCores<-detectCores()
cl <- makeCluster(numCores)
registerDoSNOW(cl)

# progress bar ------------------------------------------------------------
library(progress)

iterations <- 100                               # used for the foreach loop  

pb <- progress_bar$new(
  format = "letter = :letter [:bar] :elapsed | eta: :eta",
  total = iterations,    # 100 
  width = 60)

progress_letter <- rep(LETTERS[1:10], 10)  # token reported in progress bar

# allowing progress bar to be used in foreach -----------------------------
progress <- function(n){
  pb$tick(tokens = list(letter = progress_letter[n]))
} 

opts <- list(progress = progress)

# foreach loop ------------------------------------------------------------
library(foreach)

foreach(i = 1:iterations, .combine = rbind, .options.snow = opts) %dopar% {
  summary(rnorm(1e6))[3]
}

stopCluster(cl)