Skip to content

Using pyAgrum

Creative Commons LicenseaGrUMinteractive online version
import numpy as np
import matplotlib.pyplot as plt
  • importing pyAgrum
  • importing pyagrum.lib tools
  • loading a BN
import pyagrum as gum
import pyagrum.lib.notebook as gnb
gnb.configuration()
LibraryVersion
OSposix [darwin]
Python3.14.0 (main, Oct 7 2025, 09:34:52) [Clang 17.0.0 (clang-1700.3.19.1)]
IPython9.6.0
Matplotlib3.10.7
Numpy2.3.4
pyDot4.0.1
pyAgrum2.3.0.9
Wed Oct 29 13:55:50 2025 CET
bn = gum.loadBN("res/alarm.dsl")
gnb.showBN(bn, size="9")

svg

print(bn["SHUNT"])
SHUNT:Labelized({NORMAL|HIGH})
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 |
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

It is easy to look at result of inference

gnb.showPosterior(bn, {"SHUNT": "HIGH"}, "PRESS")

svg

gnb.showPosterior(bn, {"MINVOLSET": "NORMAL"}, "VENTALV")

svg

Overall results

gnb.showInference(bn, size="10")

svg

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

ie = gum.LazyPropagation(bn)
ie.evidenceImpact("PRESS", ["SHUNT", "VENTALV"])
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

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

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()

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.

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()

svg

Using the CSV format for the database:

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 : -15145.74
with open("out/test.csv", "r") as src:
for _ in range(10):
print(src.readline(), end="")
PULMEMBOLUS,TPR,PAP,INSUFFANESTH,MINVOL,VENTTUBE,HYPOVOLEMIA,FIO2,VENTLUNG,ERRCAUTER,PVSAT,CATECHOL,HRBP,CVP,SHUNT,LVEDVOLUME,KINKEDTUBE,VENTALV,STROKEVOLUME,SAO2,HISTORY,HREKG,MINVOLSET,VENTMACH,EXPCO2,LVFAILURE,PCWP,PRESS,HRSAT,HR,CO,INTUBATION,DISCONNECT,ANAPHYLAXIS,ARTCO2,BP,ERRLOWOUTPUT
FALSE,LOW,NORMAL,FALSE,ZERO,ZERO,FALSE,NORMAL,ZERO,FALSE,LOW,NORMAL,LOW,NORMAL,NORMAL,NORMAL,FALSE,ZERO,NORMAL,LOW,FALSE,NORMAL,NORMAL,NORMAL,LOW,FALSE,NORMAL,LOW,LOW,NORMAL,NORMAL,NORMAL,TRUE,FALSE,HIGH,LOW,FALSE
FALSE,LOW,NORMAL,FALSE,ZERO,LOW,TRUE,NORMAL,ZERO,FALSE,LOW,HIGH,HIGH,HIGH,NORMAL,HIGH,FALSE,ZERO,LOW,LOW,FALSE,HIGH,NORMAL,NORMAL,LOW,FALSE,HIGH,HIGH,HIGH,HIGH,LOW,NORMAL,FALSE,FALSE,NORMAL,LOW,FALSE
FALSE,HIGH,NORMAL,FALSE,ZERO,LOW,FALSE,NORMAL,ZERO,FALSE,LOW,HIGH,HIGH,NORMAL,NORMAL,NORMAL,FALSE,ZERO,NORMAL,LOW,FALSE,HIGH,NORMAL,NORMAL,LOW,FALSE,NORMAL,NORMAL,HIGH,HIGH,HIGH,NORMAL,FALSE,FALSE,HIGH,HIGH,FALSE
FALSE,NORMAL,LOW,FALSE,ZERO,LOW,FALSE,LOW,ZERO,FALSE,LOW,HIGH,HIGH,NORMAL,NORMAL,NORMAL,FALSE,ZERO,NORMAL,LOW,FALSE,HIGH,NORMAL,NORMAL,LOW,FALSE,NORMAL,HIGH,HIGH,HIGH,HIGH,NORMAL,FALSE,FALSE,HIGH,HIGH,FALSE
FALSE,HIGH,HIGH,FALSE,HIGH,LOW,FALSE,NORMAL,HIGH,FALSE,NORMAL,HIGH,HIGH,LOW,HIGH,NORMAL,FALSE,LOW,NORMAL,HIGH,FALSE,HIGH,NORMAL,NORMAL,HIGH,FALSE,NORMAL,LOW,HIGH,HIGH,HIGH,ONESIDED,FALSE,FALSE,HIGH,HIGH,FALSE
FALSE,HIGH,NORMAL,FALSE,ZERO,LOW,TRUE,NORMAL,HIGH,FALSE,HIGH,HIGH,NORMAL,HIGH,HIGH,HIGH,FALSE,HIGH,LOW,LOW,FALSE,HIGH,NORMAL,NORMAL,LOW,FALSE,HIGH,LOW,HIGH,HIGH,NORMAL,NORMAL,FALSE,FALSE,LOW,HIGH,TRUE
FALSE,NORMAL,NORMAL,FALSE,ZERO,LOW,FALSE,NORMAL,ZERO,FALSE,LOW,HIGH,HIGH,NORMAL,NORMAL,NORMAL,FALSE,ZERO,LOW,LOW,FALSE,HIGH,NORMAL,NORMAL,LOW,FALSE,NORMAL,HIGH,HIGH,HIGH,LOW,NORMAL,FALSE,FALSE,HIGH,LOW,FALSE
FALSE,NORMAL,NORMAL,FALSE,LOW,HIGH,FALSE,NORMAL,LOW,FALSE,LOW,HIGH,HIGH,NORMAL,HIGH,NORMAL,FALSE,ZERO,NORMAL,LOW,FALSE,HIGH,HIGH,HIGH,NORMAL,FALSE,NORMAL,HIGH,HIGH,HIGH,HIGH,NORMAL,FALSE,FALSE,HIGH,NORMAL,FALSE
FALSE,HIGH,NORMAL,FALSE,LOW,NORMAL,FALSE,NORMAL,NORMAL,FALSE,HIGH,HIGH,HIGH,NORMAL,NORMAL,NORMAL,FALSE,HIGH,NORMAL,NORMAL,FALSE,HIGH,NORMAL,NORMAL,NORMAL,FALSE,NORMAL,NORMAL,HIGH,HIGH,HIGH,NORMAL,FALSE,FALSE,LOW,NORMAL,FALSE

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

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: 0%| |

