Click here to hide/show the list of notebooks.
  pyAgrum on notebooks   pyAgrum jupyter
☰  ComparingBN 
pyAgrum 0.16.3   
Zipped notebooks   
generation: 2019-10-20 09:16  

Creative Commons License
This pyAgrum's notebook is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

In [1]:
def dict2html(di1,di2=None): 
    res= "<br/>".join([f"<b>{k:15}</b>:{v}" for k,v in di1.items()])
    if di2 is not None:
        res+="<br/><br/>"
        res+= "<br/>".join([f"<b>{k:15}</b>:{v}" for k,v in di2.items()])
    return res
In [2]:
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
import pyAgrum.lib.bn_vs_bn as gcm

How to compare two BNs

PyAgrum allows you to compare BNs in several ways. This notebook show you some of them:

  • a graphical diff between the 2 BNs
  • some scores form recal and precision
  • distance measures (for more, see notebook 26-klForBNs for more)

Between two different structures

In [3]:
bn1=gum.fastBN("A->B->C->D->E<-A->F")
bn2=gum.fastBN("A->B<-C->D->E<-A;F->E")
cmp=gcm.GraphicalBNComparator(bn1,bn2)
kl=gum.ExactBNdistance(bn1,bn2) # bruteForce is possible car the BNs are small
gnb.sideBySide(bn1,bn2,gnb.getBNDiff(bn1,bn2),dict2html(cmp.scores(),cmp.hamming()),cmp.equivalentBNs(),dict2html(kl.compute()),
              captions=['bn1','bn2','graphical diff','Scores','equivalent ?','distances'])
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
G A A B B A->B E E A->E C C C->B D D C->D D->E F F F->E
G A A B B A->B E E A->E F F A->F C C C->B D D C->D D->E F->E
count :{'tp': 4, 'tn': 22, 'fp': 2, 'fn': 2}
recall :0.6666666666666666
precision :0.6666666666666666
fscore :0.6666666666666666
dist2opt :0.47140452079103173

hamming :2
structural hamming:4
B has different parents in the two bns whose names are in {'C'}
klPQ :3.4256934591002244
errorPQ :0
klQP :4.280798777973704
errorQP :0
hellinger :0.9949138002593297
bhattacharya :0.6830517809688268
jensen-shannon :0.5969293476303733
bn1
bn2
graphical diff
Scores
equivalent ?
distances

The logic for the arcs of the graphical diff is the following. When comparaing bn1 with bn2 (in that order) :

  • full black line: the arc is common for both
  • full red line: the arc is common but inverted in bn2
  • dotted black line: the arc is added in bn2
  • dotted red line: the arc is removed in bn2

For the scores :

  • precision and recall are computed considering BN1 as the reference
  • $Fscore=\frac{2\cdot recall\cdot precision}{recall+precision}$ is the weighted average of Precision and Recall.
  • $dist2opt=\sqrt{(1-precision)^2+(1-recall)^2}$ represents the euclidian distance to the ideal(precision=1,recall=1)

EquivalentBN return "OK" if equivalent or a reason for non equivalence

Finally, BruteForceKL compute in the same time several distances : I-projection, M-projection, Hellinger and Bhattacharya. For more complex BNs, there exists a GibbsKL to approximate those distances. Of course, the computation are much slower.

Same structure, different parameters

In [4]:
bn1=gum.fastBN("A->B->C->D->E<-A->F")
bn2=gum.fastBN("A->B->C->D->E<-A->F")
cmp=gcm.GraphicalBNComparator(bn1,bn2)
kl=gum.ExactBNdistance(bn1,bn2) # bruteForce is possible car the BNs are small
gnb.sideBySide(bn1,bn2,gnb.getBNDiff(bn1,bn2),dict2html(cmp.scores(),cmp.hamming()),cmp.equivalentBNs(),dict2html(kl.compute()),
              captions=['bn1','bn2','graphical diff','Scores','equivalent ?','distances'])
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
count :{'tp': 6, 'tn': 24, 'fp': 0, 'fn': 0}
recall :1.0
precision :1.0
fscore :1.0
dist2opt :0.0

hamming :0
structural hamming:0
Different CPTs for A
klPQ :4.982470589220756
errorPQ :0
klQP :6.46318241487143
errorQP :0
hellinger :1.1664417350478242
bhattacharya :1.1403508300600567
jensen-shannon :0.781625082460194
bn1
bn2
graphical diff
Scores
equivalent ?
distances

identical BNs

In [5]:
bn1=gum.fastBN("A->B->C->D->E<-A->F")
bn2=bn1
cmp=gcm.GraphicalBNComparator(bn1,bn2)
kl=gum.ExactBNdistance(bn1,bn2) # bruteForce is possible car the BNs are small
gnb.sideBySide(bn1,bn2,gnb.getBNDiff(bn1,bn2),dict2html(cmp.scores(),cmp.hamming()),cmp.equivalentBNs(),dict2html(kl.compute()),
              captions=['bn1','bn2','graphical diff','Scores','equivalent ?','distances'])
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
G A A B B A->B E E A->E F F A->F C C B->C D D C->D D->E
count :{'tp': 6, 'tn': 24, 'fp': 0, 'fn': 0}
recall :1.0
precision :1.0
fscore :1.0
dist2opt :0.0

hamming :0
structural hamming:0
OK
klPQ :0.0
errorPQ :0
klQP :0.0
errorQP :0
hellinger :0.0
bhattacharya :-0.0
jensen-shannon :0.0
bn1
bn2
graphical diff
Scores
equivalent ?
distances

In the notebook 15-DirichletPrior, you can find an interresting discussion on how can change those scores and distance.

In [ ]: