Skip to content

Commit 96fd9a4

Browse files
Add support for 'lme4'
1 parent a6b6945 commit 96fd9a4

10 files changed

Lines changed: 254 additions & 0 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Suggests:
2525
future.apply,
2626
fwb,
2727
futurize,
28+
lme4,
2829
partykit,
2930
plyr,
3031
purrr,

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
* Add support for domain-specific CRAN package **fwb**, e.g. `res <-
1616
fwb::fwb(data, statistic, R = 100) |> progressify()`.
1717

18+
* Add support for domain-specific CRAN package **lme4**, e.g. `res <-
19+
lme4::bootMer(fit, statistic, nsim = 100) |> progressify()`.
20+
1821
* Add support for `purrr::imap_vec()`.
1922

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

R/builtin-lme4.R

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

R/register_all_transpilers.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ register_all_transpilers <- function() {
1515
append_builtin_transpilers_for_stats()
1616
append_builtin_transpilers_for_foreach()
1717
append_builtin_transpilers_for_doFuture()
18+
append_builtin_transpilers_for_lme4()
1819
} ## register_all_transpilers()

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ parallelization.
101101
|----------------------------|------------------------------------------------------------------------------|
102102
| **[boot]** | `boot()`, `censboot()`, `tsboot()` |
103103
| **[fwb]** | `fwb()` |
104+
| **[lme4]** | `bootMer()` |
104105
| **[partykit]** | `cforest()` |
105106
| **[sandwich]** | `vcovBS()`, `vcovJK()` |
106107

@@ -113,6 +114,8 @@ res <- boot::boot(data, statistic, R = 100) |> progressify()
113114

114115
res <- fwb::fwb(data, statistic, R = 100) |> progressify()
115116

117+
res <- lme4::bootMer(fit, statistic, nsim = 100) |> progressify()
118+
116119
forest <- partykit::cforest(Survived ~ ., data = as.data.frame(Titanic), ntree = 50L) |> progressify()
117120

118121
v <- sandwich::vcovBS(fit) |> progressify()
@@ -156,6 +159,7 @@ forest <- partykit::cforest(dist ~ speed, data = cars, ntree = 50L) |> progressi
156159
[futurize]: https://futurize.futureverse.org
157160
[furrr]: https://furrr.futureverse.org
158161
[fwb]: https://cran.r-project.org/package=fwb
162+
[lme4]: https://cran.r-project.org/package=lme4
159163
[partykit]: https://cran.r-project.org/package=partykit
160164
[plyr]: https://cran.r-project.org/package=plyr
161165
[progressr]: https://progressr.futureverse.org
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#' @tags pkg-lme4
2+
if (requireNamespace("lme4", quietly = TRUE)) {
3+
library(progressify)
4+
library(lme4)
5+
6+
options(progressify.debug = TRUE)
7+
8+
# 1. Fit standard linear mixed model
9+
fm1 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
10+
my_stat <- function(fit) {
11+
fixef(fit)
12+
}
13+
14+
exprs <- list(
15+
bootMer = quote(bootMer(fm1, my_stat, nsim = 10L))
16+
)
17+
18+
for (name in names(exprs)) {
19+
message(sprintf("Testing %s ...", name))
20+
expr <- exprs[[name]]
21+
22+
# Ensure reproducible seed
23+
set.seed(42)
24+
truth <- eval(expr)
25+
26+
set.seed(42)
27+
res <- eval(bquote(.(expr) |> progressify()))
28+
29+
# Ensure results are equivalent
30+
stopifnot(all.equal(res$t, truth$t))
31+
stopifnot(all.equal(res$t0, truth$t0))
32+
33+
# Ensure no stdout leakage
34+
output <- utils::capture.output({
35+
set.seed(42)
36+
res2 <- eval(bquote(.(expr) |> progressify()))
37+
})
38+
stopifnot(length(output) == 0L)
39+
40+
# Ensure repeated evaluation is identical
41+
stopifnot(all.equal(res2$t, res$t))
42+
43+
message(sprintf("Testing %s ... done", name))
44+
}
45+
}

pkgdown/_pkgdown.menus.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ cran-focus:
2727
href: articles/progressify-81-boot.html
2828
- text: Progress updates for 'fwb' functions
2929
href: articles/progressify-81-fwb.html
30+
- text: Progress updates for 'lme4' functions
31+
href: articles/progressify-81-lme4.html
3032
- text: Progress updates for 'partykit' functions
3133
href: articles/progressify-81-partykit.html
3234
- text: Progress updates for 'sandwich' functions

pkgdown/_pkgdown.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ navbar:
4242
href: articles/progressify-81-boot.html
4343
- text: Progress updates for 'fwb' functions
4444
href: articles/progressify-81-fwb.html
45+
- text: Progress updates for 'lme4' functions
46+
href: articles/progressify-81-lme4.html
4547
- text: Progress updates for 'partykit' functions
4648
href: articles/progressify-81-partykit.html
4749
- text: Progress updates for 'sandwich' functions

tests/test-progressify-lme4.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-lme4.R
3+
## Don't edit - it was autogenerated by inst/testme/deploy.R
4+
progressify:::testme("progressify-lme4")

vignettes/progressify-81-lme4.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<!--
2+
%\VignetteIndexEntry{Progress updates for 'lme4' functions}
3+
%\VignetteAuthor{Henrik Bengtsson}
4+
%\VignetteKeyword{R}
5+
%\VignetteKeyword{package}
6+
%\VignetteKeyword{lme4}
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(lme4)
23+
24+
# Fit random-slope model
25+
fm1 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
26+
my_stat <- function(fit) {
27+
fixef(fit)
28+
}
29+
30+
# Run bootstrap with progress signaling
31+
res <- bootMer(fm1, my_stat, nsim = 1000) |> progressify()
32+
```
33+
34+
35+
# Introduction
36+
37+
This vignette demonstrates how to use this approach to add progress
38+
reporting to **[lme4]** functions such as `bootMer()`. The **lme4**
39+
package provides functions for fitting linear, generalized linear, and
40+
nonlinear mixed-effects models. For example, `bootMer()` runs a
41+
statistic function `nsim` times:
42+
43+
```r
44+
library(lme4)
45+
46+
# Fit random-slope model
47+
fm1 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
48+
my_stat <- function(fit) {
49+
fixef(fit)
50+
}
51+
52+
res <- bootMer(fm1, my_stat, nsim = 1000)
53+
```
54+
55+
By default, `bootMer()` provides no progress feedback. However, we can
56+
easily add progress reporting using the `progressify()` function:
57+
58+
```r
59+
library(lme4)
60+
library(progressify)
61+
handlers(global = TRUE)
62+
63+
# Fit random-slope model
64+
fm1 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
65+
my_stat <- function(fit) {
66+
fixef(fit)
67+
}
68+
69+
res <- bootMer(fm1, my_stat, nsim = 1000) |> progressify()
70+
```
71+
72+
73+
# Supported Functions
74+
75+
The `progressify()` function supports the following **lme4**
76+
functions:
77+
78+
* `bootMer()`
79+
80+
81+
[lme4]: https://cran.r-project.org/package=lme4

0 commit comments

Comments
 (0)