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)