Library / SDK
erdogant/bnlearn avatar
erdogant/bnlearn

bnlearn: A Pipeline Wrapper for Bayesian Network Structure Learning in Python

Python package for Causal Discovery by learning the graphical structure of Bayesian networks. Structure Learning, Parameter Learning, Inferences, Sampling methods.

644 stars61 forksJupyter NotebookNOASSERTION

At a glance

What is it?
bnlearn packages structure learning, parameter learning, inference and sampling behind four fit() calls. It is a convenience layer over existing causal discovery code, and the ease of that layer is also where its limits start.
Who is it for?
Adopt bnlearn if you want a single Python entry point for structure learning, parameter learning, inference and synthetic sampling, and you accept that the package is a convenience layer rather than a from-scratch implementation. Do not adopt it if you need a formally specified licence, since the repository metadata reports NOASSERTION while the README badge points to MIT, or if your data is high-dimensional and you have not decided on a discretization scheme first.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Jupyter Notebook, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What bnlearn Is Trying to Fix

Probabilistic graphical models have a steep entry cost. Fitting a Bayesian network means choosing a structure learning algorithm, encoding the data correctly for that algorithm, estimating conditional probability tables, and then keeping the learned graph and the parameters in sync when you want to run inference. The README states the motivation plainly: because probabilistic graphical models can be difficult to use, bnlearn contains the most-wanted pipelines. The target reader is an analyst or applied machine learning engineer who wants a causal graph from a pandas-style table without assembling four separate libraries. The README leans on tutorial material, with Medium posts and podcasts linked beside most features, which suggests the intended path is guided rather than reference-driven. If you already know which scoring function and search strategy you want, the package is not aimed at you.

The Four fit() Calls and What Sits Behind Them

The public surface is deliberately flat. Structure learning is bn.structure_learning.fit(), parameter learning is bn.parameter_learning.fit(), inference is bn.inference.fit(), and prediction is bn.predict(). Synthetic data comes from bn.sampling(). Around those, the README lists supporting functions: bn.independence_test() for edge strength, bn.knn_imputer() for imputation, bn.discretize() for binning continuous columns, bn.make_DAG() for building a graph by hand, and bn.check_model() for validating model parameters. The data flow implied by the API is linear: a DataFrame goes into structure learning, the returned graph object goes into parameter learning, and the fitted model goes into inference or sampling. The presence of bn.make_DAG() matters for a different workflow, where an expert supplies the graph and only the parameters are learned from data. That split, learn the graph from data or accept it from a human, is the main architectural decision the package exposes. The README does not describe the internal scoring functions or search operators, so the choice of algorithm is hidden behind keyword arguments you have to look up in the API documentation rather than in the README.

Getting It Running: Install and First Calls

Installation follows the standard PyPI route: pip install bnlearn. The README does not show a conda command, so pip is the documented path. Once installed, the functions become available under the bn namespace, and the README lists them as bn.structure_learning.fit(), bn.parameter_learning.fit(), bn.inference.fit(), bn.predict(), bn.sampling(), bn.independence_test(), bn.knn_imputer(), bn.discretize(), bn.check_model() and bn.make_DAG(). Those names are the contract. What the README does not give is a worked call with arguments, so the concrete keyword names for the structure learning method, the scoring function, or the discretization bins are not visible in the repository front page. They live in the Sphinx site at erdogant.github.io/bnlearn, which the README links repeatedly and which is where you should read before writing code. The README does link Colab notebooks, and those are the closest thing to runnable examples in the material available. Treat the badges and the feature table as an index, not as documentation.

Discretization Is Not Optional Housekeeping

