Using pyAgrum

Creative Commons License

aGrUM

interactive online version

In [1]:
import numpy as np
import matplotlib.pyplot as plt

Initialisation

  • importing pyAgrum

  • importing pyagrum.lib tools

  • loading a BN

In [2]:
import pyagrum as gum
import pyagrum.lib.notebook as gnb

gnb.configuration()
LibraryVersion
OSposix [darwin]
Python3.14.6 (main, Jun 10 2026, 10:03:53) [Clang 21.0.0 (clang-2100.0.123.102)]
IPython9.15.0
Matplotlib3.11.0
Numpy2.5.1
pyDot4.0.1
pyAgrum3.0.0
Thu Jul 16 18:15:08 2026 CEST
In [3]:
bn = gum.loadBN("res/alarm.dsl")
gnb.showBN(bn, size="9")
../_images/notebooks_02-Tutorial_Using_pyAgrum_5_0.svg

Visualisation and inspection

In [4]:
print(bn["SHUNT"])
SHUNT:Labelized({NORMAL|HIGH})
In [5]:
print(bn.cpt(bn.idFromName("SHUNT")))

             ║  SHUNT            │
PULMEM│INTUBA║NORMAL   │HIGH     │
──────│──────║─────────│─────────│
TRUE  │NORMAL║ 0.1000  │ 0.9000  │
FALSE │NORMAL║ 0.9500  │ 0.0500  │
TRUE  │ESOPHA║ 0.1000  │ 0.9000  │
FALSE │ESOPHA║ 0.9500  │ 0.0500  │
TRUE  │ONESID║ 0.0100  │ 0.9900  │
FALSE │ONESID║ 0.0500  │ 0.9500  │

In [6]:
gnb.showTensor(bn.cpt(bn.idFromName("SHUNT")), digits=3)
SHUNT
INTUBATION
PULMEMBOLUS
NORMAL
HIGH
NORMAL
TRUE
0.1000.900
FALSE
0.9500.050
ESOPHAGEAL
TRUE
0.1000.900
FALSE
0.9500.050
ONESIDED
TRUE
0.0100.990
FALSE
0.0500.950

Results of inference

It is easy to look at result of inference

In [7]:
gnb.showPosterior(bn, {"SHUNT": "HIGH"}, "PRESS")
In [8]:
gnb.showPosterior(bn, {"MINVOLSET": "NORMAL"}, "VENTALV")

Overall results

In [9]:
gnb.showInference(bn, size="10")
../_images/notebooks_02-Tutorial_Using_pyAgrum_14_0.svg

What is the impact of observed variables (SHUNT and VENTALV for instance) on another on (PRESS) ?

In [10]:
ie = gum.LazyPropagation(bn)
ie.evidenceImpact("PRESS", ["SHUNT", "VENTALV"])
Out[10]:
PRESS
SHUNT
VENTALV
ZERO
LOW
NORMAL
HIGH
NORMAL
ZERO
0.05690.26690.20050.4757
LOW
0.02080.25150.05530.6724
NORMAL
0.07690.32670.17720.4192
HIGH
0.05010.16330.27960.5071
HIGH
ZERO
0.05890.27260.19970.4688
LOW
0.03180.22370.05210.6924
NORMAL
0.17350.58390.14020.1024
HIGH
0.07110.23470.25330.4410

Using inference as a function

It is also easy to use inference as a routine in more complex procedures.

In [11]:
import time

r = range(0, 100)
xs = [x / 100.0 for x in r]

tf = time.time()
ys = [gum.getPosterior(bn, evs={"MINVOLSET": [0, x / 100.0, 0.5]}, target="VENTALV").tolist() for x in r]
delta = time.time() - tf

plt.plot(xs, ys)
plt.legend([bn["VENTALV"].label(i) for i in range(bn["VENTALV"].domainSize())], loc=7)
plt.title("VENTALV (100 inferences in %d ms)" % delta)
plt.ylabel("posterior Probability")
plt.xlabel("Evidence on MINVOLSET : [0,x,0.5]")
plt.show()
../_images/notebooks_02-Tutorial_Using_pyAgrum_18_0.svg

Another example : python gives access to a large set of tools. Here the value for the equality of two probabilities of a posterior is easely computed.

In [12]:
x = [p / 100.0 for p in range(0, 100)]

