1# (c) Copyright by Pierre-Henri Wuillemin, UPMC, 2017 2# (pierre-henri.wuillemin@lip6.fr) 3 4# Permission to use, copy, modify, and distribute this 5# software and its documentation for any purpose and 6# without fee or royalty is hereby granted, provided 7# that the above copyright notice appear in all copies 8# and that both that copyright notice and this permission 9# notice appear in supporting documentation or portions 10# thereof, including modifications, that you make. 11 12# THE AUTHOR P.H. WUILLEMIN DISCLAIMS ALL WARRANTIES 13# WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED 14# WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT 15# SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT 16# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER 17# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 18# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE 20# OR PERFORMANCE OF THIS SOFTWARE! 21import math 22 23import matplotlib.pyplot as plt 24import numpy as np 25import pyAgrum as gum 26 27 28def _stats(pot): 29 mu = 0.0 30 mu2 = 0.0 31 v = pot.variable(0) 32 for i, p in enumerate(pot.tolist()): 33 x = v.numerical(i) 34 mu += p * x 35 mu2 += p * x * x 36 return (mu, math.sqrt(mu2 - mu * mu)) 37 38 39def _getTitleHisto(p, showMuSigma=True): 40 var = p.variable(0) 41 if var.varType() == 1 or not showMuSigma: # Labelized 42 return var.name() 43 44 (mu, std) = _stats(p) 45 return f"${var.name()}$\n$\\mu={mu:.2f}$; $\\sigma={std:.2f}$" 46 47 48def __limits(p): 49 """return vals and labs to show in the histograme 50 51 Parameters 52 ---------- 53 p : gum.Potential 54 the marginal to analyze 55 """ 56 var = p.variable(0) 57 la = [var.label(int(i)) for i in np.arange(var.domainSize())] 58 v = p.tolist() 59 nzmin = None 60 nzmax = None 61 l = len(v) - 1 62 for i in range(l + 1): 63 if v[i] != 0: 64 if nzmin is None: 65 if i > 0: 66 nzmin = i - 1 67 else: 68 nzmin = -1 69 if v[l - i] != 0: 70 if nzmax is None: 71 if i > 0: 72 nzmax = l - i + 1 73 else: 74 nzmax = -1 75 76 mi = 0 if nzmin in [-1, None] else nzmin 77 ma = l if nzmax in [-1, None] else nzmax 78 79 res = range(mi, ma + 1) 80 lres = la[mi:ma + 1] 81 if nzmin not in [-1, None]: 82 lres[0] = "..." 83 if nzmax not in [-1, None]: 84 lres[-1] = "..." 85 86 return res, [v[i] for i in res], lres 87 88 89def _getProbaLine(p, scale=1.0, txtcolor="black"): 90 """ 91 compute the representation of a matplotlib.fill_between for a mono-dim Potential 92 93 Parameters 94 ---------- 95 p : pyAgrum.Potential 96 the mono-dimensional Potential 97 scale : float 98 the scale 99 txtcolor : str 100 color for text 101 102 Returns 103 ------- 104 matplotlib.Figure 105 a matplotlib figure for a Potential p. 106 """ 107 108 var = p.variable(0) 109 if gum.config['notebook', 'histogram_mode'] == "compact": 110 ra, v, lv = __limits(p) 111 else: 112 lv = [var.label(int(i)) for i in np.arange(var.domainSize())] 113 v = p.tolist() 114 ra = range(len(v)) 115 116 fig = plt.figure() 117 fig.set_figwidth(min(scale * 6, scale * len(v) / 4.0)) 118 fig.set_figheight(scale * 2) 119 120 ax = fig.add_subplot(111) 121 ax.fill_between(ra, v, color=gum.config['notebook', 'histogram_color']) 122 123 ax.set_ylim(bottom=0, top=1.05 * p.max()) 124 # ax.set_xticks(ra) 125 # ax.set_xticklabels(lv, color=txtcolor) 126 ax.set_title(_getTitleHisto(p, True), color=txtcolor) 127 128 ax.get_xaxis().grid(True) 129 ax.get_yaxis().grid(True) 130 ax.margins(0) 131 132 ax.set_facecolor('w') 133 134 return fig 135 136 137def _getProbaV(p, scale=1.0, util=None, txtcolor="black"): 138 """ 139 compute the representation of a vertical histogram for a mono-dim Potential 140 141 Parameters 142 ---------- 143 p : pyAgrum.Potential 144 the mono-dimensional Potential 145 util : pyAgrum.Potential 146 an (optional) secondary Potential (values in labels) 147 txtcolor : str 148 color for text 149 150 Returns 151 ------- 152 matplotlib.Figure 153 a matplotlib histogram for a Potential p. 154 155 """ 156 if gum.config['notebook', 'histogram_mode'] == "compact": 157 ra, v, lv = __limits(p) 158 else: 159 var = p.variable(0) 160 if util is not None: 161 lu = util.toarray() 162 coef = -1 if gum.config["influenceDiagram", "utility_show_loss"] == "True" else 1 163 fmt = "." + gum.config["influenceDiagram", "utility_visible_digits"] + "f" 164 lv = [f"{var.label(int(i))} [{coef * lu[i]:{fmt}}]" 165 for i in np.arange(var.domainSize())] 166 else: 167 lv = [var.label(int(i)) for i in np.arange(var.domainSize())] 168 v = p.tolist() 169 ra = range(len(v)) 170 171 fig = plt.figure() 172 fig.set_figwidth(scale * len(v) / 4.0) 173 fig.set_figheight(scale * 2) 174 175 ax = fig.add_subplot(111) 176 177 bars = ax.bar(ra, v, 178 align='center', 179 color=gum.config['notebook', 'histogram_color']) 180 ma = p.max() 181 182 for b in bars: 183 if b.get_height() != 0: 184 txt = txt = f"{b.get_height():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}" 185 ax.text(b.get_x(), ma, txt, ha='left', va='top', rotation='vertical') 186 187 ax.set_ylim(bottom=0, top=p.max()) 188 ax.set_xticks(ra) 189 ax.set_xticklabels(lv, rotation='vertical', color=txtcolor) 190 # if utility, we do not show the mean/sigma of the proba. 191 ax.set_title(_getTitleHisto(p, util is None), color=txtcolor) 192 ax.get_yaxis().grid(True) 193 ax.margins(0) 194 ax.set_facecolor('w') 195 196 return fig 197 198 199def _getProbaH(p, scale=1.0, util=None, txtcolor="black"): 200 """ 201 compute the representation of an horizontal histogram for a mono-dim Potential 202 203 Parameters 204 ---------- 205 p : pyAgrum.Potential 206 the mono-dimensional Potential 207 util : pyAgrum.Potential 208 an (optional) secondary Potential (values in labels) 209 txtcolor : str 210 color for text 211 212 Returns 213 ------- 214 matplotlib.Figure 215 a matplotlib histogram for a Potential p. 216 """ 217 var = p.variable(0) 218 ra = np.arange(var.domainSize()) 219 220 ra_reverse = np.arange(var.domainSize() - 1, -1, -1) # reverse order 221 222 if util is not None: 223 lu = util.toarray() 224 fmt = "." + gum.config["influenceDiagram", "utility_visible_digits"] + "f" 225 226 if gum.config["influenceDiagram", "utility_show_loss"] == "True": 227 vx = [f"{var.label(int(i))} [{-lu[i] if lu[i] != 0 else 0:{fmt}}]" for i in ra_reverse] 228 else: 229 vx = [f"{var.label(int(i))} [{lu[i]:{fmt}}]" for i in ra_reverse] 230 else: 231 vx = [var.label(int(i)) for i in ra_reverse] 232 233 fig = plt.figure() 234 fig.set_figheight(scale * var.domainSize() / 4.0) 235 fig.set_figwidth(scale * 2) 236 237 ax = fig.add_subplot(111) 238 ax.set_facecolor('white') 239 240 vals = p.tolist() 241 vals.reverse() 242 bars = ax.barh(ra, vals, 243 align='center', 244 color=gum.config['notebook', 'histogram_color']) 245 246 for b in bars: 247 if b.get_width() != 0: 248 txt = f"{b.get_width():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}" 249 ax.text(1, b.get_y(), txt, ha='right', va='bottom') 250 251 ax.set_xlim(0, 1) 252 ax.set_yticks(np.arange(var.domainSize())) 253 ax.set_yticklabels(vx, color=txtcolor) 254 ax.set_xticklabels([]) 255 # ax.set_xlabel('Probability') 256 # if utility, we do not show the mean/sigma of the proba. 257 ax.set_title(_getTitleHisto(p, util is None), color=txtcolor) 258 ax.get_xaxis().grid(True) 259 ax.margins(0) 260 261 return fig 262 263 264def proba2histo(p, scale=1.0, util=None, txtcolor="Black"): 265 """ 266 compute the representation of an histogram for a mono-dim Potential 267 268 Parameters 269 ---------- 270 p : pyAgrum.Potential 271 the mono-dimensional Potential 272 util : pyAgrum.Potential 273 an (optional) secondary Potential (values in labels) 274 txtcolor : str 275 color for text 276 277 Returns 278 ------- 279 matplotlib.Figure 280 a matplotlib histogram for a Potential p. 281 """ 282 if util is not None: 283 return _getProbaH(p, scale, util=util, txtcolor=txtcolor) 284 285 if p.variable(0).domainSize() > int(gum.config['notebook', 'histogram_line_threshold']): 286 return _getProbaLine(p, scale, txtcolor=txtcolor) 287 288 if p.variable(0).domainSize() > int(gum.config['notebook', 'histogram_horizontal_threshold']): 289 return _getProbaV(p, scale, txtcolor=txtcolor) 290 291 return _getProbaH(p, scale, util=util, txtcolor=txtcolor) 292 293 294def saveFigProba(p, filename, util=None, bgcol=None, txtcolor="Black"): 295 """ 296 save a figure which is the representation of an histogram for a mono-dim Potential 297 298 Parameters 299 ---------- 300 p : pyAgrum.Potential 301 the mono-dimensional Potential 302 util : pyAgrum.Potential 303 an (optional) secondary Potential (values in labels) 304 bgcolor: str 305 color for background (transparent if None) 306 txtcolor : str 307 color for text 308 """ 309 fig = proba2histo(p, util=util, txtcolor=txtcolor) 310 311 if bgcol is None: 312 fc = gum.config["notebook", "figure_facecolor"] 313 else: 314 fc = bgcol 315 316 fig.savefig(filename, bbox_inches='tight', transparent=False, facecolor=fc, 317 pad_inches=0.05, dpi=fig.dpi, format=gum.config["notebook", "graph_format"]) 318 plt.close(fig) 319 320 321def probaMinMaxH(pmin, pmax, scale=1.0, txtcolor="black"): 322 """ 323 compute the representation of an horizontal histogram for a mono-dim Potential 324 325 Parameters 326 ---------- 327 pmin,pmax : pyAgrum.Potential 328 two mono-dimensional Potential 329 txtcolor : str 330 color for text 331 332 Returns 333 ------- 334 matplotlib.Figure 335 a matplotlib histogram for a bi-Potential pmin,pmax. 336 """ 337 var = pmin.variable(0) 338 ra = np.arange(var.domainSize()) 339 340 ra_reverse = np.arange(var.domainSize() - 1, -1, -1) # reverse order 341 vx = [var.label(int(i)) for i in ra_reverse] 342 343 fig = plt.figure() 344 fig.set_figheight(scale * var.domainSize() / 4.0) 345 fig.set_figwidth(scale * 2) 346 347 ax = fig.add_subplot(111) 348 ax.set_facecolor('white') 349 350 vmin = pmin.tolist() 351 vmin.reverse() 352 vmax = pmax.tolist() 353 vmax.reverse() 354 barsmax = ax.barh(ra, vmax, 355 align='center', 356 color="#BBFFAA") 357 barsmin = ax.barh(ra, vmin, 358 align='center', 359 color=gum.config['notebook', 'histogram_color']) 360 361 for b in barsmax: 362 txt = f"{b.get_width():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}" 363 ax.text(1, b.get_y(), txt, ha='right', va='bottom') 364 for b in barsmin: 365 txt = f"{b.get_width():.{gum.config['notebook', 'histogram_horizontal_visible_digits']}}" 366 ax.text(0, b.get_y(), txt, ha='left', va='bottom') 367 368 ax.set_xlim(0, 1) 369 ax.set_yticks(np.arange(var.domainSize())) 370 ax.set_yticklabels(vx, color=txtcolor) 371 ax.set_xticklabels([]) 372 ax.set_title(pmin.variable(0).name(), color=txtcolor) 373 ax.get_xaxis().grid(True) 374 ax.margins(0) 375 376 return fig 377 378 379def saveFigProbaMinMax(pmin, pmax, filename, bgcol=None, txtcolor="Black"): 380 """ 381 save a figure which is the representation of an histogram for a bi-Potential (min,max) 382 383 Parameters 384 ---------- 385 pmin : pyAgrum.Potential 386 the mono-dimensional Potential for min values 387 pmax : pyAgrum.Potential 388 the mono-dimensional Potential for max value 389 bgcolor: str 390 color for background (transparent if None) 391 txtcolor : str 392 color for text 393 """ 394 fig = probaMinMaxH(pmin, pmax, txtcolor=txtcolor) 395 396 if bgcol is None: 397 fc = gum.config["notebook", "figure_facecolor"] 398 else: 399 fc = bgcol 400 401 fig.savefig(filename, bbox_inches='tight', transparent=False, facecolor=fc, 402 pad_inches=0.05, dpi=fig.dpi, format=gum.config["notebook", "graph_format"]) 403 plt.close(fig) 404