This section connects the notation in Li, Lu, and Love (2021) to the
bpgmm interface. It describes the fitted model, the
covariance labels, and the RJMCMC switches used by the sampler. Full
fitted examples are given in the getting-started and worked-example
vignettes.
Observation model
Let
be a matrix of continuous observations with
variables and
observations. bpgmm expects this same orientation:
variables in rows and observations in columns.
For cluster , the mixture of factor analyzers model writes
where
After integrating out the latent factor , the cluster-specific covariance is
The plot below shows the geometric role of this covariance. The loading matrix controls the dominant low-dimensional direction, while adds cluster-specific noise around that subspace.
ellipse_points <- function(center, sigma, level = 0.90, n = 120) {
eig <- eigen(sigma, symmetric = TRUE)
angles <- seq(0, 2 * pi, length.out = n)
circle <- rbind(cos(angles), sin(angles))
radius <- sqrt(qchisq(level, df = 2))
points <- t(center + radius * eig$vectors %*% diag(sqrt(eig$values), 2) %*% circle)
colnames(points) <- c("x", "y")
points
}
lambda_1 <- matrix(c(1.1, 0.4), ncol = 1)
lambda_2 <- matrix(c(0.2, 1.0), ncol = 1)
psi_1 <- diag(c(0.20, 0.08))
psi_2 <- diag(c(0.10, 0.25))
sigma_1 <- lambda_1 %*% t(lambda_1) + psi_1
sigma_2 <- lambda_2 %*% t(lambda_2) + psi_2
ell_1 <- ellipse_points(c(-1.2, -0.5), sigma_1)
ell_2 <- ellipse_points(c(1.1, 0.6), sigma_2)
plot(
ell_1,
type = "l",
lwd = 2,
col = "#0072B2",
xlim = c(-3.5, 3.5),
ylim = c(-2.5, 3),
xlab = "Variable 1",
ylab = "Variable 2",
main = "Mixture-of-factor-analyzers covariance"
)
lines(ell_2, lwd = 2, col = "#D55E00")
points(rbind(c(-1.2, -0.5), c(1.1, 0.6)), pch = 19, col = c("#0072B2", "#D55E00"))
arrows(-1.2, -0.5, -1.2 + lambda_1[1], -0.5 + lambda_1[2], col = "#0072B2", lwd = 2, length = 0.08)
arrows(1.1, 0.6, 1.1 + lambda_2[1], 0.6 + lambda_2[2], col = "#D55E00", lwd = 2, length = 0.08)
legend(
"topleft",
legend = c("Cluster 1 covariance", "Cluster 2 covariance", "Loading direction"),
col = c("#0072B2", "#D55E00", "gray30"),
lwd = c(2, 2, 2),
bty = "n"
)
The mixture density is
where is the mixture weight and is the number of clusters. Equivalently, with allocation indicator ,
In pgmm_rjmcmc():
-
Xis the data matrix. -
m_initis the starting value of . -
m_rangeis the allowed range of . -
q_newis the factor dimension assigned to a newly proposed cluster. -
constraintis the starting covariance model.
PGMM covariance constraints
The paper uses three letters to describe the covariance structure.
Each letter is either C for constrained or U
for unconstrained.
| Letter | Meaning when C
|
Meaning when U
|
|---|---|---|
| 1 | all clusters share one loading matrix | each cluster has |
| 2 | all clusters share one noise covariance | each cluster has |
| 3 | noise covariance is isotropic, | noise covariance is diagonal |
The eight PGMM models are:
library(bpgmm)
#> bpgmm 1.3.4 loaded. If you use bpgmm in published work, please cite it with citation("bpgmm").
models <- c("CCC", "CCU", "CUC", "CUU", "UCC", "UCU", "UUC", "UUU")
data.frame(
model = models,
constraint = vapply(
models,
function(x) paste(model_to_constraint(x), collapse = ","),
character(1)
)
)
#> model constraint
#> CCC CCC 1,1,1
#> CCU CCU 1,1,0
#> CUC CUC 1,0,1
#> CUU CUU 1,0,0
#> UCC UCC 0,1,1
#> UCU UCU 0,1,0
#> UUC UUC 0,0,1
#> UUU UUU 0,0,0The package uses the numeric encoding internally: 1
means constrained and 0 means unconstrained.
model_to_constraint("UUU")
#> [1] 0 0 0
constraint_to_model(c(1, 1, 0))
#> [1] "CCU"Priors and posterior updates
The supplement gives the natural conjugate priors used by the MCMC updates. The main priors are:
and
The complete parameter state can be read as
with hyperparameters
The package samples the hyperparameters , , and , with gamma hyperpriors controlled by:
-
d_vec: shape parameters. -
s_vec: rate parameters. -
delta: inverse-gamma shape for the noise covariance. -
ggamma: Dirichlet concentration for mixture weights.
The allocation update uses the posterior probability
Then
The allocation update uses this probability, and the joint posterior includes the product of allocated mixture weights:
Augmented representation and sufficient statistics
The sampler augments each observation with latent factors and uses the stacked notation from the supplement. For cluster , define
Then the cluster likelihood can be written as
For allocated observations in cluster , the package accumulates
Completing the square gives the quadratic form used in every and update:
Latent scores, means, and mixture weights
Given allocations, the supplement gives the following Gibbs updates.
Latent scores for allocated observations:
Unallocated observations draw from the prior:
Cluster means:
where
is the prior mean on the working scale. pgmm_rjmcmc()
centers
internally so
on the centered scale, matching the augmented loading posterior in
Supplement A.1; sampled means are returned on the original data
scale.
Mixture weights:
Hyperparameter updates
The package samples
with gamma hyperpriors controlled by d_vec and
s_vec. With total latent dimension
,
PGMM conditional posteriors (corrected forms)
The eight covariance labels differ in whether , , and the diagonal of are shared across clusters. The table below records the prior scaling that enters the rate after completing the square. All models use the cross term and quadratic term on ; model 7 (UUC) is implemented with this corrected form rather than the misprinted coefficients in Appendix A.2.
| Model | Shared ? | Shared ? | Isotropic ? | Prior adjustment in rate |
|---|---|---|---|---|
| CCC | yes | yes | yes | , |
| CCU | yes | yes | no | , |
| CUC | yes | no | yes | , |
| CUU | yes | no | no | , |
| UCC | no | yes | yes | , |
| UCU | no | yes | no | , |
| UUC | no | no | yes | , |
| UUU | no | no | no | , |
For shared , the loading update pools information across clusters, for example in model 1 (CCC):
with the same copied to every cluster. For cluster-specific loadings (models 5–8),
Isotropic and
shared-
cases replace matrix Gamma draws by scalar or diagonal Gamma draws with
the shape and rate formulas in Appendix A.2; the stay-step sampler in
calculate_post_lambda_psi() implements these forms.
RJMCMC moves
RJMCMC is used because the number of parameters changes when the number of clusters or the covariance constraint changes.
The cluster-number moves are:
| Move | Purpose |
|---|---|
| stay | update parameters without changing |
| birth | add a new empty cluster |
| death | remove an empty cluster |
| split | split one occupied cluster into two clusters |
| combine | merge two occupied clusters |
The covariance-structure moves toggle one constraint at a time:
- toggle whether is shared across clusters;
- toggle whether is shared across clusters;
- toggle whether is isotropic or diagonal.
In pgmm_rjmcmc():
-
m_step = 1enables birth/death moves for . -
split_combine = 1adds split/combine moves whenm_step = 1. -
v_step = 1enables covariance-constraint moves.
For a proposed move from model to , the RJMCMC acceptance probability has the standard form
where is the proposal density and is the Jacobian term for dimension-changing moves such as split/combine. Birth/death and split/combine change ; covariance moves change the constraint label .
Birth and death
Birth adds an empty cluster with weight and rescales existing weights by . Death removes one empty cluster and reverses the weight transformation. The acceptance ratio includes the Jacobian exponent for birth and for death, as in Green (1995).
Split and combine
Split proposes two new clusters from one occupied component. Let and denote the merged weight and mean. With , , , and per-coordinate signs ,
with . The independent signs contribute a factor to the split proposal density. Combine merges two occupied components, runs intermediate Gibbs sweeps, and uses the reverse proposal in the acceptance ratio.
Covariance-structure moves
The v_step toggles one letter in the covariance label at
a time. Extension proposes new loadings and noise matrices under the
less constrained model; contraction proposes under the more constrained
model. When the proposal distribution is symmetric in the auxiliary
variables, the acceptance ratio reduces to a ratio of posterior
ordinals, as noted in Section 4.4 of the paper.
Interface mapping
| Paper notation | Package argument or output |
|---|---|
X, a
numeric matrix |
|
m_init, m_range,
active_cluster_samples
|
|
q_new for newly proposed clusters |
|
allocation_samples,
summary$allocation
|
|
tau_samples |
|
mean_samples |
|
lambda_samples |
|
psi_samples |
|
| covariance model |
constraint, constraint_samples
|
An applied model-selection call usually has the following structure:
fit <- pgmm_rjmcmc(
X = your_data_matrix,
m_init = 5,
m_range = c(1, 10),
q_new = 4,
burn = 5000,
niter = 15000,
constraint = model_to_constraint("UUU"),
m_step = 1,
v_step = 1,
split_combine = 1,
verbose = FALSE
)Citation
If you use these methods, cite:
Li, Y., Lu, X., & Love, T. (2021). On Bayesian Analysis of Parsimonious Gaussian Mixture Models. Journal of Classification, 38, 576-593. https://doi.org/10.1007/s00357-021-09391-8
In R:
citation("bpgmm")
#> To cite package 'bpgmm' in publications use:
#>
#> Li Y, Lu X, Love T (2021). On Bayesian Analysis of
#> Parsimonious Gaussian Mixture Models. Journal of
#> Classification, 38, 576-593. doi:10.1007/s00357-021-09391-8
#>
#> A BibTeX entry for LaTeX users is
#>
#> @Article{,
#> title = {On Bayesian Analysis of Parsimonious Gaussian Mixture Models},
#> author = {Yaoxiang Li and Xiang Lu and Tanzy Love},
#> journal = {Journal of Classification},
#> year = {2021},
#> volume = {38},
#> pages = {576--593},
#> doi = {10.1007/s00357-021-09391-8},
#> }