wviechtb / metafor

A meta-analysis package for R
https://www.metafor-project.org
233 stars 52 forks source link

adding a call back function for custom progress bar in `profile()` function (and other computationally intensive functions) #92

Open FBartos opened 1 month ago

FBartos commented 1 month ago

Classification: Feature Request

Summary

It would be nice to have an option to pass a "progress bar tick" function as an argument to the profile() function (and other computationally expensive functions). This would allow us, e.g., in JASP, notify users about the computational progress.

In the case of the profile.mv() function, it could be triggered after each new parameter is finished profiling. E.g., in JASP, we use jaspBase::progressbarTick() to incremenet the JASP progress counter. In some of my packages, I allow passing an optional hidden argument that is run as eval(expr = parse(text = "progressbarTick()")) (which also evades CRANs check for non-CRAN packages --- if it is passed as an argument, any other progress bar could be slot in).

Cheers, Frantisek

wviechtb commented 4 weeks ago

Could you clarify a bit more what exactly you have in mind? Pretty much all computationally heavy functions in metafor have some kind of way of communicating progress to the user (via verbose=TRUE or progbar=TRUE). But I guess this is not what can be made use of within JASP, so you want an argument passed via ... that is evaluated as eval(expr = parse(argument)) somewhere within various functions?

FBartos commented 4 weeks ago

Yes, that's exactly what I have in mind. We can't catch printed output in the GUI --- but we have a custom functions that can send the signal directly. An example below:

computationally_heavy_function <- function(x, ...) {

  dots <- list(...)

  # heavy computations
  out  <- list()
  for(i in 1:x){

    # some compute here
    out[[i]] <- i
    # execute the callback
    if(!is.null(dots[["custom_callback"]]))
      eval(expr = parse(text = dots[["custom_callback"]]))
  }

  return(out)
}

fit <- computationally_heavy_function(10)
fit <- computationally_heavy_function(10, custom_callback = "cat('+')")

Ideally I would know how many "tick" are gonna happen or also have some additional information about the steps (which I can further forward to the GUI, which would work like this:

computationally_heavy_function <- function(x, y, ...) {

  dots <- list(...)

  # heavy computations
  out  <- list()

  if(!is.null(dots[["custom_callback_start"]]))
    do.call(dots[["custom_callback_start"]], list(n = x, label = "compute 1"))
  for(i in 1:x){

    # some compute here
    out[[i]] <- i
    # execute the callback
    if(!is.null(dots[["custom_callback"]]))
      do.call(dots[["custom_callback"]], list())
  }

  if(!is.null(dots[["custom_callback_start"]]))
    do.call(dots[["custom_callback_start"]], list(n = y, label = "compute 2"))
  for(i in 1:y){

    # some compute here
    out[[i]] <- i
    # execute the callback
    if(!is.null(dots[["custom_callback"]]))
      do.call(dots[["custom_callback"]], list())
  }

  return(out)
}
fit <- computationally_heavy_function(10, 5, custom_callback_start = function(n, label) cat(sprintf("\nabout to start '%1$s'\n", label)), custom_callback = function () cat("+"))

but that would be a bit too much to ask :)

wviechtb commented 4 weeks ago

I see. In which functions would you like this added? You metioned profile() (and hence confint() for consistency), but where else?

FBartos commented 3 weeks ago

I also noticed that the influence.rma.mv functions take quite some time as well as the permutest() function. However, I did not look into how those functions work and whether it can be applied to them.

wviechtb commented 3 weeks ago

I think we should sit down together and go through all of the functions where this might apply, since this also depends on what JASP actually makes use of. Please get in touch via email to set something up.

FBartos commented 3 weeks ago

If you didn't receive an email from me, could you email me at f.bartos96@gmail.com?