Giter VIP home page Giter VIP logo

pbfor's Introduction

pbfor

Install with :

devtools::install_github("moodymudskipper/pbfor")

This package proposes two solutions to use progress bars conveniently with the standard for syntax, both are using the great package progress from Gábor Csárdi and Rich FitzJohn.

  • with pb_for() : override temporarily or locally the for function to wrap around base::for and support progress bars.
  • with for<- : wrap around base::for using syntax pb -> for(it in seq) {exp} where pb .

Both solutions behave as standard for calls :

  • The values changed at the previous iteration are available
  • on error the modified variables will have the value they had just before the error

Usage

library(pbfor)
#> Loading required package: progress

Using pb_for()

By default pb_for() will override the for function for one run only.

pb_for()
for (i in 1:10) {
  # DO SOMETHING
  Sys.sleep(0.5)
}

Using parameters from progress::progress_bar$new() :

pb_for(format = "Working hard: [:bar] :percent :elapsed", 
       callback = function(x) message("Were'd done!"))
for (i in 1:10) {
  # DO SOMETHING
  Sys.sleep(0.5)
}
#> Were'd done!

Using for<-

The only restriction compared to a standard for call is that the first argument must exist and can't be NULL.

i <- NA 
progress_bar$new() -> for (i in 1:10) {
  # DO SOMETHING
  Sys.sleep(0.5)
}

We can define a custom progress bar, and maybe define it conveniently in an initialisation script or in one's R profile.

pb <- progress_bar$new(format = "Working hard: [:bar] :percent :elapsed", 
       callback = function(x) ("Were'd done!"))
pb  -> for (i in 1:10) {
  # DO SOMETHING
  Sys.sleep(0.5)
}

For nested progress bars we can use the following trick :

pbi <- progress_bar$new(format = "i: [:bar] :percent\n\n")
pbj <- progress_bar$new(format = "j: [:bar] :percent  ")
i <- NA
j <- NA
pbi  -> for (i in 1:10) {
  pbj  -> for (j in 1:10) {
    # DO SOMETHING
    Sys.sleep(0.1)
  }
}

Note that bars are cleared by default but in that case the i bar will be shown at each iteration.


how they work

pb_for()

pb_for() creates a for function object in its parent environment, then the new for :

  • sets up a progress bar
  • modifies the loop content
  • adds a `*pb*`$tick() at the end of the loop content expression
  • feeds it back to base::`for` in a clean environment
  • assigns on exit all modified or created variables to the parent environment.
  • removes itself if once is TRUE (the default)

It's generally sensitive to override an operator, but it cleans after itself and won't affect the global environment if used in a function so I think it's safe enough to use.

for<-

This approach :

  • doesn't override for
  • allows the use of progress bar templates
  • has an arguably more intuitive api

It has a few drawbacks however:

  • its first argument must exist, which is the case for all assignment functions (fun<-).
  • it does some memory magic to find the name of its first argument as it's not easily done with assignment functions, this might have a performance cost, and I'm not 100% sure about the robustness
  • we need the package pryr

What it does :

  • find the name of the first argument, using a helper function
  • clone the progress bar input
  • edit it to account for the number of iterations of the loop (the length of the second argument of for<-

After this it's similar to what is described for pb_for() in the section above.

pbfor's People

Contributors

moodymudskipper avatar

Stargazers

Tomasz Kalinowski avatar

Watchers

James Cloos avatar  avatar  avatar

pbfor's Issues

more flexibility to `for<-` and additional features to `pb_for`

aright now it just takes a progress bar, so we're limited to the options of progress_bar$new()

We could feed another object, which would just be a list with a class.

progbar(...) -> for(...){...}

Arguments would contain all arguments from progress_bar$new() but also features from pb_for

Additional features for both can be :

  • silence all outputs : so the progress bar displays nicely without being duplicated
  • convert messages to pb$message() so messages can be displayed on top of bar, which won't be duplicated
  • convert all OUTPUT to pb$message(), with a prefix saying : TEXT:, WARNING: or MESSAGE:. I'm not sure how this one would work.

Maybe it's easier to code ourself the feature to print or warn above the progress bar.

continue loop where we left it

We can have an argument to start the loop when we left it before, using the value of the iterator in the environment.

It could be a relative value too.

Not sure about the name.

expand scope : control flow decorators ?

What we're doing here is in some respect decorating the for function (for one run).
For regular functions we have adverbs, but not for control flow constructs.

We can think about other useful decorations and incorporate them in a dedicated package.

Some thoughts already :

  • pb_while() and pb_repeat() show progress bars, of course we don't know how many iterations there will be, but we can chose that one tick is n iterations, or calibrate from an "expected" count.
  • verbose_for(), verbose_while(), verbose_repeat() : count the iterations and log more info
  • verbose_for() prints the iterator, optionally through a function, e.g. verbose_for(dim)
  • verbose_if() : logs condition status (makes it much easier to debug)
  • verbose_if() and verbose_while() go a bit further and log subparts of the expression if it uses a comparison/equality/inequality operator (incl %in%), if a logical operator is used, we could dig which was evaluated. For instance with if(x > 3) we'll evaluate x and log it, but won't log 3 as it's a symbol. In while(x > y && z == 2) we'll log x > y, x, y, and maybe z, if z is not evaluated it will be shown as unevaluated, or we'll use a pretty icon, or crayon colors etc.

The reason why we can do these logs is that for the result to be boolean we need length one easily printable objects on each side, so basically we evaluate the parse tree recursively at every level as long as we know the expression evaluated should be length one.

We could also track the is.* functions but then we can only log the real type, not the values, which might not be length one.


We can log :

  • all iterations
  • the last one only
  • the last n, but only at the end (even if it fails, using on.exit), we'd store the values.

beep = TRUE arg will beep once it's over, and no need to uncomment it etc


functionals :

It's straightforward to adapt pb_for() to any functional, these could use adverbs or existing packages like pbapply() but here we'd just do pb(lapply) (with optional progress params) and the next call would have a progress bar.
The function argument is found automatically when possible (looking for FUN, then for .f then for f), be we can be explicit : pb(lapply, f = "FUN")
verbose(lapply, dim) prints dim of current object (first arg of the function), this is nice because the alternative would be to write a cumbersome anonymous wrapper.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.