Customizing and exporting graphical models and CPTs as image (pdf, png)

Creative Commons License

aGrUM

interactive online version

In [1]:
from pylab import *
import matplotlib.pyplot as plt

In [2]:
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
In [3]:
bn=gum.fastBN("a->b->c->d;b->e->d->f;g->c")
gnb.flow.row(bn,gnb.getInference(bn))
G a a b b a->b e e b->e c c b->c d d f f d->f g g g->c e->d c->d
structs Inference in   3.91ms a 2024-10-17T15:43:04.638003 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b 2024-10-17T15:43:04.656034 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ a->b c 2024-10-17T15:43:04.673588 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b->c e 2024-10-17T15:43:04.712328 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b->e d 2024-10-17T15:43:04.693374 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ c->d f 2024-10-17T15:43:04.751676 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ d->f e->d g 2024-10-17T15:43:04.769529 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ g->c

customizing colours and width for model and inference

In [4]:
def nodevalue(n):
    return 0.5 if n in "aeiou" else 0.7
def arcvalue(a):
    return (10-a[0])*a[1]
def arcvalue2(a):
    return (a[0]+a[1]+5)/22
gnb.showBN(bn,
           nodeColor={n:nodevalue(n) for n in bn.names()},
           arcWidth={a:arcvalue(a) for a in bn.arcs()},
           arcLabel={a:f"v={arcvalue(a):02d}" for a in bn.arcs()},
           arcColor={a:arcvalue2(a) for a in bn.arcs()})
../_images/notebooks_98-Tools_customizingAndExportingBNs_6_0.svg
In [5]:
gnb.showInference(bn,
           targets={"a","g","f","b"},
           evs={'e':0},
           nodeColor={n:nodevalue(n) for n in bn.names()},
           arcWidth={a:arcvalue(a) for a in bn.arcs()})
../_images/notebooks_98-Tools_customizingAndExportingBNs_7_0.svg
In [6]:
gnb.flow.row(gnb.getBN(bn,
                       nodeColor={n:nodevalue(n) for n in bn.names()},
                       arcWidth={a:arcvalue(a) for a in bn.arcs()}),
             gnb.getInference(bn,
                              nodeColor={n:nodevalue(n) for n in bn.names()},
                              arcWidth={a:arcvalue(a) for a in bn.arcs()})
            )
G a a b b a->b e e b->e c c b->c d d f f d->f g g g->c e->d c->d
structs Inference in   2.55ms a 2024-10-17T15:43:05.435025 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b 2024-10-17T15:43:05.453099 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ a->b c 2024-10-17T15:43:05.472318 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b->c e 2024-10-17T15:43:05.507350 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b->e d 2024-10-17T15:43:05.489768 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ c->d f 2024-10-17T15:43:05.524349 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ d->f e->d g 2024-10-17T15:43:05.543013 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ g->c
In [7]:
import matplotlib.pyplot as plt
mycmap=plt.get_cmap('Reds')
formyarcs=plt.get_cmap('winter')
gnb.flow.row(gnb.getBN(bn,
                       nodeColor={n:nodevalue(n) for n in bn.names()},
                       arcColor={a:arcvalue2(a) for a in bn.arcs()},
                       cmapNode=mycmap,
                       cmapArc=formyarcs),
             gnb.getInference(bn,
                              nodeColor={n:nodevalue(n) for n in bn.names()},
                              arcColor={a:arcvalue2(a) for a in bn.arcs()},
                              arcWidth={a:arcvalue(a) for a in bn.arcs()},
                              cmapNode=mycmap,
                              cmapArc=formyarcs)
              )
G a a b b a->b e e b->e c c b->c d d f f d->f g g g->c e->d c->d
structs Inference in   2.60ms a 2024-10-17T15:43:05.776918 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b 2024-10-17T15:43:05.794989 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ a->b c 2024-10-17T15:43:05.812668 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b->c e 2024-10-17T15:43:05.849412 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ b->e d 2024-10-17T15:43:05.831642 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ c->d f 2024-10-17T15:43:05.866765 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ d->f e->d g 2024-10-17T15:43:05.884526 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ g->c

Modifying graph’s layout

Every graph or graphical models can be translated into a pyDot’s representaton (a pydot.Dot object). In this graphical representation, it is possible to manipulate the positions of the node. pyAgrum proposes two functions gum.utils.dot_layout to help modifying this layout.

Layout for Bayesian network

In [8]:
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
import pyAgrum.lib.utils as gutils
import pyAgrum.lib.bn2graph as gumb2g

bn=gum.fastBN("A->B<-C<-D")
bn2=gum.fastBN("A->B->C<-D")

graph=gumb2g.BN2dot(bn)

graph2=gumb2g.BN2dot(bn2)
l=gutils.dot_layout(graph)
print(f"Layout proposed by dot for BN :{l}")
gutils.apply_dot_layout(graph2,l)

