pyAgrum-nightly 2.2.1.9.dev202510271761405498__cp310-abi3-win_amd64.whl → 2.3.0.9.dev202510281761586496__cp310-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyAgrum-nightly might be problematic. Click here for more details.
- pyagrum/_pyagrum.pyd +0 -0
- pyagrum/common.py +1 -1
- pyagrum/config.py +1 -0
- pyagrum/explain/_ComputationCausal.py +75 -0
- pyagrum/explain/_ComputationConditional.py +48 -0
- pyagrum/explain/_ComputationMarginal.py +48 -0
- pyagrum/explain/_CustomShapleyCache.py +110 -0
- pyagrum/explain/_Explainer.py +176 -0
- pyagrum/explain/_Explanation.py +70 -0
- pyagrum/explain/_FIFOCache.py +54 -0
- pyagrum/explain/_ShallCausalValues.py +204 -0
- pyagrum/explain/_ShallConditionalValues.py +155 -0
- pyagrum/explain/_ShallMarginalValues.py +155 -0
- pyagrum/explain/_ShallValues.py +296 -0
- pyagrum/explain/_ShapCausalValues.py +208 -0
- pyagrum/explain/_ShapConditionalValues.py +126 -0
- pyagrum/explain/_ShapMarginalValues.py +191 -0
- pyagrum/explain/_ShapleyValues.py +298 -0
- pyagrum/explain/__init__.py +81 -0
- pyagrum/explain/_explGeneralizedMarkovBlanket.py +152 -0
- pyagrum/explain/_explIndependenceListForPairs.py +146 -0
- pyagrum/explain/_explInformationGraph.py +264 -0
- pyagrum/explain/notebook/__init__.py +54 -0
- pyagrum/explain/notebook/_bar.py +142 -0
- pyagrum/explain/notebook/_beeswarm.py +174 -0
- pyagrum/explain/notebook/_showShapValues.py +97 -0
- pyagrum/explain/notebook/_waterfall.py +220 -0
- pyagrum/explain/shapley.py +225 -0
- pyagrum/lib/explain.py +11 -490
- pyagrum/pyagrum.py +17 -10
- {pyagrum_nightly-2.2.1.9.dev202510271761405498.dist-info → pyagrum_nightly-2.3.0.9.dev202510281761586496.dist-info}/METADATA +1 -1
- {pyagrum_nightly-2.2.1.9.dev202510271761405498.dist-info → pyagrum_nightly-2.3.0.9.dev202510281761586496.dist-info}/RECORD +36 -12
- pyagrum/lib/shapley.py +0 -661
- {pyagrum_nightly-2.2.1.9.dev202510271761405498.dist-info → pyagrum_nightly-2.3.0.9.dev202510281761586496.dist-info}/LICENSE.md +0 -0
- {pyagrum_nightly-2.2.1.9.dev202510271761405498.dist-info → pyagrum_nightly-2.3.0.9.dev202510281761586496.dist-info}/LICENSES/LGPL-3.0-or-later.txt +0 -0
- {pyagrum_nightly-2.2.1.9.dev202510271761405498.dist-info → pyagrum_nightly-2.3.0.9.dev202510281761586496.dist-info}/LICENSES/MIT.txt +0 -0
- {pyagrum_nightly-2.2.1.9.dev202510271761405498.dist-info → pyagrum_nightly-2.3.0.9.dev202510281761586496.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# This file is part of the aGrUM/pyAgrum library. #
|
|
3
|
+
# #
|
|
4
|
+
# Copyright (c) 2005-2025 by #
|
|
5
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
6
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
7
|
+
# #
|
|
8
|
+
# The aGrUM/pyAgrum library is free software; you can redistribute it #
|
|
9
|
+
# and/or modify it under the terms of either : #
|
|
10
|
+
# #
|
|
11
|
+
# - the GNU Lesser General Public License as published by #
|
|
12
|
+
# the Free Software Foundation, either version 3 of the License, #
|
|
13
|
+
# or (at your option) any later version, #
|
|
14
|
+
# - the MIT license (MIT), #
|
|
15
|
+
# - or both in dual license, as here. #
|
|
16
|
+
# #
|
|
17
|
+
# (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
|
|
18
|
+
# #
|
|
19
|
+
# This aGrUM/pyAgrum library is distributed in the hope that it will be #
|
|
20
|
+
# useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
|
21
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
|
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
|
23
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
|
24
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
|
25
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
|
26
|
+
# OTHER DEALINGS IN THE SOFTWARE. #
|
|
27
|
+
# #
|
|
28
|
+
# See LICENCES for more details. #
|
|
29
|
+
# #
|
|
30
|
+
# SPDX-FileCopyrightText: Copyright 2005-2025 #
|
|
31
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
32
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
33
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
|
|
34
|
+
# #
|
|
35
|
+
# Contact : info_at_agrum_dot_org #
|
|
36
|
+
# homepage : http://agrum.gitlab.io #
|
|
37
|
+
# gitlab : https://gitlab.com/agrumery/agrum #
|
|
38
|
+
# #
|
|
39
|
+
############################################################################
|
|
40
|
+
|
|
41
|
+
import pyagrum.lib._colors as gumcols
|
|
42
|
+
import matplotlib.pyplot as plt
|
|
43
|
+
import pydot as dot
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _buildMB(model, x: int, k: int = 1):
|
|
47
|
+
"""
|
|
48
|
+
Build the nodes and arcs of Markov Blanket (of order k) of node x
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
model: pyagrum.DirectedGraphicalModel
|
|
53
|
+
i.e. a class with methods parents, children, variable(i), idFromName(name)
|
|
54
|
+
x : int
|
|
55
|
+
the nodeId of the node for the Markov blanket
|
|
56
|
+
k: int
|
|
57
|
+
the order of the Markov blanket. If k=2, build the MarkovBlanket(MarkovBlanket())
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
(nodes,arcs,depth) : the set of nodes, the set of arcs of the Markov Blanket and a dict[Str,int] that gives the MB-depth of each node in nodes.
|
|
62
|
+
"""
|
|
63
|
+
nodes = {x}
|
|
64
|
+
arcs = set()
|
|
65
|
+
depth = dict()
|
|
66
|
+
|
|
67
|
+
def _internal_build_markov_blanket(bn, x: int, k: int):
|
|
68
|
+
nodes.add(x)
|
|
69
|
+
depth[x] = k
|
|
70
|
+
if k == 0:
|
|
71
|
+
return
|
|
72
|
+
for y in bn.parents(x):
|
|
73
|
+
visit(y, k - 1)
|
|
74
|
+
arcs.add((y, x))
|
|
75
|
+
for y in bn.children(x):
|
|
76
|
+
visit(y, k - 1)
|
|
77
|
+
arcs.add((x, y))
|
|
78
|
+
for z in bn.parents(y):
|
|
79
|
+
visit(z, k - 1)
|
|
80
|
+
arcs.add((z, y))
|
|
81
|
+
|
|
82
|
+
def visit(x, k):
|
|
83
|
+
if x in nodes and depth[x] >= k:
|
|
84
|
+
return
|
|
85
|
+
_internal_build_markov_blanket(model, x, k)
|
|
86
|
+
|
|
87
|
+
_internal_build_markov_blanket(model, x, k)
|
|
88
|
+
return nodes, arcs, depth
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def generalizedMarkovBlanket(bn, var: int | str, k: int = 1, cmapNode=None):
|
|
92
|
+
"""
|
|
93
|
+
Build a pydot.Dot representation of the nested Markov Blankets (of order k) of node x
|
|
94
|
+
|
|
95
|
+
Warnings
|
|
96
|
+
--------
|
|
97
|
+
It is assumed that k<=8. If not, every thing is fine except that the colorscale will change in order to accept more colors.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
bn: pyagrum.DirectedGraphicalModel
|
|
102
|
+
i.e. a class with methods parents, children, variable(i), idFromName(name)
|
|
103
|
+
var : str|int
|
|
104
|
+
the name or nodeId of the node for the Markov blanket
|
|
105
|
+
k: int
|
|
106
|
+
the order of the Markov blanket. If k=1, build the MarkovBlanket(MarkovBlanket())
|
|
107
|
+
cmap: maplotlib.ColorMap
|
|
108
|
+
the colormap used (if not, inferno is used)
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
pydotplus.Dot object
|
|
113
|
+
"""
|
|
114
|
+
if cmapNode is None:
|
|
115
|
+
cmapNode = plt.get_cmap("inferno") # gum.config["notebook", "default_arc_cmap"])
|
|
116
|
+
|
|
117
|
+
maxcols = max(
|
|
118
|
+
8, k
|
|
119
|
+
) # It is assumed that k<=8. If not, every thing is fine except that the colorscale will change in order to accept more colors.
|
|
120
|
+
|
|
121
|
+
mb = dot.Dot(f"MB({var},{k}", graph_type="digraph", bgcolor="transparent")
|
|
122
|
+
|
|
123
|
+
if isinstance(var, str):
|
|
124
|
+
nx = bn.idFromName(var)
|
|
125
|
+
else:
|
|
126
|
+
nx = var
|
|
127
|
+
nodes, arcs, visited = _buildMB(bn, nx, k)
|
|
128
|
+
names = dict()
|
|
129
|
+
|
|
130
|
+
for n in nodes:
|
|
131
|
+
protected_name = f'"{bn.variable(n).name()}"'
|
|
132
|
+
pnode = dot.Node(protected_name, style="filled")
|
|
133
|
+
if n == var:
|
|
134
|
+
bgcol = "#99FF99"
|
|
135
|
+
fgcol = "black"
|
|
136
|
+
else:
|
|
137
|
+
bgcol = gumcols.proba2bgcolor(1 - (k - visited[n]) / maxcols, cmapNode)
|
|
138
|
+
fgcol = gumcols.proba2fgcolor(1 - (k - visited[n]) / maxcols, cmapNode)
|
|
139
|
+
pnode.set_fillcolor(bgcol)
|
|
140
|
+
pnode.set_fontcolor(fgcol)
|
|
141
|
+
mb.add_node(pnode)
|
|
142
|
+
names[n] = protected_name
|
|
143
|
+
for n in nodes:
|
|
144
|
+
for u in bn.parents(n).intersection(nodes):
|
|
145
|
+
edge = dot.Edge(names[u], names[n])
|
|
146
|
+
if (u, n) in arcs:
|
|
147
|
+
edge.set_color("black")
|
|
148
|
+
else:
|
|
149
|
+
edge.set_color("#DDDDDD")
|
|
150
|
+
mb.add_edge(edge)
|
|
151
|
+
|
|
152
|
+
return mb
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# This file is part of the aGrUM/pyAgrum library. #
|
|
3
|
+
# #
|
|
4
|
+
# Copyright (c) 2005-2025 by #
|
|
5
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
6
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
7
|
+
# #
|
|
8
|
+
# The aGrUM/pyAgrum library is free software; you can redistribute it #
|
|
9
|
+
# and/or modify it under the terms of either : #
|
|
10
|
+
# #
|
|
11
|
+
# - the GNU Lesser General Public License as published by #
|
|
12
|
+
# the Free Software Foundation, either version 3 of the License, #
|
|
13
|
+
# or (at your option) any later version, #
|
|
14
|
+
# - the MIT license (MIT), #
|
|
15
|
+
# - or both in dual license, as here. #
|
|
16
|
+
# #
|
|
17
|
+
# (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
|
|
18
|
+
# #
|
|
19
|
+
# This aGrUM/pyAgrum library is distributed in the hope that it will be #
|
|
20
|
+
# useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
|
21
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
|
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
|
23
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
|
24
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
|
25
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
|
26
|
+
# OTHER DEALINGS IN THE SOFTWARE. #
|
|
27
|
+
# #
|
|
28
|
+
# See LICENCES for more details. #
|
|
29
|
+
# #
|
|
30
|
+
# SPDX-FileCopyrightText: Copyright 2005-2025 #
|
|
31
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
32
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
33
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
|
|
34
|
+
# #
|
|
35
|
+
# Contact : info_at_agrum_dot_org #
|
|
36
|
+
# homepage : http://agrum.gitlab.io #
|
|
37
|
+
# gitlab : https://gitlab.com/agrumery/agrum #
|
|
38
|
+
# #
|
|
39
|
+
############################################################################
|
|
40
|
+
|
|
41
|
+
import pyagrum as gum
|
|
42
|
+
import pylab
|
|
43
|
+
import matplotlib as mpl
|
|
44
|
+
import itertools
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _independenceListForPairs(bn, target=None):
|
|
48
|
+
"""
|
|
49
|
+
returns a list of triples `(i,j,k)` for each non arc `(i,j)` such that `i` is independent of `j` given `k`.
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
bn: gum.BayesNet
|
|
54
|
+
the Bayesian Network
|
|
55
|
+
|
|
56
|
+
target: (optional) str or int
|
|
57
|
+
the name or id of the target variable. If a target is given, only the independence given a subset of the markov blanket of the target are tested.
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
List[(str,str,List[str])]
|
|
62
|
+
A list of independence found in the structure of BN.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def powerset(iterable):
|
|
66
|
+
xs = list(iterable)
|
|
67
|
+
# note we return an iterator rather than a list
|
|
68
|
+
return itertools.chain.from_iterable(itertools.combinations(xs, n) for n in range(len(xs) + 1))
|
|
69
|
+
|
|
70
|
+
# testing every d-separation
|
|
71
|
+
l = []
|
|
72
|
+
nams = sorted(bn.names())
|
|
73
|
+
if target is None:
|
|
74
|
+
firstnams = nams.copy()
|
|
75
|
+
indepnodes = bn.names()
|
|
76
|
+
else:
|
|
77
|
+
indepnodes = {bn.variable(i).name() for i in gum.MarkovBlanket(bn, target).nodes()}
|
|
78
|
+
if isinstance(target, str):
|
|
79
|
+
firstnams = [target]
|
|
80
|
+
else:
|
|
81
|
+
firstnams = [bn.variable(target).name()]
|
|
82
|
+
|
|
83
|
+
for i in firstnams:
|
|
84
|
+
nams.remove(i)
|
|
85
|
+
for j in nams:
|
|
86
|
+
if not (bn.existsArc(i, j) or bn.existsArc(j, i)):
|
|
87
|
+
for k in powerset(sorted(indepnodes - {i, j})):
|
|
88
|
+
if bn.isIndependent(i, j, k):
|
|
89
|
+
l.append((i, j, tuple(k)))
|
|
90
|
+
break
|
|
91
|
+
return l
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def independenceListForPairs(bn, filename, target=None, plot=True, alphabetic=False):
|
|
95
|
+
"""
|
|
96
|
+
get the p-values of the chi2 test of a (as simple as possible) independence proposition for every non arc.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
bn : gum.BayesNet
|
|
101
|
+
the Bayesian network
|
|
102
|
+
|
|
103
|
+
filename : str
|
|
104
|
+
the name of the csv database
|
|
105
|
+
|
|
106
|
+
alphabetic : bool
|
|
107
|
+
if True, the list is alphabetically sorted else it is sorted by the p-value
|
|
108
|
+
|
|
109
|
+
target: (optional) str or int
|
|
110
|
+
the name or id of the target variable
|
|
111
|
+
|
|
112
|
+
plot : bool
|
|
113
|
+
if True, plot the result
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
the list
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
learner = gum.BNLearner(filename, bn)
|
|
121
|
+
vals = {}
|
|
122
|
+
for indep in _independenceListForPairs(bn, target):
|
|
123
|
+
vals[indep] = learner.chi2(*indep)[1]
|
|
124
|
+
|
|
125
|
+
if plot:
|
|
126
|
+
plotvals = dict()
|
|
127
|
+
for indep in vals:
|
|
128
|
+
key = "$" + indep[0] + " \\perp " + indep[1]
|
|
129
|
+
if len(indep[2]) > 0:
|
|
130
|
+
key += " \\mid " + ",".join(indep[2])
|
|
131
|
+
key += "$"
|
|
132
|
+
plotvals[key] = vals[indep]
|
|
133
|
+
|
|
134
|
+
if not alphabetic:
|
|
135
|
+
sortedkeys = sorted(plotvals, key=plotvals.__getitem__, reverse=False)
|
|
136
|
+
else:
|
|
137
|
+
sortedkeys = list(plotvals.keys())
|
|
138
|
+
|
|
139
|
+
fig = pylab.figure(figsize=(10, 1 + 0.25 * len(plotvals)))
|
|
140
|
+
ax = fig.add_subplot(1, 1, 1)
|
|
141
|
+
ax.plot([plotvals[k] for k in sortedkeys], sortedkeys, "o")
|
|
142
|
+
ax.grid(True)
|
|
143
|
+
ax.vlines(x=0.05, ymin=-0.5, ymax=len(vals) - 0.5, colors="purple")
|
|
144
|
+
ax.add_patch(mpl.patches.Rectangle((0, -0.5), 0.05, len(vals), color="yellow"))
|
|
145
|
+
|
|
146
|
+
return vals
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# This file is part of the aGrUM/pyAgrum library. #
|
|
3
|
+
# #
|
|
4
|
+
# Copyright (c) 2005-2025 by #
|
|
5
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
6
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
7
|
+
# #
|
|
8
|
+
# The aGrUM/pyAgrum library is free software; you can redistribute it #
|
|
9
|
+
# and/or modify it under the terms of either : #
|
|
10
|
+
# #
|
|
11
|
+
# - the GNU Lesser General Public License as published by #
|
|
12
|
+
# the Free Software Foundation, either version 3 of the License, #
|
|
13
|
+
# or (at your option) any later version, #
|
|
14
|
+
# - the MIT license (MIT), #
|
|
15
|
+
# - or both in dual license, as here. #
|
|
16
|
+
# #
|
|
17
|
+
# (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
|
|
18
|
+
# #
|
|
19
|
+
# This aGrUM/pyAgrum library is distributed in the hope that it will be #
|
|
20
|
+
# useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
|
21
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
|
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
|
23
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
|
24
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
|
25
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
|
26
|
+
# OTHER DEALINGS IN THE SOFTWARE. #
|
|
27
|
+
# #
|
|
28
|
+
# See LICENCES for more details. #
|
|
29
|
+
# #
|
|
30
|
+
# SPDX-FileCopyrightText: Copyright 2005-2025 #
|
|
31
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
32
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
33
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
|
|
34
|
+
# #
|
|
35
|
+
# Contact : info_at_agrum_dot_org #
|
|
36
|
+
# homepage : http://agrum.gitlab.io #
|
|
37
|
+
# gitlab : https://gitlab.com/agrumery/agrum #
|
|
38
|
+
# #
|
|
39
|
+
############################################################################
|
|
40
|
+
|
|
41
|
+
import pyagrum as gum
|
|
42
|
+
import pyagrum.lib._colors as gumcols
|
|
43
|
+
from pyagrum.lib.bn2graph import BN2dot
|
|
44
|
+
|
|
45
|
+
# Matplotlib
|
|
46
|
+
import matplotlib as mpl
|
|
47
|
+
|
|
48
|
+
# GL
|
|
49
|
+
import warnings
|
|
50
|
+
|
|
51
|
+
_cdict = {
|
|
52
|
+
"red": ((0.0, 0.1, 0.3), (1.0, 0.6, 1.0)),
|
|
53
|
+
"green": ((0.0, 0.0, 0.0), (1.0, 0.6, 0.8)),
|
|
54
|
+
"blue": ((0.0, 0.0, 0.0), (1.0, 1, 0.8)),
|
|
55
|
+
}
|
|
56
|
+
_INFOcmap = mpl.colors.LinearSegmentedColormap("my_colormap", _cdict, 256)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _normalizeVals(vals, hilightExtrema=False):
|
|
60
|
+
"""
|
|
61
|
+
normalisation if vals is not a proba (max>1)
|
|
62
|
+
"""
|
|
63
|
+
ma = float(max(vals.values()))
|
|
64
|
+
mi = float(min(vals.values()))
|
|
65
|
+
if ma == mi:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
if not hilightExtrema:
|
|
69
|
+
vmi = 0.01
|
|
70
|
+
vma = 0.99
|
|
71
|
+
else:
|
|
72
|
+
vmi = 0
|
|
73
|
+
vma = 1
|
|
74
|
+
|
|
75
|
+
res = {name: vmi + (val - mi) * (vma - vmi) / (ma - mi) for name, val in vals.items()}
|
|
76
|
+
return res
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def getInformationGraph(bn, evs=None, size=None, cmap=_INFOcmap, withMinMax=False):
|
|
80
|
+
"""
|
|
81
|
+
Create a dot representation of the information graph for this BN
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
bn: gum.BayesNet
|
|
86
|
+
the BN
|
|
87
|
+
evs : Dict[str,str|int|List[float]]
|
|
88
|
+
map of evidence
|
|
89
|
+
size: str|int
|
|
90
|
+
size of the graph
|
|
91
|
+
cmap: matplotlib.colors.Colormap
|
|
92
|
+
color map
|
|
93
|
+
withMinMax: bool
|
|
94
|
+
min and max in the return values ?
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
dot.Dot | Tuple[dot.Dot,float,float,float,float]
|
|
99
|
+
graph as a dot representation and if asked, min_information_value, max_information_value, min_mutual_information_value, max_mutual_information_value
|
|
100
|
+
"""
|
|
101
|
+
if size is None:
|
|
102
|
+
size = gum.config["notebook", "default_graph_size"]
|
|
103
|
+
|
|
104
|
+
if evs is None:
|
|
105
|
+
evs = {}
|
|
106
|
+
|
|
107
|
+
ie = gum.LazyPropagation(bn)
|
|
108
|
+
ie.setEvidence(evs)
|
|
109
|
+
ie.makeInference()
|
|
110
|
+
|
|
111
|
+
idEvs = ie.hardEvidenceNodes() | ie.softEvidenceNodes()
|
|
112
|
+
|
|
113
|
+
nodevals = dict()
|
|
114
|
+
for n in bn.nodes():
|
|
115
|
+
if n not in idEvs:
|
|
116
|
+
v = ie.H(n)
|
|
117
|
+
if v != v: # is NaN
|
|
118
|
+
warnings.warn(f"For {bn.variable(n).name()}, entropy is NaN.")
|
|
119
|
+
v = 0
|
|
120
|
+
nodevals[bn.variable(n).name()] = v
|
|
121
|
+
|
|
122
|
+
arcvals = dict()
|
|
123
|
+
for x, y in bn.arcs():
|
|
124
|
+
v = ie.jointMutualInformation({x, y})
|
|
125
|
+
if v != v: # is NaN
|
|
126
|
+
warnings.warn(f"For {bn.variable(x).name()}->{bn.variable(y).name()}, mutual information is Nan.")
|
|
127
|
+
v = 0
|
|
128
|
+
arcvals[(x, y)] = v
|
|
129
|
+
|
|
130
|
+
gr = BN2dot(
|
|
131
|
+
bn,
|
|
132
|
+
size,
|
|
133
|
+
nodeColor=_normalizeVals(nodevals, hilightExtrema=False),
|
|
134
|
+
arcWidth=arcvals,
|
|
135
|
+
cmapNode=cmap,
|
|
136
|
+
cmapArc=cmap,
|
|
137
|
+
showMsg=nodevals,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if withMinMax:
|
|
141
|
+
mi_node = min(nodevals.values())
|
|
142
|
+
ma_node = max(nodevals.values())
|
|
143
|
+
mi_arc = min(arcvals.values())
|
|
144
|
+
ma_arc = max(arcvals.values())
|
|
145
|
+
return gr, mi_node, ma_node, mi_arc, ma_arc
|
|
146
|
+
else:
|
|
147
|
+
return gr
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _reprInformation(bn, evs=None, size=None, cmap=_INFOcmap, asString=False):
|
|
151
|
+
"""
|
|
152
|
+
repr a bn annotated with results from inference : Information and mutual information
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
bn: pyagrum.BayesNet
|
|
157
|
+
the model
|
|
158
|
+
evs: Dict[str|int,str|int|List[float]]
|
|
159
|
+
the observations
|
|
160
|
+
size: int|str
|
|
161
|
+
size of the rendered graph
|
|
162
|
+
cmap: matplotlib.colours.Colormap
|
|
163
|
+
the cmap
|
|
164
|
+
asString: bool
|
|
165
|
+
returns the string or display the HTML
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
str|None
|
|
170
|
+
return the HTML string or directly display it.
|
|
171
|
+
"""
|
|
172
|
+
import IPython.display
|
|
173
|
+
import IPython.core.pylabtools
|
|
174
|
+
from base64 import encodebytes
|
|
175
|
+
from matplotlib.backends.backend_agg import FigureCanvasAgg as fc
|
|
176
|
+
|
|
177
|
+
if size is None:
|
|
178
|
+
size = gum.config["notebook", "default_graph_size"]
|
|
179
|
+
|
|
180
|
+
if evs is None:
|
|
181
|
+
evs = {}
|
|
182
|
+
|
|
183
|
+
gr, mi, ma, _, _ = getInformationGraph(bn, evs, size, cmap, withMinMax=True)
|
|
184
|
+
gumcols.prepareDot(gr, size=size)
|
|
185
|
+
|
|
186
|
+
# dynamic member makes pylink unhappy
|
|
187
|
+
# pylint: disable=no-member
|
|
188
|
+
gsvg = IPython.display.SVG(gr.create_svg(encoding="utf-8"))
|
|
189
|
+
width = (
|
|
190
|
+
int(gsvg.data.split("width=")[1].split('"')[1].split("pt")[0]) / mpl.pyplot.rcParams["figure.dpi"]
|
|
191
|
+
) # pixel in inches
|
|
192
|
+
if width < 5:
|
|
193
|
+
width = 5
|
|
194
|
+
|
|
195
|
+
fig = mpl.figure.Figure(figsize=(width, 1))
|
|
196
|
+
fig.patch.set_alpha(0)
|
|
197
|
+
canvas = fc(fig)
|
|
198
|
+
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
|
|
199
|
+
norm = mpl.colors.Normalize(vmin=mi, vmax=ma)
|
|
200
|
+
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap=cmap, norm=norm, orientation="horizontal")
|
|
201
|
+
cb1.set_label("Entropy")
|
|
202
|
+
cb1.ax.text(mi, -2, f"{mi:.4f}", ha="left", va="top", color=gumcols.proba2bgcolor(0.01, cmap))
|
|
203
|
+
cb1.ax.text(ma, -2, f"{ma:.4f}", ha="right", va="top", color=gumcols.proba2bgcolor(0.99, cmap))
|
|
204
|
+
png = IPython.core.pylabtools.print_figure(canvas.figure, "png") # from IPython.core.pylabtools
|
|
205
|
+
png_legend = f"<img style='vertical-align:middle' src='data:image/png;base64,{encodebytes(png).decode('ascii')}'>"
|
|
206
|
+
|
|
207
|
+
sss = f"<div align='center'>{gsvg.data}<br/>{png_legend}</div>"
|
|
208
|
+
|
|
209
|
+
if asString:
|
|
210
|
+
return sss
|
|
211
|
+
|
|
212
|
+
return IPython.display.display(IPython.display.HTML(sss))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def getInformation(bn, evs=None, size=None, cmap=_INFOcmap) -> str:
|
|
216
|
+
"""
|
|
217
|
+
get a HTML string for a bn annotated with results from inference : entropy and mutual information
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
bn: pyagrum.BayesNet
|
|
222
|
+
the model
|
|
223
|
+
evs: Dict[str|int,str|int|List[float]]
|
|
224
|
+
the observations
|
|
225
|
+
size: int|str
|
|
226
|
+
size of the rendered graph
|
|
227
|
+
cmap: matplotlib.colours.Colormap
|
|
228
|
+
the cmap
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
str
|
|
233
|
+
return the HTML string
|
|
234
|
+
"""
|
|
235
|
+
if size is None:
|
|
236
|
+
size = gum.config["notebook", "default_graph_size"]
|
|
237
|
+
|
|
238
|
+
if evs is None:
|
|
239
|
+
evs = {}
|
|
240
|
+
|
|
241
|
+
return _reprInformation(bn, evs, size, cmap, asString=True)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def showInformation(bn, evs=None, size=None, cmap=_INFOcmap):
|
|
245
|
+
"""
|
|
246
|
+
diplay a bn annotated with results from inference : entropy and mutual information
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
bn: pyagrum.BayesNet
|
|
251
|
+
the model
|
|
252
|
+
evs: Dict[str|int,str|int|List[float]]
|
|
253
|
+
the observations
|
|
254
|
+
size: int|str
|
|
255
|
+
size of the rendered graph
|
|
256
|
+
cmap: matplotlib.colours.Colormap
|
|
257
|
+
the cmap
|
|
258
|
+
"""
|
|
259
|
+
if evs is None:
|
|
260
|
+
evs = {}
|
|
261
|
+
|
|
262
|
+
if size is None:
|
|
263
|
+
size = gum.config["notebook", "default_graph_size"]
|
|
264
|
+
return _reprInformation(bn, evs, size, cmap, asString=False)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# This file is part of the aGrUM/pyAgrum library. #
|
|
3
|
+
# #
|
|
4
|
+
# Copyright (c) 2005-2025 by #
|
|
5
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
6
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
7
|
+
# #
|
|
8
|
+
# The aGrUM/pyAgrum library is free software; you can redistribute it #
|
|
9
|
+
# and/or modify it under the terms of either : #
|
|
10
|
+
# #
|
|
11
|
+
# - the GNU Lesser General Public License as published by #
|
|
12
|
+
# the Free Software Foundation, either version 3 of the License, #
|
|
13
|
+
# or (at your option) any later version, #
|
|
14
|
+
# - the MIT license (MIT), #
|
|
15
|
+
# - or both in dual license, as here. #
|
|
16
|
+
# #
|
|
17
|
+
# (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html) #
|
|
18
|
+
# #
|
|
19
|
+
# This aGrUM/pyAgrum library is distributed in the hope that it will be #
|
|
20
|
+
# useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, #
|
|
21
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS #
|
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
|
|
23
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
|
|
24
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, #
|
|
25
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
|
|
26
|
+
# OTHER DEALINGS IN THE SOFTWARE. #
|
|
27
|
+
# #
|
|
28
|
+
# See LICENCES for more details. #
|
|
29
|
+
# #
|
|
30
|
+
# SPDX-FileCopyrightText: Copyright 2005-2025 #
|
|
31
|
+
# - Pierre-Henri WUILLEMIN(_at_LIP6) #
|
|
32
|
+
# - Christophe GONZALES(_at_AMU) #
|
|
33
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later OR MIT #
|
|
34
|
+
# #
|
|
35
|
+
# Contact : info_at_agrum_dot_org #
|
|
36
|
+
# homepage : http://agrum.gitlab.io #
|
|
37
|
+
# gitlab : https://gitlab.com/agrumery/agrum #
|
|
38
|
+
# #
|
|
39
|
+
############################################################################
|
|
40
|
+
|
|
41
|
+
__author__ = "Pierre-Henri Wuillemin"
|
|
42
|
+
__copyright__ = "(c) 2019-2025 PARIS"
|
|
43
|
+
|
|
44
|
+
from ._bar import bar
|
|
45
|
+
from ._beeswarm import beeswarm
|
|
46
|
+
from ._waterfall import waterfall
|
|
47
|
+
from ._showShapValues import showShapValues
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"bar",
|
|
51
|
+
"beeswarm",
|
|
52
|
+
"waterfall",
|
|
53
|
+
"showShapValues",
|
|
54
|
+
]
|