The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

normalblockr: Gaussian graphical models with latent clustering structure for multivariate continuous data

R-CMD-check Coverage status Lifecycle: experimental License: GPL v3

Description

The Normal-Block model1 is a Gaussian graphical model with a latent clustering structure, designed for the multivariate analysis of continuous data: it clusters variables and, building on the graphical lasso, infers a network of statistical dependencies between clusters rather than between individual variables. This package implements an efficient (variational) EM algorithm to fit it, accompanied by a set of functions for model selection, visualization and diagnostic. See all the dedicated vignettes for a comprehensive introduction.

normalblockr covers the following model variants, all built around the same normal_block()/NormalBlockData interface:

Any combination of these is reached through the same normal_block() function – known or unknown clustering, sparse or not, zero-inflated or not are independent choices, not separate model classes to learn.

Installation

normalblockr is not (yet) on CRAN; install the development version from GitHub:

# install.packages("pak")
pak::pak("jchiquet/normalblockr")

Illustration

We illustrate the known-/unknown-clusters and sparse variants on brca_rppa3: reverse-phase protein array measurements of 163 proteins across 346 breast cancer tumor samples from The Cancer Genome Atlas, together with each sample’s PAM50 molecular subtype.

library(normalblockr)
data(brca_rppa)
Y    <- as.matrix(brca_rppa$expr)
X    <- model.matrix(~ 0 + PAM50_SUBTYPE, data = brca_rppa$covariates)
data <- NormalBlockData$new(Y, X)

Known clusters

A simple, fully data-driven grouping (hierarchical clustering on the raw expression profiles, with no reference to the Normal-Block model) handed to normal_block() as fixed – only the network between the 6 blocks is estimated.

hc    <- brca_rppa$expr |> scale() |> t() |> dist() |> hclust("ward.D2")
group <- cutree(hc, 6) |> normalblockr:::as_indicator()
m_known <- normal_block(data, blocks = group)
Fitting a diagonal normal-block model with fixed blocks 

DONE
m_known
A diagonal normal-block model with fixed blocks .
===========================================================================
 nb_param q n_edges sparsity    loglik deviance      BIC      ICL   EBIC niter
      999 6      15        0 -70387.84 140775.7 146616.3 144073.3 146670    14
===========================================================================
* Useful fields
    $model_par, $posterior_par / $var_par, $clustering 
    $loglik, $BIC, $ICL, $objective, $nb_param, $criteria
* Useful S3 methods
    print(), coef(), sigma(), fitted(), predict() 

Unknown number of clusters

Leave the clustering for the model to infer, over a range of candidate cluster counts explored as a collection, then select by ICL.

m_unknown <- normal_block(data, blocks = 2:10)
Fitting a diagonal normal-block model with unknown q 
     number of blocks = 2           
     number of blocks = 3           
     number of blocks = 4           
     number of blocks = 5           
     number of blocks = 6           
     number of blocks = 7           
     number of blocks = 8           
     number of blocks = 9           
     number of blocks = 10           
DONE
m_unknown$plot(c("deviance", "BIC", "ICL", "EBIC"))

m_unknown$get_best_model("ICL")
A diagonal normal-block model with 10 unknown blocks .
===========================================================================
 nb_param  q n_edges sparsity    loglik deviance      BIC      ICL     EBIC
     1042 10      45        0 -68179.63 136359.3 142451.2 139402.8 142658.5
 niter
    16
===========================================================================
* Useful fields
    $model_par, $posterior_par / $var_par, $clustering 
    $loglik, $BIC, $ICL, $objective, $nb_param, $criteria
* Useful S3 methods
    print(), coef(), sigma(), fitted(), predict() 

Sparse network

Treating a clustering as fixed (known, or selected above), explore a path of graphical-lasso penalties on the inter-cluster network and select by BIC. Unlike the dense, unpenalized network, this is the one actually worth visualizing.

m_sparse <- normal_block(data, blocks = group, sparsity = TRUE)
Fitting a Collection of diagonal normal-block models with fixed blocks, with different sparsity penalties. 
     penalty = 0.2565018           
     penalty = 0.2188391           
     penalty = 0.1867065           
     penalty = 0.1592919           
     penalty = 0.1359028           
     penalty = 0.1159479           
     penalty = 0.098923           
     penalty = 0.08439792           
     penalty = 0.07200559           
     penalty = 0.06143286           
     penalty = 0.05241254           
     penalty = 0.04471669           
     penalty = 0.03815085           
     penalty = 0.03254907           
     penalty = 0.02776982           
     penalty = 0.02369232           
     penalty = 0.02021353           
     penalty = 0.01724553           
     penalty = 0.01471333           
     penalty = 0.01255294           
     penalty = 0.01070977           
     penalty = 0.009137229           
     penalty = 0.00779559           
     penalty = 0.006650947           
     penalty = 0.005674374           
     penalty = 0.004841193           
     penalty = 0.004130351           
     penalty = 0.003523882           
     penalty = 0.003006463           
     penalty = 0.002565018           
DONE
sp_best  <- m_sparse$get_best_model("BIC")
sp_best$plot_network()

Zero-inflated data

For data with an excess of exact zeros beyond what a plain Normal model would represent (e.g. abundance/biomass tables), zero_inflation = TRUE adds a per-variable excess-of-zero layer. Illustrated on onema4: total biomass of 46 fish species across 399 French stream electrofishing stations, where seven in ten entries are exact zeros.

data(onema)
X_zi    <- model.matrix(~ 1 + temperature_med, data = onema$covariates)
Y_zi    <- log(1 + onema$biomass)
data_zi <- NormalBlockData$new(Y_zi, X_zi)
m_zi    <- normal_block(data_zi, blocks = 2:8, zero_inflation = TRUE)
Fitting a diagonal normal-block model with unknown q 
     number of blocks = 2           
     number of blocks = 3           
     number of blocks = 4           
     number of blocks = 5           
     number of blocks = 6           
     number of blocks = 7           
     number of blocks = 8           
DONE
m_zi$get_best_model("ICL")
A zero-inflated diagonal normal-block model with 6 unknown blocks .
===========================================================================
 nb_param q n_edges sparsity    loglik deviance      BIC      ICL     EBIC
      210 6      15        0 -13519.35 27038.71 28296.39 27090.47 28350.14
 niter
    20
===========================================================================
* Useful fields
    $model_par, $posterior_par / $var_par, $clustering 
    $loglik, $BIC, $ICL, $objective, $nb_param, $criteria
* Useful S3 methods
    print(), coef(), sigma(), fitted(), predict() 

Learning more

References


  1. Tous, J., & Chiquet, J. (2026). An integrated method for clustering and association network inference. Computational Statistics & Data Analysis, 219, 108347. doi:10.1016/j.csda.2026.108347↩︎

  2. Friedman, J., Hastie, T. and Tibshirani, R. Sparse inverse covariance estimation with the graphical lasso. Biostatistics, 9(3), 2008.↩︎

  3. Cancer Genome Atlas Network. Comprehensive molecular portraits of human breast tumours. Nature, 490, 2012, 61-70. doi:10.1038/nature11412↩︎

  4. Danet, A., Mouchet, M., Bonnaffé, W., Thébault, E., Fontaine, C. Species richness and food-web structure jointly drive community biomass and its temporal stability in fish communities, data set, 2021, Zenodo. doi:10.5281/zenodo.5095656↩︎

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.