def __init__(self, show_missing=True, get_category=None, **kwargs): """ ``kwargs['attributes']`` must contain exactly one attribute. If only one of the two configurations has a value for a run, only add a coordinate if *show_missing* is True. *get_category* can be a function that takes **two** runs (dictionaries of properties) and returns a category name. This name is used to group the points in the plot. Runs for which this function returns None are shown in a default category and are not contained in the legend. For example, to group by domain use:: def domain_as_category(run1, run2): # run2['domain'] has the same value, because we always # compare two runs of the same problem return run1['domain'] *get_category* and *category_styles* (see :py:class:`PlotReport <downward.reports.plot.PlotReport>`) are best used together, e.g. to distinguish between different levels of difficulty:: def improvement(run1, run2): time1 = run1.get('search_time', 1800) time2 = run2.get('search_time', 1800) if time1 > 10 * time2: return 'strong' if time1 >= time2: return 'small' return 'worse' styles = { 'strong': ('x','r'), 'small': ('*','b'), 'worse': ('o','y'), } PlotReport(attributes=['search_time'], get_category=improvement, category_styles=styles) """ kwargs.setdefault('legend_location', (1.3, 0.5)) # If the size has not been set explicitly, make it a square. params = kwargs.get('params', {}) params.setdefault('figure.figsize', [8, 8]) kwargs['params'] = params PlotReport.__init__(self, **kwargs) assert self.attribute, 'ScatterPlotReport needs exactly one attribute' # By default all values are in the same category. self.get_category = get_category or (lambda run1, run2: None) self.show_missing = show_missing self.xlim_left = self.xlim_left or EPSILON self.ylim_bottom = self.ylim_bottom or EPSILON if self.output_format == 'tex': self.writer = ScatterPgfPlots else: self.writer = ScatterMatplotlib
def _set_scales(self, xscale, yscale): # ScatterPlots use log-scaling on the x-axis by default. default_xscale = 'log' if self.attribute and self.attribute in self.LINEAR: default_xscale = 'linear' PlotReport._set_scales(self, xscale or default_xscale, yscale) if self.xscale != self.yscale: logging.critical('Scatterplots must use the same scale on both axes.')
def _set_scales(self, xscale, yscale): # ScatterPlots use log-scaling on the x-axis by default. default_xscale = 'log' if self.attribute and self.attribute in self.LINEAR: default_xscale = 'linear' PlotReport._set_scales(self, xscale or default_xscale, yscale) if self.xscale != self.yscale: logging.critical( 'Scatterplots must use the same scale on both axes.')
def _prepare_categories(self, categories): categories = PlotReport._prepare_categories(self, categories) # Find max-value to fit plot and to draw missing values. self.missing_val = self._get_missing_val(max(self.max_x, self.max_y)) new_categories = {} for category, coords in categories.items(): X, Y = zip(*coords) X, Y = self._handle_none_values(X, Y, self.missing_val) coords = zip(X, Y) new_categories[category] = coords return new_categories
def _prepare_categories(self, categories): categories = PlotReport._prepare_categories(self, categories) # Find max-value to fit plot and to draw missing values. # self.missing_val = self._get_missing_val(max(self.max_x, self.max_y)) self.x_missing_val = self._get_missing_val(self.max_x, self.xscale) self.y_missing_val = self._get_missing_val(self.max_y, self.yscale) # print self.x_missing_val, self.y_missing_val # set minima self.xlim_left = self._get_limit([self.xlim_left, self.min_x], 'min') self.ylim_bottom = self._get_limit([self.ylim_bottom, self.min_y], 'min') # set maxima x_plot_size = y_plot_size = None if self.show_missing: x_plot_size = self._get_plot_size(self.x_missing_val, self.xscale) y_plot_size = self._get_plot_size(self.y_missing_val, self.yscale) self.xlim_right = self._get_limit( [self.xlim_right, self.max_x, x_plot_size], 'max') self.ylim_top = self._get_limit( [self.ylim_top, self.max_y, y_plot_size], 'max') # self.diagonal_start = self.diagonal_end = None # if self.show_diagonal: # self.diagonal_start = max(self.xlim_left, self.ylim_bottom) # self.diagonal_end = min(self.xlim_right, self.ylim_top) new_categories = {} for category, coords in categories.items(): X, Y = zip(*coords) # X, Y = self._handle_none_values(X, Y, self.missing_val) X, Y = self._handle_none_values(X, Y, self.x_missing_val, self.y_missing_val) coords = zip(X, Y) new_categories[category] = coords # print len(new_categories[None]) # print new_categories[None] return new_categories
def _set_scales(self, xscale, yscale): # ScatterPlots use log-scaling on the x-axis by default. default_xscale = 'log' if self.attribute and self.attribute in self.LINEAR: default_xscale = 'linear' PlotReport._set_scales(self, xscale or default_xscale, 'log')
def _set_scales(self, xscale, yscale): # ScatterPlot uses log-scaling on the x-axis by default. PlotReport._set_scales(self, xscale or self.attribute.scale or 'log', 'log')
def _set_scales(self, xscale, yscale): PlotReport._set_scales(self, xscale or self.attribute.scale or 'log', yscale) if self.xscale != self.yscale: logging.critical( 'Scatterplots must use the same scale on both axes.')
def __init__(self, x_algo, y_algo, x_attribute, y_attribute, show_missing=True, get_category=None, **kwargs): """ See :class:`.PlotReport` for inherited arguments. The keyword argument *attributes* must contain exactly one attribute. Use the *filter_algorithm* keyword argument to select exactly two algorithms. If only one of the two algorithms has a value for a run, only add a coordinate if *show_missing* is True. *get_category* can be a function that takes **two** runs (dictionaries of properties) and returns a category name. This name is used to group the points in the plot. If there is more than one group, a legend is automatically added. Runs for which this function returns None are shown in a default category and are not contained in the legend. For example, to group by domain: >>> def domain_as_category(run1, run2): ... # run2['domain'] has the same value, because we always ... # compare two runs of the same problem. ... return run1['domain'] Example grouping by difficulty: >>> def improvement(run1, run2): ... time1 = run1.get('search_time', 1800) ... time2 = run2.get('search_time', 1800) ... if time1 > time2: ... return 'better' ... if time1 == time2: ... return 'equal' ... return 'worse' >>> from downward.experiment import FastDownwardExperiment >>> exp = FastDownwardExperiment() >>> exp.add_report(ScatterPlotReport( ... attributes=['search_time'], ... get_category=improvement)) Example comparing the number of expanded states for two algorithms: >>> exp.add_report(ScatterPlotReport( ... attributes=["expansions_until_last_jump"], ... filter_algorithm=["algorithm-1", "algorithm-2"], ... get_category=domain_as_category, ... format="png", # Use "tex" for pgfplots output. ... ), ... name="scatterplot-expansions") """ # If the size has not been set explicitly, make it a square. matplotlib_options = kwargs.get('matplotlib_options', {}) matplotlib_options.setdefault('figure.figsize', [8, 8]) kwargs['matplotlib_options'] = matplotlib_options PlotReport.__init__(self, **kwargs) if not self.attribute: logging.critical('ScatterPlotReport needs exactly one attribute') # By default all values are in the same category. self.get_category = get_category or (lambda run1, run2: None) self.show_missing = show_missing self.xlim_left = self.xlim_left or MIN_AXIS self.ylim_bottom = self.ylim_bottom or MIN_AXIS if self.output_format == 'tex': self.writer = ScatterPgfPlots else: self.writer = ScatterMatplotlib self.x_algo = x_algo self.y_algo = y_algo self.x_attribute = x_attribute self.y_attribute = y_attribute
def _set_scales(self, xscale, yscale): # ScatterPlots use log-scaling on the x-axis by default. default_xscale = 'log' if self.attribute and self.attribute in self.LINEAR: default_xscale = 'linear' PlotReport._set_scales(self, xscale or default_xscale, 'linear')