From 09a5fb1282b05c90db202f8e3313309a4832aefa Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Tue, 12 Sep 2017 11:37:59 +0100
Subject: [PATCH] Cleaned up Element range handling and fixed QuadMesh range
---
holoviews/core/data/__init__.py | 25 +++++++------------------
holoviews/core/dimension.py | 20 ++++++--------------
holoviews/core/util.py | 12 ++++++++++++
holoviews/element/chart.py | 24 ++++++++----------------
holoviews/element/raster.py | 25 +++++++++----------------
tests/testannotations.py | 4 ++--
6 files changed, 44 insertions(+), 66 deletions(-)
diff --git a/holoviews/core/data/__init__.py b/holoviews/core/data/__init__.py
index 8fab4b47cf..9f47199dc0 100644
--- a/holoviews/core/data/__init__.py
+++ b/holoviews/core/data/__init__.py
@@ -9,6 +9,7 @@
import param
from ..dimension import redim
+from ..util import dimension_range
from .interface import Interface, iloc, ndloc
from .array import ArrayInterface
from .dictionary import DictInterface
@@ -238,25 +239,13 @@ def range(self, dim, data_range=True):
dim = self.get_dimension(dim)
if dim is None:
return (None, None)
- elif None not in dim.range:
- return dim.range
- elif dim in self.dimensions() and data_range:
- if len(self):
- drange = self.interface.range(self, dim)
- else:
- drange = (np.NaN, np.NaN)
- soft_range = [r for r in dim.soft_range if r is not None]
- if soft_range:
- drange = util.max_range([drange, soft_range])
- else:
- drange = dim.soft_range
- if dim.range[0] is not None:
- return (dim.range[0], drange[1])
- elif dim.range[1] is not None:
- return (drange[0], dim.range[1])
+ elif all(v is not None and np.isfinite(v) for v in dim.range):
+ return dimension.range
+ elif dim in self.dimensions() and data_range and len(self):
+ lower, upper = self.interface.range(self, dim)
else:
- return drange
-
+ lower, upper = (np.NaN, np.NaN)
+ return dimension_range(lower, upper, dim)
def add_dimension(self, dimension, dim_pos, dim_val, vdim=False, **kwargs):
diff --git a/holoviews/core/dimension.py b/holoviews/core/dimension.py
index ced12c7647..cb555d6b5a 100644
--- a/holoviews/core/dimension.py
+++ b/holoviews/core/dimension.py
@@ -15,7 +15,7 @@
group_sanitizer, label_sanitizer, max_range,
find_range, dimension_sanitizer, OrderedDict,
bytes_to_unicode, unicode, dt64_to_dt, unique_array,
- builtins, config)
+ builtins, config, dimension_range)
from .options import Store, StoreOptions
from .pprint import PrettyPrinter
@@ -1053,29 +1053,21 @@ def range(self, dimension, data_range=True):
dimension = self.get_dimension(dimension)
if dimension is None:
return (None, None)
- if None not in dimension.range:
+ elif all(v is not None and np.isfinite(v) for v in dimension.range):
return dimension.range
elif data_range:
if dimension in self.kdims+self.vdims:
dim_vals = self.dimension_values(dimension.name)
- drange = find_range(dim_vals)
+ lower, upper = find_range(dim_vals)
else:
dname = dimension.name
match_fn = lambda x: dname in x.kdims + x.vdims
range_fn = lambda x: x.range(dname)
ranges = self.traverse(range_fn, [match_fn])
- drange = max_range(ranges)
- soft_range = [r for r in dimension.soft_range if r is not None]
- if soft_range:
- drange = max_range([drange, soft_range])
+ lower, upper = max_range(ranges)
else:
- drange = dimension.soft_range
- if dimension.range[0] is not None:
- return (dimension.range[0], drange[1])
- elif dimension.range[1] is not None:
- return (drange[0], dimension.range[1])
- else:
- return drange
+ lower, upper = (np.NaN, np.NaN)
+ return dimension_range(lower, upper, dimension)
def __repr__(self):
diff --git a/holoviews/core/util.py b/holoviews/core/util.py
index d51eb3914f..e08b5af4d9 100644
--- a/holoviews/core/util.py
+++ b/holoviews/core/util.py
@@ -710,6 +710,18 @@ def max_range(ranges):
return (np.NaN, np.NaN)
+def dimension_range(lower, upper, dimension):
+ """
+ Computes the range along a dimension by combining the data range
+ with the Dimension soft_range and range.
+ """
+ lower, upper = max_range([(lower, upper), dimension.soft_range])
+ dmin, dmax = dimension.range
+ lower = lower if dmin is None or not np.isfinite(dmin) else dmin
+ upper = upper if dmax is None or not np.isfinite(dmax) else dmax
+ return lower, upper
+
+
def max_extents(extents, zrange=False):
"""
Computes the maximal extent in 2D and 3D space from
diff --git a/holoviews/element/chart.py b/holoviews/element/chart.py
index 13befc9d6d..893c8692fe 100644
--- a/holoviews/element/chart.py
+++ b/holoviews/element/chart.py
@@ -88,23 +88,19 @@ class ErrorBars(Chart):
def range(self, dim, data_range=True):
- drange = super(ErrorBars, self).range(dim, data_range)
didx = self.get_dimension_index(dim)
dim = self.get_dimension(dim)
- if didx == 1 and data_range:
+ if didx == 1 and data_range and len(self):
mean = self.dimension_values(1)
neg_error = self.dimension_values(2)
- pos_idx = 3 if len(self.dimensions()) > 3 else 2
- pos_error = self.dimension_values(pos_idx)
+ if len(self.dimensions()) > 3:
+ pos_error = self.dimension_values(3)
+ else:
+ pos_error = neg_error
lower = np.nanmin(mean-neg_error)
upper = np.nanmax(mean+pos_error)
- lower, upper = util.max_range([(lower, upper), drange])
- dmin, dmax = dim.range
- lower = lower if dmin is None or not np.isfinite(dmin) else dmin
- upper = upper if dmax is None or not np.isfinite(dmax) else dmax
- return lower, upper
- else:
- return drange
+ return util.dimension_range(lower, upper, dim)
+ return super(ErrorBars, self).range(dim, data_range)
@@ -248,11 +244,7 @@ def range(self, dimension, data_range=True):
if self.get_dimension_index(dimension) == 0 and data_range:
dim = self.get_dimension(dimension)
lower, upper = np.min(self.edges), np.max(self.edges)
- lower, upper = util.max_range([(lower, upper), dim.soft_range])
- dmin, dmax = dim.range
- lower = lower if dmin is None or not np.isfinite(dmin) else dmin
- upper = upper if dmax is None or not np.isfinite(dmax) else dmax
- return lower, upper
+ return util.dimension_range(lower, upper, dim)
else:
return super(Histogram, self).range(dimension, data_range)
diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py
index ad3d25a7c0..8a86ff8c7d 100644
--- a/holoviews/element/raster.py
+++ b/holoviews/element/raster.py
@@ -8,7 +8,7 @@
from ..core import Dimension, Element2D, Overlay, Dataset
from ..core.boundingregion import BoundingRegion, BoundingBox
from ..core.sheetcoords import SheetCoordinateSystem, Slice
-from ..core.util import max_range
+from ..core.util import max_range, dimension_range
from .chart import Curve
from .tabular import Table
from .util import compute_edges, compute_slice_bounds, categorical_aggregate2d
@@ -71,14 +71,8 @@ def range(self, dim, data_range=True):
idx = self.get_dimension_index(dim)
if data_range and idx == 2:
dimension = self.get_dimension(dim)
- drange = self.data.min(), self.data.max()
- drange = max_range([drange, dimension.soft_range])
- if dimension.range[0] is not None:
- return (dimension.range[0], drange[1])
- elif dimension.range[1] is not None:
- return (drange[0], dimension.range[1])
- else:
- return drange
+ lower, upper = np.nanmin(self.data), np.nanmax(self.data)
+ return dimension_range(lower, upper, dimension)
return super(Raster, self).range(dim, data_range)
@@ -720,15 +714,14 @@ def _coord2matrix(self, coord):
for i in [1, 0])
- def range(self, dimension):
+ def range(self, dimension, data_range=True):
idx = self.get_dimension_index(dimension)
- if idx in [0, 1]:
- data = self.data[idx]
- return np.min(data), np.max(data)
- elif idx == 2:
+ dim = self.get_dimension(dimension)
+ if idx in [0, 1, 2] and data_range:
data = self.data[idx]
- return np.nanmin(data), np.nanmax(data)
- super(QuadMesh, self).range(dimension)
+ lower, upper = np.nanmin(data), np.nanmax(data)
+ return dimension_range(lower, upper, dim)
+ return super(QuadMesh, self).range(dimension, data_range)
def dimension_values(self, dimension, expanded=True, flat=True):
diff --git a/tests/testannotations.py b/tests/testannotations.py
index 77bebb16e1..d192f87387 100644
--- a/tests/testannotations.py
+++ b/tests/testannotations.py
@@ -27,13 +27,13 @@ def test_text_string_position(self):
def test_hline_dimension_values(self):
hline = HLine(0)
- self.assertEqual(hline.range(0), (None, None))
+ self.assertEqual(hline.range(0), (np.NaN, np.NaN))
self.assertEqual(hline.range(1), (0, 0))
def test_vline_dimension_values(self):
hline = VLine(0)
self.assertEqual(hline.range(0), (0, 0))
- self.assertEqual(hline.range(1), (None, None))
+ self.assertEqual(hline.range(1), (np.NaN, np.NaN))
def test_arrow_redim_range_aux(self):
annotations = Arrow(0, 0)