Skip to contents

Draw J latent site effects from a user-supplied callback g_fn(J, ...) under one of two conventions: Convention A (g_returns = "standardized", the default) — the callback returns mean-0 unit-variance residuals \(z_j\) that the package rescales by sigma_tau and shifts by tau (and any covariate adjustment) to form tau_j. Convention B (g_returns = "raw") — the callback returns response-scale effects directly; the package leaves them untouched and back-derives z_j for provenance.

Usage

gen_effects_user(
  J,
  tau = 0,
  sigma_tau = 0.2,
  g_fn,
  g_returns = c("standardized", "raw"),
  formula = NULL,
  beta = NULL,
  data = NULL,
  g_args = list(),
  audit_g = TRUE
)

Arguments

J

Integer. Number of sites.

tau

Numeric. Grand mean on the response scale. Default 0.

sigma_tau

Numeric (\(\ge 0\)). Between-site standard deviation on the response scale. Default 0.20.

g_fn

Function. Required user callback with signature function(J, ...). Must return a finite numeric vector of length J. Under Convention A, the vector should be mean-0 unit-variance; under Convention B, the vector is the response-scale effect directly.

g_returns

Character. "standardized" (Convention A, default) — the callback returns standardized residuals \(z_j\) and the package rescales. "raw" (Convention B) — the callback returns response-scale tau_j directly; the package back-derives z_j.

formula

One-sided formula for site-level covariates, or NULL.

beta

Numeric coefficient vector matching formula, or NULL.

data

A data.frame with the predictors named in formula, or NULL.

g_args

Named list forwarded to g_fn as extra arguments after J. Default list().

audit_g

Logical. Whether to validate (Convention A only) that the callback's draws meet the unit-moment contract. Default TRUE. No-op under g_returns = "raw".

Value

A tibble with one row per site and columns site_index (integer 1:J), z_j (standardized residual; back-derived under Convention B), tau_j (response-scale effect), plus any covariate columns from data.

Details

Use the user callback when none of the seven built-in shapes (Gaussian, Student-t, skew-normal, ALD, mixture, point-mass slab, DPM) captures the effect distribution you need. Typical use cases: a DPM draw fitted offline, a custom skew-mixture, a deconvolution-derived empirical \(G\), or a numerical sampler from a non-standard distribution.

Convention A vs B. Convention A is the default and integrates cleanly with downstream diagnostics (informativeness, heterogeneity_ratio, compute_shrinkage) without further work. Convention B is for callbacks where standardization is meaningless or the package's linear rescaling is not what you want; you carry the responsibility for the scale. The package back-derives z_j = (tau_j - tau - X*beta) / sigma_tau so downstream Layer 2 / 3 / 4 code still has a z_j column to reason about.

Audit. When audit_g = TRUE (default) and g_returns = "standardized", the package draws an extra 100,000-site audit sample from g_fn (in a side RNG stream that does not disturb the caller's state) and warns if the empirical mean is more than 0.1 off zero or the empirical variance is more than 0.1 off 1. This catches the most common contract violations without aborting the run.

For the formal g_fn contract and worked examples, see the Custom G distributions vignette.

References

Lee, J., Che, J., Rabe-Hesketh, S., Feller, A., & Miratrix, L. (2025). Improving the estimation of site-specific effects and their distribution in multisite trials. Journal of Educational and Behavioral Statistics, 50(5), 731–764. doi:10.3102/10769986241254286 .

See also

gen_effects for the dispatcher and the full eight-shape catalog; gen_effects_dpm for the bridged DPM route; gen_effects_gaussian and the seven built-in shapes; the M5 Custom G distributions vignette.

Other family-effects: gen_effects(), gen_effects_ald(), gen_effects_dpm(), gen_effects_gaussian(), gen_effects_mixture(), gen_effects_pmslab(), gen_effects_skewn(), gen_effects_studentt()

Examples

# Convention A: callback returns standardized residuals.
my_g <- function(J) rnorm(J)  # mean 0, variance 1 by construction
gen_effects_user(J = 50L, g_fn = my_g)
#> # A tibble: 50 × 3
#>    site_index    z_j   tau_j
#>         <int>  <dbl>   <dbl>
#>  1          1  1.05   0.210 
#>  2          2 -0.584 -0.117 
#>  3          3 -0.527 -0.105 
#>  4          4 -0.122 -0.0244
#>  5          5 -0.586 -0.117 
#>  6          6 -0.248 -0.0496
#>  7          7  0.801  0.160 
#>  8          8  2.53   0.505 
#>  9          9  0.270  0.0540
#> 10         10 -0.886 -0.177 
#> # ℹ 40 more rows

# Convention B: callback returns response-scale effects directly.
raw_g <- function(J) rnorm(J, mean = 0.3, sd = 0.15)
gen_effects_user(J = 50L, g_fn = raw_g, g_returns = "raw",
                 tau = 0.3, sigma_tau = 0.15)
#> # A tibble: 50 × 3
#>    site_index     z_j tau_j
#>         <int>   <dbl> <dbl>
#>  1          1  0.265  0.340
#>  2          2  0.317  0.348
#>  3          3 -0.149  0.278
#>  4          4  0.322  0.348
#>  5          5 -0.324  0.251
#>  6          6 -0.783  0.182
#>  7          7 -0.0181 0.297
#>  8          8 -0.169  0.275
#>  9          9 -0.165  0.275
#> 10         10  0.714  0.407
#> # ℹ 40 more rows

# Skipping the audit (deterministic callback at small J).
gen_effects_user(
  J = 10L,
  g_fn = function(J) seq(-1, 1, length.out = J),
  audit_g = FALSE
)
#> # A tibble: 10 × 3
#>    site_index    z_j   tau_j
#>         <int>  <dbl>   <dbl>
#>  1          1 -1     -0.2   
#>  2          2 -0.778 -0.156 
#>  3          3 -0.556 -0.111 
#>  4          4 -0.333 -0.0667
#>  5          5 -0.111 -0.0222
#>  6          6  0.111  0.0222
#>  7          7  0.333  0.0667
#>  8          8  0.556  0.111 
#>  9          9  0.778  0.156 
#> 10         10  1      0.2