Bayesian Gaussian Graphical Models
Overview
Fit Bayesian Gaussian graphical models. The methods are separated into two Bayesian approaches for inference: hypothesis testing and estimation. There are extensions for confirmatory hypothesis testing, comparing Gaussian graphical models, and node wise predictability. These methods were recently introduced in the Gaussian graphical model literature, including Williams (2019) doi:10.31234/osf.io/x8dpr, Williams and Mulder (2019) doi:10.31234/osf.io/ypxd8, Williams, Rast, Pericchi, and Mulder (2019) doi:10.31234/osf.io/yt386.
New features
- **
select.explore()gains amethod = "BMA"option**: Bayesian model averaging as an alternative to the hard Bayes factor threshold (method = "BF_cut"). For each edge the posterior is a spike-and-slab mixture with massP(H0 | Y)at zero and the posterior under the alternative otherwise; the reported network weights (pcor_mat_zero) are the **median of this mixture**, computed **exactly** under a normal approximation of the posterior of the Fisher-z partial correlation (truncated at 0 for one-sided hypotheses). The result is therefore deterministic and does not require stored posterior draws. The newprior.prob.H0argument (default 0.5) sets the prior probability of the null. All alternatives are supported. Foralternative = "exhaustive"the mixture has three states -- a spike at zero (H0), a positive slab (H+), and a negative slab (H-) -- mixed by the posterior hypothesis probabilities;pos_mat/neg_mat/null_matthen classify each edge by the sign of the model-averaged median (so an edge is null when the median is 0, which can happen even when H0 is not the most probable hypothesis). Thanks to Joris Mulder for the suggestion.
Show all changes in 2.2.0Hide the rest of the changes in 2.2.0
- **
select.explore()returns edge inclusion probabilities (incl_prob)**: a matrix of posterior edge inclusion probabilitiesq * BF / (q * BF + 1 - q)with prior inclusion probabilityq = 1 - prior.prob.H0(BF_10for"two.sided",BF_20for"greater"/"less", and1 - P(H0 | Y)for"exhaustive"). - **
prior.prob.H0now affects the selected graph undermethod = "BF_cut"**, not only undermethod = "BMA". An edge is selected when its posterior inclusion probability exceedsBF_cut / (BF_cut + 1), and is called null when the posterior probability of the null hypothesis exceeds that cutoff. With the defaultprior.prob.H0 = 0.5this is identical to the previous Bayes factor rule (BF > BF_cut) for"two.sided","greater"and"less". Foralternative = "exhaustive"the null keepsprior.prob.H0and the two directional hypotheses split the remainder equally (0.5 / 0.25 / 0.25 by default); becauseBF_1u + BF_2u = 2the inclusion probability then equals the one of"two.sided", so both alternatives select the same edges, with"exhaustive"labelling each selected edge positive or negative by the larger of the two directional probabilities.alternative = "exhaustive"withmethod = "BF_cut"now also returnspcor_mat_zero. Previously"exhaustive"used fixed equal 1/3 priors and thresholded each hypothesis separately, which madeBF_cut = 3correspond to a two-sided Bayes factor of 1.5. - **
truncnormadded toImports**: required for the truncated-normal draws used in the one-sided BMA alternatives. - **
explore()no longer samples the prior by default**: the Bayes factors only need the prior sd of the Fisher-z partial correlations, which is now computed analytically (numerical integration of the marginal priorrho ~ 2 * Beta(delta/2, delta/2) - 1, independent ofp) and stored asprior_sd_z. Draws from the joint prior (prior_samp) are returned only with the new argumentstore_prior_draws = TRUE(defaultFALSE), which halves time and memory use for large networks by default.select.explore(),ggm_compare_explore()andbggm_missing()useprior_sd_z; olderexploreobjects still work inselect(). Bayes factors change slightly, because the previously sampled prior sd was affected by the matrix-F approximation (e.g. about 0.69-0.70 instead of 0.684 forprior_sd = 0.5). - **
explore()gainsstore_post_draws(defaultTRUE)**: all samplers used byexplore()now keep running posterior summaries of the partial correlations (post_samp$pcor_mat,pcor_sd,z_mean,z_sd). Withstore_post_draws = FALSEthep x p x iterarrayspost_samp$pcorsandpost_samp$fisher_zare not stored, so memory use no longer grows with the number of iterations.select()(all methods and alternatives) andsummary()work from these summaries when the draws are not stored; functions that need the draws (posterior_samples(),convergence(),coef(),pcor_to_cor(),predict(),predictability(),posterior_predict(),constrained_posterior()) stop with an informative error, andbggm_missing()always stores the draws. - **
explore()gainsburnin(default 50) andthin(default 1)**: afterburniniterations,iterpost-burn-in iterations are run and everythin-th draw is stored; the running posterior summaries use all post-burn-in iterations.explore()objects no longer store the burn-in draws:post_samp$pcors,fisher_z,betaandthreshnow contain only the kept draws (previouslyiter + 50, including 50 burn-in draws). Downstream functions locate the post-burn-in draws with an internal helper, so objects created with earlier versions keep working. Objects fromestimate(),confirm()and the other functions are unchanged.
Bug fixes
- **
explore(iter = )is now the number of post-burn-in ITERATIONS**, not the number of draws kept after thinning. Previouslyexplore(iter = i, thin = t)ranburnin + i * titerations and storedidraws, so raisingthinsilently multiplied the run time byt; it now runsburnin + iiterations and storesceiling(i / thin)of them, returned in the newn_drawselement.iterin the fitted object is the iteration count andn_drawsthe stored-draw count. With the defaultthin = 1nothing changes. - **
seed = NULLno longer re-seeds the RNG**:explore(),confirm(),ggm_compare_confirm(),ggm_search()andvar_estimate()calledset.seed(seed)unconditionally before the guardedif (!is.null(seed)) set.seed(seed). With the defaultseed = NULLthe first call wasset.seed(NULL), which re-initialises the RNG from the current time and process ID, discarding any seed the user had set and making repeated calls on the same data irreproducible even insideset.seed(). The unguarded call has been removed, so withseed = NULLthese functions now use the ambient RNG stream and are reproducible underset.seed(), asestimate()already was. Results of existing scripts that relied on the implicit re-seeding will change. - **Corrected the exhaustive posterior hypothesis probabilities**: for
alternative = "exhaustive", the positive/negative Bayes factors were referenced to the null modelH0instead of the unrestricted modelHu, so each carried an extra factor of the two-sided Bayes factor and double-counted the two-sided evidence. All three Bayes factors are now referenced toHuper Eq. 9 of Williams & Mulder (2019). Affectspost_proband the derivednull_mat/pos_mat/neg_matunder bothmethod = "BF_cut"andmethod = "BMA". - **Corrected the prior sd in the
"greater","less"and"exhaustive"selection branches ofselect.explore()**: the"greater"branch averaged a hardcoded 3x3 prior mask (upper.tri(diag(3))), giving wrong Bayes factors whenever the number of variables was not 3, and the"less"/"exhaustive"branches averaged the (near-zero) matrix diagonal into the prior standard deviation. All branches now use the off-diagonal (edge) prior sd, matching the"two.sided"branch. - **Fixed a crash in
summary()forselect.explore()withalternative = "less"**: the"less"summary branch produced an emptyRelationcolumn (mat_names[upper.tri(mat_names)]on an already-flattened vector), causing a "differing number of rows" error. The greater/less summaries now share one correct branch. - **
select.explore()now uses all post-burn-in draws** for the posterior mean/sd (previously the last 50 stored draws were dropped), and no longer returnsNAon the diagonal of the exhaustivenull_mat/pos_mat/neg_mat(where the posterior sd is 0). - **Fixed
bggm_missing()dropping the wrong column withmice>= 3.17.0** (#2):bggm_missing()removed the.idcolumn frommice::complete(action = "long")by position (column 2), which was correct only beforemice3.17.0. Sincemice3.17.0 places.imp/.idin the last two columns, this stripped a real data column and let.idleak into the model, corrupting the data passed toestimate()/explore(). The column is now removed by name, which is robust tomice's column order. Reported by \@LilyTeesson. - **
bggm_missing()now pools the posterior draws of the imputed data sets correctly**: it stacked all draws of each fit, including their 50 burn-in draws, and setitertoiter * m + 50, so the methods that use draws51:(iter + 50)included the burn-in draws of imputations 2 to m and indexed beyond the stored draws; summaries came from the first imputation only. The pooled object now contains the 50 burn-in draws of the first fit followed by the post-burn-in draws of all fits, withiter = iter * m.betaandthreshare pooled whenever present, andpcor_mat(and, forexplore, the posterior summaries) are recomputed from the pooled draws. Also works form = 1. - **
explore()for large or n < p networks**: the starting value is regularized (solve(cov(Y) + 0.1 I)), and the matrix-F prior now usesepsilon = min(0.01, 1 / (10 p))instead of a fixed 0.01, so thatnu = 1 / epsilonstays well abovep - 1(required for a proper prior, Williams & Mulder, 2020). Results change slightly forp > 10. - **C++ samplers no longer truncate the matrix-F degrees of freedom** to integers (non-integer
deltafromprior_sd). - **Fixed
rref_einot found error**: Addedsimple_rref()function inhelpers.Rto replace commented-outpracma::rref()call. This fixes a crash increate_matrices()when validating constraint matrices for hypothesis testing with multiple groups. - **Fixed
ggm_search()crash**: The C++searchfunction now handles edge cases where the adjacency matrix becomes all zeros or all ones, preventing "sample more elements than in x" errors. - **Fixed
qplot()deprecation warning**: Replaced deprecatedqplot()withggplot()+geom_density()inplot_prior(). - **Fixed
ggm_search()proposal-set shadowing bug and BIC creep**: The C++search()function had a variable-shadowing bug wherezerosandnonzeros(pools of candidate edges) were re-declared instead of re-assigned after accepting a move, freezing them at the starting graph's configuration for the entire run. This caused systematic BIC drift under probabilistic acceptance and made reversing bad moves impossible.ggm_search()now implements a proper Metropolis-Hastings sampler with a birth-death Hastings correction as the default (probabilistic = TRUE); the greedy deterministic hill-climb is still available viaprobabilistic = FALSE. Theburn_inparameter is restored and now actually applied to discard pre-burn-in samples before computing the Bayesian Model Averaging (BMA) solution (probabilistic search only). Note: the greedy hill-climb was found to be nearly non-functional on realistic test cases, typically accepting only a single edge flip out of thousands of attempts.
Performance
- **Removed unused posterior arrays from the C++ samplers used by
explore()** (also used byestimate(),confirm()and theggm_compare_*()functions) and byvar_estimate():Theta_mcmc,cors_mcmcandSigma_mcmc(each p x p x iter) were allocated but never filled or returned inTheta_continuous,sample_prior,mv_continuous,mv_binary,mv_ordinal_albert,copulaandvar;copulaalso allocated an unused n x p x iter array of latent data. This reduces memory use substantially for large networks (e.g.mv_continuous, used withformula, allocated five p x p x iter arrays and now two). - **
missing_copula(mixed data withimpute = TRUE) no longer returnspost_samp$Y_collect**: this n x p x iter array was never filled (all zeros) and was not used anywhere in BGGM.
Maintenance
- Added
rlangto Imports for proper use of.datapronoun in ggplot2 aesthetics. - Added unit tests for multiple functions.
- **Refactored
bggm_fast.cppfor clarity and efficiency**: Removed two functions unreachable from R (mv_ordinal_cowles,trunc_mvn; ~390 lines). Extractedcov_to_cor()andconditional_normal_params()helper functions, eliminating 17 duplicated blocks and a matrix inversion that was being computed twice per variable per MCMC iteration across 9 sampler functions. Removed dead commented-out debug code. No behavior change; file reduced from 3199 to ~2850 lines. - **Fixed the
ggm_search()test suite and added regression tests**: Corrected tests that were silently exercising non-existent parameters (bma = TRUEinstead ofbma_mean, astart =argument that doesn't exist,gamma =instead ofprior_prob,prior_sd =which isn't a parameter at all) and aplot.ggm_search()test that could never fail since no such method exists. Rewrote with correct parameter names and assertions on the returned object's fields, and added regression tests tied directly to the bugs above (probabilistic search must accept a healthy fraction of proposals;bma_posterior()must not error on the new default's output). Addedtests/testthat/test-search_cpp.Rexercising the C++search()/bic_fast()/hft_algorithm()functions directly, including a check for systematic BIC drift across a long run.
From NEWS.md in the release
Install
Health
- OK13 OK2026-08-05
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-07-29
- ERROR12 OK, 1 ERROR2026-07-27
- OK13 OK2026-06-09
Show 9 earlier changesHide 9 earlier changes
- ERROR12 OK, 1 ERROR2026-06-08
- OK12 OK2026-04-25
- NOTE11 OK, 3 NOTE2026-04-22
- ERROR10 OK, 3 NOTE, 1 ERROR2026-04-18
- NOTE11 OK, 3 NOTE2026-04-10
- ERROR10 OK, 3 NOTE, 1 ERROR2026-04-09
- NOTE11 OK, 3 NOTE2026-04-08
- ERROR10 OK, 3 NOTE, 1 ERROR2026-04-07
- NOTE11 OK, 3 NOTE2026-03-10
Documentation
- Help pages
- 84 (1 internal)includes a package overview page
- Examples that run
- 28%21 run in a plain check · 54 only when donttest examples are enabled (R CMD check --as-cran)
- Exported functions with examples
- 97%
- Documented parameters
- 100%
- Return-value docs
- 100%
- Help pages citing references
- 39% with a bibliography file
Downloads
Repository
- PRs merged (all time)
- 100%
- Median days to close, 50 most recently updated closed issues
- 22
- Median days to close, 50 most recently updated closed PRs
- 0
Stars over time
Forks over time
Issues over time
PRs over time
Commits over time
Commit identities over time
12 development-tooling and community-health practices detected across 8 families in the upstream repository
Checks run against github.com/rast-lab/bggm on 2026-10-11.
The R CMD check workflow runs on Linux, macOS and Windows
GitHub Pages site last deployed 2026-09-24 · The repository has a site deploy workflow
pkgdown site last built 2026-09-24
Show all practicesHide 4 practices
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-01-15Seen in the repository: .claude, .claude/agentsNamed in .Rbuildignore: .claudeCommits: at least 33 crediting it in the message
- Claude Opus 519 commits · Sep 2026
- Claude Sonnet 512 commits · Jul 2026
- Claude Opus 4.51 commit · Jan 2026
- Claude Opus 4.81 commit · Sep 2026
Dependencies
Installing BGGM adds 123 packages (64 with compiled code, 99 MB of source tarballs). Heaviest: BH 14 MB, stringi 11 MB, ggplot2 6.0 MB.
Code & Tests
Datasets
People & History
Previously maintained by Donald Williams (1.0.0 to 2.0.4).
Pick two releases to compare their code metrics.
- 2.2.0Latest
- RR 4.6.0 released · 2026-04-24
- 2.1.62025-12-02 · diff ↗
- RR 4.5.0 released · 2025-04-11
- 2.1.52024-12-22 · diff ↗
- 2.1.42024-12-13 · diff ↗
- 2.1.32024-07-05 · diff ↗
- 2.1.22024-06-22 · diff ↗
- RR 4.4.0 released · 2024-04-24
- unarchivedReturned to CRAN2024-02-23
- 2.1.1
- archivedRemoved from CRAN2023-10-13requires archived package 'BFpack'
- RR 4.3.0 released · 2023-04-21
- RR 4.2.0 released · 2022-04-22
- unarchivedReturned to CRAN2021-08-20
- 2.0.42021-08-20 · diff ↗ ·
removed 3 exports
bma_posterior, ggm_search, mvn_imputationadded 3 exports
impute_data, posterior_predict, predicted_probability
Show 9 earlier eventsHide 9 earlier events
- archivedRemoved from CRAN2021-07-06check issues were not corrected in time donttest check
- RR 4.1.0 released · 2021-05-18
- 2.0.3
- 2.0.22020-07-24 · diff ↗
- 2.0.1
- 2.0.02020-05-31 · diff ↗ ·
removed 14 exports
assess_predictability, bayes_R2, diagnostics, fisher_r2z, fisher_z2r, ggm_compare_bf, mae, mape, mse, plot_adjacency, posterior_predict, pp_check, rmse, shiny_bggmadded 16 exports
bggm_missing, fisher_r_to_z, fisher_z_to_r, gen_ordinal, ggm_compare_confirm, ggm_compare_explore, pcor_mat, pcor_sum, pcor_to_cor, plot_prior, posterior_samples, predictability, regression_summary, roll_your_own, weighted_adj_mat, zero_order_cors - RR 4.0.0 released · 2020-04-24
- 1.0.02020-02-06
- RR 3.6.0 released · 2019-04-26
Package metadata
- First published
- 2020-02-06
- Total releases
- 13 / 6 yrs
- License
- GPL-2 OSI
- Minimum R
- ≥ 4.0.0
- Bundled data
- 245 KB / 19 files
- Download size
- 3.0 MB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("BGGM")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-18, which the citation names so these numbers can be found later. More on citing and the projects behind them.