tf = time.time()
y = [
  gum.getPosterior(bn, evs={"HRBP": [1.0 - p / 100.0, 1.0 - p / 100.0, p / 100.0]}, target="TPR").tolist()
  for p in range(0, 100)
]
delta = time.time() - tf

plt.plot(x, y)
plt.title("HRBP (100 inferences in %d ms)" % delta)
v = bn["TPR"]
plt.legend([v.label(i) for i in range(v.domainSize())], loc="best")
np1 = (np.transpose(y)[0] > np.transpose(y)[2]).argmin()
plt.text(x[np1] - 0.05, y[np1][0] + 0.005, str(x[np1]), bbox=dict(facecolor="red", alpha=0.05))
plt.show()
../_images/notebooks_02-Tutorial_Using_pyAgrum_20_0.svg

BN as a classifier

Generation of databases

Using the CSV format for the database:

In [13]:
print(
  f"The log2-likelihood of the generated base : {gum.generateSample(bn, 1000, 'out/test.csv', with_labels=True):.2f}"
)
The log2-likelihood of the generated base : -15009.69
In [14]:
with open("out/test.csv", "r") as src:
  for _ in range(10):
    print(src.readline(), end="")
PCWP,MINVOLSET,HRBP,CO,HREKG,PULMEMBOLUS,VENTLUNG,BP,SHUNT,PAP,INTUBATION,VENTTUBE,MINVOL,TPR,STROKEVOLUME,LVFAILURE,ERRLOWOUTPUT,SAO2,HRSAT,FIO2,HISTORY,INSUFFANESTH,CATECHOL,ANAPHYLAXIS,HR,PVSAT,HYPOVOLEMIA,LVEDVOLUME,VENTMACH,ERRCAUTER,EXPCO2,VENTALV,CVP,KINKEDTUBE,DISCONNECT,ARTCO2,PRESS
HIGH,NORMAL,NORMAL,LOW,HIGH,FALSE,ZERO,LOW,NORMAL,NORMAL,NORMAL,ZERO,ZERO,HIGH,LOW,FALSE,TRUE,LOW,HIGH,NORMAL,FALSE,TRUE,HIGH,FALSE,HIGH,LOW,TRUE,HIGH,NORMAL,FALSE,LOW,ZERO,HIGH,FALSE,TRUE,NORMAL,LOW
NORMAL,NORMAL,HIGH,HIGH,HIGH,FALSE,ZERO,NORMAL,NORMAL,NORMAL,NORMAL,ZERO,ZERO,NORMAL,NORMAL,FALSE,FALSE,LOW,HIGH,NORMAL,FALSE,FALSE,HIGH,FALSE,HIGH,LOW,FALSE,NORMAL,LOW,FALSE,LOW,ZERO,NORMAL,FALSE,FALSE,HIGH,LOW
NORMAL,NORMAL,LOW,NORMAL,NORMAL,FALSE,ZERO,HIGH,HIGH,NORMAL,ONESIDED,LOW,LOW,HIGH,NORMAL,FALSE,FALSE,LOW,HIGH,NORMAL,FALSE,FALSE,HIGH,FALSE,NORMAL,LOW,FALSE,NORMAL,NORMAL,TRUE,LOW,NORMAL,NORMAL,FALSE,FALSE,NORMAL,LOW
HIGH,NORMAL,HIGH,LOW,HIGH,FALSE,ZERO,LOW,HIGH,NORMAL,ONESIDED,LOW,LOW,NORMAL,LOW,FALSE,FALSE,HIGH,HIGH,NORMAL,FALSE,FALSE,HIGH,FALSE,HIGH,LOW,TRUE,HIGH,NORMAL,FALSE,LOW,NORMAL,HIGH,FALSE,FALSE,NORMAL,LOW
HIGH,NORMAL,HIGH,LOW,HIGH,FALSE,ZERO,LOW,NORMAL,LOW,NORMAL,LOW,ZERO,NORMAL,LOW,FALSE,FALSE,LOW,HIGH,NORMAL,FALSE,FALSE,HIGH,FALSE,HIGH,LOW,TRUE,HIGH,NORMAL,FALSE,LOW,ZERO,HIGH,FALSE,FALSE,HIGH,HIGH
HIGH,HIGH,HIGH,LOW,HIGH,FALSE,LOW,LOW,NORMAL,NORMAL,NORMAL,HIGH,LOW,NORMAL,LOW,FALSE,FALSE,LOW,HIGH,NORMAL,FALSE,FALSE,HIGH,FALSE,HIGH,NORMAL,TRUE,HIGH,HIGH,FALSE,NORMAL,LOW,HIGH,FALSE,FALSE,HIGH,HIGH
NORMAL,NORMAL,HIGH,LOW,HIGH,FALSE,ZERO,LOW,NORMAL,NORMAL,NORMAL,LOW,ZERO,LOW,LOW,FALSE,FALSE,LOW,HIGH,NORMAL,FALSE,FALSE,HIGH,FALSE,HIGH,LOW,TRUE,NORMAL,NORMAL,FALSE,LOW,ZERO,LOW,FALSE,FALSE,HIGH,HIGH
NORMAL,NORMAL,LOW,NORMAL,NORMAL,FALSE,ZERO,NORMAL,NORMAL,NORMAL,NORMAL,LOW,ZERO,NORMAL,NORMAL,FALSE,FALSE,LOW,LOW,NORMAL,FALSE,FALSE,NORMAL,FALSE,NORMAL,LOW,FALSE,NORMAL,NORMAL,FALSE,LOW,ZERO,NORMAL,FALSE,FALSE,HIGH,NORMAL
NORMAL,NORMAL,HIGH,HIGH,LOW,FALSE,ZERO,NORMAL,NORMAL,NORMAL,NORMAL,LOW,ZERO,LOW,NORMAL,FALSE,FALSE,LOW,NORMAL,NORMAL,FALSE,FALSE,HIGH,FALSE,HIGH,LOW,FALSE,NORMAL,NORMAL,TRUE,LOW,ZERO,NORMAL,FALSE,FALSE,HIGH,HIGH

