From 5a12882da9ceeb914e8e2ba27ea4793aace67808 Mon Sep 17 00:00:00 2001 From: philippjfr Date: Tue, 9 Jun 2015 14:49:41 +0100 Subject: [PATCH] Improved legends and tick labels on Bars --- holoviews/plotting/chart.py | 36 ++++++++++++++++-------------- holoviews/plotting/element.py | 41 +++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 32 deletions(-) diff --git a/holoviews/plotting/chart.py b/holoviews/plotting/chart.py index 14aa47aab8..c889ed79dd 100644 --- a/holoviews/plotting/chart.py +++ b/holoviews/plotting/chart.py @@ -11,7 +11,7 @@ from ..core import OrderedDict, NdMapping, ViewableElement, CompositeOverlay, HoloMap from ..core.util import match_spec from ..element import Scatter, Curve, Histogram, Bars, Points, Raster, VectorField, ErrorBars, Polygons -from .element import ElementPlot +from .element import ElementPlot, LegendPlot from .plot import MPLPlot @@ -735,7 +735,7 @@ def update_handles(self, axis, view, key, ranges=None): quiver.set_clim(ranges[magnitude_dim]) -class BarPlot(ElementPlot): +class BarPlot(LegendPlot): group_index = param.Integer(default=0, doc=""" Index of the dimension in the supplied Bars @@ -764,6 +764,13 @@ class BarPlot(ElementPlot): style_opts = ['alpha', 'color', 'align', 'visible', 'edgecolor', 'log', 'facecolor', 'capsize', 'error_kw', 'hatch'] + legend_specs = dict(LegendPlot.legend_specs, **{ + 'top': dict(bbox_to_anchor=(0., 1.02, 1., .102), + ncol=3, loc=3, mode="expand", borderaxespad=0.), + 'bottom': dict(ncol=3, mode="expand", loc=2, + bbox_to_anchor=(0., -0.4, 1., .102), + borderaxespad=0.1)}) + _dimensions = OrderedDict([('group', 0), ('category',1), ('stack',2)]) @@ -833,22 +840,17 @@ def initialize_plot(self, ranges=None): ranges = match_spec(element, ranges) dims = element.dimensions('key', label=True) - self.handles['bars'], xticks = self._create_bars(axis, element) + self.handles['bars'], xticks, xlabel = self._create_bars(axis, element) self.handles['legend_handle'] = self.handles['bars'] - self._set_ticks(axis, dims, xticks) - return self._finalize_axis(key, ranges=ranges, ylabel=str(vdim)) + return self._finalize_axis(key, ranges=ranges, xticks=xticks, xlabel=xlabel, ylabel=str(vdim)) - def _set_ticks(self, axis, dims, xticks): + def _finalize_ticks(self, axis, xticks, yticks, zticks): """ Apply ticks with appropriate offsets. """ - ndims = len(dims) - idx = self.group_index if self.group_index < ndims else self.category_index ticks, labels, yalignments = zip(*sorted(xticks, key=lambda x: x[0])) - axis.set_xlabel(dims[idx], labelpad=-0.15 if idx < ndims else -0.05) - axis.set_xticks(ticks) - axis.set_xticklabels(labels) + super(BarPlot, self)._finalize_ticks(axis, [ticks, labels], yticks, zticks) for t, y in zip(axis.get_xticklabels(), yalignments): t.set_y(y) @@ -864,6 +866,7 @@ def _create_bars(self, axis, element): style_opts, color_groups, sopts = self._compute_styles(element, style_groups) dims = element.dimensions('key', label=True) ndims = len(dims) + xlabel = ' / '.join([str(d) for d in [cdim, gdim] if d is not None]) # Compute widths width = (1-(2.*self.padding)) / len(values['category']) @@ -886,8 +889,7 @@ def _create_bars(self, axis, element): style_key[idx] = grp_name val_key[gi] = grp_name if ci < ndims: - yalign = -0.125 - xticks.append((gidx+0.5, dims[ci], -0.05)) + yalign = -0.04 else: yalign = 0 xticks.append((gidx+0.5, grp_name, yalign)) @@ -921,9 +923,11 @@ def _create_bars(self, axis, element): labels.append(label) title = [str(element.kdims[indices[cg]]) for cg in self.color_by if indices[cg] < ndims] - if any(len(l) for l in labels): - axis.legend(title=', '.join(title)) - return bars, xticks + if self.show_legend and any(len(l) for l in labels): + leg_spec = self.legend_specs[self.legend_position] + if self.legend_cols: leg_spec['ncol'] = self.legend_cols + axis.legend(title=', '.join(title), **leg_spec) + return bars, xticks, xlabel def update_handles(self, axis, element, key, ranges=None): diff --git a/holoviews/plotting/element.py b/holoviews/plotting/element.py index 123d161c4f..e421d06cd9 100644 --- a/holoviews/plotting/element.py +++ b/holoviews/plotting/element.py @@ -495,38 +495,46 @@ def update_handles(self, axis, view, key, ranges=None): raise NotImplementedError -class OverlayPlot(ElementPlot): - """ - OverlayPlot supports compositors processing of Overlays across maps. - """ - - style_grouping = param.Integer(default=2, - doc="""The length of the type.group.label - spec that will be used to group Elements into style groups, i.e. - a style_grouping value of 1 will group just by type, a value of 2 - will group by type and group and a value of 3 will group by the - full specification.""") +class LegendPlot(ElementPlot): show_legend = param.Boolean(default=True, doc=""" Whether to show legend for the plot.""") + legend_cols = param.Integer(default=None, doc=""" + Number of legend columns in the legend.""") + legend_position = param.ObjectSelector(objects=['inner', 'right', 'bottom', 'top', - 'left'], + 'left', 'best'], default='inner', doc=""" Allows selecting between a number of predefined legend position options. The predefined options may be customized in the legend_specs class attribute.""") legend_specs = {'inner': {}, - 'left': dict(bbox_to_anchor=(-.15, 1)), - 'right': dict(bbox_to_anchor=(1.25, 1)), + 'best': {}, + 'left': dict(bbox_to_anchor=(-.15, 1), loc=1), + 'right': dict(bbox_to_anchor=(1.05, 1), loc=2), 'top': dict(bbox_to_anchor=(0., 1.02, 1., .102), - ncol=3, mode="expand", borderaxespad=0.), - 'bottom': dict(ncol=3, mode="expand", + ncol=3, loc=3, mode="expand", borderaxespad=0.), + 'bottom': dict(ncol=3, mode="expand", loc=2, bbox_to_anchor=(0., -0.25, 1., .102), borderaxespad=0.1)} + + +class OverlayPlot(LegendPlot): + """ + OverlayPlot supports compositors processing of Overlays across maps. + """ + + style_grouping = param.Integer(default=2, + doc="""The length of the type.group.label + spec that will be used to group Elements into style groups, i.e. + a style_grouping value of 1 will group just by type, a value of 2 + will group by type and group and a value of 3 will group by the + full specification.""") + def __init__(self, overlay, ranges=None, **params): super(OverlayPlot, self).__init__(overlay, ranges=ranges, **params) @@ -642,6 +650,7 @@ def _adjust_legend(self, axis): legend.set_visible(False) else: leg_spec = self.legend_specs[self.legend_position] + if self.legend_cols: leg_spec['ncol'] = self.legend_cols leg = axis.legend(data.keys(), data.values(), title=title, scatterpoints=1, **leg_spec)