PGMPy Example Models API
A guided exploration of pgmpy’s built-in example model subsystem:
list_models(): Discover available models from curated repositoriesload_model(): Load a model by name into one of three model types- Three return types:
DiscreteBayesianNetwork,DAG,LinearGaussianBayesianNetwork
This module provides one-line access to 250+ pre-built graphical models for teaching, benchmarking, and causal discovery evaluation.
Imports and Setup¶
%load_ext autoreload
%autoreload 2
import logging
import warnings
warnings.filterwarnings("ignore")The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
from pgmpy.example_models import list_models, load_model# Use this for most notebooks.
import helpers.htutorial as htutori
htutori.config_notebook()
import pandas as pd
# Import pgmpy utilities.
import tutorials.pgmpy.pgmpy_utils as tpgpguti
# Initialize logger.
logging.basicConfig(level=logging.INFO)
_LOG = logging.getLogger(__name__)
# Convert `display` into `print()` when running outside IPython.
try:
from IPython.display import display
except ImportError:
display = print # type: ignoreINFO:helpers.htutorial:Setting notebook style
INFO:helpers.htutorial:Notebook signature
INFO:helpers.htutorial:numpy version=2.4.6
WARNING:helpers.htutorial:pymc is not installed
INFO:helpers.htutorial:matplotlib version=3.10.9
WARNING:helpers.htutorial:arviz is not installed
WARNING:helpers.htutorial:preliz is not installed
WARNING:helpers.htutorial:sns is not installed
Python 3.12.13
Linux 6cd63cc7886d 6.12.67-linuxkit #1 SMP Sun Jan 25 02:26:28 UTC 2026 aarch64 GNU/Linux
import logging
logging.getLogger("httpx").setLevel(logging.WARNING)Part 1: Library Overview¶
What problem does pgmpy.example_models solve?¶
- Provides one-line access to 250+ pre-built graphical models across three curated repositories
- Eliminates the boilerplate of hand-constructing Bayesian networks for teaching, testing, and benchmarking
- Models come in three flavors:
- Fully parameterized discrete networks with CPD tables, ready for inference and sampling
- Structure-only causal DAGs for causal identification and reasoning
- Continuous linear Gaussian networks for continuous-variable inference
- Supports filtering by model properties (size, type, parameterization)
Key abstractions¶
- Catalog:
list_models(**filters)returns a filtered list of model name strings - Loader:
load_model(name)returns the model object for a given name - Three model types returned, depending on the source:
DiscreteBayesianNetwork: full CPD tables, supports inference (pgmpy.models)DAG: structure only, causal reasoning (pgmpy.base)LinearGaussianBayesianNetwork: continuous parameters (pgmpy.models)
How the pieces fit together¶
User -> list_models(is_discrete=True, n_nodes=5)
-> ['bnlearn/cancer', 'bnlearn/earthquake', ...]
User -> load_model('bnlearn/earthquake')
-> DiscreteBayesianNetwork(5 nodes, 5 edges, 5 CPDs)
User -> model.get_cpds() -> TabularCPD objects for each variablePart 2: Primitive-by-Primitive Exploration¶
Primitive 1: list_models(): The Catalog¶
Mental model: A searchable registry of all available pre-built graphical models.
Returns a list of name strings suitable for passing to load_model().
Each name has a repository/family format.
# Smallest construction: no arguments returns all models.
all_models = list_models()
print(f"Total models available: {len(all_models)}")Total models available: 257
Inspect the Output: Model Sources¶
Models come from three repositories, visible in the name prefix:
# Categorize models by source repository.
bnlearn = sorted([m for m in all_models if m.startswith("bnlearn/")])
bnrep = sorted([m for m in all_models if m.startswith("bnrep/")])
dagitty = sorted([m for m in all_models if m.startswith("dagitty/")])
print(f"bnlearn models (discrete parameterized): {len(bnlearn)}")
print(f" {bnlearn[:5]}...")
print(f"\nbnrep models (discrete parameterized): {len(bnrep)}")
print(f" {bnrep[:5]}...")
print(f"\ndagitty models (structure-only DAGs): {len(dagitty)}")
print(f" {dagitty[:5]}...")bnlearn models (discrete parameterized): 28
['bnlearn/alarm', 'bnlearn/andes', 'bnlearn/arth150', 'bnlearn/asia', 'bnlearn/barley']...
bnrep models (discrete parameterized): 217
['bnrep/APSsystem', 'bnrep/BOPfailure1', 'bnrep/BOPfailure2', 'bnrep/BOPfailure3', 'bnrep/GDIpathway1']...
dagitty models (structure-only DAGs): 12
['dagitty/acid_1996', 'dagitty/confounding', 'dagitty/didelez_2010', 'dagitty/kampen_2014', 'dagitty/m_bias']...
Filtering by Model Type¶
Use boolean keyword filters to narrow by data type and parameterization:
# Filter by parameterization: only models with full CPD tables.
param = list_models(is_parameterized=True)
print(f"Parameterized models (with CPDs): {len(param)}")Parameterized models (with CPDs): 245
# Filter by discrete vs continuous.
discrete = list_models(is_discrete=True)
continuous = list_models(is_continuous=True)
hybrid = list_models(is_hybrid=True)
print(f"Discrete models: {len(discrete)}")
print(f"Continuous models: {len(continuous)}")
print(f"Hybrid models: {len(hybrid)}")Discrete models: 226
Continuous models: 19
Hybrid models: 0
Filtering by Size¶
Use n_nodes or n_edges with an integer or a list of integers:
# Filter by exact node count.
print("Models by node count:")
for n in [4, 5, 8, 10]:
count = len(list_models(n_nodes=n))
print(f" n_nodes={n}: {count} models")Models by node count:
n_nodes=4: 19 models
n_nodes=5: 15 models
n_nodes=8: 19 models
n_nodes=10: 11 models
# Filter by node count using a list of values.
small = list_models(n_nodes=[4, 5, 6])
medium = list_models(n_nodes=list(range(10, 20)))
print(f"Small models (4-6 nodes): {len(small)}")
print(f" {small}")
print(f"\nMedium models (10-19 nodes): {len(medium)}")
print(f" {medium[:10]}...")Small models (4-6 nodes): 52
['bnlearn/cancer', 'bnlearn/earthquake', 'bnlearn/survey', 'bnrep/adversarialbehavior', 'bnrep/agropastoral5', 'bnrep/algorithms1', 'bnrep/algorithms2', 'bnrep/algorithms3', 'bnrep/algorithms4', 'bnrep/augmenting', 'bnrep/bank', 'bnrep/beam1', 'bnrep/beam2', 'bnrep/beatles', 'bnrep/bullet', 'bnrep/cachexia1', 'bnrep/cachexia2', 'bnrep/chds', 'bnrep/compaction', 'bnrep/conasense', 'bnrep/concrete1', 'bnrep/concrete2', 'bnrep/concrete4', 'bnrep/concrete7', 'bnrep/crypto', 'bnrep/firealarm', 'bnrep/fluids1', 'bnrep/fluids2', 'bnrep/foodsecurity', 'bnrep/hydraulicsystem', 'bnrep/knowledge', 'bnrep/navigation', 'bnrep/perioperative', 'bnrep/phdarticles', 'bnrep/rainwater', 'bnrep/ricci', 'bnrep/rockburst', 'bnrep/sallyclark', 'bnrep/simulation', 'bnrep/softwarelogs1', 'bnrep/soil', 'bnrep/theft1', 'bnrep/theft2', 'bnrep/titanic', 'bnrep/trajectories', 'bnrep/transport', 'bnrep/tubercolosis', 'bnrep/wheat', 'bnrep/witness', 'dagitty/confounding', 'dagitty/m_bias', 'dagitty/mediator']
Medium models (10-19 nodes): 72
['bnlearn/sachs', 'bnrep/APSsystem', 'bnrep/accidents', 'bnrep/adhd', 'bnrep/agropastoral1', 'bnrep/agropastoral2', 'bnrep/agropastoral3', 'bnrep/airegulation2', 'bnrep/airegulation3', 'bnrep/argument']...
Composing Multiple Filters¶
Filters combine with AND semantics: only models matching ALL criteria are returned.
# Composite filter: discrete AND parameterized AND small.
precise = list_models(
is_discrete=True,
is_parameterized=True,
n_nodes=[4, 5, 6],
)
print(f"Discrete parameterized models with 4-6 nodes: {len(precise)}")
for m in precise:
print(f" {m}")Discrete parameterized models with 4-6 nodes: 44
bnlearn/cancer
bnlearn/earthquake
bnlearn/survey
bnrep/adversarialbehavior
bnrep/agropastoral5
bnrep/algorithms3
bnrep/algorithms4
bnrep/augmenting
bnrep/bank
bnrep/beam1
bnrep/beam2
bnrep/beatles
bnrep/bullet
bnrep/chds
bnrep/compaction
bnrep/conasense
bnrep/concrete1
bnrep/concrete2
bnrep/concrete4
bnrep/concrete7
bnrep/crypto
bnrep/firealarm
bnrep/fluids1
bnrep/fluids2
bnrep/hydraulicsystem
bnrep/knowledge
bnrep/navigation
bnrep/perioperative
bnrep/phdarticles
bnrep/rainwater
bnrep/ricci
bnrep/rockburst
bnrep/sallyclark
bnrep/simulation
bnrep/softwarelogs1
bnrep/soil
bnrep/theft1
bnrep/theft2
bnrep/titanic
bnrep/trajectories
bnrep/transport
bnrep/tubercolosis
bnrep/wheat
bnrep/witness
# Filter for causal DAGs from dagitty with few nodes.
causal_dag = list_models(n_nodes=[4, 5, 6])
causal_dag = [m for m in causal_dag if m.startswith("dagitty/")]
print("Small dagitty causal DAGs (4-6 nodes):")
for m in causal_dag:
print(f" {m}")Small dagitty causal DAGs (4-6 nodes):
dagitty/confounding
dagitty/m_bias
dagitty/mediator
Primitive 2: load_model(): The Loader¶
Mental model: Given a name string from the catalog, load and return the model object. The return type depends on the source repository and model characteristics.
# Smallest construction: load a simple parameterized discrete model.
model = load_model("bnlearn/cancer")
print("Loaded model: bnlearn/cancer")
print(f"Object type: {type(model)}")
print(f"Object type name: {type(model).__name__}")
print(f"Number of nodes: {len(model.nodes())}")
print(f"Number of edges: {len(model.edges())}")Loaded model: bnlearn/cancer
Object type: <class 'pgmpy.models.DiscreteBayesianNetwork.DiscreteBayesianNetwork'>
Object type name: DiscreteBayesianNetwork
Number of nodes: 5
Number of edges: 4
_ = tpgpguti.draw_pgmpy_model(model)
# Load a structure-only DAG.
model_dag = load_model("dagitty/mediator")
print("Loaded model: dagitty/mediator")
print(f"Object type: {type(model_dag)}")
print(f"Object type name: {type(model_dag).__name__}")Loaded model: dagitty/mediator
Object type: <class 'pgmpy.base.DAG.DAG'>
Object type name: DAG
_ = tpgpguti.draw_pgmpy_model(model_dag)
Primitive 3: DiscreteBayesianNetwork (Parameterized)¶
Mental model: A fully specified Bayesian network with:
- Directed acyclic graph structure (nodes + edges)
- Conditional Probability Distributions (CPDs) for every node
- All probabilities are discrete tabular distributions
- Ready for inference, MAP queries, and simulation
# Load the small 'cancer' model: Pollution -> Cancer <- Smoker, Cancer -> Xray, Cancer -> Dyspnoea.
model = load_model("bnlearn/cancer")
print(f"Model: {model}")
print(f"Nodes: {sorted(model.nodes())}")
print(f"Edges: {sorted(model.edges())}")Model: DiscreteBayesianNetwork named 'unknown' with 5 nodes and 4 edges
Nodes: ['Cancer', 'Dyspnoea', 'Pollution', 'Smoker', 'Xray']
Edges: [('Cancer', 'Dyspnoea'), ('Cancer', 'Xray'), ('Pollution', 'Cancer'), ('Smoker', 'Cancer')]
_ = tpgpguti.draw_pgmpy_model(model)
# Inspect the CPDs (the probability tables).
cpds = model.get_cpds()
print(f"Number of CPDs: {len(cpds)}")
for cpd in cpds:
parents = cpd.variables[1:] if len(cpd.variables) > 1 else []
print(f"\n Variable: {cpd.variable}")
print(f" Cardinality: {cpd.variable_card}")
print(f" Parents: {parents}")
print(f" Values shape: {cpd.values.shape}")Number of CPDs: 5
Variable: Cancer
Cardinality: 2
Parents: ['Pollution', 'Smoker']
Values shape: (2, 2, 2)
Variable: Dyspnoea
Cardinality: 2
Parents: ['Cancer']
Values shape: (2, 2)
Variable: Pollution
Cardinality: 2
Parents: []
Values shape: (2,)
Variable: Smoker
Cardinality: 2
Parents: []
Values shape: (2,)
Variable: Xray
Cardinality: 2
Parents: ['Cancer']
Values shape: (2, 2)
# View a specific CPD in detail.
cpd_cancer = model.get_cpds("Cancer")
print(cpd_cancer)
print(
f"\nP(Cancer=no | Pollution=low, Smoker=non): {cpd_cancer.values[0, 0, 0]:.4f}"
)
print(
f"P(Cancer=yes | Pollution=high, Smoker=smoker): {cpd_cancer.values[1, 1, 1]:.4f}"
)+---------------+----------------+-----+-----------------+
| Pollution | Pollution(low) | ... | Pollution(high) |
+---------------+----------------+-----+-----------------+
| Smoker | Smoker(True) | ... | Smoker(False) |
+---------------+----------------+-----+-----------------+
| Cancer(True) | 0.03 | ... | 0.02 |
+---------------+----------------+-----+-----------------+
| Cancer(False) | 0.97 | ... | 0.98 |
+---------------+----------------+-----+-----------------+
P(Cancer=no | Pollution=low, Smoker=non): 0.0300
P(Cancer=yes | Pollution=high, Smoker=smoker): 0.9800
# Use network analysis methods.
print(f"Parents of 'Cancer': {model.get_parents('Cancer')}")
print(f"Children of 'Cancer': {model.get_children('Cancer')}")
print(f"Children of 'Smoker': {model.get_children('Smoker')}")
print(
f"Local independencies for 'Cancer': {model.local_independencies('Cancer')}"
)Parents of 'Cancer': ['Pollution', 'Smoker']
Children of 'Cancer': ['Xray', 'Dyspnoea']
Children of 'Smoker': ['Cancer']
Local independencies for 'Cancer':
# Check model validity (all CPDs consistent with graph structure).
is_valid = model.check_model()
print(f"Model is valid: {is_valid}")Model is valid: True
Primitive 4: DAG (Structure-Only)¶
Mental model: A causal graph with no quantitative parameters.
- Only the directed graph structure is present
- No CPDs, no probability tables
- Used for causal identification, d-separation checks, and reasoning about structure
- Cannot do inference or sampling
# Load a structure-only DAG.
model = load_model("dagitty/confounding")
print(f"Model: {model}")
print(f"Type: {type(model).__name__}")
print(f"Nodes: {sorted(model.nodes())}")
print(f"Edges: {sorted(model.edges())}")Model: DAG with 5 nodes and 7 edges
Type: DAG
Nodes: ['A', 'B', 'D', 'E', 'Z']
Edges: [('A', 'E'), ('A', 'Z'), ('B', 'D'), ('B', 'Z'), ('E', 'D'), ('Z', 'D'), ('Z', 'E')]
_ = tpgpguti.draw_pgmpy_model(model)
# Confirm no CPDs available.
try:
cpds = model.get_cpds()
print(f"get_cpds() returned: {cpds}")
except AttributeError:
print("No CPDs: DAG has no CPD methods.")
print(" Only DiscreteBayesianNetwork has CPDs.")No CPDs: DAG has no CPD methods.
Only DiscreteBayesianNetwork has CPDs.
# Structure analysis works on DAGs.
print(f"Is directed: {model.is_directed()}")
print(f"Parents of 'A': {list(model.predecessors('A'))}")
print(f"Children of 'Z': {list(model.successors('Z'))}")
print(f"All ancestors of 'D': {model.get_ancestors('D')}")
print(f"Root nodes (no parents): {model.get_roots()}")Is directed: True
Parents of 'A': []
Children of 'Z': ['D', 'E']
All ancestors of 'D': {'A', 'B', 'D', 'E', 'Z'}
Root nodes (no parents): ['A', 'B']
# DAGs support d-separation checks: are variables independent conditional on others?
print(f"A and B d-connected? {model.is_dconnected('A', 'B')}")
print(
f"A and B d-separated given Z? {model.is_dconnected('A', 'B', observed='Z')}"
)A and B d-connected? False
A and B d-separated given Z? True
# Get conditional independences implied by the graph structure.
independencies = model.get_independencies()
assertions = independencies.get_assertions()
print(f"Number of implied independencies: {len(assertions)}")
print("Sample independencies:")
for assertion in assertions[:5]:
print(
f" ({assertion.event1}) _|_ ({assertion.event2}) | ({assertion.event3})"
)Number of implied independencies: 3
Sample independencies:
(frozenset({'B'})) _|_ (frozenset({'E'})) | (frozenset({'A', 'Z'}))
(frozenset({'A'})) _|_ (frozenset({'B'})) | (frozenset())
(frozenset({'A'})) _|_ (frozenset({'D'})) | (frozenset({'B', 'E', 'Z'}))
# Visualize the DAG.
_ = tpgpguti.draw_pgmpy_model(model)
Subtypes: dagitty/mediator (Mediation Pattern)¶
# Mediator: X -> I -> Y, with Z confounder of I and X.
model_m = load_model("dagitty/mediator")
print(f"Mediator model nodes: {sorted(model_m.nodes())}")
print(f"Mediator model edges: {sorted(model_m.edges())}")
# The mediator pattern: X affects Y through I (the mediator).
print(f"\nIs X and Y d-connected? {model_m.is_dconnected('X', 'Y')}")
print(
f"Is X and Y d-separated given I? {model_m.is_dconnected('X', 'Y', observed='I')}"
)Mediator model nodes: ['I', 'X', 'Y', 'Z']
Mediator model edges: [('I', 'Y'), ('X', 'I'), ('X', 'Y'), ('Z', 'I'), ('Z', 'X')]
Is X and Y d-connected? True
Is X and Y d-separated given I? True
_ = tpgpguti.draw_pgmpy_model(model_m)
Subtypes: dagitty/m_bias (M-Bias Pattern)¶
# M-bias: two independent causes (u_D_Z, u_E_Z) both affect a collider Z.
model_mb = load_model("dagitty/m_bias")
print(f"M-bias model nodes: {sorted(model_mb.nodes())}")
print(f"M-bias model edges: {sorted(model_mb.edges())}")
# Classic M-bias: conditioning on Z opens a path between D and E.
print(
f"\nD and E d-connected unconditionally? {model_mb.is_dconnected('D', 'E')}"
)
print(
f"D and E d-connected given Z? {model_mb.is_dconnected('D', 'E', observed='Z')}"
)M-bias model nodes: ['D', 'E', 'Z', 'u_D_Z', 'u_E_Z']
M-bias model edges: [('E', 'D'), ('u_D_Z', 'D'), ('u_D_Z', 'Z'), ('u_E_Z', 'E'), ('u_E_Z', 'Z')]
D and E d-connected unconditionally? True
D and E d-connected given Z? True
_ = tpgpguti.draw_pgmpy_model(model_mb)
Primitive 5: LinearGaussianBayesianNetwork (Continuous)¶
Mental model: A continuous-variable Bayesian network where:
- Each node follows a linear Gaussian distribution conditional on its parents
- Parameters are mean and variance (linear combination of parent values)
- Suitable for continuous data modeling and Gaussian inference
- Typically larger networks (many nodes)
# Load a small continuous model.
cont_models = list_models(is_continuous=True)
print(f"Continuous models available: {len(cont_models)}")
print(f" {cont_models[:5]}")Continuous models available: 19
['bnlearn/arth150', 'bnlearn/ecoli70', 'bnlearn/magic_irri', 'bnlearn/magic_niab', 'bnrep/algal2']
# Load one continuous model.
model_cont = load_model(cont_models[4])
print(f"Model: {cont_models[4]}")
print(f"Type: {type(model_cont).__name__}")
print(f"Number of nodes: {len(model_cont.nodes())}")
print(f"Number of edges: {len(model_cont.edges())}")Model: bnrep/algal2
Type: LinearGaussianBayesianNetwork
Number of nodes: 9
Number of edges: 9
_ = tpgpguti.draw_pgmpy_model(model_cont)
Part 3: Composition Examples¶
Example 1: List a Category, Load a Model, Inspect Structure¶
Minimal workflow: discover -> load -> explore.
# Discover small discrete models.
tiny = list_models(is_discrete=True, is_parameterized=True, n_nodes=[4, 5, 6])
print(f"Small discrete parameterized models: {tiny}")
# Load the first one.
model = load_model(tiny[0])
print(f"\nName: {tiny[0]}")
print(f"Nodes: {sorted(model.nodes())}")
print(f"Edges: {sorted(model.edges())}")
print(f"CPDs: {len(model.get_cpds())}")Small discrete parameterized models: ['bnlearn/cancer', 'bnlearn/earthquake', 'bnlearn/survey', 'bnrep/adversarialbehavior', 'bnrep/agropastoral5', 'bnrep/algorithms3', 'bnrep/algorithms4', 'bnrep/augmenting', 'bnrep/bank', 'bnrep/beam1', 'bnrep/beam2', 'bnrep/beatles', 'bnrep/bullet', 'bnrep/chds', 'bnrep/compaction', 'bnrep/conasense', 'bnrep/concrete1', 'bnrep/concrete2', 'bnrep/concrete4', 'bnrep/concrete7', 'bnrep/crypto', 'bnrep/firealarm', 'bnrep/fluids1', 'bnrep/fluids2', 'bnrep/hydraulicsystem', 'bnrep/knowledge', 'bnrep/navigation', 'bnrep/perioperative', 'bnrep/phdarticles', 'bnrep/rainwater', 'bnrep/ricci', 'bnrep/rockburst', 'bnrep/sallyclark', 'bnrep/simulation', 'bnrep/softwarelogs1', 'bnrep/soil', 'bnrep/theft1', 'bnrep/theft2', 'bnrep/titanic', 'bnrep/trajectories', 'bnrep/transport', 'bnrep/tubercolosis', 'bnrep/wheat', 'bnrep/witness']
Name: bnlearn/cancer
Nodes: ['Cancer', 'Dyspnoea', 'Pollution', 'Smoker', 'Xray']
Edges: [('Cancer', 'Dyspnoea'), ('Cancer', 'Xray'), ('Pollution', 'Cancer'), ('Smoker', 'Cancer')]
CPDs: 5
_ = tpgpguti.draw_pgmpy_model(model)
Example 2: Compare Parameterized vs Structure-Only Models¶
Load two models and compare their capabilities.
# Discrete Bayesian network (parameterized).
bn = load_model("bnlearn/cancer")
# Structure-only DAG.
dag = load_model("dagitty/confounding")
# Build a comparison DataFrame.
comparison_df = pd.DataFrame(
{
"Property": [
"Type",
"Nodes",
"Edges",
"Has CPDs",
"Can do inference",
"Parents(X)",
],
"bnlearn/cancer (BN)": [
type(bn).__name__,
str(len(bn.nodes())),
str(len(bn.edges())),
"Yes",
"Yes",
str(bn.get_parents("Cancer")),
],
"dagitty/confounding (DAG)": [
type(dag).__name__,
str(len(dag.nodes())),
str(len(dag.edges())),
"No",
"No (structure only)",
str(dag.get_parents("A")),
],
}
)
display(comparison_df)Example 3: Use a DAG for Causal Structure Analysis¶
Load the Asia DAG (a classic model from Lauritzen and Spiegelhalter, 1988) to analyze its causal structure.
# The Asia network: a model about tuberculosis, lung cancer, and bronchitis.
asia = load_model("bnlearn/asia")
print(f"Asia network: {len(asia.nodes())} nodes, {len(asia.edges())} edges")
print(f"\nNodes: {sorted(asia.nodes())}")
print(f"Edges: {sorted(asia.edges())}")Asia network: 8 nodes, 8 edges
Nodes: ['asia', 'bronc', 'dysp', 'either', 'lung', 'smoke', 'tub', 'xray']
Edges: [('asia', 'tub'), ('bronc', 'dysp'), ('either', 'dysp'), ('either', 'xray'), ('lung', 'either'), ('smoke', 'bronc'), ('smoke', 'lung'), ('tub', 'either')]
# Analyze the causal structure.
print(f"Root nodes: {asia.get_roots()}")
print(f"Leaf nodes: {asia.get_leaves()}")
print(f"Markov blanket of 'lung': {asia.get_markov_blanket('lung')}")
print(
f"Active trail from 'smoke' to 'dysp': {asia.is_dconnected('smoke', 'dysp')}"
)
print(
f"Does 'tub' d-separate 'asia' from 'either'?: {asia.is_dconnected('asia', 'either', observed='tub')}"
)Root nodes: ['asia', 'smoke']
Leaf nodes: ['xray', 'dysp']
Markov blanket of 'lung': ['either', 'smoke', 'tub']
Active trail from 'smoke' to 'dysp': True
Does 'tub' d-separate 'asia' from 'either'?: False
_ = tpgpguti.draw_pgmpy_model(asia)
Example 4: Use a Parameterized Model for Inference¶
Load the Earthquake (Burglar Alarm) network and perform inference queries.
from pgmpy.inference import VariableElimination
# Load the classic Burglar Alarm model.
alarm = load_model("bnlearn/earthquake")
print(
f"Earthquake model: {len(alarm.nodes())} nodes, {len(alarm.edges())} edges"
)
print(f"Nodes: {sorted(alarm.nodes())}")
print(f"Edges: {sorted(alarm.edges())}")Earthquake model: 5 nodes, 4 edges
Nodes: ['Alarm', 'Burglary', 'Earthquake', 'JohnCalls', 'MaryCalls']
Edges: [('Alarm', 'JohnCalls'), ('Alarm', 'MaryCalls'), ('Burglary', 'Alarm'), ('Earthquake', 'Alarm')]
_ = tpgpguti.draw_pgmpy_model(alarm)
# View the CPDs.
for cpd in alarm.get_cpds():
print(cpd)+--------------+------------------+-----+-------------------+
| Burglary | Burglary(True) | ... | Burglary(False) |
+--------------+------------------+-----+-------------------+
| Earthquake | Earthquake(True) | ... | Earthquake(False) |
+--------------+------------------+-----+-------------------+
| Alarm(True) | 0.95 | ... | 0.001 |
+--------------+------------------+-----+-------------------+
| Alarm(False) | 0.05 | ... | 0.999 |
+--------------+------------------+-----+-------------------+
+-----------------+------+
| Burglary(True) | 0.01 |
+-----------------+------+
| Burglary(False) | 0.99 |
+-----------------+------+
+-------------------+------+
| Earthquake(True) | 0.02 |
+-------------------+------+
| Earthquake(False) | 0.98 |
+-------------------+------+
+------------------+-------------+--------------+
| Alarm | Alarm(True) | Alarm(False) |
+------------------+-------------+--------------+
| JohnCalls(True) | 0.9 | 0.05 |
+------------------+-------------+--------------+
| JohnCalls(False) | 0.1 | 0.95 |
+------------------+-------------+--------------+
+------------------+-------------+--------------+
| Alarm | Alarm(True) | Alarm(False) |
+------------------+-------------+--------------+
| MaryCalls(True) | 0.7 | 0.01 |
+------------------+-------------+--------------+
| MaryCalls(False) | 0.3 | 0.99 |
+------------------+-------------+--------------+
# Perform inference: given John calls, what is P(Burglary)?
# Note: state names are strings ('True', 'False'), not integers.
inference = VariableElimination(alarm)
result = inference.query(variables=["Burglary"], evidence={"JohnCalls": "True"})
print("P(Burglary | JohnCalls=True):")
print(result)P(Burglary | JohnCalls=True):
+-----------------+-----------------+
| Burglary | phi(Burglary) |
+=================+=================+
| Burglary(True) | 0.1333 |
+-----------------+-----------------+
| Burglary(False) | 0.8667 |
+-----------------+-----------------+
Part 5: Interactive Exploration¶
Experiment 1: Inspect a Large Model (Sachs)¶
# Load the Sachs protein signaling network (a benchmark model).
sachs = load_model("bnlearn/sachs")
print(f"Sachs network: {len(sachs.nodes())} nodes, {len(sachs.edges())} edges")
print(f"Nodes: {sorted(sachs.nodes())}")
print(f"Edges: {sorted(sachs.edges())}")
# Check state counts (some variables have more than 2 states).
print("\nVariable cardinalities:")
for node in sorted(sachs.nodes()):
cpd = sachs.get_cpds(node)
print(f" {node}: {cpd.variable_card} states")Sachs network: 11 nodes, 17 edges
Nodes: ['Akt', 'Erk', 'Jnk', 'Mek', 'P38', 'PIP2', 'PIP3', 'PKA', 'PKC', 'Plcg', 'Raf']
Edges: [('Erk', 'Akt'), ('Mek', 'Erk'), ('PIP3', 'PIP2'), ('PKA', 'Akt'), ('PKA', 'Erk'), ('PKA', 'Jnk'), ('PKA', 'Mek'), ('PKA', 'P38'), ('PKA', 'Raf'), ('PKC', 'Jnk'), ('PKC', 'Mek'), ('PKC', 'P38'), ('PKC', 'PKA'), ('PKC', 'Raf'), ('Plcg', 'PIP2'), ('Plcg', 'PIP3'), ('Raf', 'Mek')]
Variable cardinalities:
Akt: 3 states
Erk: 3 states
Jnk: 3 states
Mek: 3 states
P38: 3 states
PIP2: 3 states
PIP3: 3 states
PKA: 3 states
PKC: 3 states
Plcg: 3 states
Raf: 3 states
_ = tpgpguti.draw_pgmpy_model(sachs)
Experiment 2: Explore the Model API with dir()¶
# Use dir() to see what methods are available on a model.
model = load_model("bnlearn/cancer")
methods = [m for m in dir(model) if not m.startswith("_")]
print(f"Methods on DiscreteBayesianNetwork ({len(methods)}):")
# Group by category.
graph_methods = [
m
for m in methods
if any(
x in m
for x in [
"edge",
"node",
"graph",
"parent",
"child",
"ancestor",
"neighbor",
"root",
"leaf",
"successor",
"predecessor",
]
)
]
inference_methods = [
m
for m in methods
if any(
x in m
for x in [
"cpd",
"factor",
"markov",
"moral",
"independ",
"dconnec",
"simul",
"predict",
]
)
]
other = [m for m in methods if m not in graph_methods + inference_methods]
print(f"\n Graph structure methods: {sorted(graph_methods)}")
print(f"\n Inference/CPD methods: {sorted(inference_methods)}")
print(f"\n Other methods: {sorted(other)}")Methods on DiscreteBayesianNetwork (114):
Graph structure methods: ['active_trail_nodes', 'add_edge', 'add_edges_from', 'add_node', 'add_nodes_from', 'add_weighted_edges_from', 'clear_edges', 'edge_attr_dict_factory', 'edge_strength', 'edge_subgraph', 'edges', 'get_ancestors', 'get_ancestral_graph', 'get_children', 'get_edge_data', 'get_parents', 'get_roots', 'graph', 'graph_attr_dict_factory', 'has_edge', 'has_node', 'has_predecessor', 'has_successor', 'in_edges', 'is_multigraph', 'neighbors', 'node_attr_dict_factory', 'node_dict_factory', 'nodes', 'number_of_edges', 'number_of_nodes', 'out_edges', 'predecessors', 'remove_edge', 'remove_edges_from', 'remove_node', 'remove_nodes_from', 'subgraph', 'successors', 'to_graphviz']
Inference/CPD methods: ['add_cpds', 'adjlist_inner_dict_factory', 'adjlist_outer_dict_factory', 'cpds', 'edge_attr_dict_factory', 'get_cpds', 'get_factorized_product', 'get_immoralities', 'get_independencies', 'get_markov_blanket', 'get_random_cpds', 'graph_attr_dict_factory', 'is_dconnected', 'local_independencies', 'moralize', 'node_attr_dict_factory', 'node_dict_factory', 'predict', 'predict_probability', 'remove_cpds', 'simulate', 'to_markov_model']
Other methods: ['adj', 'adjacency', 'cardinalities', 'check_model', 'clear', 'copy', 'degree', 'do', 'exposures', 'fit', 'fit_update', 'from_dagitty', 'from_lavaan', 'get_cardinality', 'get_leaves', 'get_random', 'get_role', 'get_role_dict', 'get_roles', 'get_state_probability', 'get_stats', 'has_role', 'in_degree', 'in_degree_iter', 'is_directed', 'is_iequivalent', 'is_imap', 'is_valid_causal_structure', 'latents', 'load', 'minimal_dseparator', 'name', 'nbunch_iter', 'observed', 'order', 'out_degree', 'out_degree_iter', 'outcomes', 'pred', 'reverse', 'save', 'size', 'states', 'succ', 'to_daft', 'to_dagitty', 'to_directed', 'to_directed_class', 'to_junction_tree', 'to_lavaan', 'to_pdag', 'to_undirected', 'to_undirected_class', 'update', 'with_role', 'without_role']