probabilistic classifier using BN

(because of the use of from-bn-generated csv files, quite good ROC curves are expected)

In [15]:
from pyagrum.lib.bn2roc import showROC_PR

showROC_PR(
  bn,
  "out/test.csv",
  target="CATECHOL",
  label="HIGH",  # class and label
  show_progress=True,
  show_fig=True,
  with_labels=True,
)
out/test.csv: 100%|██████████|
../_images/notebooks_02-Tutorial_Using_pyAgrum_26_1.svg
Out[15]:
(np.float64(0.9790081780144542),
 np.float64(0.8384562795999999),
 np.float64(0.9990416686975433),
 np.float64(0.32016631365000003))

Using another class variable

In [16]:
showROC_PR(bn, "out/test.csv", "SAO2", "HIGH", show_progress=True)
out/test.csv: 100%|██████████|
../_images/notebooks_02-Tutorial_Using_pyAgrum_28_1.svg
Out[16]:
(np.float64(0.2959592363490722),
 np.float64(0.010294167391823879),
 np.float64(0.02293462929761632),
 np.float64(0.012257517612538345))

Fast prototyping for BNs

In [17]:
bn1 = gum.fastBN("a->b;a->c;b->c;c->d", 3)

gnb.sideBySide(
  *[gnb.getInference(bn1, evs={"c": val}, targets={"a", "c", "d"}) for val in range(3)],
  captions=[f"Inference given that $c={val}$" for val in range(3)],
)
structs Inference in   0.07ms a 2026-07-16T18:15:13.830346 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ b b a->b c 2026-07-16T18:15:13.853615 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ a->c b->c d 2026-07-16T18:15:13.875474 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ c->d
Inference given that $c=0$
structs Inference in   0.15ms a 2026-07-16T18:15:14.160959 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ b b a->b c 2026-07-16T18:15:14.191692 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ a->c b->c d 2026-07-16T18:15:14.212993 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ c->d
Inference given that $c=1$
structs Inference in   0.21ms a 2026-07-16T18:15:14.764857 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ b b a->b c 2026-07-16T18:15:14.796006 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ a->c b->c d 2026-07-16T18:15:14.826541 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ c->d
Inference given that $c=2$
In [18]:
print(gum.getPosterior(bn1, evs={"c": 0}, target="c"))
print(gum.getPosterior(bn1, evs={"c": 0}, target="d"))

# using pyagrum.lib.notebook's helpers
gnb.flow.row(gum.getPosterior(bn1, evs={"c": 0}, target="c"), gum.getPosterior(bn1, evs={"c": 0}, target="d"))

  c                          │
0        │1        │2        │
─────────│─────────│─────────│
 1.0000  │ 0.0000  │ 0.0000  │


  d                          │
0        │1        │2        │
─────────│─────────│─────────│
 0.2845  │ 0.5330  │ 0.1826  │

