Skip to content

Commit

Permalink
Merge pull request #715 from ioam/redim
Browse files Browse the repository at this point in the history
Implemented redim method on Dimensioned objects
  • Loading branch information
jlstevens committed Jun 9, 2016
2 parents 16d97f4 + 7d56136 commit 81b5601
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 3 deletions.
25 changes: 24 additions & 1 deletion holoviews/core/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
except ImportError:
pass

from ..dimension import Dimension
from ..dimension import Dimension, replace_dimensions
from ..element import Element
from ..spaces import HoloMap
from .. import util
Expand Down Expand Up @@ -390,6 +390,29 @@ def shape(self):
return self.interface.shape(self)


def redim(self, specs=None, **dimensions):
"""
Replace dimensions on the dataset and allows renaming
dimensions in the dataset. Dimension mapping should map
between the old dimension name and a dictionary of the new
attributes, a completely new dimension or a new string name.
"""
if specs is not None:
if not isinstance(specs, list):
specs = [specs]
if not any(self.matches(spec) for spec in specs):
return self

kdims = replace_dimensions(self.kdims, dimensions)
vdims = replace_dimensions(self.vdims, dimensions)
zipped_dims = zip(self.kdims+self.vdims, kdims+vdims)
renames = {pk.name: nk for pk, nk in zipped_dims if pk != nk}
data = self.data
if renames:
data = self.interface.redim(self, renames)
return self.clone(data, kdims=kdims, vdims=vdims)


def dimension_values(self, dim, expanded=True, flat=True):
"""
Returns the values along a particular dimension. If unique
Expand Down
4 changes: 4 additions & 0 deletions holoviews/core/data/dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ def add_dimension(cls, dataset, dimension, dim_pos, values, vdim):
data.insert(dim_pos, (dim, values))
return OrderedDict(data)

@classmethod
def redim(cls, dataset, dimensions):
return OrderedDict([(dimensions.get(k, dataset.get_dimension(k)).name, v)
for k,v in dataset.data.items()])

@classmethod
def concat(cls, dataset_objs):
Expand Down
4 changes: 4 additions & 0 deletions holoviews/core/data/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,7 @@ def shape(cls, dataset):
@classmethod
def length(cls, dataset):
return len(dataset.data)

@classmethod
def redim(cls, dataset, dimensions):
return dataset.data
15 changes: 15 additions & 0 deletions holoviews/core/data/iris.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ def range(cls, dataset, dimension):
return (np.nanmin(values), np.nanmax(values))


@classmethod
def redim(cls, dataset, dimensions):
"""
Rename coords on the Cube.
"""
new_dataset = dataset.data.copy()
for name, new_dim in dimensions.items():
if name == new_dataset.name():
new_dataset.rename(new_dim.name)
for coord in new_dataset.dim_coords:
if name == coord.name():
coord.rename(new_dim.name)
return new_dataset


@classmethod
def length(cls, dataset):
"""
Expand Down
4 changes: 4 additions & 0 deletions holoviews/core/data/ndelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def validate(cls, columns):
def dimension_type(cls, columns, dim):
return Dimensioned.get_dimension_type(columns, dim)

@classmethod
def redim(cls, dataset, dimensions):
return dataset.data.redim(**dimensions)

@classmethod
def shape(cls, columns):
return (len(columns), len(columns.dimensions()))
Expand Down
8 changes: 7 additions & 1 deletion holoviews/core/data/pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ def reindex(cls, columns, kdims=None, vdims=None):
return columns.data


@classmethod
def redim(cls, dataset, dimensions):
column_renames = {k: v.name for k, v in dimensions.items()}
return dataset.data.rename(columns=column_renames)


@classmethod
def sort(cls, columns, by=[]):
import pandas as pd
Expand Down Expand Up @@ -210,7 +216,7 @@ def add_dimension(cls, columns, dimension, dim_pos, values, vdim):
@classmethod
def dframe(cls, columns, dimensions):
if dimensions:
return columns.reindex(columns=dimensions)
return columns.reindex(dimensions).data.copy()
else:
return columns.data.copy()

Expand Down
58 changes: 58 additions & 0 deletions holoviews/core/dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,35 @@ def param_aliases(d):
return d


def replace_dimensions(dimensions, overrides):
"""
Replaces dimensions in a list with a dictionary of overrides.
Overrides should be indexed by the dimension name with values that
is either a Dimension object, a string name or a dictionary
specifying the dimension parameters to override.
"""
replaced = []
for d in dimensions:
if d.name in overrides:
override = overrides[d.name]
else:
override = None

if override is None:
replaced.append(d)
elif isinstance(override, basestring):
replaced.append(d(override))
elif isinstance(override, Dimension):
replaced.append(override)
elif isinstance(override, dict):
replaced.append(d(**override))
else:
raise ValueError('Dimension can only be overridden '
'with another dimension or a dictionary '
'of attributes')
return replaced


