From 617b6dd19fd17315317690899a3ad0dc97b3c451 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Wed, 1 Feb 2017 21:06:04 +0000
Subject: [PATCH] Add step_curve operation and Curve interpolation plot option
---
holoviews/operation/element.py | 47 ++++++++++++++++++++++++++++++
holoviews/plotting/bokeh/chart.py | 23 +++++++++++++--
holoviews/plotting/mpl/chart.py | 10 +++++++
holoviews/plotting/plotly/chart.py | 10 +++++++
4 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/holoviews/operation/element.py b/holoviews/operation/element.py
index 130402fac5..b2b1d4acb5 100644
--- a/holoviews/operation/element.py
+++ b/holoviews/operation/element.py
@@ -605,6 +605,53 @@ def _process(self, element, key=None):
return sliced
+def pts_to_prestep(x, y):
+ steps = np.zeros((2, 2 * len(x) - 1))
+ steps[0, 0::2] = x
+ steps[0, 1::2] = steps[0, 0:-2:2]
+ steps[1:, 0::2] = y
+ steps[1:, 1::2] = steps[1:, 2::2]
+ return steps
+
+def pts_to_poststep(x, y):
+ steps = np.zeros((2, 2 * len(x) - 1))
+ steps[0, 0::2] = x
+ steps[0, 1::2] = steps[0, 2::2]
+ steps[1:, 0::2] = y
+ steps[1:, 1::2] = steps[1:, 0:-2:2]
+ return steps
+
+def pts_to_midstep(x, y):
+ steps = np.zeros((2, 2 * len(x)))
+ x = np.asanyarray(x)
+ steps[0, 1:-1:2] = steps[0, 2::2] = (x[:-1] + x[1:]) / 2
+ steps[0, 0], steps[0, -1] = x[0], x[-1]
+ steps[1:, 0::2] = y
+ steps[1:, 1::2] = steps[1:, 0::2]
+ return steps
+
+
+class step_curve(ElementOperation):
+ """
+ Resamples a Curve to represent changes in y-values as steps.
+ The transition point of the step along the x-axis is configurable
+ via the ``where`` parameter.
+ """
+
+ where = param.ObjectSelector(objects=['pre', 'mid', 'post'],
+ default='mid', doc="""
+ Controls the transition point of the step along the x-axis""")
+
+ STEP_FUNCS = {'pre': pts_to_prestep, 'mid': pts_to_midstep,
+ 'post': pts_to_poststep}
+
+ def _process(self, element, key=None):
+ x, y = element.dimension_values(0), element.dimension_values(1)
+ array = self.STEP_FUNCS[self.p.where](x, y)
+ dvals = tuple(element.dimension_values(d) for d in element.dimensions()[2:])
+ return element.clone((array[0, :], array[1, :])+dvals)
+
+
#==================#
# Other operations #
#==================#
diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py
index 0045947f2d..a38d994dd3 100644
--- a/holoviews/plotting/bokeh/chart.py
+++ b/holoviews/plotting/bokeh/chart.py
@@ -13,6 +13,7 @@
from ...element import Raster, Points, Polygons, Spikes
from ...core.util import max_range, basestring, dimension_sanitizer
from ...core.options import abbreviated_exception
+from ...operation import step_curve
from ..util import compute_sizes, get_sideplot_ranges, match_spec, map_colors
from .element import ElementPlot, ColorbarPlot, LegendPlot, line_properties, fill_properties
from .path import PathPlot, PolygonPlot
@@ -138,11 +139,20 @@ def _init_glyph(self, plot, mapping, properties):
class CurvePlot(ElementPlot):
+ interpolation = param.ObjectSelector(objects=['linear', 'steps-mid',
+ 'steps-pre', 'steps-post'],
+ default='linear', doc="""
+ Defines how the samples of the Curve are interpolated,
+ default is 'linear', other options include 'steps-mid',
+ 'steps-pre' and 'steps-post'.""")
+
style_opts = ['color'] + line_properties
_plot_methods = dict(single='line', batched='multi_line')
_mapping = {p: p for p in ['xs', 'ys', 'color', 'line_alpha']}
def get_data(self, element, ranges=None, empty=False):
+ if 'steps' in self.interpolation:
+ element = step_curve(element, where=self.interpolation.split('-')[1])
xidx, yidx = (1, 0) if self.invert_axes else (0, 1)
x = element.get_dimension(xidx).name
y = element.get_dimension(yidx).name
@@ -562,9 +572,7 @@ def _update_chart(self, key, element, ranges):
@property
def current_handles(self):
- plot = self.handles['plot']
- sources = plot.select(type=ColumnDataSource)
- return sources
+ return self.state.select(type=(ColumnDataSource, Range1d))
class BoxPlot(ChartPlot):
@@ -596,6 +604,15 @@ def _init_chart(self, element, ranges):
**properties)
+ def _update_chart(self, key, element, ranges):
+ super(BoxPlot, self)._update_chart(key, element, ranges)
+ vdim = element.vdims[0].name
+ start, end = ranges[vdim]
+ self.state.y_range.start = start
+ self.state.y_range.end = end
+
+
+
class BarPlot(ChartPlot):
"""
BarPlot allows generating single- or multi-category
diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py
index 3cfe8eccb5..bcb1148d7a 100644
--- a/holoviews/plotting/mpl/chart.py
+++ b/holoviews/plotting/mpl/chart.py
@@ -14,6 +14,7 @@
from ...core.util import (match_spec, unique_iterator, safe_unicode,
basestring, max_range, unicode)
from ...element import Points, Raster, Polygons, HeatMap
+from ...operation import step_curve
from ..util import compute_sizes, get_sideplot_ranges, map_colors
from .element import ElementPlot, ColorbarPlot, LegendPlot
from .path import PathPlot
@@ -41,6 +42,13 @@ class CurvePlot(ChartPlot):
Whether to let matplotlib automatically compute tick marks
or to allow the user to control tick marks.""")
+ interpolation = param.ObjectSelector(objects=['linear', 'steps-mid',
+ 'steps-pre', 'steps-post'],
+ default='linear', doc="""
+ Defines how the samples of the Curve are interpolated,
+ default is 'linear', other options include 'steps-mid',
+ 'steps-pre' and 'steps-post'.""")
+
relative_labels = param.Boolean(default=False, doc="""
If plotted quantity is cyclic and center_cyclic is enabled,
will compute tick labels relative to the center.""")
@@ -59,6 +67,8 @@ class CurvePlot(ChartPlot):
_plot_methods = dict(single='plot')
def get_data(self, element, ranges, style):
+ if 'steps' in self.interpolation:
+ element = step_curve(element, where=self.interpolation.split('-')[1])
xs = element.dimension_values(0)
ys = element.dimension_values(1)
dims = element.dimensions()
diff --git a/holoviews/plotting/plotly/chart.py b/holoviews/plotting/plotly/chart.py
index a703903526..efdc3fd131 100644
--- a/holoviews/plotting/plotly/chart.py
+++ b/holoviews/plotting/plotly/chart.py
@@ -3,6 +3,7 @@
from plotly.tools import FigureFactory as FF
from ...core import util
+from ...operation import step_curve
from .element import ElementPlot, ColorbarPlot
@@ -41,11 +42,20 @@ def get_data(self, element, ranges):
class CurvePlot(ElementPlot):
+ interpolation = param.ObjectSelector(objects=['linear', 'steps-mid',
+ 'steps-pre', 'steps-post'],
+ default='linear', doc="""
+ Defines how the samples of the Curve are interpolated,
+ default is 'linear', other options include 'steps-mid',
+ 'steps-pre' and 'steps-post'.""")
+
graph_obj = go.Scatter
style_opts = ['color', 'dash', 'width']
def graph_options(self, element, ranges):
+ if 'steps' in self.interpolation:
+ element = step_curve(element, where=self.interpolation.split('-')[1])
opts = super(CurvePlot, self).graph_options(element, ranges)
opts['mode'] = 'lines'
style = self.style[self.cyclic_index]