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.
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.
normalblockr is not (yet) on CRAN; install the development version from GitHub:
# install.packages("pak")
pak::pak("jchiquet/normalblockr")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)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_knownA 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()
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()
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()
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()
normal-block
(vignette("normal-block")): a general introduction on
simulated data – known/unknown clusters, sparsity, zero-inflation.zero-inflated-normal-block
(vignette("zero-inflated-normal-block")): a full worked
example on onema, with the math behind the zero-inflation
extension.breast-cancer-proteomics
(vignette("breast-cancer-proteomics")): a full worked
example on brca_rppa – known vs. inferred clustering, model
selection, the post-hoc refine() step, sparsifying the
selected network.inst/normal_block_models.qmd in the package sources: a
reference card with every model variant’s exact criterion and E/M (or
VE/M) updates, as implemented in src/.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↩︎
Friedman, J., Hastie, T. and Tibshirani, R. Sparse inverse covariance estimation with the graphical lasso. Biostatistics, 9(3), 2008.↩︎
Cancer Genome Atlas Network. Comprehensive molecular portraits of human breast tumours. Nature, 490, 2012, 61-70. doi:10.1038/nature11412↩︎
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.