craceplot 0.1.0__py3-none-any.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.
Files changed (61) hide show
  1. craceplot/__init__.py +60 -0
  2. craceplot/_containers/__init__.py +0 -0
  3. craceplot/_containers/_core.py +1602 -0
  4. craceplot/_containers/_draw.py +104 -0
  5. craceplot/_inst/examples/acotsp/crace-3240078.stdout +838 -0
  6. craceplot/_inst/examples/acotsp/crace.stderr +8 -0
  7. craceplot/_inst/examples/acotsp/crace.test +216 -0
  8. craceplot/_inst/examples/acotsp/crace.train +622 -0
  9. craceplot/_inst/examples/cats200/crace-2814581.stdout +1665 -0
  10. craceplot/_inst/examples/cats200/crace.stderr +10 -0
  11. craceplot/_inst/examples/cats200/crace.test +1010 -0
  12. craceplot/_inst/examples/cats200/crace.train +655 -0
  13. craceplot/_plots/__init__.py +8 -0
  14. craceplot/_plots/_core.py +80 -0
  15. craceplot/_plots/parameters.py +1805 -0
  16. craceplot/_plots/quality.py +1406 -0
  17. craceplot/_scripts/__init__.py +4 -0
  18. craceplot/_scripts/_main.py +132 -0
  19. craceplot/_scripts/_utils.py +66 -0
  20. craceplot/_scripts/main +3 -0
  21. craceplot/_scripts/open_guide +12 -0
  22. craceplot/_settings/_description.py +168 -0
  23. craceplot/_settings/_options.json +299 -0
  24. craceplot/_utils/__init__.py +0 -0
  25. craceplot/_utils/_base.py +196 -0
  26. craceplot/_utils/_const.py +8 -0
  27. craceplot/_utils/_crace.py +42 -0
  28. craceplot/_utils/_format.py +191 -0
  29. craceplot/_vergit.py +2 -0
  30. craceplot/_version.py +24 -0
  31. craceplot/_vignettes/guide.ipynb +18943 -0
  32. craceplot-0.1.0.dist-info/METADATA +369 -0
  33. craceplot-0.1.0.dist-info/RECORD +61 -0
  34. craceplot-0.1.0.dist-info/WHEEL +5 -0
  35. craceplot-0.1.0.dist-info/entry_points.txt +2 -0
  36. craceplot-0.1.0.dist-info/licenses/LICENSE.md +21 -0
  37. craceplot-0.1.0.dist-info/top_level.txt +2 -0
  38. docs/_config.yml +51 -0
  39. docs/_static/css/custom.css +168 -0
  40. docs/_static/js/navbar.js +40 -0
  41. docs/_toc.yml +5 -0
  42. docs/index.md +4 -0
  43. docs/references/404.md +14 -0
  44. docs/references/authors.md +46 -0
  45. docs/references/citiation.bib +17 -0
  46. docs/references/functions/index.md +5 -0
  47. docs/references/functions/param_boxplot.ipynb +19 -0
  48. docs/references/index.md +44 -0
  49. docs/references/license.md +21 -0
  50. docs/references/others/favicon.ico +0 -0
  51. docs/references/others/favicon_io/about.txt +6 -0
  52. docs/references/others/favicon_io/android-chrome-192x192.png +0 -0
  53. docs/references/others/favicon_io/android-chrome-512x512.png +0 -0
  54. docs/references/others/favicon_io/apple-touch-icon.png +0 -0
  55. docs/references/others/favicon_io/favicon-16x16.png +0 -0
  56. docs/references/others/favicon_io/favicon-32x32.png +0 -0
  57. docs/references/others/favicon_io/site.webmanifest +1 -0
  58. docs/references/others/logo.png +0 -0
  59. docs/references/others/logo1.png +0 -0
  60. docs/references/plots/acotsp-parallelcoord-all.png +0 -0
  61. docs/requirements.txt +5 -0
