使用进度条包装到FOR循环 [英] Wrapper to FOR loops with progress bar
问题描述
我喜欢在为循环运行缓慢的时使用进度条。这可以很容易地与几个帮手完成,但是我喜欢从 tcltk 包中的
tkProgressBar
。
一个小例子:
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)
我想设置一个小功能来存储在我的 .Rprofile 中,命名为 forp
(as:for循环带进度条),调用就像 for
,但使用自动添加的进度条 - 但不幸的是不知道如何实现和抓住循环函数的 expr
部分。我有一些实验与 do.call
但没有成功:(
And I would like to set up a small function to store in my .Rprofile named to forp
(as: for loop with progressbar), to call just like for
but with auto added progress bar - but unfortunately have no idea how to implement and grab the expr
part of the loop function. I had some experiments with do.call
but without success :(
虚构的工作示例(其行为像一个 for
循环,但创建一个 TkProgressBar
,并在每次迭代中自动更新它):
Imaginary working example (which acts like a for
loop but creates a TkProgressBar
and auto updates it in each iteration):
forp (i in 1:10) {
#do something
}
更新:我认为问题的核心是如何编写一个函数,不仅在函数后面的括号中有参数(如: foo(bar)
),还可以处理在括号后指定的 expr
,如: foo(bar)expr
。
UPDATE: I think the core of the question is how to write a function which no only have parameters in the parentheses after the function (like: foo(bar)
), but also can handle expr
specified after the closing parentheses, like: foo(bar) expr
.
BOUNTY OFFER 会去任何可以修改我建议的功能像的语法一样工作,例如
BOUNTY OFFER: would go to any answer that could modify my suggested function to work like the syntax of basic for
loops. E.g. instead of
> forp(1:1000, {
+ a<-i
+ })
> a
[1] 1000
它可以像:
> forp(1:1000) {
+ a<-i
+ }
> a
[1] 1000
只是为了澄清任务再次:我们如何抓住一个函数调用的 {expression}
?我恐怕这是不可能的,但是会为了这个利益而离开赏金几天:)
Just to clarify the task again: how could we grab the { expression }
part of a function call? I am afraid of that this is not possible, but will leave on the bounty for a few days for the pros :)
推荐答案
我的解决方案非常类似于Andrie的,除了它使用base R,我再次提出他的意见,需要在函数中包装你想要做的,随后需要使用< < - code>在更高的环境中修改东西。
My solution is very similar to Andrie's except it uses base R, and I second his comments on the need to wrap what you want to do in a function and the subsequent need to use <<-
to modify stuff in a higher environment.
这是一个不起作用的功能,并且缓慢执行:
Here's a function that does nothing, and does it slowly:
myfun <- function(x, text) {
Sys.sleep(0.2)
cat("running ",x, " with text of '", text, "'\n", sep="")
x
}
这是我的 forp
函数。请注意,无论我们实际上是循环,而是循环遍历序列 1:n
,并在循环中获得正确的条件。
Here's my forp
function. Note that regardless of what we're actually looping over, it instead loops over the sequence 1:n
instead and get the right term of what we actually want within the loop. plyr
does this automatically.
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)
}
可以使用和 forp
的,如果我们要做的只是调用
myfun
:
x <- LETTERS[1:5]
for(xi in x) myfun(xi, "hi")
forp(x, myfun, text="hi")
如果我们想要修改某些东西,可以使用它们。
And here's how they might be used if we want to modify something along the way.
out <- "result:"
for(xi in x) {
out <- paste(out, myfun(xi, "hi"))
}
out <- "result:"
forp(x, function(xi) {
out <<- paste(out, myfun(xi, "hi"))
})
对于这两个版本,结果是
For both versions the result is
> out
[1] "result: A B C D E"
编辑:看到你的(daroczig)解决方案,我有另一个想法可能不是那么笨重,这是评估父框架中的表达式。这使得更容易允许除 i
之外的值(现在使用索引
参数指定),尽管如此现在我不认为它处理一个函数作为表达式,虽然只是放在而不是一个for循环,不要紧。
After seeing your (daroczig's) solution, I have another idea that might not be quite so unwieldy, which is to evaluate the expression in the parent frame. This makes it easier to allow for values other than i
(now specified with the index
argument), though as of right now I don't think it handles a function as the expression, though just to drop in instead a for loop that shouldn't matter.
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)
}
从上面运行我的例子的代码将是
The code to run my example from above would be
out <- "result:"
forp2("xi", LETTERS[1:5], {
out <- paste(out, myfun(xi, "hi"))
})
,结果是一样的。
其他编辑,根据您的赏金报价中的其他信息:
ANOTHER EDIT, based on the additional information in your bounty offer:
语法 forX(1:1000) %doX $ {expression}
是可能的;这就是 foreach
软件包。我现在太懒了,把它从你的解决方案中建造出来,但是建立我的,可能看起来像这样:
The syntax forX(1:1000) %doX$ { expression }
is possible; that's what the foreach
package does. I'm too lazy right now to build it off of your solution, but building off mine, it could look like this:
`%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
}
然后使用语法是这样,结果与上述相同。
Then the use syntax is this, and the result is the same as above.
out <- "result:"
forX(xi=LETTERS[1:5]) %doX% {
out <- paste(out, myfun(xi, "hi"))
}
out
这篇关于使用进度条包装到FOR循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!