Out[18]:
c
0
1
2
1.00000.00000.0000
d
0
1
2
0.28450.53300.1826

Joint posterior, impact of multiple evidence

In [19]:
bn = gum.fastBN("a->b->c->d;b->e->d->f;g->c")
gnb.sideBySide(bn, gnb.getInference(bn))
G e e d d e->d b b b->e c c b->c f f c->d d->f g g g->c a a a->b
structs Inference in   0.16ms a 2026-07-16T18:15:15.258746 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ b 2026-07-16T18:15:15.287669 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ a->b c 2026-07-16T18:15:15.311830 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ b->c e 2026-07-16T18:15:15.367425 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ b->e d 2026-07-16T18:15:15.340407 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ c->d f 2026-07-16T18:15:15.399859 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ d->f e->d g 2026-07-16T18:15:15.527534 image/svg+xml Matplotlib v3.11.0, https://matplotlib.org/ g->c
In [20]:
ie = gum.LazyPropagation(bn)
ie.addJointTarget({"e", "f", "g"})
ie.makeInference()
gnb.sideBySide(
  ie.jointPosterior({"e", "f", "g"}),
  ie.jointPosterior({"e", "g"}),
  captions=["Joint posterior $P(e,f,g)$", "Joint posterior $P(e,f)$"],
)
g
f
e
0
1
0
0
0.06360.0892
1
0.12020.2022
1
0
0.09730.1605
1
0.11080.1562

Joint posterior $P(e,f,g)$
g
e
0
1
0
0.16090.2497
1
0.23100.3584

Joint posterior $P(e,f)$
In [21]:
gnb.sideBySide(
  ie.evidenceImpact("a", ["e", "f"]),
  ie.evidenceImpact("a", ["d", "e", "f"]),
  captions=["$\\forall e,f, P(a|e,f)$", "$\\forall d,e,f, P(a|d,e,f)=P(a|d,e)$ using d-separation"],
)
a
f
e
0
1
0
0
0.45950.5405
1
0.43370.5663
1
0
0.45270.5473
1
0.44090.5591

$\forall e,f, P(a|e,f)$
a
e
d
0
1
0
0
0.46320.5368
1
0.45150.5485
1
0
0.43220.5678
1
0.44410.5559

$\forall d,e,f, P(a|d,e,f)=P(a|d,e)$ using d-separation
In [22]:
gnb.sideBySide(
  ie.evidenceJointImpact(["a", "b"], ["e", "f"]),
  ie.evidenceJointImpact(["a", "b"], ["d", "e", "f"]),
  captions=["$\\forall e,f, P(a,b|e,f)$", "$\\forall d,e,f, P(a,b|d,e,f)=P(a,b|d,e)$ using d-separation"],
)
b
f
e
a
0
1
0
0
0
0.10290.3566
1
0.38370.1568
1
0
0.11420.3195
1
0.42580.1405
1
0
0
0.10580.3469
1
0.39470.1525
1
0
0.11100.3299
1
0.41410.1451

$\forall e,f, P(a,b|e,f)$
b
e
d
a
0
1
0
0
0
0.10120.3620
1
0.37760.1592
1
0
0.10640.3451
1
0.39670.1518
1
0
0
0.11480.3174
1
0.42830.1396
1
0
0.10960.3346
1
0.40870.1471

$\forall d,e,f, P(a,b|d,e,f)=P(a,b|d,e)$ using d-separation

Most Probable Explanation

The Most Probable Explanation (MPE) is a concept commonly used in the field of probabilistic reasoning and Bayesian statistics. It refers to the set of values for the variables in a given probabilistic model that is the most consistent with (that maximizes the likelihood of) the observed evidence. Essentially, it represents the most likely scenario or explanation given the available evidenceand the underlying probabilistic model.

In [23]:
ie = gum.LazyPropagation(bn)
print(ie.mpe())
<d:0|e:1|c:0|b:0|a:1|g:1|f:0>
In [24]:
evs = {"e": 0, "g": 0}
ie.setEvidence(evs)
vals = ie.mpeLog2Posterior()
print(
  f"The most probable explanation for observation {evs} is \n - the configuration {vals[0]} \n - for a log probability of {vals[1]:.6f}"
)
The most probable explanation for observation {'e': 0, 'g': 0} is
 - the configuration <g:0|e:0|d:1|f:1|c:1|b:0|a:1>
 - for a log probability of -2.315552