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]