From 54283926a8408ef7786c037266e8e3258097d91a Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 12:14:12 -0500 Subject: [PATCH 01/26] Ensure DictInterface does not consume unexpected types --- holoviews/core/data/dictionary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index 06574d0b20..51ac46a520 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -56,7 +56,7 @@ def init(cls, eltype, data, kdims, vdims): data = {k: data[:,i] for i,k in enumerate(dimensions)} elif isinstance(data, list) and np.isscalar(data[0]): data = {dimensions[0]: np.arange(len(data)), dimensions[1]: data} - elif not isinstance(data, dict): + elif not any(isinstance(data, interface) for interface in cls.interfaces): data = {k: v for k, v in zip(dimensions, zip(*data))} elif isinstance(data, dict) and not all(d in data for d in dimensions): dict_data = zip(*((util.wrap_tuple(k)+util.wrap_tuple(v)) From a280914687cb1ce171d93615fdeaa68d93988e0b Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 12:14:50 -0500 Subject: [PATCH 02/26] Reverted fix to flipped iris interface --- holoviews/core/data/iris.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index d4c13c1abf..14acda9a40 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -133,8 +133,6 @@ def values(cls, dataset, dim, expanded=True, flat=True): dim_inds += [i for i in range(len(dataset.data.dim_coords)) if i not in dim_inds] data = data.transpose(dim_inds) - else: - data = np.flipud(data) elif expanded: idx = dataset.get_dimension_index(dim) data = util.cartesian_product([dataset.data.coords(d.name)[0].points From 44262748790ad64a20981866d8b28e1356244edf Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 12:15:25 -0500 Subject: [PATCH 03/26] Generalized and fixed xarray constructor --- holoviews/core/data/xarray.py | 39 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index abbd2bcb12..175f4faade 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -40,37 +40,40 @@ def init(cls, eltype, data, kdims, vdims): vdim_param = element_params['vdims'] if kdims: - kdim_names = [kd.name if isinstance(kd, Dimension) else kd for kd in kdims] + kdim_names = [kd.name if isinstance(kd, Dimension) + else kd for kd in kdims] else: kdim_names = [kd.name for kd in eltype.kdims] if not isinstance(data, xr.Dataset): - ndims = len(kdim_names) kdims = [kd if isinstance(kd, Dimension) else Dimension(kd) for kd in kdims] - vdim = vdims[0].name if isinstance(vdims[0], Dimension) else vdims[0] + vdims = [d if isinstance(vd, Dimension) else Dimension(vd) + for vd in vdims] if isinstance(data, tuple): - value_array = np.array(data[-1]) - data = {d: vals for d, vals in zip(kdim_names + [vdim], data)} - elif isinstance(data, dict): - value_array = np.array(data[vdim]) - if value_array.ndim > 1: - value_array = value_array.T - dims, coords = zip(*[(kd.name, data[kd.name]) - for kd in kdims]) + data = {d.name: vals for d, vals in zip(kdims + vdims, data)} + if not isinstance(data, dict): + raise TypeError('XArrayInterface could not interpret data type') + coords = [(kd.name, data[kd.name]) for kd in kdims][::-1] + arrays = {} + for vdim in vdims: + arr = data[vdim.name] + if not isinstance(arr, xr.DataArray): + arr = xr.DataArray(arr, coords=coords) + arrays[vdim.name] = arr try: - arr = xr.DataArray(value_array, coords=coords, dims=dims) - data = xr.Dataset({vdim: arr}) + data = xr.Dataset(arrays) except: pass - if not isinstance(data, xr.Dataset): - raise TypeError('Data must be be an xarray Dataset type.') - - if isinstance(data, xr.Dataset): + else: if vdims is None: vdims = list(data.data_vars.keys()) if kdims is None: - kdims = list(data.dims.keys()) + kdims = [name for name in data.dims + if isinstance(data[name].data, np.ndarray)] + + if not isinstance(data, xr.Dataset): + raise TypeError('Data must be be an xarray Dataset type.') return data, {'kdims': kdims, 'vdims': vdims}, {} From 4124f299929ef430a037614294caf6574b0fab60 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 12:15:47 -0500 Subject: [PATCH 04/26] Fix for XArrayInterface values method --- holoviews/core/data/xarray.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 175f4faade..53087f3146 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -125,8 +125,12 @@ def values(cls, dataset, dim, expanded=True, flat=True): if dim in dataset.vdims: if data.ndim == 1: return np.array(data) - else: - return data.T.flatten() if flat else data + dims = [name for name in dataset.data.coords + if isinstance(dataset.data[name].data, np.ndarray) and + name in dataset.data.dims] + inds = [dims.index(kd.name) for kd in dataset.kdims] + transposed = data.transpose(inds[::-1]) + return transposed.flatten() if flat else transposed elif not expanded: return data else: From 0079735605e46b85a0c0523a0e982f99cc389c9c Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 12:17:33 -0500 Subject: [PATCH 05/26] Pass parameters through in dynamic groupby --- holoviews/core/data/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/holoviews/core/data/__init__.py b/holoviews/core/data/__init__.py index 9e00a837c1..8e58956825 100644 --- a/holoviews/core/data/__init__.py +++ b/holoviews/core/data/__init__.py @@ -393,13 +393,15 @@ def groupby(self, dimensions=[], container_type=HoloMap, group_type=None, if dynamic: group_dims = [d.name for d in self.kdims if d not in dimensions] + group_kwargs = dict(util.get_param_values(self)) + group_kwargs.update(kwargs) def load_subset(*args): constraint = dict(zip(dim_names, args)) group = self.select(**constraint) if np.isscalar(group): return group_type(([group],), group=self.group, label=self.label, vdims=self.vdims) - return group_type(group.reindex(group_dims)) + return group_type(group.reindex(group_dims), **group_kwargs) dynamic_dims = [d(values=list(self.interface.values(self, d.name, False))) for d in dimensions] return DynamicMap(load_subset, kdims=dynamic_dims) From f8e4ede1c1f43176ad38059222d968529cdb7f02 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 12:17:59 -0500 Subject: [PATCH 06/26] Fixed GridImage dimension declarations --- holoviews/element/raster.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index b2bf91e21a..6edb6aef65 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -648,9 +648,10 @@ class GridImage(Dataset, Element2D): group = param.String(default='GridImage', constant=True) - kdims = param.List(default=['x', 'y'], bounds=(2, 2)) + kdims = param.List(default=[Dimension('x'), Dimension('y')], + bounds=(2, 2)) - vdims = param.List(default=['z'], bounds=(1, 1)) + vdims = param.List(default=[Dimension('z')], bounds=(1, 1)) def __init__(self, data, **params): super(GridImage, self).__init__(data, **params) From 33b316ac3693a9dd448a8c05a430d40ede20a1fb Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 16:14:35 -0400 Subject: [PATCH 07/26] Fix for DictInterface constructor --- holoviews/core/data/dictionary.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index 51ac46a520..06cf26011d 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -56,7 +56,8 @@ def init(cls, eltype, data, kdims, vdims): data = {k: data[:,i] for i,k in enumerate(dimensions)} elif isinstance(data, list) and np.isscalar(data[0]): data = {dimensions[0]: np.arange(len(data)), dimensions[1]: data} - elif not any(isinstance(data, interface) for interface in cls.interfaces): + elif not any(isinstance(data, tuple(t for t in interface.types if t is not None)) + for interface in cls.interfaces.values()): data = {k: v for k, v in zip(dimensions, zip(*data))} elif isinstance(data, dict) and not all(d in data for d in dimensions): dict_data = zip(*((util.wrap_tuple(k)+util.wrap_tuple(v)) From 5fd7ef44fa39c5fe5ab1b3d6d341d789a88f428c Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 16:14:55 -0400 Subject: [PATCH 08/26] Fixed CubeInterface test --- tests/testirisinterface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/testirisinterface.py b/tests/testirisinterface.py index 9c44859955..7386c8cf85 100644 --- a/tests/testirisinterface.py +++ b/tests/testirisinterface.py @@ -51,10 +51,10 @@ def test_dimension_values_kdim(self): def test_dimension_values_vdim(self): cube = Dataset(self.cube, kdims=['longitude', 'latitude']) self.assertEqual(cube.dimension_values('unknown', flat=False), - np.flipud(np.array([[ 0, 4, 8], - [ 1, 5, 9], - [ 2, 6, 10], - [ 3, 7, 11]], dtype=np.int32).T)) + np.array([[ 0, 4, 8], + [ 1, 5, 9], + [ 2, 6, 10], + [ 3, 7, 11]], dtype=np.int32).T) def test_range_kdim(self): cube = Dataset(self.cube, kdims=['longitude', 'latitude']) From b80437c8e3ca4e1d7d85be69774e0131b8136c9f Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 18 Jul 2016 16:16:32 -0400 Subject: [PATCH 09/26] Fixed dynamic groupby parameter passing --- holoviews/core/data/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/holoviews/core/data/__init__.py b/holoviews/core/data/__init__.py index 8e58956825..a159f297a1 100644 --- a/holoviews/core/data/__init__.py +++ b/holoviews/core/data/__init__.py @@ -393,8 +393,8 @@ def groupby(self, dimensions=[], container_type=HoloMap, group_type=None, if dynamic: group_dims = [d.name for d in self.kdims if d not in dimensions] - group_kwargs = dict(util.get_param_values(self)) - group_kwargs.update(kwargs) + group_kwargs = dict(util.get_param_values(self), **kwargs) + group_kwargs['kdims'] = [self.get_dimension(d) for d in group_dims] def load_subset(*args): constraint = dict(zip(dim_names, args)) group = self.select(**constraint) From a984d64262f080384b68d7abfcd69f8c2ce9dc0e Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 02:51:39 +0100 Subject: [PATCH 10/26] Added inverted coordinate systems in grid interfaces --- holoviews/core/data/grid.py | 26 ++++++++++++++++++++++++-- holoviews/core/data/iris.py | 26 ++++++++++++++++++++++++-- holoviews/core/data/xarray.py | 24 ++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 683007c86c..8e78282362 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -101,18 +101,40 @@ def length(cls, dataset): return np.product([len(dataset.data[d.name]) for d in dataset.kdims]) + @classmethod + def invert(cls, dataset): + invert = False + slices = [] + for d in dataset.kdims: + data = dataset.data[d.name] + if np.all(data[1:] < data[:-1]): + slices.append(slice(None, None, -1)) + invert = True + else: + slices.append(slice(None)) + return slices if invert else [] + + @classmethod def values(cls, dataset, dim, expanded=True, flat=True): if dim in dataset.kdims: if not expanded: - return dataset.data[dim] - prod = util.cartesian_product([dataset.data[d.name] for d in dataset.kdims]) + data = dataset.data[dim] + return data[::-1] if np.all(data[1:] < data[:-1]) else data + prod = util.cartesian_product([dataset.data[d.name] + for d in dataset.kdims]) idx = dataset.get_dimension_index(dim) values = prod[idx] + invert = cls.invert(dataset) + if invert: + values = values.__getitem__(invert) return values.flatten() if flat else values else: dim = dataset.get_dimension(dim) values = dataset.data.get(dim.name) + invert = cls.invert(dataset) + if invert: + values = values.__getitem__(invert[::-1]) return values.T.flatten() if flat else values diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 14acda9a40..6d79871939 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -118,6 +118,20 @@ def validate(cls, dataset): pass + @classmethod + def invert(cls, dataset): + invert = False + slices = [] + for d in dataset.kdims: + data = dataset.data.coords(d.name)[0].points + if np.all(data[1:] < data[:-1]): + slices.append(slice(None, None, -1)) + invert = True + else: + slices.append(slice(None)) + return slices if invert else [] + + @classmethod def values(cls, dataset, dim, expanded=True, flat=True): """ @@ -128,18 +142,26 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data.copy().data coord_names = [c.name() for c in dataset.data.dim_coords if c.name() in dataset.kdims] + inversions = cls.invert(dataset) + if inversions: + data = data.__getitem__(inversions[::-1]) if flat: dim_inds = [coord_names.index(d.name) for d in dataset.kdims] dim_inds += [i for i in range(len(dataset.data.dim_coords)) if i not in dim_inds] - data = data.transpose(dim_inds) + data = data.transpose(dim_inds).flatten() + return data elif expanded: idx = dataset.get_dimension_index(dim) data = util.cartesian_product([dataset.data.coords(d.name)[0].points for d in dataset.kdims])[idx] + inversions = cls.invert(dataset) + if inversions: + data = data.__getitem__(inversions) + return data.flatten() if flat else data else: data = dataset.data.coords(dim.name)[0].points - return data.flatten() if flat else data + return data if np.all(data[1:] >= data[:-1]) else data[::-1] @classmethod diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 53087f3146..a445a8b332 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -119,6 +119,20 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs): return container_type(data) + @classmethod + def invert(cls, dataset): + invert = False + slices = [] + for d in dataset.data.dims: + data = dataset.data[d].data + if np.all(data[1:] < data[:-1]): + slices.append(slice(None, None, -1)) + invert = True + else: + slices.append(slice(None)) + return slices if invert else [] + + @classmethod def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data[dim].data @@ -130,12 +144,18 @@ def values(cls, dataset, dim, expanded=True, flat=True): name in dataset.data.dims] inds = [dims.index(kd.name) for kd in dataset.kdims] transposed = data.transpose(inds[::-1]) - return transposed.flatten() if flat else transposed + inversions = cls.invert(dataset) + if inversions: + transposed = transposed.__getitem__(inversions[::-1]) + return transposed.T.flatten() if flat else transposed elif not expanded: - return data + return data if np.all(data[1:] >= data[:-1]) else data[::-1] else: arrays = [dataset.data[d.name].data for d in dataset.kdims] product = util.cartesian_product(arrays)[dataset.get_dimension_index(dim)] + inversions = cls.invert(dataset) + if inversions: + product = product.__getitem__(inversions) return product.flatten() if flat else product From 46bb8fa91e80e37206efebdad8b08783f1cab5aa Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 02:53:16 +0100 Subject: [PATCH 11/26] Fixed GridImage density --- holoviews/element/raster.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index 6edb6aef65..4a8f98c79c 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -657,8 +657,8 @@ def __init__(self, data, **params): super(GridImage, self).__init__(data, **params) (l, r), (b, t) = self.interface.range(self, 0), self.interface.range(self, 1) (ys, xs) = self.dimension_values(2, flat=False).shape - xsampling = (float(r-l)/xs)/2. - ysampling = (float(t-b)/ys)/2. + xsampling = (float(r-l)/(xs-1))/2. + ysampling = (float(t-b)/(ys-1))/2. l, r = l-xsampling, r+xsampling b, t = b-ysampling, t+ysampling self.bounds = BoundingBox(points=((l, b), (r, t))) From 98725de7acc714df377825b085d20f7686101811 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 02:53:51 +0100 Subject: [PATCH 12/26] Added unit tests for Dataset --- tests/testdataset.py | 89 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/tests/testdataset.py b/tests/testdataset.py index 058a015eca..23902e065e 100644 --- a/tests/testdataset.py +++ b/tests/testdataset.py @@ -437,6 +437,95 @@ def init_data(self): self.y_ints = [i*2 for i in range(11)] self.dataset_hm = Dataset((self.xs, self.y_ints), kdims=['x'], vdims=['y']) + self.grid_xs = [0, 1] + self.grid_ys = [0.1, 0.2, 0.3] + self.grid_zs = [[0, 1], [2, 3], [4, 5]] + self.dataset_grid = Dataset((self.grid_xs, self.grid_ys, + self.grid_zs), kdims=['x', 'y'], + vdims=['z']) + self.dataset_grid_inv = Dataset((self.grid_xs[::-1], self.grid_ys[::-1], + self.grid_zs), kdims=['x', 'y'], + vdims=['z']) + + def test_dataset_dim_vals_grid_kdims_xs(self): + self.assertEqual(self.dataset_grid.dimension_values(0, expanded=False), + np.array([0, 1])) + + def test_dataset_dim_vals_grid_kdims_xs_inv(self): + self.assertEqual(self.dataset_grid_inv.dimension_values(0, expanded=False), + np.array([0, 1])) + + def test_dataset_dim_vals_grid_kdims_expanded_xs_flat(self): + expanded_xs = np.array([0, 0, 0, 1, 1, 1]) + self.assertEqual(self.dataset_grid.dimension_values(0), + expanded_xs) + + def test_dataset_dim_vals_grid_kdims_expanded_xs_flat_inv(self): + expanded_xs = np.array([0, 0, 0, 1, 1, 1]) + self.assertEqual(self.dataset_grid_inv.dimension_values(0), + expanded_xs) + + def test_dataset_dim_vals_grid_kdims_expanded_xs(self): + expanded_xs = np.array([[0, 0, 0], [1, 1, 1]]) + self.assertEqual(self.dataset_grid.dimension_values(0, flat=False), + expanded_xs) + + def test_dataset_dim_vals_grid_kdims_expanded_xs_inv(self): + expanded_xs = np.array([[0, 0, 0], [1, 1, 1]]) + self.assertEqual(self.dataset_grid_inv.dimension_values(0, flat=False), + expanded_xs) + + def test_dataset_dim_vals_grid_kdims_ys(self): + self.assertEqual(self.dataset_grid.dimension_values(1, expanded=False), + np.array([0.1, 0.2, 0.3])) + + def test_dataset_dim_vals_grid_kdims_ys_inv(self): + self.assertEqual(self.dataset_grid_inv.dimension_values(1, expanded=False), + np.array([0.1, 0.2, 0.3])) + + def test_dataset_dim_vals_grid_kdims_expanded_ys_flat(self): + expanded_ys = np.array([0.1, 0.2, 0.3, + 0.1, 0.2, 0.3]) + self.assertEqual(self.dataset_grid.dimension_values(1), + expanded_ys) + + def test_dataset_dim_vals_grid_kdims_expanded_ys_flat_inv(self): + expanded_ys = np.array([0.1, 0.2, 0.3, + 0.1, 0.2, 0.3]) + self.assertEqual(self.dataset_grid_inv.dimension_values(1), + expanded_ys) + + def test_dataset_dim_vals_grid_kdims_expanded_ys(self): + expanded_ys = np.array([[0.1, 0.2, 0.3], + [0.1, 0.2, 0.3]]) + self.assertEqual(self.dataset_grid.dimension_values(1, flat=False), + expanded_ys) + + def test_dataset_dim_vals_grid_kdims_expanded_ys_inv(self): + expanded_ys = np.array([[0.1, 0.2, 0.3], + [0.1, 0.2, 0.3]]) + self.assertEqual(self.dataset_grid_inv.dimension_values(1, flat=False), + expanded_ys) + + def test_dataset_dim_vals_grid_vdims_zs_flat(self): + expanded_zs = np.array([0, 2, 4, 1, 3, 5]) + self.assertEqual(self.dataset_grid.dimension_values(2), + expanded_zs) + + def test_dataset_dim_vals_grid_vdims_zs_flat_inv(self): + expanded_zs = np.array([5, 3, 1, 4, 2, 0]) + self.assertEqual(self.dataset_grid_inv.dimension_values(2), + expanded_zs) + + def test_dataset_dim_vals_grid_vdims_zs(self): + expanded_zs = np.array([[0, 1], [2, 3], [4, 5]]) + self.assertEqual(self.dataset_grid.dimension_values(2, flat=False), + expanded_zs) + + def test_dataset_dim_vals_grid_vdims_zs_inv(self): + expanded_zs = np.array([[5, 4], [3, 2], [1, 0]]) + self.assertEqual(self.dataset_grid_inv.dimension_values(2, flat=False), + expanded_zs) def test_dataset_array_init_hm(self): "Tests support for arrays (homogeneous)" From 5d08919d0a0b4b16d6f3a3d1e4eafd5c3f83bf5d Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 12:35:19 +0100 Subject: [PATCH 13/26] Avoid duplication of invert method on grid interfaces --- holoviews/core/data/grid.py | 15 ++++++++++++++- holoviews/core/data/iris.py | 17 ++++------------- holoviews/core/data/xarray.py | 13 ++----------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 8e78282362..fe675eebde 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -101,12 +101,25 @@ def length(cls, dataset): return np.product([len(dataset.data[d.name]) for d in dataset.kdims]) + @classmethod + def get_coords(cls, dataset, dim): + """ + Returns the coordinates along a dimension. + """ + return dataset.data[dim] + + @classmethod def invert(cls, dataset): + """ + Detects if coordinates along a dimension are inverted + and returns slices to flip the array to the expected + orientation. + """ invert = False slices = [] for d in dataset.kdims: - data = dataset.data[d.name] + data = cls.get_coords(dataset, d.name) if np.all(data[1:] < data[:-1]): slices.append(slice(None, None, -1)) invert = True diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 6d79871939..58a01ef9dd 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -119,17 +119,8 @@ def validate(cls, dataset): @classmethod - def invert(cls, dataset): - invert = False - slices = [] - for d in dataset.kdims: - data = dataset.data.coords(d.name)[0].points - if np.all(data[1:] < data[:-1]): - slices.append(slice(None, None, -1)) - invert = True - else: - slices.append(slice(None)) - return slices if invert else [] + def get_coords(cls, dataset, dim): + return dataset.data.coords(dim)[0].points @classmethod @@ -153,14 +144,14 @@ def values(cls, dataset, dim, expanded=True, flat=True): return data elif expanded: idx = dataset.get_dimension_index(dim) - data = util.cartesian_product([dataset.data.coords(d.name)[0].points + data = util.cartesian_product([cls.get_coords(dataset, d.name) for d in dataset.kdims])[idx] inversions = cls.invert(dataset) if inversions: data = data.__getitem__(inversions) return data.flatten() if flat else data else: - data = dataset.data.coords(dim.name)[0].points + data = cls.get_coords(dataset, dim.name) return data if np.all(data[1:] >= data[:-1]) else data[::-1] diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index a445a8b332..c4af15cab5 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -120,17 +120,8 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs): @classmethod - def invert(cls, dataset): - invert = False - slices = [] - for d in dataset.data.dims: - data = dataset.data[d].data - if np.all(data[1:] < data[:-1]): - slices.append(slice(None, None, -1)) - invert = True - else: - slices.append(slice(None)) - return slices if invert else [] + def get_coords(cls, dataset, dim): + return dataset.data[dim].data @classmethod From 75031a588c5fab15a5f0d5818a5420e5873f43f1 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 14:27:52 +0100 Subject: [PATCH 14/26] Further fixes for grid interface orientations --- holoviews/core/data/grid.py | 2 +- holoviews/core/data/xarray.py | 10 ++++------ holoviews/plotting/bokeh/raster.py | 2 +- holoviews/plotting/mpl/raster.py | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index fe675eebde..6f754c1f9a 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -140,7 +140,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): values = prod[idx] invert = cls.invert(dataset) if invert: - values = values.__getitem__(invert) + values = values.__getitem__(invert[::-1]) return values.flatten() if flat else values else: dim = dataset.get_dimension(dim) diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index c4af15cab5..4ed0e7902d 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -128,16 +128,14 @@ def get_coords(cls, dataset, dim): def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data[dim].data if dim in dataset.vdims: - if data.ndim == 1: - return np.array(data) - dims = [name for name in dataset.data.coords + inversions = cls.invert(dataset) + if inversions: + data = data.__getitem__(inversions[::-1]) + dims = [name for name in dataset.data[dim].dims if isinstance(dataset.data[name].data, np.ndarray) and name in dataset.data.dims] inds = [dims.index(kd.name) for kd in dataset.kdims] transposed = data.transpose(inds[::-1]) - inversions = cls.invert(dataset) - if inversions: - transposed = transposed.__getitem__(inversions[::-1]) return transposed.T.flatten() if flat else transposed elif not expanded: return data if np.all(data[1:] >= data[:-1]) else data[::-1] diff --git a/holoviews/plotting/bokeh/raster.py b/holoviews/plotting/bokeh/raster.py index 6763832114..a6183c927c 100644 --- a/holoviews/plotting/bokeh/raster.py +++ b/holoviews/plotting/bokeh/raster.py @@ -82,7 +82,7 @@ def _update_glyph(self, glyph, properties, mapping): class ImagePlot(RasterPlot): def get_data(self, element, ranges=None, empty=False): - img = np.flipud(element.dimension_values(2, flat=False)) + img = element.dimension_values(2, flat=False) l, b, r, t = element.bounds.lbrt() dh, dw = t-b, r-l mapping = dict(image='image', x='x', y='y', dw='dw', dh='dh') diff --git a/holoviews/plotting/mpl/raster.py b/holoviews/plotting/mpl/raster.py index c6d137f801..a734fecf39 100644 --- a/holoviews/plotting/mpl/raster.py +++ b/holoviews/plotting/mpl/raster.py @@ -183,7 +183,7 @@ def update_handles(self, key, axis, element, ranges, style): class ImagePlot(RasterPlot): def get_data(self, element, ranges, style): - data = element.dimension_values(2, flat=False) + data = np.flipud(element.dimension_values(2, flat=False)) data = np.ma.array(data, mask=np.logical_not(np.isfinite(data))) vdim = element.vdims[0] self._norm_kwargs(element, ranges, style, vdim) From 9004db449947861da8b0a0785360f5159259ce26 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 15:06:21 +0100 Subject: [PATCH 15/26] Small fix for reorienting 1D arrays --- holoviews/core/data/xarray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 4ed0e7902d..7b39a1531d 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -129,7 +129,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data[dim].data if dim in dataset.vdims: inversions = cls.invert(dataset) - if inversions: + if inversions and not (flat and data.ndim > 1): data = data.__getitem__(inversions[::-1]) dims = [name for name in dataset.data[dim].dims if isinstance(dataset.data[name].data, np.ndarray) and From b29cd210dfba94a7e2a0b3890c73e8183c021582 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 15:07:03 +0100 Subject: [PATCH 16/26] HeatMap avoids reaggregating grid based data --- holoviews/element/raster.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index 4a8f98c79c..c82a913b7c 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -6,7 +6,8 @@ import param from ..core import util -from ..core.data import (ArrayInterface, NdElementInterface, DictInterface) +from ..core.data import (ArrayInterface, NdElementInterface, + DictInterface, GridInterface) from ..core import (Dimension, NdMapping, Element2D, Overlay, Element, Dataset, NdElement) from ..core.boundingregion import BoundingRegion, BoundingBox @@ -396,6 +397,8 @@ def __init__(self, data, extents=None, **params): def _compute_raster(self): + if issubclass(self.interface, GridInterface): + return self, self.dimension_values(2, flat=False) d1keys = self.dimension_values(0, False) d2keys = self.dimension_values(1, False) coords = [(d1, d2, np.NaN) for d1 in d1keys for d2 in d2keys] From 30196f15c488a799f8eb8d11ce1b194923660969 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 15:32:39 +0100 Subject: [PATCH 17/26] Revert bad fix for xarray 1D dimension_values --- holoviews/core/data/xarray.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 7b39a1531d..c4062aeb2f 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -129,14 +129,15 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data[dim].data if dim in dataset.vdims: inversions = cls.invert(dataset) - if inversions and not (flat and data.ndim > 1): + if inversions: data = data.__getitem__(inversions[::-1]) dims = [name for name in dataset.data[dim].dims if isinstance(dataset.data[name].data, np.ndarray) and name in dataset.data.dims] inds = [dims.index(kd.name) for kd in dataset.kdims] - transposed = data.transpose(inds[::-1]) - return transposed.T.flatten() if flat else transposed + if inds: + data = data.transpose(inds[::-1]) + return data.T.flatten() if flat else data elif not expanded: return data if np.all(data[1:] >= data[:-1]) else data[::-1] else: From cb461b1b35f40ef8ade0a7ae60e074f74cad8a06 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 16:26:56 +0100 Subject: [PATCH 18/26] Further orientation fixes for grid interfaces --- holoviews/core/data/grid.py | 7 ++++--- holoviews/core/data/xarray.py | 4 ++-- holoviews/element/raster.py | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 6f754c1f9a..31c76235c2 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -110,16 +110,17 @@ def get_coords(cls, dataset, dim): @classmethod - def invert(cls, dataset): + def invert(cls, dataset, dims=None): """ Detects if coordinates along a dimension are inverted and returns slices to flip the array to the expected orientation. """ + dims = dataset.dimensions('key', True) if dims is None else dims invert = False slices = [] - for d in dataset.kdims: - data = cls.get_coords(dataset, d.name) + for d in dims: + data = cls.get_coords(dataset, d) if np.all(data[1:] < data[:-1]): slices.append(slice(None, None, -1)) invert = True diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index c4062aeb2f..0cc01635bc 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -48,7 +48,7 @@ def init(cls, eltype, data, kdims, vdims): if not isinstance(data, xr.Dataset): kdims = [kd if isinstance(kd, Dimension) else Dimension(kd) for kd in kdims] - vdims = [d if isinstance(vd, Dimension) else Dimension(vd) + vdims = [vd if isinstance(vd, Dimension) else Dimension(vd) for vd in vdims] if isinstance(data, tuple): data = {d.name: vals for d, vals in zip(kdims + vdims, data)} @@ -128,7 +128,7 @@ def get_coords(cls, dataset, dim): def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data[dim].data if dim in dataset.vdims: - inversions = cls.invert(dataset) + inversions = cls.invert(dataset, dataset.data[dim].dims[::-1]) if inversions: data = data.__getitem__(inversions[::-1]) dims = [name for name in dataset.data[dim].dims diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index c82a913b7c..2bdfe1140e 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -398,7 +398,7 @@ def __init__(self, data, extents=None, **params): def _compute_raster(self): if issubclass(self.interface, GridInterface): - return self, self.dimension_values(2, flat=False) + return self, np.flipud(self.dimension_values(2, flat=False)) d1keys = self.dimension_values(0, False) d2keys = self.dimension_values(1, False) coords = [(d1, d2, np.NaN) for d1 in d1keys for d2 in d2keys] From 4ecb99363643437753ff1160321da836ca74b60e Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 25 Jul 2016 16:27:20 +0100 Subject: [PATCH 19/26] GridInterface reindex fixes --- holoviews/core/data/grid.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 31c76235c2..1b4da4bc69 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -71,7 +71,7 @@ def init(cls, eltype, data, kdims, vdims): if shape != expected[::-1] and not (not expected and shape == (1,)): raise ValueError('Key dimension values and value array %s ' 'shape do not match. Expected shape %s, ' - 'actual shape: %s' % (vdim, expected, shape)) + 'actual shape: %s' % (vdim, expected[::-1], shape)) return data, {'kdims':kdims, 'vdims':vdims}, {} @@ -318,17 +318,17 @@ def reindex(cls, dataset, kdims, vdims): if k not in dropped_kdims+dropped_vdims} if kdims != dataset.kdims: - dropped_axes = tuple(dataset.ndims-dataset.kdims.index(d)-1 + joined_dims = kdims+dropped_kdims + axes = tuple(dataset.ndims-dataset.kdims.index(d)-1 + for d in joined_dims) + dropped_axes = tuple(dataset.ndims-joined_dims.index(d)-1 for d in dropped_kdims) - old_kdims = [d for d in dataset.kdims if not d in dropped_kdims] - axes = tuple(dataset.ndims-old_kdims.index(d)-1 - for d in kdims) for vdim in vdims: vdata = data[vdim.name] + if len(axes) > 1: + vdata = vdata.transpose(axes[::-1]) if dropped_axes: vdata = vdata.squeeze(axis=dropped_axes) - if len(axes) > 1: - vdata = np.transpose(vdata, axes) data[vdim.name] = vdata return data From 2c8aeed49fe1a0eb4fa942928f9354aa17cd97b6 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 26 Jul 2016 14:13:53 +0100 Subject: [PATCH 20/26] Refactored grid interface canonicalization code into one method --- holoviews/core/data/grid.py | 75 +++++++++++++++++++++-------------- holoviews/core/data/iris.py | 30 +++++--------- holoviews/core/data/xarray.py | 30 +++++--------- 3 files changed, 67 insertions(+), 68 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 1b4da4bc69..3c106c4dcf 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -102,54 +102,71 @@ def length(cls, dataset): @classmethod - def get_coords(cls, dataset, dim): + def get_coords(cls, dataset, dim, ordered=False): """ Returns the coordinates along a dimension. """ - return dataset.data[dim] + data = dataset.data[dim] + if ordered and np.all(data[1:] < data[:-1]): + data = data[::-1] + return data @classmethod - def invert(cls, dataset, dims=None): + def expanded_coords(cls, dataset, dim): + arrays = [cls.get_coords(dataset, d.name, True) + for d in dataset.kdims] + idx = dataset.get_dimension_index(dim) + return util.cartesian_product(arrays)[idx] + + + @classmethod + def canonicalize(cls, dataset, data, coord_dims=None): """ - Detects if coordinates along a dimension are inverted - and returns slices to flip the array to the expected - orientation. + Canonicalize takes an array of values as input and + reorients and transposes it to match the canonical + format expected by plotting functions. In addition + to the dataset and the particular array to apply + transforms to a list of coord_dims may be supplied + in case the array indexing does not match the key + dimensions of the dataset. """ - dims = dataset.dimensions('key', True) if dims is None else dims + if coord_dims is None: + coord_dims = dataset.dimensions('key', True) + + # Reorient data invert = False slices = [] - for d in dims: - data = cls.get_coords(dataset, d) - if np.all(data[1:] < data[:-1]): + for d in coord_dims: + coords = cls.get_coords(dataset, d) + if np.all(coords[1:] < coords[:-1]): slices.append(slice(None, None, -1)) invert = True else: slices.append(slice(None)) - return slices if invert else [] + data = data.__getitem__(slices[::-1]) if invert else data + + # Transpose data + dims = [name for name in coord_dims[::-1] + if isinstance(cls.get_coords(dataset, name), np.ndarray)] + inds = [dims.index(kd.name) for kd in dataset.kdims] + if inds: + data = data.transpose(inds[::-1]) + return data @classmethod def values(cls, dataset, dim, expanded=True, flat=True): - if dim in dataset.kdims: - if not expanded: - data = dataset.data[dim] - return data[::-1] if np.all(data[1:] < data[:-1]) else data - prod = util.cartesian_product([dataset.data[d.name] - for d in dataset.kdims]) - idx = dataset.get_dimension_index(dim) - values = prod[idx] - invert = cls.invert(dataset) - if invert: - values = values.__getitem__(invert[::-1]) - return values.flatten() if flat else values - else: + if dim in dataset.vdims: dim = dataset.get_dimension(dim) - values = dataset.data.get(dim.name) - invert = cls.invert(dataset) - if invert: - values = values.__getitem__(invert[::-1]) - return values.T.flatten() if flat else values + data = dataset.data.get(dim.name) + data = cls.canonicalize(dataset, data) + return data.T.flatten() if flat else data + elif expanded: + data = cls.expanded_coords(dataset, dim) + return data.flatten() if flat else data + else: + return cls.get_coords(dataset, dim, True) @classmethod diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 58a01ef9dd..461973a6e4 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -119,8 +119,11 @@ def validate(cls, dataset): @classmethod - def get_coords(cls, dataset, dim): - return dataset.data.coords(dim)[0].points + def get_coords(cls, dataset, dim, ordered=False): + data = dataset.data.coords(dim)[0].points + if ordered and np.all(data[1:] < data[:-1]): + data = data[::-1] + return data @classmethod @@ -130,29 +133,16 @@ def values(cls, dataset, dim, expanded=True, flat=True): """ dim = dataset.get_dimension(dim) if dim in dataset.vdims: - data = dataset.data.copy().data coord_names = [c.name() for c in dataset.data.dim_coords if c.name() in dataset.kdims] - inversions = cls.invert(dataset) - if inversions: - data = data.__getitem__(inversions[::-1]) - if flat: - dim_inds = [coord_names.index(d.name) for d in dataset.kdims] - dim_inds += [i for i in range(len(dataset.data.dim_coords)) - if i not in dim_inds] - data = data.transpose(dim_inds).flatten() - return data + data = dataset.data.copy().data.T + data = cls.canonicalize(dataset, data, coord_names) + return data.T.flatten() if flat else data elif expanded: - idx = dataset.get_dimension_index(dim) - data = util.cartesian_product([cls.get_coords(dataset, d.name) - for d in dataset.kdims])[idx] - inversions = cls.invert(dataset) - if inversions: - data = data.__getitem__(inversions) + data = cls.expanded_coords(dataset, dim) return data.flatten() if flat else data else: - data = cls.get_coords(dataset, dim.name) - return data if np.all(data[1:] >= data[:-1]) else data[::-1] + return cls.get_coords(dataset, dim.name, True) @classmethod diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 0cc01635bc..fc28acb4b9 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -120,33 +120,25 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs): @classmethod - def get_coords(cls, dataset, dim): - return dataset.data[dim].data + def get_coords(cls, dataset, dim, ordered=False): + data = dataset.data[dim].data + if ordered and np.all(data[1:] < data[:-1]): + data = data[::-1] + return data @classmethod def values(cls, dataset, dim, expanded=True, flat=True): data = dataset.data[dim].data if dim in dataset.vdims: - inversions = cls.invert(dataset, dataset.data[dim].dims[::-1]) - if inversions: - data = data.__getitem__(inversions[::-1]) - dims = [name for name in dataset.data[dim].dims - if isinstance(dataset.data[name].data, np.ndarray) and - name in dataset.data.dims] - inds = [dims.index(kd.name) for kd in dataset.kdims] - if inds: - data = data.transpose(inds[::-1]) + coord_dims = dataset.data[dim].dims[::-1] + data = cls.canonicalize(dataset, data, coord_dims=coord_dims) return data.T.flatten() if flat else data - elif not expanded: - return data if np.all(data[1:] >= data[:-1]) else data[::-1] + elif expanded: + data = cls.expanded_coords(dataset, dim) + return data.flatten() if flat else data else: - arrays = [dataset.data[d.name].data for d in dataset.kdims] - product = util.cartesian_product(arrays)[dataset.get_dimension_index(dim)] - inversions = cls.invert(dataset) - if inversions: - product = product.__getitem__(inversions) - return product.flatten() if flat else product + return cls.get_coords(dataset, dim, True) @classmethod From 3b5b042694af389ee7e479338497ad08a0077d68 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 26 Jul 2016 19:19:34 +0100 Subject: [PATCH 21/26] Handle views into higher dimensional gridded datasets --- holoviews/core/data/grid.py | 8 ++++++++ holoviews/core/data/iris.py | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 3c106c4dcf..1add444d7f 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -149,9 +149,17 @@ def canonicalize(cls, dataset, data, coord_dims=None): # Transpose data dims = [name for name in coord_dims[::-1] if isinstance(cls.get_coords(dataset, name), np.ndarray)] + dropped = [dims.index(d) for d in dims if d not in dataset.kdims] inds = [dims.index(kd.name) for kd in dataset.kdims] + inds += dropped if inds: data = data.transpose(inds[::-1]) + + # Allow lower dimensional views into data + if len(dataset.kdims) < 2: + data = data.flatten() + elif dropped: + data = data.squeeze(axis=tuple(range(len(dropped)))) return data diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 461973a6e4..8ac21b5650 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -133,8 +133,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): """ dim = dataset.get_dimension(dim) if dim in dataset.vdims: - coord_names = [c.name() for c in dataset.data.dim_coords - if c.name() in dataset.kdims] + coord_names = [c.name() for c in dataset.data.dim_coords] data = dataset.data.copy().data.T data = cls.canonicalize(dataset, data, coord_names) return data.T.flatten() if flat else data From b557f8464ad0f3bf26ff768b20119614debfc93e Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 27 Jul 2016 12:41:47 +0100 Subject: [PATCH 22/26] Added attribute to interface to check for gridded data --- holoviews/core/data/grid.py | 2 ++ holoviews/core/data/interface.py | 2 ++ holoviews/element/raster.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 1add444d7f..4dea3ee6ee 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -37,6 +37,8 @@ class GridInterface(DictInterface): datatype = 'grid' + gridded = True + @classmethod def init(cls, eltype, data, kdims, vdims): if kdims is None: diff --git a/holoviews/core/data/interface.py b/holoviews/core/data/interface.py index c9fceb348f..ab5a0b5278 100644 --- a/holoviews/core/data/interface.py +++ b/holoviews/core/data/interface.py @@ -11,6 +11,8 @@ class Interface(param.Parameterized): datatype = None + gridded = False + @classmethod def register(cls, interface): cls.interfaces[interface.datatype] = interface diff --git a/holoviews/element/raster.py b/holoviews/element/raster.py index 2bdfe1140e..2d780d728e 100644 --- a/holoviews/element/raster.py +++ b/holoviews/element/raster.py @@ -397,7 +397,7 @@ def __init__(self, data, extents=None, **params): def _compute_raster(self): - if issubclass(self.interface, GridInterface): + if self.interface.gridded: return self, np.flipud(self.dimension_values(2, flat=False)) d1keys = self.dimension_values(0, False) d2keys = self.dimension_values(1, False) From 3dbdbd4811178c60ec3284943bfcf480dff43125 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 27 Jul 2016 12:45:32 +0100 Subject: [PATCH 23/26] Added comment to clarify dict interface type handling --- holoviews/core/data/dictionary.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index 06cf26011d..2992abe328 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -56,6 +56,8 @@ def init(cls, eltype, data, kdims, vdims): data = {k: data[:,i] for i,k in enumerate(dimensions)} elif isinstance(data, list) and np.isscalar(data[0]): data = {dimensions[0]: np.arange(len(data)), dimensions[1]: data} + # Ensure that interface does not consume data of other types + # with an iterator interface elif not any(isinstance(data, tuple(t for t in interface.types if t is not None)) for interface in cls.interfaces.values()): data = {k: v for k, v in zip(dimensions, zip(*data))} From 6e8e46f5c3ce5da4b66e25a93d707400c22a1543 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 27 Jul 2016 12:51:05 +0100 Subject: [PATCH 24/26] Fixed various error messages in interfaces --- holoviews/core/data/array.py | 2 +- holoviews/core/data/dictionary.py | 6 +++--- holoviews/core/data/grid.py | 4 ++-- holoviews/core/data/iris.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/holoviews/core/data/array.py b/holoviews/core/data/array.py index 435319a77c..4c15432327 100644 --- a/holoviews/core/data/array.py +++ b/holoviews/core/data/array.py @@ -72,7 +72,7 @@ def validate(cls, dataset): ncols = dataset.data.shape[1] if dataset.data.ndim > 1 else 1 if ncols < ndims: raise ValueError("Supplied data does not match specified " - "dimensions, expected at least %s dataset." % ndims) + "dimensions, expected at least %s columns." % ndims) @classmethod def array(cls, dataset, dimensions): diff --git a/holoviews/core/data/dictionary.py b/holoviews/core/data/dictionary.py index 2992abe328..7a31b04054 100644 --- a/holoviews/core/data/dictionary.py +++ b/holoviews/core/data/dictionary.py @@ -87,7 +87,7 @@ def validate(cls, dataset): raise ValueError('Following dimensions not found in data: %s' % not_found) lengths = [len(dataset.data[dim]) for dim in dimensions] if len({l for l in lengths if l > 1}) > 1: - raise ValueError('Length of dataset do not match') + raise ValueError('Length of columns do not match') @classmethod @@ -135,8 +135,8 @@ def concat(cls, dataset_objs): cast_objs = cls.cast(dataset_objs) cols = set(tuple(c.data.keys()) for c in cast_objs) if len(cols) != 1: - raise Exception("In order to concatenate, all Column objects " - "should have matching set of dataset.") + raise Exception("In order to concatenate, all Dataset objects " + "should have matching set of columns.") concatenated = OrderedDict() for column in cols.pop(): concatenated[column] = np.concatenate([obj[column] for obj in cast_objs]) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 4dea3ee6ee..2ba2cd2b53 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -55,8 +55,8 @@ def init(cls, eltype, data, kdims, vdims): if isinstance(data, tuple): data = {d: v for d, v in zip(dimensions, data)} elif not isinstance(data, dict): - raise ValueError('GridInterface must be instantiated as a ' - 'dictionary or tuple') + raise TypeError('GridInterface must be instantiated as a ' + 'dictionary or tuple') for dim in kdims+vdims: name = dim.name if isinstance(dim, Dimension) else dim diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 8ac21b5650..5deb9b04a6 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -93,7 +93,7 @@ def init(cls, eltype, data, kdims, vdims): except: pass if not isinstance(data, iris.cube.Cube): - raise TypeError('Data must be be an iris dataset type.') + raise TypeError('Data must be be an iris Cube type.') if kdims: coords = [] From 309bedc7f097fa5e6ece41a5b0bc836faca058c8 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 27 Jul 2016 12:53:15 +0100 Subject: [PATCH 25/26] Renamed GridInterface get_coords to coords --- holoviews/core/data/grid.py | 10 +++++----- holoviews/core/data/iris.py | 4 ++-- holoviews/core/data/xarray.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 2ba2cd2b53..22951963fb 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -104,7 +104,7 @@ def length(cls, dataset): @classmethod - def get_coords(cls, dataset, dim, ordered=False): + def coords(cls, dataset, dim, ordered=False): """ Returns the coordinates along a dimension. """ @@ -116,7 +116,7 @@ def get_coords(cls, dataset, dim, ordered=False): @classmethod def expanded_coords(cls, dataset, dim): - arrays = [cls.get_coords(dataset, d.name, True) + arrays = [cls.coords(dataset, d.name, True) for d in dataset.kdims] idx = dataset.get_dimension_index(dim) return util.cartesian_product(arrays)[idx] @@ -140,7 +140,7 @@ def canonicalize(cls, dataset, data, coord_dims=None): invert = False slices = [] for d in coord_dims: - coords = cls.get_coords(dataset, d) + coords = cls.coords(dataset, d) if np.all(coords[1:] < coords[:-1]): slices.append(slice(None, None, -1)) invert = True @@ -150,7 +150,7 @@ def canonicalize(cls, dataset, data, coord_dims=None): # Transpose data dims = [name for name in coord_dims[::-1] - if isinstance(cls.get_coords(dataset, name), np.ndarray)] + if isinstance(cls.coords(dataset, name), np.ndarray)] dropped = [dims.index(d) for d in dims if d not in dataset.kdims] inds = [dims.index(kd.name) for kd in dataset.kdims] inds += dropped @@ -176,7 +176,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = cls.expanded_coords(dataset, dim) return data.flatten() if flat else data else: - return cls.get_coords(dataset, dim, True) + return cls.coords(dataset, dim, True) @classmethod diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 5deb9b04a6..063ea87f1a 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -119,7 +119,7 @@ def validate(cls, dataset): @classmethod - def get_coords(cls, dataset, dim, ordered=False): + def coords(cls, dataset, dim, ordered=False): data = dataset.data.coords(dim)[0].points if ordered and np.all(data[1:] < data[:-1]): data = data[::-1] @@ -141,7 +141,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = cls.expanded_coords(dataset, dim) return data.flatten() if flat else data else: - return cls.get_coords(dataset, dim.name, True) + return cls.coords(dataset, dim.name, True) @classmethod diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index fc28acb4b9..67d1bf67a9 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -120,7 +120,7 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs): @classmethod - def get_coords(cls, dataset, dim, ordered=False): + def coords(cls, dataset, dim, ordered=False): data = dataset.data[dim].data if ordered and np.all(data[1:] < data[:-1]): data = data[::-1] @@ -138,7 +138,7 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = cls.expanded_coords(dataset, dim) return data.flatten() if flat else data else: - return cls.get_coords(dataset, dim, True) + return cls.coords(dataset, dim, True) @classmethod From 1ee04d7a05dfbfc129e960c1466608ad4a49e662 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 27 Jul 2016 13:31:25 +0100 Subject: [PATCH 26/26] Factored grid coordinate expansion out into utility --- holoviews/core/data/grid.py | 20 ++++++++------------ holoviews/core/data/iris.py | 8 +++++--- holoviews/core/data/xarray.py | 10 ++++++---- holoviews/core/util.py | 13 +++++++++++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/holoviews/core/data/grid.py b/holoviews/core/data/grid.py index 22951963fb..36c533b1b7 100644 --- a/holoviews/core/data/grid.py +++ b/holoviews/core/data/grid.py @@ -104,24 +104,20 @@ def length(cls, dataset): @classmethod - def coords(cls, dataset, dim, ordered=False): + def coords(cls, dataset, dim, ordered=False, expanded=False): """ - Returns the coordinates along a dimension. + Returns the coordinates along a dimension. Ordered ensures + coordinates are in ascending order and expanded creates + ND-array matching the dimensionality of the dataset. """ + if expanded: + return util.expand_grid_coords(dataset, dim) data = dataset.data[dim] if ordered and np.all(data[1:] < data[:-1]): data = data[::-1] return data - @classmethod - def expanded_coords(cls, dataset, dim): - arrays = [cls.coords(dataset, d.name, True) - for d in dataset.kdims] - idx = dataset.get_dimension_index(dim) - return util.cartesian_product(arrays)[idx] - - @classmethod def canonicalize(cls, dataset, data, coord_dims=None): """ @@ -173,10 +169,10 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = cls.canonicalize(dataset, data) return data.T.flatten() if flat else data elif expanded: - data = cls.expanded_coords(dataset, dim) + data = cls.coords(dataset, dim, expanded=True) return data.flatten() if flat else data else: - return cls.coords(dataset, dim, True) + return cls.coords(dataset, dim, ordered=True) @classmethod diff --git a/holoviews/core/data/iris.py b/holoviews/core/data/iris.py index 063ea87f1a..732e3f44dc 100644 --- a/holoviews/core/data/iris.py +++ b/holoviews/core/data/iris.py @@ -119,7 +119,9 @@ def validate(cls, dataset): @classmethod - def coords(cls, dataset, dim, ordered=False): + def coords(cls, dataset, dim, ordered=False, expanded=False): + if expanded: + return util.expand_grid_coords(dataset, dim) data = dataset.data.coords(dim)[0].points if ordered and np.all(data[1:] < data[:-1]): data = data[::-1] @@ -138,10 +140,10 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = cls.canonicalize(dataset, data, coord_names) return data.T.flatten() if flat else data elif expanded: - data = cls.expanded_coords(dataset, dim) + data = cls.coords(dataset, dim, expanded=True) return data.flatten() if flat else data else: - return cls.coords(dataset, dim.name, True) + return cls.coords(dataset, dim.name, ordered=True) @classmethod diff --git a/holoviews/core/data/xarray.py b/holoviews/core/data/xarray.py index 67d1bf67a9..6cca1a83b9 100644 --- a/holoviews/core/data/xarray.py +++ b/holoviews/core/data/xarray.py @@ -120,8 +120,10 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs): @classmethod - def coords(cls, dataset, dim, ordered=False): - data = dataset.data[dim].data + def coords(cls, dataset, dim, ordered=False, expanded=False): + if expanded: + return util.expand_grid_coords(dataset, dim) + data = dataset.data[dim].data if ordered and np.all(data[1:] < data[:-1]): data = data[::-1] return data @@ -135,10 +137,10 @@ def values(cls, dataset, dim, expanded=True, flat=True): data = cls.canonicalize(dataset, data, coord_dims=coord_dims) return data.T.flatten() if flat else data elif expanded: - data = cls.expanded_coords(dataset, dim) + data = cls.coords(dataset, dim, expanded=True) return data.flatten() if flat else data else: - return cls.coords(dataset, dim, True) + return cls.coords(dataset, dim, ordered=True) @classmethod diff --git a/holoviews/core/util.py b/holoviews/core/util.py index 2f2afe91ea..7e558c268f 100644 --- a/holoviews/core/util.py +++ b/holoviews/core/util.py @@ -904,3 +904,16 @@ def get_dynamic_item(map_obj, dimensions, key): else: el = None return key, el + + +def expand_grid_coords(dataset, dim): + """ + Expand the coordinates along a dimension of the gridded + dataset into an ND-array matching the dimensionality of + the dataset. + """ + arrays = [dataset.interface.coords(dataset, d.name, True) + for d in dataset.kdims] + idx = dataset.get_dimension_index(dim) + return cartesian_product(arrays)[idx] +