class Dimension(param.Parameterized):
"""
Dimension objects are used to specify some important general
Expand Down Expand Up @@ -766,6 +795,35 @@ def select(self, selection_specs=None, **kwargs):
return selection


def redim(self, specs=None, **dimensions):
"""
Replaces existing dimensions in an object with new dimensions
or changing specific attributes of a dimensions. Dimension
mapping should map between the old dimension name and a
dictionary of the new attributes, a completely new dimension
or a new string name.
"""
if specs is None:
applies = True
else:
if not isinstance(specs, list):
specs = [specs]
applies = any(self.matches(spec) for spec in specs)

redimmed = self
if self._deep_indexable:
deep_mapped = [(k, v.redim(specs, **dimensions))
for k, v in self.items()]
redimmed = self.clone(deep_mapped)

if applies:
kdims = replace_dimensions(self.kdims, dimensions)
vdims = replace_dimensions(self.vdims, dimensions)
return redimmed.clone(kdims=kdims, vdims=vdims)
else:
return redimmed


def dimension_values(self, dimension, expanded=True, flat=True):
"""
Returns the values along the specified dimension. This method
Expand Down
11 changes: 11 additions & 0 deletions tests/testdataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ def test_dataset_sort_vdim_hm(self):
kdims=['x'], vdims=['y'])
self.assertEqual(dataset.sort('y'), dataset_sorted)


def test_dataset_redim_hm_kdim(self):
redimmed = self.dataset_hm.redim(x='Time')
self.assertEqual(redimmed.dimension_values('Time'),
self.dataset_hm.dimension_values('x'))

def test_dataset_redim_hm_vdim(self):
redimmed = self.dataset_hm.redim(y='Value')
self.assertEqual(redimmed.dimension_values('Value'),
self.dataset_hm.dimension_values('y'))

def test_dataset_sample_hm(self):
samples = self.dataset_hm.sample([0, 5, 10]).dimension_values('y')
self.assertEqual(samples, np.array([0, 10, 20]))
Expand Down
21 changes: 20 additions & 1 deletion tests/testdimensions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Test cases for Dimension and Dimensioned object behaviour.
"""
from holoviews.core import Dimensioned
from holoviews.core import Dimensioned, Dimension
from holoviews.element.comparison import ComparisonTestCase


Expand All @@ -18,3 +18,22 @@ def test_dimensioned_constant_label(self):
view.label = 'another label'
raise AssertionError("Label should be a constant parameter.")
except TypeError: pass

def test_dimensionsed_redim_string(self):
dimensioned = Dimensioned('Arbitrary Data', kdims=['x'])
redimensioned = dimensioned.clone(kdims=['Test'])
self.assertEqual(redimensioned, dimensioned.redim(x='Test'))

def test_dimensionsed_redim_dimension(self):
dimensioned = Dimensioned('Arbitrary Data', kdims=['x'])
redimensioned = dimensioned.clone(kdims=['Test'])
self.assertEqual(redimensioned, dimensioned.redim(x=Dimension('Test')))

def test_dimensionsed_redim_dict(self):
dimensioned = Dimensioned('Arbitrary Data', kdims=['x'])
redimensioned = dimensioned.clone(kdims=['Test'])
self.assertEqual(redimensioned, dimensioned.redim(x={'name': 'Test'}))

def test_dimensionsed_redim_dict_range(self):
redimensioned = Dimensioned('Arbitrary Data', kdims=['x']).redim(x={'range': (0, 10)})
self.assertEqual(redimensioned.kdims[0].range, (0, 10))
21 changes: 21 additions & 0 deletions tests/testndmapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ def test_idxmapping_reindex(self):

self.assertEqual([d.name for d in reduced_ndmap.kdims], reduced_dims)

def test_idxmapping_redim(self):
data = [((0, 0.5), 'a'), ((1, 0.5), 'b')]
ndmap = MultiDimensionalMapping(data, kdims=[self.dim1, self.dim2])
redimmed = ndmap.redim(intdim='Integer')
self.assertEqual(redimmed.kdims, [Dimension('Integer'), Dimension('floatdim')])

def test_idxmapping_add_dimension(self):
ndmap = MultiDimensionalMapping(self.init_items_1D_list, kdims=[self.dim1])
ndmap2d = ndmap.add_dimension(self.dim2, 0, 0.5)
Expand Down Expand Up @@ -136,6 +142,21 @@ def setUp(self):
self.columns = Dataset(np.column_stack([self.xs, self.y_ints]),
kdims=['x'], vdims=['y'])

def test_holomap_redim(self):
hmap = HoloMap({i: Dataset({'x':self.xs, 'y': self.ys * i},
kdims=['x'], vdims=['y'])
for i in range(10)}, kdims=['z'])
redimmed = hmap.redim(x='Time')
self.assertEqual(redimmed.dimensions('all', True),
['z', 'Time', 'y'])

def test_holomap_redim_nested(self):
hmap = HoloMap({i: Dataset({'x':self.xs, 'y': self.ys * i},
kdims=['x'], vdims=['y'])
for i in range(10)}, kdims=['z'])
redimmed = hmap.redim(x='Time', z='Magnitude')
self.assertEqual(redimmed.dimensions('all', True),
['Magnitude', 'Time', 'y'])

def test_columns_collapse_heterogeneous(self):
collapsed = HoloMap({i: Dataset({'x':self.xs, 'y': self.ys * i},
Expand Down

0 comments on commit 81b5601

Please sign in to comment.