From 9fdad77891d755017ede926cd139e04133ff4286 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Fri, 29 Jan 2016 00:04:09 +0000
Subject: [PATCH 1/6] Added Area Element and plotting classes for mpl and bokeh
---
holoviews/element/chart.py | 13 ++++++
holoviews/plotting/bokeh/__init__.py | 8 +++-
holoviews/plotting/bokeh/chart.py | 33 ++++++++++++--
holoviews/plotting/mpl/__init__.py | 3 ++
holoviews/plotting/mpl/chart.py | 66 ++++++++++++++++++----------
5 files changed, 95 insertions(+), 28 deletions(-)
diff --git a/holoviews/element/chart.py b/holoviews/element/chart.py
index 57b049fca8..eab37db4fe 100644
--- a/holoviews/element/chart.py
+++ b/holoviews/element/chart.py
@@ -379,3 +379,16 @@ class Spikes(Chart):
_1d = True
+
+class Area(Curve):
+ """
+ An Area Element represents the area under a Curve
+ and is specified in the same format as a regular
+ Curve, with the key dimension corresponding to a
+ column of x-values and the value dimension
+ corresponding to a column of y-values. Optionally
+ a second value dimension may be supplied to shade
+ the region between the curves.
+ """
+
+ group = param.String(default='Area')
diff --git a/holoviews/plotting/bokeh/__init__.py b/holoviews/plotting/bokeh/__init__.py
index 725ab4f203..8f1962362d 100644
--- a/holoviews/plotting/bokeh/__init__.py
+++ b/holoviews/plotting/bokeh/__init__.py
@@ -4,7 +4,8 @@
RGB, Histogram, Spread, HeatMap, Contours, Bars,
Box, Bounds, Ellipse, Polygons, BoxWhisker,
ErrorBars, Text, HLine, VLine, Spline, Spikes,
- Table, ItemTable, Surface, Scatter3D, Trisurface)
+ Table, ItemTable, Surface, Scatter3D, Trisurface,
+ Area)
from ...core.options import Options, Cycle
from ...interface import DFrame
from ..plot import PlotSelector
@@ -14,7 +15,8 @@
from .callbacks import Callbacks # noqa (API import)
from .element import OverlayPlot, BokehMPLWrapper, BokehMPLRawWrapper
from .chart import (PointPlot, CurvePlot, SpreadPlot, ErrorPlot, HistogramPlot,
- SideHistogramPlot, BoxPlot, BarPlot, SpikesPlot, SideSpikesPlot)
+ SideHistogramPlot, BoxPlot, BarPlot, SpikesPlot,
+ SideSpikesPlot, AreaPlot)
from .path import PathPlot, PolygonPlot
from .plot import GridPlot, LayoutPlot, AdjointLayoutPlot
from .raster import RasterPlot, RGBPlot, HeatmapPlot
@@ -40,6 +42,7 @@
Spikes: SpikesPlot,
BoxWhisker: BoxPlot,
Bars: BarPlot,
+ Area: AreaPlot,
# Rasters
Image: RasterPlot,
@@ -118,6 +121,7 @@
options.Histogram = Options('style', fill_color="#036564", line_color="#033649")
options.Points = Options('style', color=Cycle())
options.Spikes = Options('style', color='black')
+options.Area = Options('style', color=Cycle(), line_color='black')
# Paths
options.Contours = Options('style', color=Cycle())
diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py
index 8d6f9d6adb..418bf0f655 100644
--- a/holoviews/plotting/bokeh/chart.py
+++ b/holoviews/plotting/bokeh/chart.py
@@ -4,6 +4,7 @@
import param
from ...element import Raster, Points, Polygons, Spikes
+from ...core.util import max_range
from ..util import compute_sizes, get_sideplot_ranges, match_spec
from .element import ElementPlot, line_properties, fill_properties
from .path import PathPlot, PolygonPlot
@@ -111,13 +112,38 @@ def get_data(self, element, ranges=None, empty=False):
dict(x=x, y=y))
+class AreaPlot(PolygonPlot):
+
+ def get_extents(self, element, ranges):
+ vdims = element.vdims
+ vdim = vdims[0].name
+ if len(vdims) > 1:
+ ranges[vdim] = max_range([ranges[vd.name] for vd in vdims])
+ else:
+ vdim = vdims[0].name
+ ranges[vdim] = (np.nanmin([0, ranges[vdim][0]]), ranges[vdim][1])
+ return super(AreaPlot, self).get_extents(element, ranges)
+
+ def get_data(self, element, ranges=None, empty=False):
+ mapping = dict(self._mapping)
+ if empty: return {'xs': [], 'ys': []}
+ xs = element.dimension_values(0)
+ x2 = np.hstack((xs[::-1], xs))
+
+ if len(element.vdims) > 1:
+ bottom = element.dimension_values(2)
+ else:
+ bottom = np.zeros(len(element))
+ ys = np.hstack((bottom[::-1], element.dimension_values(1)))
+
+ data = dict(xs=[x2], ys=[ys])
+ return data, mapping
+
+
class SpreadPlot(PolygonPlot):
style_opts = ['color'] + line_properties + fill_properties
- def __init__(self, *args, **kwargs):
- super(SpreadPlot, self).__init__(*args, **kwargs)
-
def get_data(self, element, ranges=None, empty=None):
if empty:
return dict(xs=[], ys=[]), self._mapping
@@ -477,4 +503,3 @@ def _init_chart(self, element, ranges):
plot = Bar(element.dframe(), values=vdim,
continuous_range=crange, **kwargs)
return plot
-
diff --git a/holoviews/plotting/mpl/__init__.py b/holoviews/plotting/mpl/__init__.py
index 19d93e85e6..e258e86a8d 100644
--- a/holoviews/plotting/mpl/__init__.py
+++ b/holoviews/plotting/mpl/__init__.py
@@ -110,6 +110,7 @@ def grid_selector(grid):
Spread: SpreadPlot,
Spikes: SpikesPlot,
BoxWhisker: BoxPlot,
+ Area: AreaPlot,
# General plots
GridSpace: GridPlot,
@@ -180,8 +181,10 @@ def grid_selector(grid):
options.Scatter3D = Options('plot', fig_size=150)
options.Surface = Options('plot', fig_size=150)
options.Spikes = Options('style', color='black')
+options.Area = Options('style', color=Cycle(), edgecolor='black')
options.BoxWhisker = Options('style', boxprops=dict(color='k'),
whiskerprops=dict(color='k'))
+
# Rasters
options.Image = Options('style', cmap='hot', interpolation='nearest')
options.Raster = Options('style', cmap='hot', interpolation='nearest')
diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py
index 8ec189d1e0..2da7a931b1 100644
--- a/holoviews/plotting/mpl/chart.py
+++ b/holoviews/plotting/mpl/chart.py
@@ -9,7 +9,8 @@
import param
from ...core import OrderedDict
-from ...core.util import match_spec, unique_iterator, safe_unicode, basestring
+from ...core.util import (match_spec, unique_iterator, safe_unicode,
+ basestring, max_range)
from ...element import Points, Raster, Polygons
from ..util import compute_sizes, get_sideplot_ranges
from .element import ElementPlot, ColorbarPlot, LegendPlot
@@ -228,18 +229,12 @@ def update_handles(self, axis, element, key, ranges=None):
[xvals[i], tdata[i]]])
-class SpreadPlot(ChartPlot):
- """
- SpreadPlot plots the Spread Element type.
- """
- style_opts = ['alpha', 'color', 'linestyle', 'linewidth',
- 'edgecolor', 'facecolor', 'hatch']
-
- def __init__(self, *args, **kwargs):
- super(SpreadPlot, self).__init__(*args, **kwargs)
- self._extent = None
+class AreaPlot(ChartPlot):
+ style_opts = ['color', 'facecolor', 'alpha', 'edgecolor', 'linewidth',
+ 'hatch', 'linestyle', 'joinstyle',
+ 'fill', 'capstyle', 'interpolate']
def initialize_plot(self, ranges=None):
element = self.hmap.last
@@ -248,27 +243,54 @@ def initialize_plot(self, ranges=None):
ranges = self.compute_ranges(self.hmap, key, ranges)
ranges = match_spec(element, ranges)
+
self.update_handles(axis, element, key, ranges)
- return self._finalize_axis(self.keys[-1], ranges=ranges)
+ ylabel = str(element.vdims[0])
+ return self._finalize_axis(self.keys[-1], ranges=ranges, ylabel=ylabel)
+
+ def get_data(self, element):
+ xs = element.dimension_values(0)
+ ys = [element.dimension_values(vdim) for vdim in element.vdims]
+ return tuple([xs]+ys)
+ def get_extents(self, element, ranges):
+ vdims = element.vdims
+ vdim = vdims[0].name
+ ranges[vdim] = max_range([ranges[vd.name] for vd in vdims])
+ return super(AreaPlot, self).get_extents(element, ranges)
def update_handles(self, axis, element, key, ranges=None):
- if 'paths' in self.handles:
- self.handles['paths'].remove()
+ if 'artist' in self.handles:
+ self.handles['artist'].remove()
- xvals = element.dimension_values(0)
+ # Create line segments and apply style
+ style = self.style[self.cyclic_index]
+ data = self.get_data(element)
+ fill_fn = axis.fill_betweenx if self.invert_axes else axis.fill_between
+ stack = fill_fn(*data, zorder=self.zorder, **style)
+ self.handles['artist'] = stack
+
+
+
+class SpreadPlot(AreaPlot):
+ """
+ SpreadPlot plots the Spread Element type.
+ """
+
+
+ def __init__(self, element, **params):
+ self.table = element.table()
+ super(SpreadPlot, self).__init__(element, **params)
+ self._extents = None
+
+ def get_data(self, element):
+ xs = element.dimension_values(0)
mean = element.dimension_values(1)
neg_error = element.dimension_values(2)
pos_idx = 3 if len(element.dimensions()) > 3 else 2
pos_error = element.dimension_values(pos_idx)
-
- paths = axis.fill_between(xvals, mean-neg_error,
- mean+pos_error, zorder=self.zorder,
- label=element.label if self.show_legend else None,
- **self.style[self.cyclic_index])
- self.handles['paths'] = paths
-
+ return xs, mean-neg_error, mean+pos_error
class HistogramPlot(ChartPlot):
From 1f68433fdbfc8a736e4d4a0d58314fc4725bf81e Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Fri, 29 Jan 2016 00:05:17 +0000
Subject: [PATCH 2/6] Added examples of Area Element to Elements Tutorial
---
doc/Tutorials/Elements.ipynb | 53 ++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/doc/Tutorials/Elements.ipynb b/doc/Tutorials/Elements.ipynb
index c727f23fb3..ff9d4b3af1 100644
--- a/doc/Tutorials/Elements.ipynb
+++ b/doc/Tutorials/Elements.ipynb
@@ -28,6 +28,7 @@
" Curve
A continuous relation between a dependent and an independent variable.\n",
" ErrorBars
A collection of x-/y-coordinates with associated error magnitudes.\n",
" Spread
Continuous version of ErrorBars.\n",
+ " Area
\n",
" Bars
Data collected and binned into categories.\n",
" Histogram
Data collected and binned in a continuous space using specified bin edges.\n",
" BoxWhisker
Distributions of data varying by 0-N key dimensions.\n",
@@ -280,6 +281,58 @@
" vdims=['y', 'yerrneg', 'yerrpos'])"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### ``Area`` "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "** *Area under the curve* **\n",
+ "\n",
+ "By default the Area Element draws just the area under the curve, i.e. the region between the curve and the origin."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "xs = np.linspace(0, np.pi*4, 40)\n",
+ "hv.Area((xs, np.sin(xs)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "** * Area between curves * **\n",
+ "\n",
+ "When supplied a second value dimension the area is defined as the area between two curves."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": [
+ "X = np.linspace(0,3,200)\n",
+ "Y = X**2 + 3\n",
+ "Y2 = np.exp(X) + 2\n",
+ "Y3 = np.cos(X)\n",
+ "hv.Area((X, Y, Y2), vdims=['y', 'y2']) * hv.Area((X, Y, Y3), vdims=['y', 'y3'])"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
From 760df94e9156dd84f1008b916e867f4930588c12 Mon Sep 17 00:00:00 2001
From: philippjfr
Date: Fri, 29 Jan 2016 02:18:00 +0000
Subject: [PATCH 3/6] Updated reference_data submodule reference
---
doc/reference_data | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/reference_data b/doc/reference_data
index feca6b6bcd..ab3290b23b 160000
--- a/doc/reference_data
+++ b/doc/reference_data
@@ -1 +1 @@
-Subproject commit feca6b6bcdb21a5face716f92921e10084bad3c1
+Subproject commit ab3290b23b00438314b42023e63bf788cd6c8cb9
From 864c6dffa1873d9fe866cb96127a853eb7941424 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Fri, 29 Jan 2016 02:36:09 +0000
Subject: [PATCH 4/6] Added Area Element comparison
---
holoviews/element/comparison.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/holoviews/element/comparison.py b/holoviews/element/comparison.py
index 740ed0d934..6fa7aa7a87 100644
--- a/holoviews/element/comparison.py
+++ b/holoviews/element/comparison.py
@@ -149,6 +149,7 @@ def register(cls):
cls.equality_type_funcs[Curve] = cls.compare_curve
cls.equality_type_funcs[ErrorBars] = cls.compare_errorbars
cls.equality_type_funcs[Spread] = cls.compare_spread
+ cls.equality_type_funcs[Area] = cls.compare_area
cls.equality_type_funcs[Scatter] = cls.compare_scatter
cls.equality_type_funcs[Scatter3D] = cls.compare_scatter3d
cls.equality_type_funcs[Trisurface] = cls.compare_trisurface
@@ -471,6 +472,10 @@ def compare_errorbars(cls, el1, el2, msg='ErrorBars'):
def compare_spread(cls, el1, el2, msg='Spread'):
cls.compare_columns(el1, el2, msg)
+ @classmethod
+ def compare_area(cls, el1, el2, msg='Area'):
+ cls.compare_columns(el1, el2, msg)
+
@classmethod
def compare_scatter(cls, el1, el2, msg='Scatter'):
cls.compare_columns(el1, el2, msg)
From 8d2173791b2778a71111454c0a7967e6066ec696 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Fri, 29 Jan 2016 02:36:31 +0000
Subject: [PATCH 5/6] Declared Area Element group parameter constant
---
holoviews/element/chart.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/holoviews/element/chart.py b/holoviews/element/chart.py
index eab37db4fe..62062d0ffd 100644
--- a/holoviews/element/chart.py
+++ b/holoviews/element/chart.py
@@ -391,4 +391,4 @@ class Area(Curve):
the region between the curves.
"""
- group = param.String(default='Area')
+ group = param.String(default='Area', constant=True)
From f6e96fe2faaabca8234e1afb8baf998063621d23 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Fri, 29 Jan 2016 03:05:42 +0000
Subject: [PATCH 6/6] Renamed Area matplotlib color with facecolor to avoid
ordering issues
---
holoviews/plotting/mpl/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/holoviews/plotting/mpl/__init__.py b/holoviews/plotting/mpl/__init__.py
index e258e86a8d..2ee94bbb5e 100644
--- a/holoviews/plotting/mpl/__init__.py
+++ b/holoviews/plotting/mpl/__init__.py
@@ -181,7 +181,7 @@ def grid_selector(grid):
options.Scatter3D = Options('plot', fig_size=150)
options.Surface = Options('plot', fig_size=150)
options.Spikes = Options('style', color='black')
-options.Area = Options('style', color=Cycle(), edgecolor='black')
+options.Area = Options('style', facecolor=Cycle(), edgecolor='black')
options.BoxWhisker = Options('style', boxprops=dict(color='k'),
whiskerprops=dict(color='k'))