graph3=gumb2g.BN2dot(bn2)
#l["C"],l["A"]=l["A"],l["C"]
l["D"],l["C"],l["B"],l["A"]=(gutils.DotPoint(0,0),
                             gutils.DotPoint(1,1),
                             gutils.DotPoint(2,2),
                             gutils.DotPoint(3,3))
gutils.apply_dot_layout(graph3,l)
gnb.flow.row(bn,bn2,graph2,graph3,
              captions=["BN","BN2","BN2 with the same layoutas BN","Layout changed by hand"])
Layout proposed by dot for BN :{'C': DotPoint(x=1.375, y=1.25), 'B': DotPoint(x=0.875, y=0.25), 'A': DotPoint(x=0.375, y=1.25), 'D': DotPoint(x=1.375, y=2.25)}
G C C B B C->B A A A->B D D D->C
BN
G C C B B B->C A A A->B D D D->C
BN2
G C C B B B->C A A A->B D D D->C
BN2 with the same layoutas BN
G C C B B B->C A A A->B D D D->C
Layout changed by hand

Layout for other graphical models and for inference

In [9]:
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
import pyAgrum.lib.utils as gutils
import pyAgrum.lib.id2graph as gum2gr

model=gum.fastID("*D->$L<-E<-H->L;E->D")
gnb.flow.add(model)
gum.config.push()
gum.config['influenceDiagram','utility_shape']='diamond'

figure=gum2gr.ID2dot(model)
l=gutils.dot_layout(figure)

# changing LAYOUT
# making some horizontal space
for i,p in l.items():
    l[i]=gutils.DotPoint(1.5*p.x,p.y)
# E at the vertical of L, at the horizontal of D
l['E']=gutils.DotPoint(l['L'].x,
                       l['D'].y)
# H symetric of D w.r.t (EL)
l['H']=gutils.DotPoint(2*l['E'].x-l['D'].x,l['D'].y)

gutils.apply_dot_layout(figure,l)
gnb.flow.add(figure)

gnb.flow.display()
gum.config.pop()
G E E D D E->D L L E->L H H H->E H->L D->L
G E E D D E->D L L E->L H H H->E H->L D->L
In [10]:
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
import pyAgrum.lib.utils as gutils
import pyAgrum.lib.id2graph as gum2gr

model=gum.fastID("*D->$L<-E<-H->L;E->D")
gnb.flow.add(gnb.getInference(model))

figure=gum2gr.LIMIDinference2dot(model,evs={},targets={},size=None,engine=None)
l=gutils.dot_layout(figure)

# changing LAYOUT
# making some horizontal space
for i,p in l.items():
    l[i]=gutils.DotPoint(3*p.x,p.y)
# E at the vertical of L, at the horizontal of D
l['E']=gutils.DotPoint(l['L'].x,
                       l['D'].y)
l['D']=gutils.DotPoint(l['D'].x/2,l['D'].y)
l['H']=gutils.DotPoint(l['L'].x*3/2,
                       l['D'].y)

gutils.apply_dot_layout(figure,l)
gnb.flow.add(figure)

gnb.flow.display()
structs MEU 15.92 (stdev=15.79) Inference in   0.16ms D 2024-10-17T15:43:06.777254 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ L L : 15.92 (15.79) D->L E 2024-10-17T15:43:06.793769 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ E->D E->L H 2024-10-17T15:43:06.813699 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ H->L H->E
structs MEU 15.92 (stdev=15.79) Inference in   0.22ms D 2024-10-17T15:43:06.938556 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ L L : 15.92 (15.79) D->L E 2024-10-17T15:43:06.954102 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ E->D E->L H 2024-10-17T15:43:06.971080 image/svg+xml Matplotlib v3.9.2, https://matplotlib.org/ H->L H->E

Exporting model and inference as image

Exporting as image (pdf, png, etc.) has been gathered in 2 functions : pyAgrum.lib.image.export() and pyAgrum.lib.image.exportInference(). The argument are the same as for pyAgrum.notebook.show{Model} and pyAgrum.notebook.show{Inference}.

In [11]:
import pyAgrum.lib.image as gumimage
from IPython.display import Image # to display the exported images
In [12]:
gumimage.export(bn,"out/test_export.png")

Image(filename='out/test_export.png')
Out[12]:
../_images/notebooks_98-Tools_customizingAndExportingBNs_17_0.png
In [13]:
bn = gum.fastBN("a->b->d;a->c->d[3]->e;f->b")
gumimage.export(bn,"out/test_export.png",
                nodeColor={'a': 1,
                           'b': 0.3,
                           'c': 0.4,
                           'd': 0.1,
                           'e': 0.2,
                           'f': 0.5},
                arcColor={(0, 1): 0.2,
                          (1, 2): 0.5},
                arcWidth={(0, 3): 0.4,
                          (3, 2): 0.5,
                          (2,4) :0.6})

Image(filename='out/test_export.png')
Out[13]:
../_images/notebooks_98-Tools_customizingAndExportingBNs_18_0.png
In [14]:
gumimage.exportInference(bn,"out/test_export.png")

