Source code for pypesto.visualize.misc

import warnings
from numbers import Number
from typing import Iterable, List, Optional, Union

import numpy as np

from ..C import (
    LEN_RGB,
    LEN_RGBA,
    RGB,
    RGB_RGBA,
    RGBA_ALPHA,
    RGBA_MAX,
    RGBA_MIN,
    RGBA_WHITE,
)
from .clust_color import assign_colors, assign_colors_for_list


[docs]def process_result_list(results, colors=None, legends=None): """ Assign colors and legends to a list of results, check user provided lists. Parameters ---------- results: list or pypesto.Result list of pypesto.Result objects or a single pypesto.Result colors: list, optional list of RGBA colors legends: str or list labels for line plots Returns ------- results: list of pypesto.Result list of pypesto.Result objects colors: list of RGBA One for each element in 'results'. legends: list of str labels for line plots """ # check how many results were passed single_result = False legend_error = False if isinstance(results, list): if len(results) == 1: single_result = True else: single_result = True results = [results] # handle results according to their number if single_result: # assign colors and create list for later handling if colors is not None: colors = assign_colors(results, colors) colors = [colors] # create list of legends for later handling if not isinstance(legends, list): legends = [legends] else: # if more than one result is passed, we use one color per result colors = assign_colors_for_list(len(results), colors) # check whether list of legends has the correct length if legends is None: # No legends were passed: create some custom legends legends = [] for i_leg in range(len(results)): legends.append('Result ' + str(i_leg)) else: # legends were passed by user: check length if isinstance(legends, list): if len(legends) != len(results): legend_error = True else: legend_error = True # size of legend list and size of results does not match if legend_error: raise ValueError('List of results passed and list of labels do ' 'not have the same length but should. Stopping.') return results, colors, legends
[docs]def process_offset_y(offset_y: Optional[float], scale_y: str, min_val: float) -> float: """ Compute offset for y-axis, depend on user settings. Parameters ---------- offset_y: value for offsetting the later plotted values, in order to ensure positivity if a semilog-plot is used scale_y: Can be 'lin' or 'log10', specifying whether values should be plotted on linear or on log10-scale min_val: Smallest value to be plotted Returns ------- offset_y: float value for offsetting the later plotted values, in order to ensure positivity if a semilog-plot is used """ # check whether the offset specified by the user is sufficient if offset_y is not None: if (scale_y == 'log10') and (min_val + offset_y <= 0.): warnings.warn("Offset specified by user is insufficient. " "Ignoring specified offset and using " + str(np.abs(min_val) + 1.) + " instead.") else: return offset_y else: # check whether scaling is lin or log10 if scale_y == 'lin': # linear scaling doesn't need any offset return 0. return 1. - min_val
[docs]def process_y_limits(ax, y_limits): """ Apply user specified limits of y-axis. Parameters ---------- ax: matplotlib.Axes, optional Axes object to use. y_limits: ndarray y_limits, minimum and maximum, for current axes object Returns ------- ax: matplotlib.Axes, optional Axes object to use. """ # apply y-limits, if they were specified by the user if y_limits is not None: y_limits = np.array(y_limits) # check validity of bounds if y_limits.size == 0: y_limits = np.array(ax.get_ylim()) elif y_limits.size == 1: # if the user specified only an upper bound tmp_y_limits = ax.get_ylim() y_limits = [tmp_y_limits[0], y_limits] elif y_limits.size > 1: y_limits = [y_limits[0], y_limits[1]] # check validity of bounds if plotting in log-scale if ax.get_yscale() == 'log' and y_limits[0] <= 0.: tmp_y_limits = ax.get_ylim() if y_limits[1] <= 0.: y_limits = tmp_y_limits warnings.warn("Invalid bounds for plotting in " "log-scale. Using defaults bounds.") else: y_limits = [tmp_y_limits[0], y_limits[1]] warnings.warn("Invalid lower bound for plotting in " "log-scale. Using only upper bound.") # set limits ax.set_ylim(y_limits) else: # No limits passed, but if we have a result list: check the limits ax_limits = np.array(ax.get_ylim()) data_limits = ax.dataLim.ymin, ax.dataLim.ymax # Check if data fits to axes and adapt limits, if necessary if ax_limits[0] > data_limits[0] or ax_limits[1] < data_limits[1]: # Get range of data data_range = data_limits[1] - data_limits[0] if ax.get_yscale() == 'log': data_range = np.log10(data_range) new_limits = ( np.power(10, np.log10(data_limits[0]) - 0.02 * data_range), np.power(10, np.log10(data_limits[1]) + 0.02 * data_range)) else: new_limits = (data_limits[0] - 0.02 * data_range, data_limits[1] + 0.02 * data_range) # set limits ax.set_ylim(new_limits) return ax
def process_start_indices(start_indices: Union[int, Iterable[int]], max_length: int) -> List[int]: """ Process the start_indices. Create an array of indices if a number was provided and checks that the indices do not exceed the max_index. Parameters ---------- start_indices: list of indices or int specifying an endpoint of the sequence of indices max_length: maximum possible index for the start_indices """ if isinstance(start_indices, Number): start_indices = range(int(start_indices)) start_indices = np.array(start_indices, dtype=int) # check, whether index set is not too big start_indices = [start_index for start_index in start_indices if start_index < max_length] return start_indices def rgba2rgb(fg: RGB_RGBA, bg: RGB_RGBA = None) -> RGB: """Combine two colors, removing transparency. Parameters ---------- fg: Foreground color. bg: Background color. Returns ------- The combined color. """ if bg is None: bg = RGBA_WHITE if len(bg) == LEN_RGBA: # return foreground if background is fully transparent if bg[RGBA_ALPHA] == RGBA_MIN: return fg else: if len(bg) != LEN_RGB: raise IndexError( 'A background color of unexpected length was provided: {bg}' ) bg = (*bg, RGBA_MAX) # return the foreground color if has no transparency if len(fg) == LEN_RGB or fg[RGBA_ALPHA] == RGBA_MAX: return fg if len(fg) != LEN_RGBA: raise IndexError( 'A foreground color of unexpected length was provided: {fg}' ) def apparent_composite_color_component( fg_component: float, bg_component: float, fg_alpha: float = fg[RGBA_ALPHA], bg_alpha: float = bg[RGBA_ALPHA], ) -> float: """ Composite a foreground over a background color component. Porter and Duff equations are used for alpha compositing. Parameters ---------- fg_component: The foreground color component. bg_component: The background color component. fg_alpha: The foreground color transparency/alpha component. bg_alpha: The background color transparency/alpha component. Returns ------- The component of the new color. """ return ( fg_component * fg_alpha + bg_component * bg_alpha * (RGBA_MAX - fg_alpha) ) / (fg_alpha + bg_alpha * (RGBA_MAX - fg_alpha)) return [ apparent_composite_color_component(fg[i], bg[i]) for i in range(LEN_RGB) ]