In the two-block Gibbs samplers, Block 1 draws a coefficient vector \(b_j\) for every group \(j\). Given Block 2, those draws are conditionally independent: each group is a separate Bayesian regression on its own rows, with its own prior. glmbayesCore provides the pieces for that step:
normalize_group(): turns a group specification into a
standard row partition;Prior_SetupGroup(): runs Prior_Setup()
(Core 10) separately on each group’s rows;pfamily_list(): converts those priors into one
pfamily per group;rNormal_reg_group() and
rNormalGLM_reg_group(): draw every group’s coefficients in
one call. A C++ loop runs the same samplers as
rNormal_reg() and rNormalGLM_reg() (Parts 2
and 3) on each group.These functions also work on their own, for any model that fits a
separate regression per group with no pooling. Unlike
Prior_Setup_GLMM() (Core 20-S02),
Prior_SetupGroup() shares no \(\gamma\) or \(\tau^2\) across groups.
normalize_group()Every function in this vignette accepts the grouping
(group) in one of three forms, where \(l_2\) is the number of rows:
| Form | Example | Notes |
|---|---|---|
| factor or vector of length \(l_2\) | iris$Species |
Factor level order is kept; other vectors are converted with
factor() |
| group sizes (length \(k < l_2\)) | c(2, 3) |
Rows must already be stored group by group; sizes must sum to \(l_2\) |
| list of row-index vectors | list(a = c(1, 3), b = c(2, 4, 5)) |
Must not overlap and must together cover 1:l2 |
Prior_SetupGroup() also accepts a column name in
data. The return value is a list with the number of groups
k, labels ids, sizes l2_blocks,
contiguous start positions starts and row indices
rows.
Prior_SetupGroup()Prior_SetupGroup(formula, group, family = gaussian(), data = NULL,
weights = NULL, subset = NULL, na.action = na.fail,
offset = NULL, contrasts = NULL,
pwt = NULL, pwt_default_low = 0.01, pwt_default_high = 0.05,
n_prior = NULL, sd = NULL, dispersion = NULL,
intercept_source, effects_source, mu = NULL, k = 1, ...)
print(x, blocks = NULL, ...)gaussian(), poisson(),
binomial().pwt,
n_prior, sd, mu,
dispersion and k take either the usual
Prior_Setup() form (applied to every group) or a
list named by group with one value per group. A
NULL element keeps the default for that group."Prior_SetupGroup", one
Prior_Setup() result per group. Its attributes are
call, formula, group and
group_info (the normalize_group()
result).print() shows a header. blocks = (names or
indices) also prints the selected groups’ setups.pfamily_list(<Prior_SetupGroup>, ptypes) builds
one pfamily per group:
ptypes |
Constructor call | Requires |
|---|---|---|
"dNormal" (default) |
dNormal(mu, Sigma, dispersion) (dispersion 1 if
absent) |
any family |
"dNormal_Gamma" |
dNormal_Gamma(mu, Sigma_0, shape, rate) |
Gaussian |
"dIndependent_Normal_Gamma" |
dIndependent_Normal_Gamma(mu, Sigma, shape_ING, rate) |
Gaussian |
rNormal_reg_group() and
rNormalGLM_reg_group()rNormal_reg_group(n, y, x, group, prior_list = NULL, prior_lists = NULL,
offset = NULL, weights = 1, Gridtype = 2L)
rNormalGLM_reg_group(n, y, x, group, prior_list = NULL, prior_lists = NULL,
offset = NULL, weights = 1, family = gaussian(),
Gridtype = 2L, n_envopt = NULL, use_parallel = TRUE,
use_opencl = FALSE, verbose = FALSE, progbar = FALSE)| Argument | Role |
|---|---|
n |
Draws per group. Use n = 1 (one Gibbs step); the
returned matrices hold one draw per group |
y, x, group |
Response, design (same columns in every group) and grouping |
prior_list |
One prior for all groups (mu, P or
Sigma, dispersion). Group-specific variants:
mu as an \(l_1 \times k\)
matrix, P/Sigma as a list,
dispersion with one value per group, or a
blocks sublist |
prior_lists |
Alternatively, a list with one prior per group, in group
order (ids from normalize_group()).
Length 1 is recycled |
offset, weights |
Length 1 or \(l_2\); split by group
like y |
family |
rNormalGLM_reg_group() only: poisson,
binomial, quasipoisson,
quasibinomial or Gamma (not
gaussian) |
Gridtype, n_envopt,
use_parallel, use_opencl,
verbose |
Envelope settings passed to each group’s sampler (Core 06–08) |
rNormal_reg_group() needs dispersion in
every group’s prior; it holds \(\sigma^2\) fixed for the draw. For families
without a dispersion parameter, rNormalGLM_reg_group() sets
it to 1.
Both return a list (class "rNormal_reg_group" or
"rNormalGLM_reg_group"):
| Field | Content |
|---|---|
coefficients |
\(k \times l_1\) matrix: one draw per group (rows named by group) |
coef.mode |
\(k \times l_1\) matrix of posterior modes |
dispersion |
Dispersion used for each group |
group_info |
normalize_group() result |
group_results |
Each group’s full sampler output |
prior_lists |
Each group’s prior after normalization (mu,
Sigma, P, …) |
n, k, l1, l2,
y, x, offset,
prior.weights, call (and
family) |
Inputs and dimensions |
str(normalize_group(factor(c("a", "a", "b", "b", "b")), l2 = 5L))
#> List of 5
#> $ k : int 2
#> $ ids : chr [1:2] "a" "b"
#> $ l2_blocks: Named int [1:2] 2 3
#> ..- attr(*, "names")= chr [1:2] "a" "b"
#> $ starts : Named int [1:2] 1 3
#> ..- attr(*, "names")= chr [1:2] "" "a"
#> $ rows :List of 2
#> ..$ a: int [1:2] 1 2
#> ..$ b: int [1:3] 3 4 5
str(normalize_group(c(2L, 3L), l2 = 5L))
#> List of 5
#> $ k : int 2
#> $ ids : chr [1:2] "group1" "group2"
#> $ l2_blocks: int [1:2] 2 3
#> $ starts : int [1:2] 1 3
#> $ rows :List of 2
#> ..$ : int [1:2] 1 2
#> ..$ : int [1:3] 3 4 5
str(normalize_group(list(g1 = c(1L, 3L), g2 = c(2L, 4L, 5L)), l2 = 5L))
#> List of 5
#> $ k : int 2
#> $ ids : chr [1:2] "g1" "g2"
#> $ l2_blocks: Named int [1:2] 2 3
#> ..- attr(*, "names")= chr [1:2] "g1" "g2"
#> $ starts : Named int [1:2] 1 3
#> ..- attr(*, "names")= chr [1:2] "" "g1"
#> $ rows :List of 2
#> ..$ g1: int [1:2] 1 3
#> ..$ g2: int [1:3] 2 4 5ps_block <- Prior_SetupGroup(
Sepal.Length ~ Sepal.Width + Petal.Length,
group = "Species",
data = iris,
family = gaussian()
)
#> Using default pwt = 0.01 (low-d default).
#> Using default pwt = 0.01 (low-d default).
#> Using default pwt = 0.01 (low-d default).
ps_block
#>
#> Call:
#> Prior_SetupGroup(formula = Sepal.Length ~ Sepal.Width + Petal.Length,
#> group = "Species", family = gaussian(), data = iris)
#>
#> --- Row-block prior setup ---
#> formula : Sepal.Length ~ Sepal.Width + Petal.Length
#> blocks : 3 (setosa, versicolor, virginica)
#> Each element is a Prior_Setup() result; build pfamilies with
#> pfamily_list(). Use print(x, blocks = ...) for details.
names(ps_block)
#> [1] "setosa" "versicolor" "virginica"
ps_block$setosa$mu
#> mu
#> (Intercept) 5.006
#> Sepal.Width 0.000
#> Petal.Length 0.000
ps_block$setosa$dispersion
#> [1] 0.05640586Use a different prior weight for each species, keeping the default for versicolor:
ps_block2 <- Prior_SetupGroup(
Sepal.Length ~ Sepal.Width + Petal.Length,
group = "Species",
data = iris,
family = gaussian(),
pwt = list(setosa = 0.05, versicolor = NULL, virginica = 0.2)
)
#> Using default pwt = 0.01 (low-d default).
sapply(ps_block2, function(p) p$PriorSettings$pwt[1])
#> setosa versicolor virginica
#> 0.05 0.01 0.20pfamily objectspf <- pfamily_list(ps_block)
attr(pf, "ptypes")
#> setosa versicolor virginica
#> "dNormal" "dNormal" "dNormal"
str(pf$setosa$prior_list)
#> List of 4
#> $ mu : num [1:3, 1] 5.01 0 0
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:3] "(Intercept)" "Sepal.Width" "Petal.Length"
#> .. ..$ : chr "mu"
#> $ Sigma : num [1:3, 1:3] 14.892 -2.343 -4.616 -2.343 0.819 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : chr [1:3] "(Intercept)" "Sepal.Width" "Petal.Length"
#> .. ..$ : chr [1:3] "(Intercept)" "Sepal.Width" "Petal.Length"
#> $ dispersion: num 0.0564
#> $ ddef : logi FALSE
#> - attr(*, "Prior Type")= chr "dNormal"
pf_ng <- pfamily_list(ps_block, ptypes = "dNormal_Gamma")
attr(pf_ng, "ptypes")
#> setosa versicolor virginica
#> "dNormal_Gamma" "dNormal_Gamma" "dNormal_Gamma"Prior_SetupGroup()The prior_list inside each dNormal element
of pfamily_list(ps_block) has the form
prior_lists expects. Because prior_lists is
matched by position, order it by the group levels:
X_iris <- model.matrix(~ Sepal.Width + Petal.Length, data = iris)
prior_lists <- lapply(pf[levels(iris$Species)], function(p) p$prior_list)
out_iris <- rNormal_reg_group(
n = 1L,
y = iris$Sepal.Length,
x = X_iris,
group = iris$Species,
prior_lists = prior_lists
)
out_iris$coef.mode
#> (Intercept) Sepal.Width Petal.Length
#> setosa 2.3307608 0.6607420 0.2805851
#> versicolor 2.1546271 0.2451657 0.7282309
#> virginica 0.6844146 0.2573544 0.9254707
out_iris$dispersion
#> [1] 0.05640586 0.11713771 0.10376182set.seed(1)
grp2 <- factor(rep(c("A", "B"), each = 15L))
x2 <- cbind(1, rnorm(length(grp2)))
colnames(x2) <- c("(Intercept)", "x1")
eta <- x2 %*% c(-0.5, 0.3) + ifelse(grp2 == "A", 0, 0.4)
y2 <- rpois(length(grp2), exp(eta))
out_glm <- rNormalGLM_reg_group(
n = 1L,
y = y2,
x = x2,
group = grp2,
prior_list = list(mu = rep(0, 2), P = diag(10, 2), dispersion = 1),
family = poisson(),
use_parallel = FALSE
)
out_glm$coefficients
#> (Intercept) x1
#> A -0.5460683 0.1416992
#> B 0.2841374 0.3334142
out_glm$coef.mode
#> (Intercept) x1
#> A -0.35332535 -0.001877103
#> B 0.04112814 0.349076535Here the prior is given by its precision P.
Sigma works too.
Prior_SetupGroup() and its
pfamily_list() methodPrior_SetupGroup(formula, group, family, data, ..., pwt, n_prior, sd, mu,
dispersion, k)
├─ family check (gaussian / poisson / binomial)
├─ .prior_setup_group_formula_block_meta() model frame + partition
│ ├─ stats::model.frame()
│ ├─ .prior_setup_group_resolve_block() column name / vector / list → group
│ ├─ normalize_group() exported
│ └─ stats::model.matrix() predictor names
├─ .prior_setup_group_resolve_calibration_arg() x6: shared value or named per-group list
└─ for each group b:
├─ .prior_setup_group_rows_to_data_subset() model-frame rows → data rows
└─ Prior_Setup(formula, subset = rows_b, ...) exported (Core 10)
print.Prior_SetupGroup(x, blocks)
├─ .prior_setup_group_select_named_list_keys()
└─ print.PriorSetup() for each selected group
pfamily_list.Prior_SetupGroup(object, ptypes)
└─ for each group:
├─ "dNormal" → dNormal(mu, (Sigma + t(Sigma))/2, dispersion)
├─ "dNormal_Gamma" → dNormal_Gamma(mu, Sigma_0, shape, rate)
└─ "dIndependent_Normal_Gamma" → dIndependent_Normal_Gamma(mu, Sigma, shape_ING, rate)
rNormal_reg_group() rNormalGLM_reg_group()
R/simfunction_group.R R/simfunction_group.R
├─ recycle offset / weights ├─ family + link checks, recycle offset / weights
├─ glmbfamfunc(gaussian()) ├─ glmbfamfunc(family) f2 / f3 callbacks
└─ .group_rNormalReg_cpp() └─ .group_rNormalGLM_cpp() R/rcpp_wrappers.R
│ .Call │ .Call
▼ ▼
group_rNormalReg_cpp_export() group_rNormalGLM_cpp_export() src/group_utils.cpp
├─ normalize_group_cpp() ├─ normalize_group_cpp()
│ └─ split_factor_rows() │ └─ split_factor_rows()
├─ normalize_prior_for_groups_cpp() ├─ normalize_prior_for_groups_cpp()
│ ├─ base_prior_block() │ ├─ base_prior_block()
│ │ └─ prior_list_to_P_Sigma() │ │ └─ prior_list_to_P_Sigma()
│ └─ rep_list_blocks() │ └─ rep_list_blocks()
├─ require dispersion in each group │
├─ prior_payload_from_groups() ├─ prior_payload_from_groups()
└─ rNormalRegGroups() └─ rNormalGLMGroups()
src/rNormalRegGroups.cpp src/rNormalGLMGroups.cpp
for each group b: for each group b:
├─ slice y, x, offset, wt ├─ slice y, x, offset, wt
├─ mu / P / dispersion for b ├─ mu / P / dispersion for b
└─ glmbayes::sim::rNormalReg() └─ glmbayes::sim::rNormalGLM()
(Gaussian conjugate draw) (envelope accept-reject; Parts 2-3)
| Helper | File | Role |
|---|---|---|
.prior_setup_group_formula_block_meta() |
Prior_SetupGroup.R |
Builds the model frame once, resolves group against it
and partitions the rows |
.prior_setup_group_resolve_block() |
Prior_SetupGroup.R |
Converts a column name, vector or list into a group vector aligned with model-frame rows |
.prior_setup_group_resolve_calibration_arg() |
Prior_SetupGroup.R |
Turns one calibration argument into a per-group list; checks names exactly match the group IDs |
.prior_setup_group_rows_to_data_subset() |
Prior_SetupGroup.R |
Maps model-frame row positions back to data rows for
Prior_Setup(subset = ) |
.prior_setup_group_select_named_list_keys() |
Prior_SetupGroup.R |
Resolves blocks = for
print.Prior_SetupGroup() |
.group_rNormalReg_cpp(),
.group_rNormalGLM_cpp() |
rcpp_wrappers.R |
.Call wrappers for the C++ entry points |
.rNormalRegGroups_cpp(),
.rNormalGLMGroups_cpp() |
rcpp_wrappers.R |
Lower-level wrappers that call the per-group loops directly with an already normalized partition and prior; not called by the exported functions in this vignette |
glmbfamfunc() |
simulationpipeline.R |
Exported; returns the family’s log-posterior (f2) and
gradient (f3) callbacks |
glmbayes::sim::group)| Function | File | Role |
|---|---|---|
group_rNormalReg_cpp_export(),
group_rNormalGLM_cpp_export() |
group_utils.cpp |
Entry points: normalize the partition and priors, then call the per-group loop |
normalize_group_cpp(),
split_factor_rows() |
group_utils.cpp |
C++ equivalent of normalize_group() |
normalize_prior_for_groups_cpp() |
group_utils.cpp |
Expands prior_list / prior_lists /
blocks / matrix mu into one prior per
group |
prior_list_to_P_Sigma(),
base_prior_block() |
group_utils.cpp |
Check symmetry and positive definiteness; fill in whichever of
P and Sigma is missing; check dimensions |
rep_list_blocks() |
group_utils.cpp |
Copies one prior to all groups |
prior_payload_from_groups() |
group_utils.cpp |
Packs per-group priors into the mu matrix /
P_blocks / dispersion layout; detects when all
groups share one prior |
rNormalRegGroups(),
rNormalGLMGroups() |
rNormalRegGroups.cpp,
rNormalGLMGroups.cpp |
Per-group loops: slice data, select that group’s prior, call the single-model sampler, and collect draws and modes |
slice_*, *_for_block,
validate_row_blocks*,
coef*_row_from_block* |
same files | Small slicing and validation utilities used by the loops |
In lmerb() and glmerb(), each Gibbs
iteration draws Block 1 with these row-group samplers. Each group’s
prior mean comes from the current Block 2 draw (\(W_{k,j}^\top \gamma_k\) for each
coefficient \(k\)), and its prior
covariance from the current \(\tau^2_k\). The Gibbs engines that build
these per-sweep priors are in lmebayesCore. The
per-group draw itself is the glmbayesCore code shown
here.