Image(filename='out/test_export.png')
Out[14]:
../_images/notebooks_98-Tools_customizingAndExportingBNs_19_0.png
In [15]:
gumimage.export(bn,"out/test_export.pdf")

Link to out/test_export.pdf

exporting inference with evidence

In [16]:
bn=gum.loadBN("res/alarm.dsl")
gumimage.exportInference(bn,"out/test_export.pdf",
                         evs={"CO":1,"VENTLUNG":1},
                         targets={"VENTALV",
                                  "CATECHOL",
                                  "HR",
                                  "MINVOLSET",
                                  "ANAPHYLAXIS",
                                  "STROKEVOLUME",
                                  "ERRLOWOUTPUT",
                                  "HBR",
                                  "PULMEMBOLUS",
                                  "HISTORY",
                                  "BP",
                                  "PRESS",
                                  "CO"},
                         size="15!")

Link to out/test_export.pdf

Other models

Other models can also use these functions.

In [17]:
infdiag=gum.loadID("res/OilWildcatter.bifxml")
gumimage.export(infdiag,"out/test_export.pdf")

Link to out/test_export.pdf

In [18]:
gumimage.exportInference(infdiag,"out/test_export.pdf")

Link to out/test_export.pdf

Exporting any object with toDot() method

In [19]:
import pyAgrum.causal as csl
obs1 = gum.fastBN("Smoking->Cancer")
modele3 = csl.CausalModel(obs1, [("Genotype", ["Smoking","Cancer"])], True)
gumimage.export(modele3,"out/test_export.png") # a causal model has a toDot method.
Image(filename='out/test_export.png')
Out[19]:
../_images/notebooks_98-Tools_customizingAndExportingBNs_31_0.png
In [20]:
bn=gum.fastBN("a->b->c->d;b->e->d->f;g->c")
ie=gum.LazyPropagation(bn)
jt=ie.junctionTree()
gumimage.export(jt,"out/test_export.png") # a JunctionTree has a method jt.toDot()
Image(filename='out/test_export.png')
Out[20]:
../_images/notebooks_98-Tools_customizingAndExportingBNs_32_0.png

… or even a string in dot syntax

In [21]:
gumimage.export(jt.toDotWithNames(bn),"out/test_export.png") # jt.toDotWithNames(bn) creates a dot-string for a junction tree with names of variables
Image(filename='out/test_export.png')
Out[21]:
../_images/notebooks_98-Tools_customizingAndExportingBNs_34_0.png

Exporting to pyplot

In [22]:
import matplotlib.pyplot as plt

bn=gum.fastBN("A->B->C<-D")

plt.imshow(gumimage.export(bn))
plt.show()

plt.imshow(gumimage.exportInference(bn,size="15!"))
plt.show()

plt.figure(figsize = (10,10))
plt.imshow(gumimage.exportInference(bn,size="15!"))
plt.show()
../_images/notebooks_98-Tools_customizingAndExportingBNs_36_0.svg
../_images/notebooks_98-Tools_customizingAndExportingBNs_36_1.svg
../_images/notebooks_98-Tools_customizingAndExportingBNs_36_2.svg

Exporting CPTs, sideBySide, explain.Information (and other html strings)

pyAgrum uses the package playwright in order to export html string. It proposes a function pyAgrum.utils.async_html2image. In pyAgrum.notebook, the functions get... return HTML objects. The functions show... display the result.

As a result, every pyAgrum.lib.notebook.get... can be exported as pdf, png … using pyAgrum.utils.async_html2image.

In [23]:
bn=gum.fastBN("A->B<-C",3)
await gutils.async_html2image(gnb.getPotential(bn.cpt("B")),"out/cpt_B_.pdf")
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[23], line 2
      1 bn=gum.fastBN("A->B<-C",3)
----> 2 await gutils.async_html2image(gnb.getPotential(bn.cpt("B")),"out/cpt_B_.pdf")

File ~/.virtualenvs/devAgrum/lib/python3.13/site-packages/pyAgrum/lib/utils.py:112, in async_html2image(htmlcontent, filename)
    100 async def async_html2image(htmlcontent: str, filename: str):
    101   """
    102   convert an html content to an image. The format is determined by the extension of the filename.
    103   This function is asynchronous and uses playwright. To install playwright, use `pip install playwright` and then `playwright install`.
   (...)
    110     the filename of the image to create. The format is determined by the extension of the filename (at least pdf and png).
    111   """
--> 112   from playwright.async_api import async_playwright
    114   playwright = await async_playwright().start()
    115   browser = await playwright.chromium.launch(headless=True)

ModuleNotFoundError: No module named 'playwright'
In [ ]:
await gutils.async_html2image(gnb.getSideBySide(bn,bn.cpt("A"),bn.cpt("B"),bn.cpt("C")),"out/sideBySide.pdf")
In [ ]:
import pyAgrum.lib.explain as gexplain
await gutils.async_html2image(gexplain.getInformation(bn),"out/informationBN.pdf")
In [ ]: