使用进度条的FOR循环包装器

dar*_*zig 19 expression loops r function wrapper

我喜欢在运行慢for循环时使用进度条.这可以通过几个助手轻松完成,但我确实喜欢tkProgressBar来自tcltk包.

一个小例子:

pb <- tkProgressBar(title = "Working hard:", min = 0, max = length(urls), width = 300)
for (i in 1:300) {
    # DO SOMETHING
    Sys.sleep(0.5)
    setTkProgressBar(pb, i, label=paste( round(i/length(urls)*100, 0), "% ready!"))
}
close(pb)
Run Code Online (Sandbox Code Playgroud)

而且我想设置一个小函数存储在我的.Rprofile中,命名为forp(as:for循环带进度条),调用就像for但是使用自动添加的进度条 - 但遗憾的是不知道如何实现和抓取expr部分循环函数.我做了一些实验do.call但没有成功:(

虚构的工作示例(其作用类似于for循环,但TkProgressBar在每次迭代中创建并自动更新它):

forp (i in 1:10) {
    #do something
}
Run Code Online (Sandbox Code Playgroud)

更新:我认为问题的核心是如何编写一个函数,该函数不仅在函数后面的括号中有参数(如:)foo(bar),而且还可以expr在结束括号后指定,如:foo(bar) expr.


BOUNTY OFFER:会找到任何可以修改我建议的函数的答案,就像基本for循环的语法一样.而不是

> forp(1:1000, {
+   a<-i
+ })
> a
[1] 1000
Run Code Online (Sandbox Code Playgroud)

它可以被称为:

> forp(1:1000) {
+   a<-i
+ }
> a
[1] 1000
Run Code Online (Sandbox Code Playgroud)

只是为了再次澄清这个任务:我们怎么能抓住{ expression }函数调用的一部分?我担心这是不可能的,但会为专业人士留下几天的赏金:)

And*_*rie 6

鉴于提供的其他答案,我怀疑它是 不可能 很难按照你指定的方式去做.

但是,如果您plyr创造性地使用包,我相信有一种非常接近的方式.诀窍是使用l_ply将列表作为输入并且不创建输出.

此解决方案与您的规范之间唯一真正的区别是,在for循环中,您可以直接在同一环境中修改变量.使用l_ply您需要发送一个函数,因此如果您想在父环境中修改内容,则必须更加小心.

请尝试以下方法:

library(plyr)
forp <- function(i, .fun){
  l_ply(i, .fun, .progress="tk")
}

a <- 0
forp(1:100, function(i){
  Sys.sleep(0.01)
  a<<-a+i
  })
print(a)
[1] 5050
Run Code Online (Sandbox Code Playgroud)

这将创建一个进度条并修改a全局环境中的值.


编辑.

为避免疑问:参数.fun将始终是具有单个参数的函数,例如.fun=function(i){...}.

例如:

for(i in 1:10){expr} 相当于 forp(1:10, function(i){expr})

换一种说法:

  • i 是循环的循环参数
  • .fun 是一个带有单个参数的函数 i


Aar*_*ica 6

我的解决方案与Andrie的解决方案非常相似,只不过它使用了基础R,而我的第二个评论就是需要在函数中包装你想要做的事情以及随后需要<<-在更高的环境中修改内容.

这是一个什么都不做的功能,慢慢地做:

myfun <- function(x, text) {
  Sys.sleep(0.2)
  cat("running ",x, " with text of '", text, "'\n", sep="")
  x
}
Run Code Online (Sandbox Code Playgroud)

这是我的forp功能.请注意,无论我们实际上是什么循环,它都会反而循环遍历序列,1:n并获得我们在循环中实际需要的正确术语.plyr自动执行此操作.

library(tcltk)
forp <- function(x, FUN, ...) {
  n <- length(x)
  pb <- tkProgressBar(title = "Working hard:", min = 0, max = n, width = 300)
  out <- vector("list", n)
  for (i in seq_len(n)) {
    out[[i]] <- FUN(x[i], ...)
    setTkProgressBar(pb, i, label=paste( round(i/n*100, 0), "% ready!"))
  }
  close(pb)
  invisible(out)
}
Run Code Online (Sandbox Code Playgroud)

如果我们想要做的就是调用以下两种方式for和方式:forpmyfun

x <- LETTERS[1:5]
for(xi in x) myfun(xi, "hi")
forp(x, myfun, text="hi")
Run Code Online (Sandbox Code Playgroud)

如果我们想要沿途修改某些内容,可以使用它们.

out <- "result:"
for(xi in x) {
  out <- paste(out, myfun(xi, "hi"))
}

out <- "result:"
forp(x, function(xi) {
    out <<- paste(out, myfun(xi, "hi"))
})
Run Code Online (Sandbox Code Playgroud)

对于这两个版本,结果是

> out
[1] "result: A B C D E"
Run Code Online (Sandbox Code Playgroud)

编辑:看到你的(daroczig)解决方案后,我有另一个想法,可能不是那么笨拙,这是评估父框架中的表达式.这样可以更容易地允许除了i(现在使用index参数指定)之外的值,但是现在我不认为它将函数作为表达式处理,尽管只是为了放入一个无关紧要的for循环.

forp2 <- function(index, x, expr) {
  expr <- substitute(expr)
  n <- length(x)
  pb <- tkProgressBar(title = "Working hard:", min = 0, max = n, width = 300)
  for (i in seq_len(n)) {
    assign(index, x[i], envir=parent.frame())
    eval(expr, envir=parent.frame())
    setTkProgressBar(pb, i, label=paste( round(i/n*100, 0), "% ready!"))
  }
  close(pb)
}
Run Code Online (Sandbox Code Playgroud)

从上面运行我的例子的代码是

out <- "result:"
forp2("xi", LETTERS[1:5], {
    out <- paste(out, myfun(xi, "hi"))
})
Run Code Online (Sandbox Code Playgroud)

结果是一样的.

另一个编辑,根据您的赏金优惠中的其他信息:

语法forX(1:1000) %doX$ { expression }是可能的; 这是foreach包的作用.我现在懒得把它从你的解决方案中解脱出来,但是建立起来,它可能看起来像这样:

`%doX%` <- function(index, expr) {
  x <- index[[1]]
  index <- names(index)
  expr <- substitute(expr)
  n <- length(x)
  pb <- tkProgressBar(title = "Working hard:", min = 0, max = n, width = 300)
  for (i in seq_len(n)) {
    assign(index, x[i], envir=parent.frame())
    eval(expr, envir=parent.frame())
    setTkProgressBar(pb, i, label=paste( round(i/n*100, 0), "% ready!"))
  }
  close(pb)
  invisible(out)
}

forX <- function(...) {
  a <- list(...)
  if(length(a)!=1) {
    stop("index must have only one element")
  }
  a
}
Run Code Online (Sandbox Code Playgroud)

然后使用语法是这个,结果与上面相同.

out <- "result:"
forX(xi=LETTERS[1:5]) %doX% {
  out <- paste(out, myfun(xi, "hi"))
}
out
Run Code Online (Sandbox Code Playgroud)


Ari*_*man 3

如果您使用plyr命令系列而不是 for 循环(如果可能的话,通常是个好主意),您将获得整个进度条系统的额外奖励。

R.utils它还内置了一些进度条,并且存在在 for 循环中使用它们的说明