Source code for modeva.utils.results

import copy
from typing import Tuple, Union

import mocharts as mc
from IPython.display import HTML


[docs] class ValidationResult: """A class to handle validation results and plotting functionality. This class stores validation data and provides methods to plot and save figures using the mocharts library. Attributes ---------- key : str Name of the validation result data : str, default=None Data used in the validation model : str, default=None Model used in the validation inputs : dict, default=None Input arguments used in the validation value : any, default=None Detailed validation result values table : pd.DataFrame or dict of pd.DataFrame, default=None Table data associated with the validation func : any, default=None Function used in the validation options : dict, default=None Plotting options and configurations """ def __init__(self, key, data=None, model=None, inputs=None, value=None, table=None, func=None, options=None): self.key = key self.data = data self.model = model self.inputs = inputs self.value = value self.table = table self.func = func self.options = options def _flatten_options(self, options, parent_key=None): """Flatten nested options dictionary into a list of (key, options) pairs. This internal method traverses a nested dictionary of plotting options and yields tuples containing the full path to each chart configuration and its options. Parameters ---------- options : dict The nested dictionary of plotting options parent_key : str, int, float, tuple, optional The parent key in the nested structure Yields ------ tuple A tuple of (key_path, chart_options) where key_path can be None, a single key, or a tuple of keys representing the path to the chart options """ if options is None: yield None, None elif 'chart_id' in options: yield None, options else: for key, sub_options in options.items(): if sub_options is None: continue if 'chart_id' in sub_options: if parent_key is None: yield key, sub_options elif isinstance(parent_key, (str, int, float)): yield (parent_key,) + (key,), sub_options elif isinstance(parent_key, tuple): yield parent_key + (key,), sub_options else: if parent_key is None: yield from self._flatten_options(sub_options, parent_key=key) elif isinstance(parent_key, (str, int, float)): yield from self._flatten_options(sub_options, parent_key=(parent_key,) + (key,)) elif isinstance(parent_key, tuple): yield from self._flatten_options(sub_options, parent_key=parent_key + (key,), )
[docs] def get_figure_names(self): """Show the list of figure names. Returns -------- name_list: list of str The list of figure names. Will return an empty list if there is only one figure, and it has no names. """ options_list = [] if self.options is None: return options_list options_copy = copy.deepcopy(self.options) options_list = list(self._flatten_options(options_copy)) name_list = [key for key, options in options_list if options is not None] return name_list
[docs] def plot_save(self, name: Union[str, int, float, Tuple] = None, figsize: Tuple[int] = (6, 6), file_name: str = None, format: str = 'png'): """Generate and save figures based on validation options. This method visualizes and saves figures created from the validation options using the mocharts library. The figures can be filtered by name, resized, and saved to a specified file path in the given format. Parameters ---------- name : str, int, float, tuple, default=None The name(s) of the figure(s) to visualize and save. Can be a single name or a tuple/list of names. To retrieve valid figure names, call `self.get_figure_names()`. If None, all available figures are processed. figsize : tuple, default=(6, 6) The size (width, height) of the figure(s) to save, in inches. file_name : str, default=None The base file path and name to save the figure(s). If None, the figure will be saved in the 'images/' directory using the `self.key` as the filename prefix. If multiple figures are saved, this will serve as the prefix for each figure's name. format : str, default='png' The format to save the figure(s), e.g., 'png', 'jpg', 'svg'. Raises ------ ValueError If an invalid name is provided, or if an unsupported key type is encountered. Notes ----- - The function automatically creates an 'images/' folder if no file path is specified. - Figure names are constructed by combining the `file_name` (if provided) and the figure's key. """ if self.options is None: return options_copy = copy.deepcopy(self.options) if name is not None: if name in options_copy: options_copy = options_copy[name] elif isinstance(name, (list, tuple)): for key in name: options_copy = options_copy[key] else: raise ValueError("Invalid name; call result.get_figure_names() to get the feasible names.") options_list = list(self._flatten_options(options_copy)) for key, options in options_list: if options is None: continue # TODO: improve the logic for checking whether this is a mocharts dict options['figsize'] = {'width': figsize[0] * 100, 'height': figsize[1] * 100} if file_name is None: folder_path = 'images/' new_file_name = self.key else: folder_path = '/'.join(file_name.split('/')[:-1]) + '/' new_file_name = file_name.split('/')[-1] if key is None: key = new_file_name elif isinstance(key, str): key = new_file_name + '-' + key elif isinstance(key, tuple): key = new_file_name + '-' + "-".join(key) else: raise ValueError("Invalid key type") save_fig_path = folder_path + key mc.mocharts_save(options, file_name=save_fig_path, format=format)
def _bar_plot_setting(self, options: dict, n_bars: int): for yaxis in options['yAxis']: if yaxis['type'] == 'category': yaxis['data'] = yaxis['data'][-min(n_bars, len(yaxis['data'])):] return options
[docs] def plot(self, name: [str, int, float, Tuple] = None, figsize: Tuple[int] = (6, 6), display: bool = False, n_bars: int = None, popup: bool = True): """Show the figure results. Parameters ---------- name : str, int, float, tuple, default=None The name of the figure to visualize. The available figure names can be fetched by func get_figure_names. If None, then all figures will be displayed. figsize : tuple, default=(6, 6) The size of displayed figure(s). display : bool, default=False Whether to display the figure. n_bars: int, default=None The number of displayed bars (from top). popup: bool, default=True Whether to support popup for each plot Returns ---------- fig : HTML object The figure object in html format. """ if self.options is None: return options_copy = copy.deepcopy(self.options) if name is not None: if name in options_copy: options_copy = options_copy[name] elif isinstance(name, (list, tuple)): for key in name: options_copy = options_copy[key] else: raise ValueError("Invalid name; call result.get_figure_names() to get the feasible names.") figure_outputs = "" options_list = list(self._flatten_options(options_copy)) for key, options in options_list: if options is None: continue # TODO: improve the logic for checking whether this is a mochart dict if n_bars is not None: for serie in options['series']: if serie['type'] == 'bar': options = self._bar_plot_setting(options=options, n_bars=n_bars) break options['figsize'] = {'width': figsize[0] * 100, 'height': figsize[1] * 100} if not popup: options['toolbox']['popup'] = False htmlstr = mc.mocharts_plot(options, return_html=True, silent=not display) figure_outputs += htmlstr fig = HTML(figure_outputs) return fig