The README lists discrete, continuous and mixed datasets as supported, and separately lists a discretize function with its own documentation page. That combination is worth reading carefully. Structure learning over discrete data and structure learning over continuous data are different problems with different scoring functions, and mixing the two in one table forces a decision about which columns get binned and how many bins they get. The package gives you bn.discretize() to make that decision explicit, and the README groups it with a comparison page, which hints that the library authors see discretization as a place where tools diverge. The practical consequence: the number of bins you choose changes the conditional probability tables that parameter learning later estimates, and changes which edges the structure search finds plausible. Two runs of the same pipeline with different bin counts can return different graphs. That is not a defect in bnlearn, it is a property of the underlying method, but a wrapper that makes the pipeline look like four uniform fit() calls can obscure it.

Where the Wrapper Design Costs You

The value of bnlearn is that it hides orchestration. The cost is the same thing. When a learned graph comes back with an edge you did not expect, you need to know which scoring function and which search procedure produced it, and the README does not surface that. You will be reading the Sphinx API pages. A second limitation is the licence signal. The repository metadata reports NOASSERTION, while the README carries an MIT badge pointing at the LICENSE file on master. Those two statements do not agree, and for a package you intend to ship inside a product, that discrepancy is the first thing to resolve by opening the LICENSE file itself. A third constraint is scale. The README advertises exhaustive search alongside the other structure learning methods, and exhaustive search over parent sets grows with the number of variables in a way that makes it unsuitable past a modest column count. The README does not state a variable limit, so the honest position is that no threshold is documented and you should measure on your own data before assuming the default method is appropriate. Finally, the repository is primarily Jupyter Notebook, which means the notebook examples are likely to be ahead of or behind the packaged Python in ways that are hard to detect from the file listing alone.

pgmpy and the Difference in Approach

The README's own causal inference row links to pgmpy.org, which makes pgmpy the obvious comparison. The difference is one of scope and posture. pgmpy is a modelling library: you construct a BayesianNetwork object, add nodes and edges, define conditional probability distributions, and then run inference. Control sits with you at every step, and the library assumes you know what graph you want or how to specify the search. bnlearn inverts that. It starts from a DataFrame and offers fit() calls that pick the mechanics for you, with bn.make_DAG() available as the escape hatch when you want to supply the structure yourself. If your work is exploratory, where you want a candidate graph quickly and are willing to iterate on discretization and method choices, bnlearn's shape is a better fit. If your work is confirmatory, where the graph is a hypothesis you must specify and defend line by line, pgmpy's explicit construction matches that intent more closely. Neither is a superset of the other, and the README itself treats pgmpy as a reference point rather than a rival.

Maintenance, Releases and Upgrade Surface

The release cadence visible in the material is active: 0.14.0 in July 2026, 0.14.1 in late August 2026, and 0.14.2 on the same day as the last push, with the repository not archived. Patch releases at that interval usually mean bug fixes and small API adjustments rather than redesigns, but the material does not include a changelog, so the contents of each release cannot be confirmed from what is available here. The upgrade cost you should plan for is not the install, it is the reproducibility of your graphs. A structure learning result depends on the data, the discretization and the algorithm defaults. If a patch release changes a default, your previously learned graph may not reproduce byte for byte. Pinning the version in your requirements file and re-running bn.check_model() after an upgrade is the cheap safeguard. On licensing, the README badge says MIT and the repository metadata says NOASSERTION. I am not giving legal advice; the point is that you should read the LICENSE file on the master branch and confirm which of the two statements is authoritative before you depend on the package commercially.

Editorial conclusion

Adopt bnlearn if you want a single Python entry point for structure learning, parameter learning, inference and synthetic sampling, and you accept that the package is a convenience layer rather than a from-scratch implementation. Do not adopt it if you need a formally specified licence, since the repository metadata reports NOASSERTION while the README badge points to MIT, or if your data is high-dimensional and you have not decided on a discretization scheme first. Before committing, verify three things: which structure learning method each call defaults to, how the chosen discretization changes the learned graph, and the exact licence text in the LICENSE file on the master branch.

Official sources

  1. erdogant/bnlearn on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Community notes

Community notes