@@ -0,0 +1,1805 @@
1
+ # craceplot._plot.parameters.py
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ import re
8
+ import random
9
+ import inspect
10
+ import math
11
+ import time
12
+ import traceback
13
+
14
+ import numpy as np
15
+ import pandas as pd
16
+ from types import SimpleNamespace
17
+ from matplotlib.colors import Colormap
18
+ from typing import TYPE_CHECKING, Union, Literal, Callable, TypeVar, ParamSpec, Optional
19
+
20
+ from craceplot._plots._core import *
21
+ from craceplot._utils._base import *
22
+ from craceplot._containers._core import bold, underline, reset, CplotOptions, Errors as CE
23
+
24
+ P = ParamSpec("P")
25
+ R = TypeVar("R")
26
+
27
+ if TYPE_CHECKING:
28
+ from crace.containers.crace_results import CraceResults
29
+
30
+ __all__ = []
31
+
32
+ def export(func: Callable[P, R]) -> Callable[P, R]:
33
+ """append func name to list __all__"""
34
+ __all__.append(func.__name__)
35
+ return func
36
+
37
+ exclude_cols = ['configuration_id', 'slice_sampled']
38
+ missing_label = "<NA>"
39
+
40
+ args = SimpleNamespace(
41
+ # required parameters
42
+ data=None,
43
+ options=None,
44
+ configs=None,
45
+ sel_vars=None,
46
+ sel_kvar=None,
47
+ slice=True,
48
+ out_dir=None,
49
+ file_name=None,
50
+ tile=None,
51
+ cat=False,
52
+ console=True,
53
+ # optional parameters
54
+ colorby=None,
55
+ showscale=True,
56
+ shownan=True,
57
+ showfliers=False,
58
+ showmeans=False,
59
+ dpi=800,
60
+ nbins=5,
61
+ )
62
+
63
+ def add_slice(data: pd.DataFrame, slice: pd.DataFrame):
64
+ """add slice information to selected data"""
65
+ last_configs = slice["last_config_id"].to_numpy(dtype=int)
66
+ idx = np.searchsorted(last_configs, data["configuration_id"].values, side="right")
67
+ new = data.assign(slice_sampled=idx + 1)
68
+
69
+ return new
70
+
71
+ def sel_conf(data: pd.DataFrame, args, configs=None):
72
+ """
73
+ return a dataframe based on configuration types of IDs
74
+
75
+ :param data: full experiments data
76
+ :type data: pd.DataFrame
77
+ :param configs: string or list
78
+ """
79
+ # configs: Union[Literal['all', 'final', 'elites', 'else'], list[int], None]='all',
80
+
81
+ if not configs: return data
82
+ elif isinstance(configs, str):
83
+ c = configs.lower()
84
+ if c == 'all': return data
85
+ elif c == 'final':
86
+ sel = args.data.elites
87
+ return data[data['configuration_id'].isin(sel)]
88
+ elif c == 'elites':
89
+ sel = args.data.all_elites
90
+ return data[data['configuration_id'].isin(sel)]
91
+ elif isinstance(configs, list):
92
+ return data[data['configuration_id'].isin(configs)]
93
+ else:
94
+ raise CE.OptionError(f"Failed to select data based on provided configuration value {configs}, "
95
+ f"please check {underline}configs{reset} or {underline}numConfigurations{reset}/{underline}selConfigurations{reset}")
96
+
97
+ def parse_parameters(parameters, data):
98
+ """
99
+ load the range of each parameter in the provided Crace results
100
+ """
101
+ # parse all parameters from log files
102
+ all_parameters = {}
103
+ tmp_params = parameters.get_names()
104
+ for p in tmp_params:
105
+ all_parameters[p] = {}
106
+ all_parameters[p]['type'] = parameters.get_parameter(p).type
107
+ all_parameters[p]['domain'] = parameters.get_parameter(p).domain
108
+
109
+ # add user defined parameters
110
+ all_parameters['configuration_id'] = {}
111
+ all_parameters['configuration_id']['type'] = 'i'
112
+ all_parameters['configuration_id']['domain'] = [int(data['configuration_id'].min()), int(data['configuration_id'].max())]
113
+ all_parameters['slice_sampled'] = {}
114
+ all_parameters['slice_sampled']['type'] = 'i'
115
+ all_parameters['slice_sampled']['domain'] = [int(data['slice_sampled'].min()), int(data['slice_sampled'].max())]
116
+ return all_parameters
117
+
118
+ @enforce_types
119
+ def _check_options(name: str, args: SimpleNamespace):
120
+ """
121
+ check options
122
+
123
+ options provided by args have higher priority
124
+
125
+ each option will be read from args when plotting
126
+
127
+ ONLY modify args
128
+
129
+ # required parameters
130
+ data=None,
131
+ options=None,
132
+ sel_vars=None,
133
+ sel_kvar=None,
134
+ slice=True,
135
+ out_dir=None,
136
+ file_name=None,
137
+ tile=None,
138
+ cat=False,
139
+ console=True,
140
+ # optional parameters
141
+ showscale=True,
142
+ shownan=True,
143
+ dpi=800,
144
+ nbins=5,
145
+
146
+ """
147
+ # data must be provided
148
+ if not args.data:
149
+ raise CE.OptionError(f"{bold}data{reset} must be provided for plotting.")
150
+
151
+ # check onlytest
152
+ onlytest = args.data.options.onlytest.value
153
+ if onlytest: raise CE.CplotError(
154
+ f"The provided crace log files only has results for test part, "
155
+ f"which is unavaliable for plotting parameters."
156
+ )
157
+
158
+ # slice: bool=True
159
+ # options.slice: Union[Literal['budget','b','experiment','e','exp','time','t','T'], bool, None]
160
+ if args.options.slice.value is True or args.slice:
161
+ args.slice = True
162
+ elif (args.options.slice.value is False
163
+ or args.options.slice.value is None
164
+ or not args.slice):
165
+ args.slice = False
166
+ else:
167
+ print(f"#\n# Option {underline}slice{reset} value {bold}{args.options.slice.value}{reset} "
168
+ f"is not avaliable when plotting for parameters\n"
169
+ f"# selected parameters: {', '.join(args.sel_vars)}")
170
+
171
+ # showscale: bool=True
172
+ args.showscale = True if args.options.showscale.value or args.slice else False
173
+
174
+ # dpi: int
175
+ args.dpi = args.options.dpi.value if not args.options.dpi.is_default() else args.dpi
176
+
177
+ # out_dir: str=None
178
+ if not args.out_dir and args.options.outDir.is_set():
179
+ args.out_dir = args.options.outDir.value
180
+ if not os.path.exists(args.out_dir):
181
+ raise CE.OptionError(f"Provided {bold}outDir/out_dir{reset} is not exist.")
182
+
183
+ # file_name: str=None
184
+ if not args.file_name and args.options.fileName.is_set():
185
+ args.file_name = args.options.fileName.value
186
+
187
+ # tile: str=None
188
+ if not args.tile and args.options.title.is_set():
189
+ args.tile = args.options.title.value
190
+
191
+ # ===================================================================================
192
+ # check multiParameters (args.sel_vars)
193
+ multi_required = ['pair']
194
+ key_required = ['pair']
195
+
196
+ if not args.sel_vars:
197
+
198
+ # random selected parameters only when multiParameters is [] and sel_vars is None
199
+ if not args.options.multiParameters.value:
200
+ if name == 'coord': k=10
201
+ elif name == 'cat': k=2
202
+ elif name == 'sun': k=6
203
+ elif name == 'hist':
204
+ if args.slice: k=1
205
+ else: k=8
206
+ elif name == 'box': k=1
207
+ else:
208
+ raise CE.OptionError(f"{underline}multiParamters/parameters/sel_vars{reset} must be provided here.")
209
+
210
+ if name not in []:
211
+ random.seed(time.time())
212
+ args.sel_vars = sorted(random.sample(args.data.parameters.get_names(), k=k), key=str.lower)
213
+ print(f"#\n# No parameters selected to draw plot, "
214
+ f"set to the default(random selected {k} parameters).\n"
215
+ f"# selected parameters: {', '.join(args.sel_vars)}"
216
+ )
217
+
218
+ # check each parameters when multiParameters is provided
219
+ # and assign valid multiParameters to args.sel_vars
220
+ else:
221
+ for x in args.options.multiParameters.value:
222
+ if x not in args.data.parameters.get_names():
223
+ raise CE.ParameterValueError(f"Provided {bold}{x}{reset} is an invalid parameter.")
224
+ args.sel_vars = args.options.multiParameters.value
225
+
226
+ # check sel_vars for specific plots
227
+ if name in ['cat', 'heat', 'joint'] and len(args.sel_vars) > 2:
228
+ del args.sel_vars[2:]
229
+ print(f"#\n# More than two parameters are provided for drawing plot.\n"
230
+ f"# selected parameters: {', '.join(args.sel_vars)}")
231
+
232
+ if name in ['joint'] and len(args.sel_vars) == 1:
233
+ print(f"#\n# Two parameters must be provided for drawing plot.\n")
234
+
235
+ if name in ['pair', 'joint']:
236
+ for x in args.sel_vars:
237
+ if args.data.parameters.get_parameter(x).type == 'c':
238
+ raise CE.OptionError(f"When {bold}{methods_table_param[name]}{reset} is selected, "
239
+ f"{underline}multiParameters/sel_vars{reset} ({bold}{x}{reset}) must be numeric.")
240
+
241
+
242
+ # ====================================================================================
243
+ # check keyParameter (args.sel_kvar)
244
+ # provided key parameter
245
+ if args.sel_kvar is not None:
246
+ key_v = args.sel_kvar
247
+ elif args.options and args.options.keyParameter.is_set():
248
+ key_v = args.options.keyParameter.value
249
+ else:
250
+ key_v = None
251
+
252
+ # if key parameter is provided
253
+ if key_v:
254
+ if name == 'coord':
255
+ print(f"#\n# Parameter {bold}{key_v}{reset} is used for colorscale.")
256
+ if name == 'pair' and args.data.parameters.get_parameter(key_v).type != 'c':
257
+ raise CE.OptionError(f"When {bold}pairplot{reset} is selected, {underline}keyParameter/sel_kvar{reset} must be categorical.")
258
+
259
+ # if key parameter is not provided: set as default
260
+ else:
261
+ if name == 'coord':
262
+ print(f"#\n# No {underline}keyParameter{reset} or {bold}sel_kvar{reset} provided, set colorscale as default.")
263
+ elif name == 'box' and not args.sel_vars:
264
+ raise CE.OptionError(f"Either {underline}multiParamters/sel_vars{reset} or "
265
+ f"{underline}keyParameter/hue/sel_kvar{reset} must be provided here "
266
+ f"for {bold}{methods_table_param[name]}{reset}")
267
+
268
+
269
+ elif name in key_required:
270
+ raise CE.OptionError(f"{underline}keyParameter/hue/sel_kvar{reset} must be provided here.")
271
+
272
+ if args.options.slice.value or args.slice:
273
+ key_v = 'slice_sampled'
274
+ else:
275
+ key_v = 'configuration_id'
276
+
277
+ args.sel_kvar = key_v if name not in ['heat', 'cat'] else None
278
+
279
+ if name == 'pair' and args.data.parameters.get_parameter(args.sel_kvar).type != 'c':
280
+ raise CE.OptionError(f"When {bold}pairplot{reset} is selected, {underline}keyParameter/sel_kvar{reset} must be categorical.")
281
+
282
+
283
+ # check configurations
284
+ if not args.configs:
285
+ c = args.options.numConfigurations.value
286
+ if c.lower() in ['all', 'final', 'elites']:
287
+ args.configs = c
288
+ elif c.lower() == 'else':
289
+ args.configs = c
290
+ else:
291
+ args.configs = None
292
+
293
+
294
+ def _check_str_list(lst):
295
+ import ast
296
+ try:
297
+ for x in lst:
298
+ if not isinstance(x, str):
299
+ return False
300
+ v = ast.literal_eval(x)
301
+ if not isinstance(v, (int, float)):
302
+ return False
303
+ return True
304
+ except Exception:
305
+ return False
306
+
307
+ @enforce_types
308
+ def _map_values(name: str, data: pd.DataFrame, ori_params, parameters, cat: bool=False, nbins: int=5, shownan: bool=True):
309
+ """map parameter values"""
310
+ data = data[list(parameters.keys())].copy()
311
+
312
+ # print("#\n# The selected crace results:")
313
+ # print(data)
314
+
315
+ num = len(data)
316
+
317
+ vars_dict = []
318
+ vars_dict = safe_copy(parameters)
319
+
320
+ # add ticktext
321
+ for pname in parameters.keys():
322
+ vars_dict[pname]['vals'] = safe_copy(vars_dict[pname]['domain'])
323
+ vars_dict[pname]['text'] = safe_copy(vars_dict[pname]['domain'])
324
+
325
+ map_dic = {}
326
+ show_info = False
327
+ print_info = True if name in ['cat', 'coord'] else False
328
+
329
+ types = set(parameters[pname]['type'] for pname in ori_params.keys())
330
+ max_l = max([len(x) for x in vars_dict.keys() if x not in exclude_cols])
331
+
332
+ col = data[pname].copy()
333
+ data[pname] = (col.replace(['null', 'NULL', 'none', 'None', 'missing', 'NA', 'NaN', None], np.nan))
334
+
335
+
336
+ # ====================================================================================
337
+ # replace NaN values
338
+ if shownan:
339
+ all_nan_cols = data.count() == 0
340
+ ratios = [data[pname].isna().sum() / float(num) for pname in all_nan_cols.index]
341
+ # if any (r > 0 for r in ratios): print("#\n# Replacig MISSING values..")
342
+
343
+ for pname, per in zip(all_nan_cols.index, ratios):
344
+ if pname not in vars_dict.keys(): continue
345
+ if pname in exclude_cols: continue
346
+
347
+ ptype = vars_dict[pname]['type']
348
+ pdomain = safe_copy(vars_dict[pname]['domain'])
349
+
350
+ if per > 0:
351
+ # replace nan
352
+ # === categorical / ordinal ===
353
+ if ptype in ('c', 'o'):
354
+ # expand domain
355
+ if data[pname].isna().any() and missing_label not in pdomain:
356
+ vars_dict[pname]['domain'] = pdomain + [missing_label]
357
+ vars_dict[pname]['vals'] = pdomain + [missing_label]
358
+ vars_dict[pname]['text'] = pdomain + [missing_label]
359
+
360
+ col = data[pname].copy()
361
+ data[pname] = (col.fillna(missing_label))
362
+
363
+
364
+ # === continuous ===
365
+ else:
366
+ pmin, pmax = pdomain
367
+
368
+ minimo = pmin
369
+ maximo = round(pmax * 5 / 4, 1)
370
+
371
+ medio = round(pmax / 4, 1)
372
+ medio2 = round(pmax / 2, 1)
373
+ medio3 = round(pmax * 3 / 4, 1)
374
+
375
+ vars_dict[pname]['domain'] = [minimo, maximo]
376
+ vars_dict[pname]['vals'] = [minimo, medio, medio2, medio3, pmax, maximo]
377
+ vars_dict[pname]['text'] = [minimo, medio, medio2, medio3, pmax, missing_label]
378
+
379
+ col = data[pname].copy()
380
+ data[pname] = (col.fillna(maximo).astype(float))
381
+
382
+ elif ptype in ('i', 'i,log', 'r', 'r,log'):
383
+ minimo, maximo = pdomain
384
+
385
+ medio = round(maximo / 4, 1)
386
+ medio2 = round(maximo / 2, 1)
387
+ medio3 = round(maximo * 3 / 4, 1)
388
+
389
+ vars_dict[pname]['domain'] = [minimo, maximo]
390
+ vars_dict[pname]['vals'] = vars_dict[pname]['text'] = [minimo, medio, medio2, medio3, maximo]
391
+
392
+
393
+ # ====================================================================================
394
+ # convert parameter type
395
+ # if name not in ['hist', 'box', 'sun', 'coord']:
396
+ if name in ['cat', 'heat', 'coord']:
397
+ for pname in parameters.keys():
398
+ if pname in exclude_cols: continue
399
+ ptype = parameters[pname]['type']
400
+
401
+ # continouse -> categorical
402
+ if cat and ptype != 'c':
403
+ old = vars_dict[pname]['domain']
404
+ colmin = vars_dict[pname]['domain'][0]
405
+ colmax = vars_dict[pname]['domain'][1]
406
+ safe_max = np.nextafter(colmax, np.inf)
407
+ safe_min = np.nextafter(colmin, -np.inf)
408
+ # get sub-domains
409
+ bins = np.linspace(safe_min, safe_max, nbins + 1)
410
+ display_bins = np.linspace(colmin, colmax, nbins+1)
411
+ # digitize
412
+ # return 1..N_bins
413
+ vals = data[pname].values
414
+ binned = np.full_like(vals, fill_value=np.nan, dtype=float)
415
+ mask = ~np.isnan(vals)
416
+ # map value to sub-domains
417
+ # from 0 to nbins-1
418
+ res = np.digitize(vals[mask], bins, right=False) - 1
419
+ res = np.clip(res, 0, nbins - 1)
420
+
421
+ # 2. update data
422
+ binned[mask] = res
423
+ data[pname] = binned
424
+ # 3. update domain and type
425
+ if missing_label not in vars_dict[pname]['text']:
426
+ vars_dict[pname]['text'] = [f"[{display_bins[i]:.1f}, {display_bins[i+1]:.1f})" for i in range(nbins)]
427
+ else:
428
+ vars_dict[pname]['text'] = [f"[{display_bins[i]:.1f}, {display_bins[i+1]:.1f})" for i in range(nbins-1)]+[missing_label]
429
+
430
+ vars_dict[pname]['domain'] = list(range(0, nbins))
431
+ vars_dict[pname]['vals'] = list(range(0, nbins))
432
+ vars_dict[pname]['type'] = 'c'
433
+
434
+ if not show_info:
435
+ print("#\n# Mapping parameter type..")
436
+ show_info = True
437
+ print(f"# (WARNING: convert parameter {bold}{pname}{reset}({ptype}) to categorical for plotting)")
438
+ # print("# %*s: old domain %s -> new domain %s " % (max_l, pname, old, vars_dict[pname]['text']))
439
+
440
+ # catgorical -> continouse
441
+ if not cat and ptype == 'c':
442
+ old = list(vars_dict[pname]['domain'])
443
+ map_dic[pname] = {v: int(i) for i, v in enumerate(old)}
444
+ vals = list(map_dic[pname].values())
445
+
446
+ col = data[pname].copy()
447
+ data[pname] = (col.map(map_dic[pname]).astype(int))
448
+
449
+ vars_dict[pname]['domain'] = [min(vals), max(vals)]
450
+ vars_dict[pname]['vals'] = vals
451
+ vars_dict[pname]['text'] = old
452
+
453
+ if not show_info:
454
+ print("#\n# Mapping parameter type..")
455
+ show_info = True
456
+ print(f"# (WARNING: map values of parameter {bold}{pname}{reset}({ptype}) for ploltting)")
457
+ # print("# %*s: old domain %s -> new domain %s " % (max_l, pname, old, vars_dict[pname]['domain']))
458
+
459
+ return data, vars_dict
460
+
461
+ @enforce_types
462
+ def _load_data(name: str, args: SimpleNamespace):
463
+ """load data"""
464
+ src_configs = args.data.configurations.print_all().kwargs['all']
465
+ src_configs = src_configs.rename(columns={".ID": "configuration_id"})
466
+ src_configs = src_configs.rename(columns={".PARENT": "parent_id"})
467
+
468
+ src_pd = add_slice(data=src_configs, slice=args.data.training.slice)
469
+ sel_pd = sel_conf(data=src_pd, configs=args.configs, args=args)
470
+
471
+ src_vars = parse_parameters(args.data.parameters, sel_pd)
472
+
473
+ # print("#\n# The original crace results:")
474
+ # print(sel_pd)
475
+
476
+ ori_dict = {}
477
+ sel_dict = {}
478
+ for x in args.sel_vars:
479
+ ori_dict[x] = {}
480
+ ori_dict[x] = safe_copy(src_vars[x])
481
+ sel_dict[x] = {}
482
+ sel_dict[x] = safe_copy(src_vars[x])
483
+ if name in ['hist', 'box', 'coord'] or (args.sel_kvar and args.sel_kvar not in exclude_cols):
484
+ sel_dict[args.sel_kvar] = {}
485
+ sel_dict[args.sel_kvar] = safe_copy(src_vars[args.sel_kvar])
486
+ if name == 'heat':
487
+ for x in exclude_cols:
488
+ if x not in sel_dict: sel_dict[x] = safe_copy(src_vars[x])
489
+
490
+ new_data, new_vars = _map_values(name=name, data=sel_pd, ori_params=ori_dict, parameters=sel_dict, cat=args.cat, nbins=args.nbins, shownan=args.shownan)
491
+
492
+ # print("#\n# The new Crace results:")
493
+ # print(new_data)
494
+
495
+ return new_data, src_vars, new_vars
496
+
497
+ @enforce_types
498
+ def _resolve_input(name: str, args: SimpleNamespace):
499
+ """
500
+ resolve input
501
+ """
502
+ new_data= None
503
+ new_vars=None
504
+
505
+ _check_options(name=name, args=args)
506
+
507
+ new_data, src_vars, new_vars = _load_data(name=name, args=args)
508
+
509
+ return new_data, src_vars, new_vars
510
+
511
+ def plot_parameters(method: str, **kwargs):
512
+ """
513
+ entrance to call plotting function from file _draw.py
514
+ including try-except
515
+ """
516
+ # drawMethod: (boxplot, violinplot, parallelcoord, parallelcat, sunburst, pairplot, histplot, jointplot, heatmap)
517
+
518
+ func = dispatch_table_param.get(method)
519
+
520
+ try:
521
+ if func: return func(**kwargs)
522
+ else: raise ValueError(f"Method '{method}' is not defined in dispatch table.")
523
+
524
+ except Exception as e:
525
+ print("\n! There was an error while plotting for parameters:")
526
+ if any(isinstance(e, cls) for cls in [x[1] for x in inspect.getmembers(CE, inspect.isclass)]):
527
+ print(f"! {e}")
528
+ else:
529
+ err = traceback.format_exc()
530
+ print(err)
531
+ if kwargs["_console"]: return None
532
+ sys.exit(1)
533
+
534
+
535
+ @export
536
+ @enforce_types
537
+ def param_parallelcoord(
538
+ # required parameters
539
+ data: CraceResults=None,
540
+ options: CplotOptions=None,
541
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
542
+ parameters: Union[list[str], str, None]=None,
543
+ colorby: str=None,
544
+ slice: bool=True,
545
+ out_dir: str=None,
546
+ file_name: str=None,
547
+ tile: str=None,
548
+ _console: bool=True,
549
+ # optional parameters
550
+ showscale: bool=True,
551
+ shownan: bool=True,
552
+ nbins: int=5,
553
+ # specific parameters
554
+ width: int=2560,
555
+ height: int=1440,
556
+ colorscale: str='Tealrose',
557
+ ):
558
+ """
559
+ Entrance to call parallel coord in python console
560
+
561
+ :param data: object CraceResults that must be provided.
562
+ :param options: object CplotOptions that must be provided.
563
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations. Supported values are 'all', 'final', 'elites' or a list of configuration IDs. Default is 'all'.
564
+ :param parameters: A list of parameter names selected for plotting, higher priority than option multiParameters.
565
+ :param colorby: A string of parameter name selected for plotting, higher priority than option keyParameter.
566
+ :param showscale: Boolean used to enable/diable showing colorscale.
567
+ :param shownan: Boolean used to plot including/excluding missing values.
568
+ :param colorscale: A string of palatte name for plotting.
569
+ """
570
+ kwargs = locals()
571
+ plot_parameters(method='coord', **kwargs)
572
+
573
+ @enforce_types
574
+ @register_param(['parallelcoord', 'coord'])
575
+ def _parallel_coord(
576
+ # required parameters
577
+ data: CraceResults=None,
578
+ options: CplotOptions=None,
579
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
580
+ parameters: Union[list[str], str, None]=None,
581
+ colorby: str=None,
582
+ slice: bool=True,
583
+ out_dir: str=None,
584
+ file_name: str=None,
585
+ tile: str=None,
586
+ _console: bool=True,
587
+ # optional parameters
588
+ showscale: bool=True,
589
+ shownan: bool=True,
590
+ nbins: int=5,
591
+ # specific parameters
592
+ width: int=2560,
593
+ height: int=1440,
594
+ colorscale: str='Tealrose',
595
+ ):
596
+ """drawing parallel coord plot"""
597
+ try:
598
+ import plotly.graph_objects as go
599
+ except ImportError as e:
600
+ raise e
601
+
602
+ if isinstance(parameters, str):
603
+ parameters = [parameters]
604
+
605
+ args = SimpleNamespace(
606
+ data=safe_copy(data), options=safe_copy(options), sel_vars=safe_copy(parameters),
607
+ sel_kvar=colorby, slice=slice,
608
+ out_dir=out_dir, file_name=file_name, tile=tile,
609
+ cat=False, console=_console,
610
+ configs=configs,
611
+ # optional parameters
612
+ showscale=showscale, shownan=shownan,
613
+ dpi=800, nbins=nbins,
614
+ )
615
+
616
+ new_data, src_vars, new_vars = _resolve_input(name='coord', args=args)
617
+
618
+ # draw parallelcoord
619
+ plot_dict = []
620
+ dimensions = {}
621
+ for name in new_vars.keys():
622
+ if new_vars[name]['type'] != 'c':
623
+ dimensions = dict(
624
+ range = new_vars[name]['domain'],
625
+ tickvals = new_vars[name]['vals'],
626
+ ticktext = new_vars[name]['text'],
627
+ label = name,
628
+ values = new_data[name]
629
+ )
630
+ else:
631
+ dimensions = dict(
632
+ range = new_vars[name]['domain'],
633
+ tickvals = new_vars[name]['vals'],
634
+ ticktext = new_vars[name]['text'],
635
+ label = name,
636
+ values = new_data[name]
637
+ )
638
+ plot_dict.append(dimensions)
639
+
640
+ if src_vars[args.sel_kvar]['type'] != 'c':
641
+ cmin = src_vars[args.sel_kvar]['domain'][0]
642
+ cmax = src_vars[args.sel_kvar]['domain'][1]
643
+ data_key = pd.to_numeric(new_data[args.sel_kvar])
644
+
645
+ else:
646
+ cmin = 0
647
+ cmax = len(src_vars[args.sel_kvar]['domain'])
648
+ data_key = new_data[args.sel_kvar]
649
+
650
+ fig = go.Figure(data=
651
+ go.Parcoords(
652
+ line = dict(color = data_key,
653
+ colorscale = colorscale,
654
+ showscale = showscale,
655
+ cmin = cmin,
656
+ cmax = cmax),
657
+ dimensions = list([line for line in plot_dict])
658
+ ))
659
+
660
+ # if not _console:
661
+ # fig.write_image(f"{args.out_dir}/{args.file_name}", width=width, height=height)
662
+ # print("# {} has been saved in {}.".format(args.file_name+'.png', args.out_dir))
663
+ fig.show()
664
+
665
+ # @export
666
+ @enforce_types
667
+ def param_parallelcat(
668
+ # required parameters
669
+ data: CraceResults=None,
670
+ options: CplotOptions=None,
671
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
672
+ sel_vars: list=None,
673
+ sel_kvar: str=None,
674
+ slice: bool=True,
675
+ out_dir: str=None,
676
+ file_name: str=None,
677
+ tile: str=None,
678
+ _console: bool=True,
679
+ # optional parameters
680
+ shownan: bool=True,
681
+ nbins: int=5,
682
+ # specific parameters
683
+ width: int=2560,
684
+ height: int=1440,
685
+ ):
686
+ kwargs = locals()
687
+ plot_parameters(method='cat', **kwargs)
688
+
689
+ @enforce_types
690
+ # @register_param(['parallelcat', 'cat'])
691
+ def _parallel_cat(
692
+ # required parameters
693
+ data: CraceResults=None,
694
+ options: CplotOptions=None,
695
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
696
+ sel_vars: list=None,
697
+ sel_kvar: str=None,
698
+ slice: bool=True,
699
+ out_dir: str=None,
700
+ file_name: str=None,
701
+ tile: str=None,
702
+ _console: bool=True,
703
+ # optional parameters
704
+ shownan: bool=True,
705
+ nbins: int=5,
706
+ # specific parameters
707
+ width: int=2560,
708
+ height: int=1440,
709
+ ):
710
+ """drawing parallel cat"""
711
+
712
+ try:
713
+ import plotly.graph_objects as go
714
+ except ImportError as e:
715
+ raise e
716
+
717
+ args = SimpleNamespace(
718
+ data=safe_copy(data), options=safe_copy(options), sel_vars=safe_copy(sel_vars),
719
+ sel_kvar=sel_kvar, slice=slice,
720
+ out_dir=out_dir, file_name=file_name, tile=tile,
721
+ cat=True, console=_console,
722
+ configs=configs,
723
+ # optional parameters
724
+ showscale=False, shownan=shownan,
725
+ dpi=800, nbins=nbins,
726
+ )
727
+
728
+ new_data, _, new_vars = _resolve_input(name='cat', args=args)
729
+
730
+ if args.sel_vars:
731
+ if len(args.sel_vars) > 2:
732
+ raise CE.OptionError(f"When {bold}parallelcat{reset} is called, "
733
+ f"two parameter names must be provided for option {underline}multiParameters{reset}!")
734
+ else:
735
+ args.sel_vars = list(new_vars.keys())
736
+
737
+ catx = args.sel_vars[0]
738
+ caty = args.sel_vars[1]
739
+
740
+ plot_dict = []
741
+ dimensions = {}
742
+ for name in new_vars.keys():
743
+ if new_vars[name]['type'] == 'c' and name not in exclude_cols:
744
+ dimensions = dict(
745
+ label = name,
746
+ values = new_data[name]
747
+ )
748
+ plot_dict.append(dimensions)
749
+
750
+ color = np.zeros(len(plot_dict), dtype='uint8')
751
+ colorscale = [[0, 'gray'], [1, 'firebrick']]
752
+
753
+ # Build figure as FigureWidget
754
+ fig = go.FigureWidget(data=[
755
+ go.Scatter(
756
+ x = new_data[catx],
757
+ y = new_data[caty],
758
+ marker={'color': 'gray'},
759
+ mode='markers',
760
+ selected={'marker': {'color': 'firebrick'}},
761
+ unselected={'marker': {'opacity': 0.5}}
762
+ ),
763
+ go.Parcats(
764
+ domain={'y': [0, 0.4]},
765
+ dimensions=list([line for line in plot_dict]),
766
+ line={'colorscale': colorscale, 'cmin': 0, 'cmax': 1, 'color': color, 'shape': 'hspline'}),
767
+ ])
768
+
769
+ fig.add_trace(fig.data[0])
770
+ fig.add_trace(fig.data[1])
771
+
772
+ fig.update_layout(
773
+ height=height,
774
+ xaxis={'title': catx},
775
+ yaxis={'title': caty, 'domain': [0.6, 1]},
776
+ dragmode='lasso',
777
+ hovermode='closest',
778
+ overwrite=True)
779
+
780
+ # Update color callback
781
+ def update_color(trace, points, state):
782
+ global color, color_numeric
783
+ print("Event triggered", points.point_inds)
784
+
785
+ for i in range(len(color)):
786
+ color[i] = 'gray'
787
+ for ind in points.point_inds:
788
+ color[ind] = 'firebrick'
789
+
790
+ color_numeric = [0 if c == 'gray' else 1 for c in color]
791
+ fig.data[1].line.color = color_numeric
792
+ fig.update_traces()
793
+
794
+ # Register callback on scatter selection...
795
+ fig.data[0].on_selection(update_color)
796
+ # and parcats click
797
+ fig.data[1].on_click(update_color)
798
+
799
+ # if not _console:
800
+ # fig.write_image(f"{args.out_dir}/{args.file_name}", width=width, height=height)
801
+ # print("# {} has been saved in {}.".format(args.file_name+'.png', args.out_dir))
802
+ fig.show()
803
+
804
+ @export
805
+ @enforce_types
806
+ def param_sunburst(
807
+ # required parameters
808
+ data: CraceResults=None,
809
+ options: CplotOptions=None,
810
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
811
+ parameters: Union[list[str], str, None]=None,
812
+ _console: bool=True,
813
+ # optional paramters
814
+ shownan: bool=True,
815
+ nbins: int=5,
816
+ # specific parameters
817
+ palette: str='vlag',
818
+ autosize: bool=True,
819
+ branchvalues: str='total',
820
+ maxdepth: int=3,
821
+ count: str='branches',
822
+ insidetextorientation: str='horizontal',
823
+ textinfo: str="label+value",
824
+ ):
825
+ """
826
+ Entrance to call sunburst in python console
827
+
828
+ :param data: object CraceResults that must be provided.
829
+ :param options: object CplotOptions that must be provided.
830
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations.
831
+ | Union[Literal['all', 'final', 'elites'], list[int], None]='all'
832
+ :param parameters: A list of parameter names selected for plotting, higher priority than option multiParameters.
833
+ :param shownan: Boolean used to plot including/excluding missing values.
834
+ :param palette: A string of palatte name for plotting.
835
+ :param branchvalues: parameter of function Sunburst
836
+ :param count: parameter of function Sunburst
837
+ :param insidetextorientation: parameter of function Sunburst
838
+ :param textinfo: paramter of function Sunburst
839
+ """
840
+
841
+ kwargs = locals()
842
+ plot_parameters(method='sun', **kwargs)
843
+
844
+ @enforce_types
845
+ @register_param(['sunburst', 'sun'])
846
+ def _sun_burst(
847
+ # required parameters
848
+ data: CraceResults=None,
849
+ options: CplotOptions=None,
850
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
851
+ parameters: Union[list[str], str, None]=None,
852
+ _console: bool=True,
853
+ # optional paramters
854
+ shownan: bool=True,
855
+ nbins: int=5,
856
+ # specific parameters
857
+ palette: str='vlag',
858
+ autosize: bool=True,
859
+ branchvalues: str='total',
860
+ maxdepth: int=3,
861
+ count: str='branches',
862
+ insidetextorientation: str='horizontal',
863
+ textinfo: str="label+value",
864
+ ):
865
+ """drawing sun burst plot"""
866
+
867
+ try:
868
+ import plotly.graph_objects as go
869
+ import seaborn as sns
870
+ except ImportError as e:
871
+ raise e
872
+
873
+ if isinstance(parameters, str):
874
+ parameters = [parameters]
875
+
876
+ args = SimpleNamespace(
877
+ data=safe_copy(data), options=safe_copy(options), sel_vars=safe_copy(parameters),
878
+ sel_kvar=None, slice=True, out_dir=None, file_name=None, tile=None,
879
+ cat=True, console=_console, configs=configs,
880
+ # optional parameters
881
+ showscale=False, shownan=shownan,
882
+ dpi=800, nbins=nbins,
883
+ )
884
+
885
+ new_data, src_vars, new_vars = _resolve_input(name='sun', args=args)
886
+
887
+ subgroup = {}
888
+ for name in new_vars.keys():
889
+ if name in exclude_cols: continue
890
+ subgroup[name] = {}
891
+ subgroup[name]['names'] = []
892
+ subgroup[name]['props'] = []
893
+ if new_vars[name]['type'] != 'c':
894
+ # new_vars: new domain including parsed <NA>
895
+ # missing_label <--> round(pmax * 5 / 4, 1)
896
+ new_data[name] = pd.to_numeric(new_data[name])
897
+ left = src_vars[name]['domain'][0]
898
+ right = src_vars[name]['domain'][1]
899
+ tmp = (left+right)/3
900
+ sub1 = '[{:.2f}, {:.2f}]'.format(left, tmp)
901
+ sub2 = '[{:.2f}, {:.2f}]'.format(tmp, 2*tmp)
902
+ sub3 = '[{:.2f}, {:.2f}]'.format(2*tmp, right)
903
+
904
+ subgroup[name]['names'].append(sub1)
905
+ s_bool = ((new_data[name] >= left) & (new_data[name] < tmp))
906
+ subgroup[name]['props'].append(s_bool.sum())
907
+
908
+ subgroup[name]['names'].append(sub2)
909
+ s_bool = ((new_data[name] >= tmp) & (new_data[name] < 2*tmp))
910
+ subgroup[name]['props'].append(s_bool.sum())
911
+
912
+ subgroup[name]['names'].append(sub3)
913
+ s_bool = ((new_data[name] >= 2*tmp) & (new_data[name] <= right))
914
+ subgroup[name]['props'].append(s_bool.sum())
915
+
916
+ if shownan and sum(subgroup[name]['props']) < len(new_data[name]):
917
+ # considering <NA> values
918
+ subgroup[name]['names'].append(missing_label)
919
+ s_bool = (new_data[name] > right)
920
+ subgroup[name]['props'].append(s_bool.sum())
921
+
922
+ else:
923
+ for x in new_vars[name]['domain']:
924
+ subgroup[name]['names'].append(x)
925
+ s_bool = new_data[name] == x
926
+ subgroup[name]['props'].append(s_bool.sum())
927
+ if shownan and sum(subgroup[name]['props']) < len(new_data[name]):
928
+ subgroup[name]['names'].append(missing_label)
929
+ s_bool = ~new_data[name].isin(new_vars[name]['domain'])
930
+ subgroup[name]['props'].append(s_bool.sum())
931
+
932
+ labels = []
933
+ values = []
934
+ parents = []
935
+ for name in new_vars.keys():
936
+ if name in exclude_cols: continue
937
+ i = 0
938
+ labels.append(name)
939
+ # values.append(self.count_values(sun_data[name]))
940
+ values.append(sum(subgroup[name]['props']))
941
+ # values[0] += sum(subgroup[name]['props'])
942
+ parents.append("")
943
+ for label in subgroup[name]['names']:
944
+ labels.append(label)
945
+ values.append(subgroup[name]['props'][i])
946
+ parents.append(name)
947
+ i += 1
948
+ ids = [f"{parent}/{label}" if parent else label for label, parent in zip(labels, parents)]
949
+
950
+ # print(f'\nvalues: {values}')
951
+ # print(f'\nlabels: {labels}')
952
+ # print(f'\nparents: {parents}')
953
+
954
+ mapped_palette = sns.color_palette(palette, len(labels)).as_hex()
955
+
956
+ fig = go.Figure()
957
+ fig.add_trace(go.Sunburst(
958
+ ids=ids,
959
+ labels = labels,
960
+ parents = parents,
961
+ values = values,
962
+ branchvalues=branchvalues,
963
+ maxdepth=maxdepth,
964
+ count=count,
965
+ insidetextorientation=insidetextorientation,
966
+ insidetextfont=dict(size=12,color='black'),
967
+ outsidetextfont=dict(size=12),
968
+ textinfo=textinfo,
969
+ marker=dict(colors=mapped_palette),
970
+ ))
971
+
972
+ fig.update_layout(
973
+ margin = dict(t=0, l=0, r=0, b=0),
974
+ uniformtext=dict(minsize=8, mode='hide'),
975
+ autosize=autosize,
976
+ )
977
+
978
+ # if not _console:
979
+ # fig.write_image(f"{args.out_dir}/{args.file_name}", width=width, height=height)
980
+ # print("# {} has been saved in {}.".format(args.file_name+'.png', args.out_dir))
981
+ fig.show()
982
+
983
+ @export
984
+ @enforce_types
985
+ def param_pairplot(
986
+ # required parameters
987
+ data: CraceResults=None,
988
+ options: CplotOptions=None,
989
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
990
+ parameters: Union[list[str], str, None]=None,
991
+ slice: bool=True,
992
+ _console: bool=True,
993
+ # optional parameters
994
+ shownan: bool=True,
995
+ dpi: int=800,
996
+ # specific parameters
997
+ hue: str=None,
998
+ palette: str='vlag',
999
+ kind: Literal['auto', 'hist', 'kde', None] = 'kde',
1000
+ height: int=4
1001
+ ):
1002
+ """
1003
+ Entrance to call pairplot in python console
1004
+
1005
+ :param data: object CraceResults that must be provided.
1006
+ :param options: object CplotOptions that must be provided.
1007
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations.
1008
+ | Union[Literal['all', 'final', 'elites'], list[int], None]='all'
1009
+ :param parameters: A list of parameter names selected for plotting, higher priority than option multiParameters.
1010
+ :param hue: A string of parameter name selected for plotting.
1011
+ :param shownan: Boolean used to plot including/excluding missing values.
1012
+ :param palette: A string of palatte name for plotting.
1013
+ :param kind: A string from provided values for the type/shape of plots.
1014
+ """
1015
+
1016
+ kwargs = locals()
1017
+ plot_parameters(method='pair', **kwargs)
1018
+
1019
+ @enforce_types
1020
+ @register_param(['pairplot', 'pair'])
1021
+ def _pair_plot(
1022
+ # required parameters
1023
+ data: CraceResults=None,
1024
+ options: CplotOptions=None,
1025
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1026
+ parameters: Union[list[str], str, None]=None,
1027
+ slice: bool=True,
1028
+ _console: bool=True,
1029
+ # optional parameters
1030
+ shownan: bool=True,
1031
+ dpi: int=800,
1032
+ # specific parameters
1033
+ hue: str=None,
1034
+ palette: str='deep',
1035
+ kind: Literal['auto', 'hist', 'kde'] = 'kde',
1036
+ height: int=4
1037
+ ):
1038
+ """drawing pair plot"""
1039
+
1040
+ try:
1041
+ import seaborn as sns
1042
+ import matplotlib.pyplot as plt
1043
+ except ImportError as e:
1044
+ raise e
1045
+
1046
+ if isinstance(parameters, str):
1047
+ parameters = [parameters]
1048
+
1049
+ args = SimpleNamespace(
1050
+ data=safe_copy(data), options=safe_copy(options), sel_vars=safe_copy(parameters),
1051
+ sel_kvar=hue, slice=slice,
1052
+ out_dir=None, file_name=None, tile=None,
1053
+ cat=False, console=_console,
1054
+ configs=configs,
1055
+ # optional parameters
1056
+ showscale=False, shownan=shownan,
1057
+ dpi=dpi, nbins=5
1058
+ )
1059
+
1060
+ new_data, _, _ = _resolve_input(name='pair', args=args)
1061
+
1062
+ init_plot_style(size=1.2)
1063
+
1064
+ import warnings
1065
+ warnings.filterwarnings("ignore", message="Ignoring `palette` because no `hue` variable has been assigned.")
1066
+
1067
+ levels = new_data[hue].unique()
1068
+ # auto canonicalize palette
1069
+ palette_dict = dict(zip(levels, sns.color_palette(palette, n_colors=len(levels))))
1070
+ # enforce categorical
1071
+ new_data[hue] = pd.Categorical(new_data[hue], categories=list(palette_dict.keys()))
1072
+
1073
+ fig = sns.pairplot(data=new_data,
1074
+ hue=hue,
1075
+ vars=parameters,
1076
+ palette=palette_dict,
1077
+ diag_kind=kind,
1078
+ height=height,
1079
+ dropna=not(shownan),)
1080
+
1081
+ # remove legend from pariplot
1082
+ if hasattr(fig, "_legend") and fig._legend is not None:
1083
+ fig._legend.remove()
1084
+
1085
+ # add legend
1086
+ # especially for the case: no
1087
+ import matplotlib.patches as mpatches
1088
+ handles = [mpatches.Patch(color=palette_dict[lvl], label=lvl) for lvl in palette_dict]
1089
+ fig._legend = fig.fig.legend(handles=handles, title=hue, loc='center right')
1090
+ fig._legend.get_frame().set_visible(False)
1091
+
1092
+ if not _console:
1093
+ fig.savefig(f"{args.out_dir}/{args.file_name}.png", dpi=args.dpi)
1094
+ print("# {} has been saved in {}.".format(args.file_name, args.out_dir))
1095
+ plt.show()
1096
+
1097
+ @export
1098
+ @enforce_types
1099
+ def param_heatmap(
1100
+ # required parameters
1101
+ data: CraceResults=None,
1102
+ options: CplotOptions=None,
1103
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1104
+ parameters: Union[list[str], str, None]=None,
1105
+ _console: bool=True,
1106
+ # optional parameters
1107
+ shownan: bool=True,
1108
+ nbins: int=5,
1109
+ dpi: int=800,
1110
+ # specific parameters
1111
+ colormap: Optional[Union[str, list, Colormap]] = 'PuBu',
1112
+ fmt: str='g',
1113
+ ):
1114
+ """
1115
+ Entrance to call heatmap in python console
1116
+
1117
+ :param data: object CraceResults that must be provided.
1118
+ :param options: object CplotOptions that must be provided.
1119
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations.
1120
+ | Union[Literal['all', 'final', 'elites'], list[int], None]='all'
1121
+ :param parameters: a list of parameter names selected for plotting, higher priority than option multiParameters.
1122
+ :param shownan: boolean used to plot including/excluding missing values.
1123
+ :param colormap: a string of matplotlib colormap name for plotting.
1124
+ :param nbins: integer used to split the domain for continouos paramters
1125
+ """
1126
+ kwargs = locals()
1127
+ plot_parameters(method='heat', **kwargs)
1128
+
1129
+ @enforce_types
1130
+ @register_param(['heatmap', 'heat'])
1131
+ def _heatmap(
1132
+ # required parameters
1133
+ data: CraceResults=None,
1134
+ options: CplotOptions=None,
1135
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1136
+ parameters: Union[list[str], str, None]=None,
1137
+ _console: bool=True,
1138
+ # optional parameters
1139
+ shownan: bool=True,
1140
+ nbins: int=5,
1141
+ dpi: int=800,
1142
+ # specific parameters
1143
+ colormap: Optional[Union[str, list, Colormap]] = 'PuBu',
1144
+ fmt: str='g',
1145
+ ):
1146
+
1147
+ try:
1148
+ import seaborn as sns
1149
+ import matplotlib.pyplot as plt
1150
+ except ImportError as e:
1151
+ raise e
1152
+
1153
+ if isinstance(parameters, str):
1154
+ parameters = [parameters]
1155
+
1156
+ args = SimpleNamespace(
1157
+ data=safe_copy(data), options=safe_copy(options),
1158
+ sel_vars=safe_copy(parameters),
1159
+ sel_kvar=None, slice=True,
1160
+ out_dir=None, file_name=None, tile=None,
1161
+ cat=True, console=_console,
1162
+ configs=configs,
1163
+ # optional parameters
1164
+ showscale=False, shownan=shownan,
1165
+ dpi=dpi, nbins=nbins,
1166
+ )
1167
+
1168
+ new_data, _, new_vars = _resolve_input(name='heat', args=args)
1169
+ param_names = [x for x in new_vars.keys() if x not in exclude_cols]
1170
+
1171
+ if len(param_names) == 1:
1172
+ index='slice_sampled'
1173
+ columns=param_names[0]
1174
+ else:
1175
+ index=param_names[0]
1176
+ columns=param_names[1]
1177
+
1178
+ pivot_table = new_data.pivot_table(index=index, columns=columns, values='configuration_id', aggfunc='count')
1179
+
1180
+ if pivot_table.empty:
1181
+ raise CE.CplotError(f"The selected two parameters are mutually exclusive, "
1182
+ f"with no overlapping value combinations.")
1183
+
1184
+ init_plot_style()
1185
+
1186
+ fig = sns.heatmap(pivot_table, annot=True, cmap=colormap, fmt=fmt)
1187
+
1188
+ # y_name, x_name = index, columns
1189
+ for name in param_names:
1190
+
1191
+ if name == columns:
1192
+ cols = pivot_table.columns
1193
+ fig.set_xticklabels(new_vars[name]['text'])
1194
+ else:
1195
+ rows = pivot_table.index
1196
+ fig.set_yticklabels(new_vars[name]['text'])
1197
+
1198
+ # if not _console:
1199
+ # fig.savefig(f"{args.out_dir}/{args.file_name}.png", dpi=args.dpi)
1200
+ # print("# {} has been saved in {}.".format(args.file_name, args.out_dir))
1201
+ plt.show()
1202
+
1203
+ @export
1204
+ @enforce_types
1205
+ def param_jointplot(
1206
+ # required parameters
1207
+ data: CraceResults=None,
1208
+ options: CplotOptions=None,
1209
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1210
+ parameters: Union[list[str], str, None]=None,
1211
+ slice: bool=True,
1212
+ _console: bool=True,
1213
+ # optional parameters
1214
+ shownan: bool=True,
1215
+ nbins: int=5,
1216
+ dpi: int=800,
1217
+ # specific parameters
1218
+ kind: Literal['scatter', 'kde', 'hist', 'hex', 'reg', 'resid']='hist',
1219
+ space: float=0.1,
1220
+ ratio: int=4,
1221
+ palette: str='vlag',
1222
+ hue: str=None,
1223
+ height: int=5,
1224
+ ):
1225
+ """
1226
+ Entrance to call pairplot in python console
1227
+
1228
+ :param data: object CraceResults that must be provided.
1229
+ :param options: object CplotOptions that must be provided.
1230
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations.
1231
+ | Union[Literal['all', 'final', 'elites'], list[int], None]='all'
1232
+ :param parameters: A list of parameter names selected for plotting, higher priority than option multiParameters.
1233
+ :param hue: A string of parameter name selected for plotting.
1234
+ :param shownan: Boolean used to plot including/excluding missing values.
1235
+ :param palette: A string of palatte name for plotting.
1236
+ :param kind: A string from provided values for the type/shape of plots.
1237
+ | Literal['scatter', 'kde', 'hist', 'hex', 'reg', 'resid']='hist'
1238
+ """
1239
+
1240
+ kwargs = locals()
1241
+ plot_parameters(method='joint', **kwargs)
1242
+
1243
+ @enforce_types
1244
+ @register_param(['jointplot', 'joint'])
1245
+ def _jointplot(
1246
+ # required parameters
1247
+ data: CraceResults=None,
1248
+ options: CplotOptions=None,
1249
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1250
+ parameters: Union[list[str], str, None]=None,
1251
+ slice: bool=True,
1252
+ _console: bool=True,
1253
+ # optional parameters
1254
+ shownan: bool=True,
1255
+ nbins: int=5,
1256
+ dpi: int=800,
1257
+ # specific parameters
1258
+ kind: Literal['scatter', 'kde', 'hist', 'hex', 'reg', 'resid']='hist',
1259
+ space: float=0.1,
1260
+ ratio: int=4,
1261
+ palette: str='vlag',
1262
+ hue: str=None,
1263
+ height: int=5,
1264
+ ):
1265
+ """drawing joint plot"""
1266
+
1267
+ try:
1268
+ import seaborn as sns
1269
+ import matplotlib.pyplot as plt
1270
+ except ImportError as e:
1271
+ raise e
1272
+
1273
+ if isinstance(parameters, str):
1274
+ parameters = [parameters]
1275
+
1276
+ args = SimpleNamespace(
1277
+ data=safe_copy(data), options=safe_copy(options), sel_vars=safe_copy(parameters),
1278
+ sel_kvar=hue, slice=slice,
1279
+ out_dir=None, file_name=None, tile=None,
1280
+ cat=False, console=_console,
1281
+ configs=configs,
1282
+ # optional parameters
1283
+ showscale=False, shownan=shownan,
1284
+ dpi=dpi, nbins=nbins,
1285
+ )
1286
+
1287
+ new_data, _, new_vars = _resolve_input(name='joint', args=args)
1288
+ param_names = args.sel_vars
1289
+
1290
+ x=param_names[0]
1291
+ y=param_names[1]
1292
+
1293
+ init_plot_style()
1294
+
1295
+ fig = sns.jointplot(data=new_data, x=x, y=y, hue=hue,
1296
+ kind=kind, space=space, ratio=ratio,
1297
+ palette=palette, height=height)
1298
+
1299
+ xmin, xmax = new_vars[x]['domain']
1300
+ ymin, ymax = new_vars[y]['domain']
1301
+ fig.ax_joint.set_xlim(xmin, xmax)
1302
+ fig.ax_joint.set_ylim(ymin, ymax)
1303
+ fig.ax_marg_x.set_xlim(xmin, xmax)
1304
+ fig.ax_marg_y.set_ylim(ymin, ymax)
1305
+
1306
+ # if not _console:
1307
+ # fig.savefig(f"{args.out_dir}/{args.file_name}.png", dpi=args.dpi)
1308
+ # print("# {} has been saved in {}.".format(args.file_name, args.out_dir))
1309
+ plt.show()
1310
+
1311
+ @export
1312
+ @enforce_types
1313
+ def param_histplot(
1314
+ # required parameters
1315
+ data: CraceResults=None,
1316
+ options: CplotOptions=None,
1317
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1318
+ parameters: Union[list[str], str, None]=None,
1319
+ slice: bool=False,
1320
+ _console: bool=True,
1321
+ # optional parameters
1322
+ shownan: bool=True,
1323
+ nbins: int=5,
1324
+ dpi: int=1600,
1325
+ # specific parameters
1326
+ stat: Literal['count', 'frequency', 'probability', 'percent', 'density']='percent',
1327
+ density: bool=False,
1328
+ sharex: bool=False,
1329
+ sharey: bool=True,
1330
+ ):
1331
+ """
1332
+ Entrance to call histplot in python console
1333
+
1334
+ :param data: object CraceResults that must be provided.
1335
+ :param options: object CplotOptions that must be provided.
1336
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations.
1337
+ | Union[Literal['all', 'final', 'elites'], list[int], None]
1338
+ :param parameters: A list of parameter names selected for plotting, higher priority than option multiParameters.
1339
+ :param slice: A boolean value used to enable/disable mapping plot aspects to different slices.
1340
+ :param shownan: A boolean value used to plot including/excluding missing values.
1341
+ :param stat: A string from provided values for the type/shape of plots.
1342
+ | Literal['count', 'frequency', 'probability', 'percent', 'density']='percent'
1343
+ :param density: A boolean value used to enable/disable plotting the rugplot in addition.
1344
+ :param sharex: A boolean value used to enable/disable sharing x axis when plotting.
1345
+ :param sharey: A boolean value used to enable/disable sharing y axis when plotting.
1346
+ """
1347
+
1348
+ kwargs = locals()
1349
+ plot_parameters(method='hist', **kwargs)
1350
+
1351
+ @enforce_types
1352
+ @register_param(['histplot', 'hist'])
1353
+ def _histplot(
1354
+ # required parameters
1355
+ data: CraceResults=None,
1356
+ options: CplotOptions=None,
1357
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1358
+ parameters: Union[list[str], str, None]=None,
1359
+ slice: bool=False,
1360
+ _console: bool=True,
1361
+ # optional parameters
1362
+ shownan: bool=False,
1363
+ nbins: int=5,
1364
+ dpi: int=1600,
1365
+ # specific parameters
1366
+ stat: Literal['count', 'frequency', 'probability', 'percent', 'density']='percent',
1367
+ density: bool=False,
1368
+ sharex: bool=False,
1369
+ sharey: bool=True,
1370
+ ):
1371
+
1372
+ try:
1373
+ import seaborn as sns
1374
+ import matplotlib.pyplot as plt
1375
+ except ImportError as e:
1376
+ raise e
1377
+
1378
+ if isinstance(parameters, str):
1379
+ parameters = [parameters]
1380
+
1381
+ args = SimpleNamespace(
1382
+ data=safe_copy(data), options=safe_copy(options),
1383
+ configs=configs,
1384
+ sel_vars=safe_copy(parameters), sel_kvar=None,
1385
+ out_dir=None, file_name=None, tile=None,
1386
+ slice=slice, cat=False, console=_console,
1387
+ # optional parameters
1388
+ showscale=False, shownan=shownan,
1389
+ dpi=dpi, nbins=nbins,
1390
+ )
1391
+
1392
+ new_data, src_vars, new_vars = _resolve_input(name='hist', args=args)
1393
+ param_names = [x for x in new_vars.keys() if x not in exclude_cols]
1394
+
1395
+ if args.slice:
1396
+ init_plot_style(size=1.1)
1397
+
1398
+ page_num = len(param_names)
1399
+ file_names = locals()
1400
+ for i in range(0, page_num):
1401
+ file_names['plot%s' % i] = args.file_name.split('.')[0]+str(i) if args.file_name else str(i)
1402
+ name = param_names[i]
1403
+ max_slice = max(new_data['slice_sampled'])
1404
+ fig, axis = plt.subplots(max_slice, 1, sharey=True, sharex=True, figsize=(max_slice/1.2, max_slice/1.2))
1405
+
1406
+ for j in range(1, max_slice+1):
1407
+ ax = axis[j-1]
1408
+ kde = False
1409
+ if src_vars[name]['type'] != 'c':
1410
+ kde=True
1411
+
1412
+ # statstr
1413
+ # Aggregate statistic to compute in each bin.
1414
+ # count: show the number of observations in each bin
1415
+ # frequency: show the number of observations divided by the bin width
1416
+ # probability or proportion: normalize such that bar heights sum to 1
1417
+ # percent: normalize such that bar heights sum to 100
1418
+ # density: normalize such that the total area of the histogram equals 1
1419
+
1420
+ fig = sns.histplot(data=new_data[new_data['slice_sampled'] == j].sort_values(by=name, na_position='last', ascending=True),
1421
+ x=name, stat=stat, kde=kde, ax=ax)
1422
+ if kde and density:
1423
+ fig = sns.rugplot(data=new_data[new_data['slice_sampled'] == j].sort_values(by=name, na_position='last', ascending=True),
1424
+ linewidth=1.0, height=0.1, x=name, ax=ax)
1425
+
1426
+ ax.set_ylabel(f' {j}', rotation=0)
1427
+ ax.yaxis.set_label_position('right')
1428
+
1429
+ fig.text(-0.07, 0.5*max_slice, stat, horizontalalignment='left', verticalalignment='baseline', rotation='vertical', transform=ax.transAxes)
1430
+ fig.text(1.05, 0.5*max_slice, 'slice', horizontalalignment='right', verticalalignment='baseline', rotation='vertical', transform=ax.transAxes)
1431
+
1432
+ # plot = fig.get_figure()
1433
+ # file_name = file_names['plot%s' % i]
1434
+ # save_name = f"{args.out_dir}/{file_name}.png"
1435
+ # if not _console:
1436
+ # plt.suptitle(args.tile, size=10)
1437
+ # plot.savefig(save_name, dpi=args.dpi)
1438
+ # print("# {} has been saved in {}.".format(file_name, args.out_dir))
1439
+ plt.show()
1440
+
1441
+ else:
1442
+ init_plot_style(size=1.3)
1443
+
1444
+ num = 8
1445
+ page_num = math.ceil(len(param_names)/num)
1446
+ params = locals()
1447
+ file_names = locals()
1448
+ start = 0
1449
+ for i in range(0, page_num):
1450
+ fig, axis = plt.subplots(2, 4, sharey=sharey, sharex=sharex, figsize=(16, 12))
1451
+ plt.subplots_adjust(hspace=0.3, wspace=0.25, bottom=0.2)
1452
+ # title = '\nPage ' + str(i+1) + ' of ' + str(page_num)
1453
+
1454
+ if start+num-1 <= len(param_names):
1455
+ params['params%s' % i] = param_names[start:start+num]
1456
+ else:
1457
+ params['params%s' % i] = param_names[start:]
1458
+ start = start+num
1459
+ file_names['plot%s' % i] = args.file_name.split('.')[0]+str(i) if args.file_name else str(i)
1460
+
1461
+ page_plots = min(num, len(params['params%s' % i]))
1462
+
1463
+ row = column = idx = 0
1464
+ for idx in range(num):
1465
+ ax = axis[row, column]
1466
+ if idx < page_plots:
1467
+ name = params['params%s' % i][idx]
1468
+ kde = False
1469
+ if src_vars[name]['type'] != 'c':
1470
+ kde = True
1471
+
1472
+ fig = sns.histplot(data=new_data.sort_values(by=name, na_position='last', ascending=True),
1473
+ x=name, stat=stat, kde=kde, ax=ax)
1474
+ if kde and density:
1475
+ fig = sns.rugplot(data=new_data.sort_values(by=name, na_position='last', ascending=True),
1476
+ linewidth=0.5, height=0.05, x=name, ax=ax)
1477
+
1478
+ if len(name) > 25:
1479
+ name = re.sub(r"(.{25})", "\\1\n", name)
1480
+ if column != 0:
1481
+ fig.set_ylabel('')
1482
+ fig.set_xlabel('\n'+name, rotation=0)
1483
+ else:
1484
+ ax.axis('off')
1485
+
1486
+ if column < 3:
1487
+ column += 1
1488
+ else:
1489
+ column = 0
1490
+ row += 1
1491
+
1492
+ # plot = fig.get_figure()
1493
+ # file_name = file_names['plot%s' % i]
1494
+ # save_name = f"{args.out_dir}/{file_name}.png"
1495
+
1496
+ # if not _console:
1497
+ # plot.savefig(save_name, dpi=args.dpi)
1498
+ # print("# {} has been saved in {}.".format(file_name, args.out_dir))
1499
+ plt.show()
1500
+
1501
+
1502
+ @export
1503
+ @enforce_types
1504
+ def param_boxplot(
1505
+ # required parameters
1506
+ data: CraceResults=None,
1507
+ options: CplotOptions=None,
1508
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1509
+ parameters: Union[list[str], str, None]=None,
1510
+ _console: bool=True,
1511
+ # optional parameters
1512
+ shownan: bool=True,
1513
+ nbins: int=5,
1514
+ showfliers: bool=False,
1515
+ showmeans: bool=False,
1516
+ dpi: int=800,
1517
+ # specific parameters
1518
+ y: str='slice_sampled',
1519
+ palette: str='vlag',
1520
+ fliersize: float=.5,
1521
+ monochrome: bool=True,
1522
+ ):
1523
+ """
1524
+ Entrance to call boxplot in python console
1525
+
1526
+ :param data: object CraceResults that must be provided.
1527
+ :param options: object CplotOptions that must be provided.
1528
+ :param configs: Optional. Selected configurations for plotting, higher priority than option numConfigurations/selConfigurations.
1529
+ | Union[Literal['all', 'final', 'elites'], list[int], None]
1530
+ :param parameters: A list of parameter names selected for plotting, higher priority than option multiParameters.
1531
+ :param y: A string of parameter name used to map value aspects.
1532
+ | str='slice_sampled'
1533
+ :param shownan: A boolean value used to plot including/excluding missing values.
1534
+ :param showfliers: A boolean value used to plot including/excluding outliers.
1535
+ :param showmeans: A boolean value used to plot including/excluding mean values.
1536
+ :param palette: A string of palatte name for plotting.
1537
+ :param monochrome: A boolean value used to enable/disable coloring the plot.
1538
+ """
1539
+ kwargs = locals()
1540
+ plot_parameters(method='box', **kwargs)
1541
+
1542
+ @enforce_types
1543
+ @register_param(['boxplot', 'box'])
1544
+ def _boxplot(
1545
+ # required parameters
1546
+ data: CraceResults=None,
1547
+ options: CplotOptions=None,
1548
+ configs: Union[Literal['all', 'final', 'elites'], list[int], None]='all',
1549
+ parameters: Union[list[str], str, None]=None,
1550
+ _console: bool=True,
1551
+ # optional parameters
1552
+ shownan: bool=True,
1553
+ nbins: int=5,
1554
+ showfliers: bool=False,
1555
+ showmeans: bool=False,
1556
+ dpi: int=800,
1557
+ # specific parameters
1558
+ y: str='slice_sampled',
1559
+ palette: str='vlag',
1560
+ fliersize: float=.5,
1561
+ monochrome: bool=True,
1562
+ ):
1563
+ """drawing box plot"""
1564
+
1565
+ try:
1566
+ import matplotlib.pyplot as plt
1567
+ except ImportError as e:
1568
+ raise e
1569
+
1570
+ if isinstance(parameters, str):
1571
+ parameters = [parameters]
1572
+
1573
+ args = SimpleNamespace(
1574
+ data=safe_copy(data), options=safe_copy(options), sel_vars=safe_copy(parameters),
1575
+ sel_kvar=y, slice=True,
1576
+ out_dir=None, file_name=None, tile=None,
1577
+ cat=True, console=_console,
1578
+ configs=configs,
1579
+ # optional parameters
1580
+ showscale=False, shownan=shownan, showfliers=showfliers, showmeans=showmeans,
1581
+ dpi=dpi, nbins=nbins,
1582
+ )
1583
+
1584
+ new_data, _, new_vars = _resolve_input(name='box', args=args)
1585
+ param_names = args.sel_vars
1586
+
1587
+ init_plot_style()
1588
+
1589
+ if len(param_names) >= 6:
1590
+ num = 6
1591
+ page_num = math.ceil(len(param_names)/num)
1592
+ params = locals()
1593
+ file_names = locals()
1594
+ start = 0
1595
+ for i in range(0, page_num):
1596
+ fig, axis = plt.subplots(2, 3, sharey=False, sharex=False)
1597
+ plt.subplots_adjust(hspace=0.5, wspace=0.4, bottom=0.2)
1598
+ # title = '\nPage ' + str(i+1) + ' of ' + str(page_num)
1599
+
1600
+ if start+num-1 <= len(param_names):
1601
+ params['params%s' % i] = param_names[start:start+num]
1602
+ else:
1603
+ params['params%s' % i] = param_names[start:]
1604
+ start = start+num
1605
+ file_names['plot%s' % i] = args.file_name.split('.')[0]+str(i) if args.file_name else str(i)
1606
+
1607
+ page_plots = min(num, len(params['params%s' % i]))
1608
+
1609
+ row = column = idx = 0
1610
+ for idx in range(num):
1611
+ ax = axis[row, column]
1612
+ if idx < page_plots:
1613
+ name = params['params%s' % i][idx]
1614
+ if new_vars[name]['type'] != 'c':
1615
+ fig = _sns_boxplot(data=new_data, x=name, y=y, hue=y, ax=ax,
1616
+ fliersize=fliersize, palette=palette, monochrome=monochrome, args=args)
1617
+
1618
+ if len(name) > 25:
1619
+ name = re.sub(r"(.{25})", "\\1\n", name)
1620
+ if column != 0:
1621
+ fig.set_ylabel('')
1622
+ fig.set_xlabel('\n'+name, rotation=0)
1623
+
1624
+ else:
1625
+ _barh_plot(data=new_data, x=name, y=y, fliersize=fliersize, palette=palette, ax=ax, monochrome=monochrome)
1626
+
1627
+ if len(name) > 25:
1628
+ name = re.sub(r"(.{25})", "\\1\n", name)
1629
+ if column != 0:
1630
+ ax.set_ylabel('')
1631
+ else:
1632
+ ax.set_ylabel(y, rotation=90)
1633
+ ax.set_xlabel('\n'+name, rotation=0)
1634
+
1635
+ else:
1636
+ ax.axis('off')
1637
+
1638
+ if column < 2:
1639
+ column += 1
1640
+ else:
1641
+ column = 0
1642
+ row += 1
1643
+
1644
+ # plot = fig.get_figure()
1645
+ # file_name = file_names['plot%s' % i]
1646
+ # save_name = f"{args.out_dir}/{file_name}.png"
1647
+
1648
+ # if not _console:
1649
+ # plt.suptitle(args.tile, size=15)
1650
+ # plot.savefig(save_name, dpi=args.dpi)
1651
+ # print("# {} has been saved in {}.".format(file_name, args.out_dir))
1652
+ plt.show()
1653
+
1654
+ else:
1655
+ page_num = len(param_names)
1656
+ file_names = locals()
1657
+ start = 0
1658
+ for i in range(0, page_num):
1659
+ plt.clf()
1660
+ ax = plt.gca()
1661
+
1662
+ # title = '\nPage ' + str(i+1) + ' of ' + str(page_num)
1663
+ # file_names['plot%s' % i] = args.file_name.split('.')[0]+'-'+param_names[i] if args.file_name else str(param_names[i])
1664
+ name = param_names[i]
1665
+
1666
+ if new_vars[name]['type'] != 'c':
1667
+ fig = _sns_boxplot(data=new_data, x=name, y=y, hue=y, ax=ax, args=args,
1668
+ fliersize=fliersize, palette=palette, monochrome=monochrome)
1669
+ if len(name) > 25:
1670
+ name = re.sub(r"(.{25})", "\\1\n", name)
1671
+ fig.set_xlabel('\n'+name, rotation=0)
1672
+
1673
+ else:
1674
+ _barh_plot(data=new_data, x=name, y=y, fliersize=fliersize, palette=palette, ax=ax, monochrome=monochrome)
1675
+
1676
+ if len(name) > 25:
1677
+ name = re.sub(r"(.{25})", "\\1\n", name)
1678
+ ax.set_xlabel('\n'+name, rotation=0)
1679
+ ax.set_ylabel(y, rotation=90)
1680
+
1681
+ # plot = fig.get_figure()
1682
+ # file_name = file_names['plot%s' % i]
1683
+ # save_name = f"{args.out_dir}/{file_name}.png"
1684
+
1685
+ # if not _console:
1686
+ # plt.suptitle(title, size=15)
1687
+ # plot.savefig(save_name, dpi=args.dpi)
1688
+ # print("# {} has been saved in {}.".format(file_name, args.out_dir))
1689
+ plt.show()
1690
+
1691
+ def _sns_boxplot(data, x, y, hue, ax, fliersize, palette, monochrome, args):
1692
+ try:
1693
+ import seaborn as sns
1694
+ except ImportError as e:
1695
+ raise e
1696
+
1697
+ if monochrome:
1698
+ fig = sns.boxplot(data=data, x=x, y=y, hue=hue, legend=None, ax=ax,
1699
+ whis=0.5, showfliers=args.showfliers, fliersize=fliersize,
1700
+ showmeans=args.showmeans, orient='h',
1701
+ width=0.4, linewidth=2*fliersize,
1702
+ palette=palette,
1703
+ meanprops={"marker": "x",
1704
+ "markeredgecolor": "black",
1705
+ "markersize": 1,
1706
+ "linewidth": 2*fliersize},
1707
+ boxprops=dict(facecolor="white"),
1708
+ medianprops={"linewidth": 2*fliersize, "color": "black"},
1709
+ whiskerprops={"linestyle": "--", "linewidth": 2*fliersize},
1710
+ )
1711
+ else:
1712
+ fig = sns.boxplot(data=data, x=x, y=y, hue=hue, legend=None, ax=ax,
1713
+ whis=0.5, showfliers=args.showfliers, fliersize=fliersize,
1714
+ showmeans=args.showmeans, orient='h',
1715
+ width=0.4, linewidth=2*fliersize,
1716
+ palette=palette,)
1717
+ return fig
1718
+
1719
+ def _make_hatches(n, base=['//', '||', '--', '\\\\', '++', 'xx', '..',], max_repeat=7):
1720
+ hatches = []
1721
+ for r in range(1, max_repeat + 1):
1722
+ for b in base:
1723
+ hatches.append(b * r)
1724
+ if len(hatches) >= n:
1725
+ return hatches
1726
+ return hatches
1727
+
1728
+ def _barh_plot(data, x, y, ax, fliersize, palette, monochrome):
1729
+ """
1730
+ Draw 'boxplot' for categorical parameter
1731
+ """
1732
+ try:
1733
+ import seaborn as sns
1734
+ except ImportError as e:
1735
+ raise e
1736
+
1737
+ new_data = (data.groupby([y, x]).size().reset_index(name="count"))
1738
+ new_data["ratio"] = new_data.groupby(y)["count"].transform(lambda x: x / x.sum())
1739
+
1740
+ mat = new_data.pivot(index=y, columns=x, values="ratio").fillna(0)
1741
+ mat = mat.sort_index(ascending=False)
1742
+
1743
+ y_new = np.arange(len(mat))
1744
+ left = np.zeros(len(mat))
1745
+
1746
+ if monochrome:
1747
+ try:
1748
+ import matplotlib.patches as mpatches
1749
+ except ImportError as e:
1750
+ raise e
1751
+
1752
+ hatches = _make_hatches(len(mat.columns))
1753
+ handles = []
1754
+
1755
+ for i, col in enumerate(mat.columns):
1756
+ ax.barh(y_new, mat[col].values,
1757
+ left=left, height=0.4,
1758
+ facecolor="white",
1759
+ edgecolor="black",
1760
+ linewidth=2 * fliersize,
1761
+ hatch=hatches[i])
1762
+
1763
+ handles.append(
1764
+ mpatches.Patch(
1765
+ facecolor="white",
1766
+ edgecolor="black",
1767
+ hatch=hatches[i],
1768
+ label=str(col)
1769
+ )
1770
+ )
1771
+
1772
+ left += mat[col].values
1773
+
1774
+ ax.legend(
1775
+ handles=handles,
1776
+ title=x,
1777
+ bbox_to_anchor=(1.02, 0.5),
1778
+ loc="center left",
1779
+ frameon=False
1780
+ )
1781
+
1782
+ else:
1783
+ levels = list(mat.columns)
1784
+ palette_dict = dict(zip(levels, sns.color_palette(palette, n_colors=len(levels))))
1785
+
1786
+ for col in mat.columns:
1787
+ ax.barh(y_new, mat[col].values,
1788
+ left=left, height=0.4,
1789
+ facecolor=palette_dict[col],
1790
+ edgecolor=None,
1791
+ linewidth=2 * fliersize,
1792
+ label=str(col))
1793
+
1794
+ left += mat[col].values
1795
+
1796
+ ax.legend(
1797
+ title=x,
1798
+ bbox_to_anchor=(1.02, 0.5),
1799
+ loc="center left",
1800
+ frameon=False
1801
+ )
1802
+
1803
+ ax.set_xlim(0, 1)
1804
+ ax.set_yticks(y_new)
1805
+ ax.set_yticklabels(mat.index)