Gaussian Graphical Models with Latent Clustering Structure
Overview
Implements the Normal-Block model, a Gaussian graphical model with a latent clustering structure for the multivariate analysis of continuous data. The model clusters variables and, building on the graphical lasso, infers a network of statistical dependencies between clusters rather than between individual variables, for known or unknown clusterings, with an optional zero-inflation extension for data with an excess of exact zeros. A complementary family clusters variables by their regression response to covariates rather than by their covariance, sharing one profile per cluster. See Tous & Chiquet (2026) doi:10.1016/j.csda.2026.108347 for the model itself and its variational expectation-maximization estimation procedure.
New model family: clustering in the mean
The package now fits two complementary families. The original Normal-Block
model clusters variables by how they *covary*; the new mean-block family
clusters them by how they *respond* to the covariates, constraining the mean
as mu_i = C B' X_i with one regression profile per cluster.
normal_block(..., model = "mean")fits it, for a known clustering (NormalBlockMeanKnownClusters) or an unknown one inferred by variational EM (NormalBlockMeanUnknownClusters), over a range of cluster counts (NormalBlockMeanCollectionClusters), a penalty path (NormalBlockMeanCollectionSparsity) or both (NormalBlockMeanCollectionClustersSparsity).- Zero-inflated counterparts (
ZINormalBlockMeanKnownClusters,ZINormalBlockMeanUnknownClusters), and collections of those over a range of cluster counts.
Show all changes in 0.3.0Hide the rest of the changes in 0.3.0
noise_covariancegains a"full"shape for this family, whose Sigma is the p x p residual covariance rather than a diagonal noise term."diagonal"is the default: a full Sigma spends p(p+1)/2 parameters that drown the mean structure the criteria are weighing, and selects the number of clusters markedly worse as p approaches n. Asking forsparsity > 0implies"full", since a diagonal precision matrix has nothing for the graphical lasso to penalize.normal_block_sequential()chains the two families: a mean-block fit, then a variance-block fit on its residuals. A heuristic two-stage estimator, not a joint model.NormalBlockData$new()gains azerosargument, to carry an explicit zero-inflation mask when the matrix handed to a model is no longer the one carrying the zeros (the residuals of a first stage, say).
The graphical lasso is now in-package
This also fixes the segfault CRAN reported on
r-devel-linux-x86_64-fedora-gcc when re-building
breast-cancer-proteomics.Rmd (address 0x580, cause 'memory not mapped'),
which our own CI had been hitting intermittently on Linux.
glassoFastis no longer a dependency. The C++ (V)EM used to call back into R once per M-step to reach it -- thousands of round trips from inside a single.Callfor one sparsity path, and a recurring source of intermittent crashes. The solver now lives insrc/graphical_lasso.h.- Two bugs in
glassoFastare fixed on the way. When the empirical covariance has no off-diagonal mass the problem separates and the solution is diagonal; it returned1 / max(rho_ii, eps)there, dropping the variance term, so with an unpenalized diagonal it returned about 9e15 instead of1 / S_ii-- and everyq = 1problem takes that branch. Separately, its inner loop could not terminate on non-finite input. - Each M-step warm-starts from the previous one, at a tightened threshold: faster *and* closer to the optimum than the previous cold starts.
Bug fixes
- Zero-inflation with more than one zero-inflation covariate (
X0with two or more columns) failed with "non-conformable arguments":B0was built transposed. A second bug behind it madekappanon-finite when the response had no zeros at all. Both were unreachable before, so no working fit changes. - Fixed two crashes on the penalized path, one of them a long-standing memory-safety bug (an R object cached in a C++
static).
Other changes
- A collection over cluster counts no longer repeats work that does not depend on the cluster count: the OLS fit, the zero-inflation logistic regressions, and the shareable part of each clustering heuristic (one hierarchical tree cut at each q, one eigendecomposition, one lossless row compression) are computed once. Fitted values are unchanged.
- Printed model descriptions now name the residual-covariance shape for every family, and the zero-inflated variance-block models name their family.
From NEWS.md in the release
Install
Health
- OK13 OK2026-09-13
- ERROR6 OK, 1 ERROR2026-09-04
Documentation
- Help pages
- 49 (8 internal)includes a package overview page
- Examples that run
- 81%29 run in a plain check · 7 hold no code
- Exported functions with examples
- 77%
- Documented parameters
- 98%
- Return-value docs
- 27%
- Help pages citing references
- 0%
- Analyzing multivariate Gaussian data with the Normal-Block model (variance clustering) - First steps
- Mean-Block models: clustering variables by their response to covariates
- Normal-Block models: clustering variables by their covariance -- a worked example with breast cancer proteomics data
- Zero-inflated Normal-Block models (variance clustering): a worked example with fish biomass data
Downloads
Repository
- PRs merged (all time)
- 100%
- Median days to close, 50 most recently updated closed issues
- 91
- Median days to close, 50 most recently updated closed PRs
- 0
Releases over time
Issues over time
PRs over time
Commits over time
Commit identities over time
11 development-tooling and community-health practices detected across 6 families in the upstream repository
Checks run against github.com/jchiquet/normalblockr on 2026-10-18.
The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel
GitHub Pages site last deployed 2026-10-07 · The repository has a site deploy workflow
Show all practicesHide 2 practices
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-06-26Named in .Rbuildignore: .claudePull request it wrote: opened by a project memberCommits: at least 50 crediting it in the message
- Claude Opus 536 commits · Aug to Sep 2026
- Claude Sonnet 513 commits · Jul to Sep 2026
- Claude Opus 5.51 commit · Sep 2026
Dependencies
Installing normalblockr adds 55 packages (28 with compiled code, 54 MB of source tarballs). Heaviest: stringi 11 MB, ggplot2 6.0 MB, data.table 5.7 MB.
Code & Tests
Datasets
People & History
Pick two releases to compare their code metrics.
- 0.3.0Latest2026-09-11 · current release · diff ↗ ·
added 9 exports
NormalBlockMeanCollectionClusters, NormalBlockMeanCollectionClustersSparsity, NormalBlockMeanCollectionSparsity, NormalBlockMeanKnownClusters, NormalBlockMeanUnknownClusters, ZINormalBlockMeanKnownClusters, ZINormalBlockMeanUnknownClusters, generate_normal_block_mean_data, normal_block_sequential - 0.2.12026-09-03
- RR 4.6.0 released · 2026-04-24
Package metadata
- First published
- 2026-09-03
- Total releases
- 2 / 1 yrs
- License
- GPL (>= 3) OSI
- Minimum R
- ≥ 4.1.0
- Bundled data
- 509 KB / 3 files
- Download size
- 1.5 MB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("normalblockr")The package provides a citation file that builds its entry when R runs it. Run citation() to see it.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-21, which the citation names so these numbers can be found later. More on citing and the projects behind them.