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)