out/test.csv: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████|

svg

(np.float64(0.9767975840221672),
np.float64(0.8384562795999999),
np.float64(0.9983441101534809),
np.float64(0.4221808066))

Using another class variable

showROC_PR(bn, "out/test.csv", "SAO2", "HIGH", show_progress=True)

out/test.csv: 0%| |

out/test.csv: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████|

svg

(np.float64(0.9816651969429747),
np.float64(0.048531175),
np.float64(0.8035085440124431),
np.float64(0.18330785165))
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.38ms a 2025-10-29T13:55:53.602262 image/svg+xml Matplotlib v3.10.7, b b a->b c 2025-10-29T13:55:53.623299 image/svg+xml Matplotlib v3.10.7, a->c b->c d 2025-10-29T13:55:53.643008 image/svg+xml Matplotlib v3.10.7, c->d
Inference given that $c=0$
structs Inference in   0.54ms a 2025-10-29T13:55:53.831442 image/svg+xml Matplotlib v3.10.7, b b a->b c 2025-10-29T13:55:53.855256 image/svg+xml Matplotlib v3.10.7, a->c b->c d 2025-10-29T13:55:53.880517 image/svg+xml Matplotlib v3.10.7, c->d
Inference given that $c=1$
structs Inference in   0.51ms a 2025-10-29T13:55:54.048955 image/svg+xml Matplotlib v3.10.7, b b a->b c 2025-10-29T13:55:54.068559 image/svg+xml Matplotlib v3.10.7, a->c b->c d 2025-10-29T13:55:54.089267 image/svg+xml Matplotlib v3.10.7, c->d
Inference given that $c=2$
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.3613 | 0.2883 | 0.3504 |
c
0
1
2
1.00000.00000.0000
d
0
1
2
0.36130.28830.3504

Joint posterior, impact of multiple evidence

Section titled “Joint posterior, impact of multiple evidence”
bn = gum.fastBN("a->b->c->d;b->e->d->f;g->c")
gnb.sideBySide(bn, gnb.getInference(bn))
G d d f f d->f e e e->d g g c c g->c b b b->e b->c a a a->b c->d
structs Inference in   1.76ms a 2025-10-29T13:55:54.244613 image/svg+xml Matplotlib v3.10.7, b 2025-10-29T13:55:54.266072 image/svg+xml Matplotlib v3.10.7, a->b c 2025-10-29T13:55:54.287563 image/svg+xml Matplotlib v3.10.7, b->c e 2025-10-29T13:55:54.330017 image/svg+xml Matplotlib v3.10.7, b->e d 2025-10-29T13:55:54.309168 image/svg+xml Matplotlib v3.10.7, c->d f 2025-10-29T13:55:54.352686 image/svg+xml Matplotlib v3.10.7, d->f e->d g 2025-10-29T13:55:54.378114 image/svg+xml Matplotlib v3.10.7, g->c
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.17870.1726
1
0.21220.2178
1
0
0.04270.0436
1
0.07230.0600

Joint posterior $P(e,f,g)$
g
e
0
1
0
0.22140.2162
1
0.28450.2778

Joint posterior $P(e,f)$
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.53050.4695
1
0.52640.4736
1
0
0.57330.4267
1
0.43550.5645

$\forall e,f, P(a|e,f)$
a
e
d
0
1
0
0
0.57580.4242
1
0.51730.4827
1
0
0.43140.5686
1
0.56450.4355

$\forall d,e,f, P(a|d,e,f)=P(a|d,e)$ using d-separation
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.26020.2703
1
0.01010.4594
1
0
0.25350.2729
1
0.00980.4638
1
0
0
0.32980.2435
1
0.01280.4139
1
0
0.10580.3297
1
0.00410.5604

$\forall e,f, P(a,b|e,f)$
b
e
d
a
0
1
0
0
0
0.33380.2420
1
0.01300.4113
1
0
0.23880.2786
1
0.00930.4734
1
0
0
0.09910.3323
1
0.00390.5647
1
0
0.31550.2490
1
0.01230.4232

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

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.

ie = gum.LazyPropagation(bn)
print(ie.mpe())

<d:1|e:0|c:1|b:1|a:1|g:0|f:0>

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:0|c:1|b:1|a:1>
- for a log probability of -1.761226