Skip to content

Commit 8b9f71f

Browse files
Add support for package 'fwb'
1 parent 52a9e11 commit 8b9f71f

10 files changed

Lines changed: 263 additions & 5 deletions

File tree

DESCRIPTION

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: progressify
2-
Version: 0.1.0-9007
2+
Version: 0.1.0-9008
33
Title: Progress Reporting of Common Functions via One Magic Function
44
Description: The progressify() function rewrites (transpiles) calls to sequential and parallel map-reduce functions such as base::lapply(), purrr::map(), foreach::foreach(), and plyr::llply() to signal progress updates. By combining this function with R's native pipe operator, you have a straightforward way to report progress on iterative computations with minimal refactoring, e.g. 'lapply(x, fcn) |> progressify()' and 'purrr::map(x, fcn) |> progressify()'. It is compatible with the 'futurize' package for parallelization, e.g. 'lapply(x, fcn) |> progressify() |> futurize()' and 'purrr::map(x, fcn) |> futurize() |> progressify()'.
55
Authors@R: c(
@@ -23,6 +23,7 @@ Suggests:
2323
foreach,
2424
furrr,
2525
future.apply,
26+
fwb,
2627
futurize,
2728
partykit,
2829
plyr,

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
`future_map_vec()`, `future_map2_vec()`, `future_pmap_vec()`, and
1313
`future_imap_vec()`.
1414

15+
* Add support for domain-specific CRAN package **fwb**, e.g. `res <-
16+
fwb::fwb(data, statistic, R = 100) |> progressify()`.
17+
1518
* Add support for `purrr::imap_vec()`.
1619

1720
* Add support for domain-specific CRAN package **sandwich**, e.g. `v <-

R/builtin-fwb.R

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# fwb::fwb(...) =>
2+
#
3+
# local({
4+
# .progressr_steps <- R
5+
# .progressr_simple <- if (is.null(simple)) wtype != "multinom" else simple
6+
# .progressr_skip_count <- if (.progressr_simple) 2L else 1L
7+
# .progressr_progressor <- progressr::progressor(steps = .progressr_steps)
8+
# fwb::fwb(
9+
# data = x,
10+
# statistic = function(..., ...FUN, .progressr_progressor) {
11+
# if (.progressr_skip_count > 0L) {
12+
# .progressr_skip_count <<- .progressr_skip_count - 1L
13+
# } else {
14+
# on.exit(.progressr_progressor())
15+
# }
16+
# ...FUN(...)
17+
# },
18+
# R = .progressr_steps,
19+
# ...FUN = statistic,
20+
# .progressr_progressor = .progressr_progressor
21+
# )
22+
# })
23+
#
24+
progressify_fwb <- local({
25+
template_fwb_outer <- bquote_compile(local({
26+
.progressr_steps <- .(STEPS)
27+
.progressr_simple <- if (is.null(.(SIMPLE))) .(WTYPE) != "multinom" else .(SIMPLE)
28+
.progressr_skip_count <- if (.progressr_simple) 2L else 1L
29+
.progressr_progressor <- progressr::progressor(steps = .progressr_steps)
30+
.(EXPR)
31+
}))
32+
33+
template_fwb_FUN <- bquote_compile(function(..., ...FUN, .progressr_progressor) {
34+
if (.progressr_skip_count > 0L) {
35+
.progressr_skip_count <<- .progressr_skip_count - 1L
36+
} else {
37+
on.exit(.progressr_progressor())
38+
}
39+
...FUN(...)
40+
})
41+
42+
function(expr, fcn_name, fcn, ..., envir = parent.frame()) {
43+
names <- names(expr)
44+
if (is.null(names)) names <- rep("", length.out = length(expr))
45+
names <- names[-1]
46+
target_names <- names(formals(fcn))[seq_along(names)]
47+
unnamed <- setdiff(target_names, names)
48+
## Handle '...' in formals - only resolve positional args before it
49+
ddd <- which(unnamed == "...")
50+
if (length(ddd) > 0L) {
51+
stopifnot(length(ddd) == 1L)
52+
unnamed <- unnamed[seq_len(ddd - 1L)]
53+
}
54+
empty_idxs <- which(names == "")
55+
n <- min(length(empty_idxs), length(unnamed))
56+
if (n > 0L) names[empty_idxs[seq_len(n)]] <- unnamed[seq_len(n)]
57+
names <- c("", names)
58+
59+
idx_R <- which(names == "R")
60+
idx_statistic <- which(names == "statistic")
61+
idx_simple <- which(names == "simple")
62+
idx_wtype <- which(names == "wtype")
63+
64+
parts <- as.list(expr)
65+
66+
steps <- if (length(idx_R) == 1L) parts[[idx_R]] else 999L
67+
parts[[idx_R]] <- quote(.progressr_steps)
68+
69+
simple_expr <- if (length(idx_simple) == 1L) parts[[idx_simple]] else quote(NULL)
70+
wtype_expr <- if (length(idx_wtype) == 1L) parts[[idx_wtype]] else quote(getOption("fwb_wtype", "exp"))
71+
72+
stopifnot(length(idx_statistic) == 1L)
73+
statistic <- expr[[idx_statistic]]
74+
parts[[idx_statistic]] <- bquote_apply(template_fwb_FUN, FUN = statistic)
75+
76+
idx_verbose <- which(names == "verbose")
77+
if (length(idx_verbose) == 0L) {
78+
parts$verbose <- FALSE
79+
}
80+
81+
progressr_args <- list(
82+
...FUN = statistic,
83+
.progressr_progressor = quote(.progressr_progressor)
84+
)
85+
parts <- c(parts, progressr_args)
86+
87+
bquote_apply(template_fwb_outer, STEPS = steps, SIMPLE = simple_expr, WTYPE = wtype_expr, EXPR = as.call(parts))
88+
} ## progressify_fwb()
89+
})
90+
91+
92+
append_builtin_transpilers_for_fwb <- local({
93+
known_fcns <- list(
94+
fwb = c
95+
)
96+
97+
template <- bquote_compile(function(expr, options) {
98+
ns <- getNamespace("fwb")
99+
fcn <- get(.(fcn_name), mode = "function", envir = ns)
100+
progressify_fwb(expr, fcn_name = .(fcn_name), fcn = fcn, envir = parent.frame())
101+
})
102+
103+
make_transpiler <- function(fcn_name) {
104+
transpiler <- eval(bquote_apply(template))
105+
eval(transpiler)
106+
}
107+
108+
function() {
109+
## fwb::fwb()
110+
transpilers <- list()
111+
for (fcn_name in names(known_fcns)) {
112+
transpilers[[fcn_name]] <- list(
113+
label = sprintf("fwb::%s() transpiler", fcn_name),
114+
transpiler = make_transpiler(fcn_name)
115+
)
116+
} ## for (fcn_name ...)
117+
transpilers <- list(fwb = transpilers)
118+
119+
append_transpilers("progressify::built-in", transpilers)
120+
121+
## Return required packages
122+
c("fwb", "progressr")
123+
}
124+
})

R/register_all_transpilers.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ register_all_transpilers <- function() {
66
append_builtin_transpilers_for_boot()
77
append_builtin_transpilers_for_crossmap()
88
append_builtin_transpilers_for_future.apply()
9+
append_builtin_transpilers_for_fwb()
910
append_builtin_transpilers_for_purrr()
1011
append_builtin_transpilers_for_furrr()
1112
append_builtin_transpilers_for_partykit()

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ parallelization.
100100
| Package | Functions |
101101
|----------------------------|------------------------------------------------------------------------------|
102102
| **[boot]** | `boot()`, `censboot()`, `tsboot()` |
103+
| **[fwb]** | `fwb()` |
103104
| **[partykit]** | `cforest()` |
104105
| **[sandwich]** | `vcovBS()`, `vcovJK()` |
105106

@@ -110,6 +111,8 @@ Here are some examples:
110111
```r
111112
res <- boot::boot(data, statistic, R = 100) |> progressify()
112113

114+
res <- fwb::fwb(data, statistic, R = 100) |> progressify()
115+
113116
forest <- partykit::cforest(Survived ~ ., data = as.data.frame(Titanic), ntree = 50L) |> progressify()
114117

115118
v <- sandwich::vcovBS(fit) |> progressify()
@@ -152,6 +155,7 @@ forest <- partykit::cforest(dist ~ speed, data = cars, ntree = 50L) |> progressi
152155
[future.apply]: https://future.apply.futureverse.org
153156
[futurize]: https://futurize.futureverse.org
154157
[furrr]: https://furrr.futureverse.org
158+
[fwb]: https://cran.r-project.org/package=fwb
155159
[partykit]: https://cran.r-project.org/package=partykit
156160
[plyr]: https://cran.r-project.org/package=plyr
157161
[progressr]: https://progressr.futureverse.org

inst/testme/test-progressify-fwb.R

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#' @tags pkg-fwb
2+
if (requireNamespace("fwb", quietly = TRUE)) {
3+
library(progressify)
4+
library(fwb)
5+
6+
options(progressify.debug = TRUE)
7+
8+
# 1. fwb::fwb test setups
9+
data <- mtcars
10+
my_stat <- function(data, w) {
11+
coef(lm(mpg ~ cyl, data = data, weights = w))
12+
}
13+
14+
exprs <- list(
15+
fwb_simple_true = quote(fwb(data = data, statistic = my_stat, R = 10L, simple = TRUE, verbose = FALSE)),
16+
fwb_simple_false = quote(fwb(data = data, statistic = my_stat, R = 10L, simple = FALSE, verbose = FALSE)),
17+
fwb_verbose_default = quote(fwb(data = data, statistic = my_stat, R = 10L))
18+
)
19+
20+
for (name in names(exprs)) {
21+
message(sprintf("Testing %s ...", name))
22+
expr <- exprs[[name]]
23+
24+
# Ensure reproducible seed
25+
set.seed(42)
26+
truth <- eval(expr)
27+
28+
set.seed(42)
29+
res <- eval(bquote(.(expr) |> progressify()))
30+
31+
# Ensure results are equivalent
32+
# We compare the $t slot (simulations results matrix) and $t0 (original estimate)
33+
stopifnot(all.equal(res$t, truth$t))
34+
stopifnot(all.equal(res$t0, truth$t0))
35+
36+
# Ensure no stdout leakage
37+
output <- utils::capture.output({
38+
set.seed(42)
39+
res2 <- eval(bquote(.(expr) |> progressify()))
40+
})
41+
stopifnot(length(output) == 0L)
42+
43+
# Ensure repeated evaluation is identical
44+
stopifnot(all.equal(res2$t, res$t))
45+
46+
message(sprintf("Testing %s ... done", name))
47+
}
48+
}

pkgdown/_pkgdown.menus.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ cran-focus:
2424
menu:
2525
- text: "CRAN packages supporting progressify()"
2626
- text: Progress updates for 'boot' functions
27-
href: articles/progressify-83-boot.html
27+
href: articles/progressify-81-boot.html
28+
- text: Progress updates for 'fwb' functions
29+
href: articles/progressify-81-fwb.html
2830
- text: Progress updates for 'partykit' functions
2931
href: articles/progressify-81-partykit.html
3032
- text: Progress updates for 'sandwich' functions
31-
href: articles/progressify-82-sandwich.html
33+
href: articles/progressify-81-sandwich.html

pkgdown/_pkgdown.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ navbar:
3939
menu:
4040
- text: CRAN packages supporting progressify()
4141
- text: Progress updates for 'boot' functions
42-
href: articles/progressify-83-boot.html
42+
href: articles/progressify-81-boot.html
43+
- text: Progress updates for 'fwb' functions
44+
href: articles/progressify-81-fwb.html
4345
- text: Progress updates for 'partykit' functions
4446
href: articles/progressify-81-partykit.html
4547
- text: Progress updates for 'sandwich' functions
46-
href: articles/progressify-82-sandwich.html
48+
href: articles/progressify-81-sandwich.html
4749

4850
news:
4951
text: News

tests/test-progressify-fwb.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#! /usr/bin/env Rscript
2+
## This runs testme test script inst/testme/test-progressify-fwb.R
3+
## Don't edit - it was autogenerated by inst/testme/deploy.R
4+
progressify:::testme("progressify-fwb")

vignettes/progressify-81-fwb.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<!--
2+
%\VignetteIndexEntry{Progress updates for 'fwb' functions}
3+
%\VignetteAuthor{Henrik Bengtsson}
4+
%\VignetteKeyword{R}
5+
%\VignetteKeyword{package}
6+
%\VignetteKeyword{fwb}
7+
%\VignetteKeyword{vignette}
8+
%\VignetteKeyword{progressify}
9+
%\VignetteEngine{progressify::selfonly}
10+
-->
11+
12+
The **progressify** package allows you to easily add progress
13+
reporting to sequential and parallel map-reduce code by piping to the
14+
`progressify()` function. Easy!
15+
16+
17+
# TL;DR
18+
19+
```r
20+
library(progressify)
21+
handlers(global = TRUE)
22+
library(fwb)
23+
24+
# Run fractional weighted bootstrap with progress signaling
25+
my_stat <- function(data, w) coef(lm(mpg ~ cyl, data = data, weights = w))
26+
res <- fwb(data = mtcars, statistic = my_stat, R = 1000) |> progressify()
27+
```
28+
29+
30+
# Introduction
31+
32+
This vignette demonstrates how to use this approach to add progress
33+
reporting to the **[fwb]** package's main function `fwb()`.
34+
35+
The **fwb** package provides functions for generating fractional weighted bootstrap replicates.
36+
Because these computations are iterative and computationally intensive, they can
37+
benefit significantly from progress reporting.
38+
39+
For example, `fwb()` runs a statistic function `R` times:
40+
41+
```r
42+
library(fwb)
43+
my_stat <- function(data, w) coef(lm(mpg ~ cyl, data = data, weights = w))
44+
res <- fwb(data = mtcars, statistic = my_stat, R = 1000)
45+
```
46+
47+
By default, `fwb()` provides no feedback on how far it has progressed.
48+
However, we can easily add progress reporting using the `progressify()` function:
49+
50+
```r
51+
library(fwb)
52+
53+
library(progressify)
54+
handlers(global = TRUE)
55+
56+
my_stat <- function(data, w) coef(lm(mpg ~ cyl, data = data, weights = w))
57+
res <- fwb(data = mtcars, statistic = my_stat, R = 1000) |> progressify()
58+
```
59+
60+
61+
# Supported Functions
62+
63+
The `progressify()` function supports the following **fwb**
64+
functions:
65+
66+
* `fwb()`
67+
68+
69+
[fwb]: https://cran.r-project.org/package=fwb

0 commit comments

Comments
 (0)