Using a progress bar with pmap for a function that can go in package on CRAN

Viewed 563

I am trying to write a function that will download different vectors and combine them to return a data frame. I am using pmap() in the purrr package to loop through each of the vectors. I want to add a progress bar, from the progress package, to show the end user the progress of the data frame. This is possible using the <<- as suggested in the package Read Me...

A simplified version of the code:

library(tidyverse)
library(progress)

f_one_col <- function(x, m, s){
  d <- tibble(i = rnorm(n = 5, mean = m, sd = s))
  names(d) <- x
  # slow things down to check if the progress bar is appearing
  Sys.sleep(1)
  pb$tick()
  return(d)
} 

f <- function(d){
  pb <<- progress_bar$new(total = nrow(d))
  pb$tick(0)
  d$col <- pmap(.l = d, .f = f_one_col)
  pb$terminate() 
  rm(pb, envir = .GlobalEnv)
  return(bind_cols(d$col))
}

d0 <- tibble(
  x = c("a", "b", "c"),
  m = c(10, 20, 30),
  s = c(5, 200, 1000)
)
f(d = d0)
# # A tibble: 5 x 3
#        a     b      c
#    <dbl> <dbl>  <dbl>
# 1  6.50   70.8 -1071.
# 2  3.51  -52.0  -542.
# 3  3.76  369.   -351.
# 4 11.4   171.   1745.
# 5  0.421 111.   1886.

But when i come to putting the function into my R package I get an ERROR in the build check...

no visible binding for '<<-' assignment to 'pb'

and from what I have read so far, using <<- seems to be a no no for CRAN. Is there any other way to use a progress bar without having to use <<- or a hacky way to allow <<- in a package?

2 Answers

Avoid having to use <<- by placing the helper function (f_one_col()) directly in the pmap() function.

... and use pmap_dfc() rather than pmap() and bind_cols()

f <- function(d){
  pb <- progress_bar$new(total = nrow(d))
  pb$tick(0)
  pmap_dfc(
    .l = d, 
    .f = function(x, m, s){
      d <- tibble(i = rnorm(n = 5, mean = m, sd = s))
      names(d) <- x
      pb$tick()
      Sys.sleep(0.5)
      return(d)
    })
}

Follow the example for progress_progress from the progress package Readme.md file. Superassignment is not guaranteed to assign into the global environment, if first looks for an object with the symbol that it is assigning to in parent environments and creates a new object in the global environment only if it doesn't find one, as a kind of fallback. Read help("<<-"). This "helpful" fallback behavior is not what you should be relying on in package code (it's also completely unintuitive, has no obvious analog in other languages and is a great way to create code with unintended side effects!)

Package functions should not be changing state in the global environment, that is a horrible code smell.

Related