Skip to content

Commit

Permalink
Merge branch 'master' into batched_plots
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Jun 16, 2016
2 parents 6e398e6 + 856f26b commit 2763871
Show file tree
Hide file tree
Showing 16 changed files with 302 additions and 70 deletions.
25 changes: 24 additions & 1 deletion holoviews/core/data/__init__.py
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
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
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
53 changes: 45 additions & 8 deletions holoviews/core/data/iris.py
Expand Up @@ -75,16 +75,18 @@ def init(cls, eltype, data, kdims, vdims):
kdim_names = [kd.name for kd in eltype.kdims]

if not isinstance(data, iris.cube.Cube):
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]
if isinstance(data, tuple):
coords = [iris.coords.DimCoord(vals, long_name=kd)
for kd, vals in zip(kdim_names, data)]
value_array = data[-1]
vdim = vdims[0].name if isinstance(vdims[0], Dimension) else vdims[0]
data = {d: vals for d, vals in zip(kdim_names + [vdim], data)}
elif isinstance(data, dict):
vdim = vdims[0].name if isinstance(vdims[0], Dimension) else vdims[0]
coords = [iris.coords.DimCoord(vals, long_name=kd)
for kd, vals in data.items() if kd in kdims]
value_array = data[vdim]
coords = [(iris.coords.DimCoord(data[kd.name], long_name=kd.name,
units=kd.unit), ndims-n-1)
for n, kd in enumerate(kdims)]
try:
data = iris.cube.Cube(value_array, long_name=vdim,
dim_coords_and_dims=coords)
Expand Down Expand Up @@ -198,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 All @@ -222,6 +239,25 @@ def aggregate(cls, columns, kdims, function, **kwargs):
raise NotImplementedError


@classmethod
def sample(cls, dataset, samples=[]):
"""
Sampling currently not implemented.
"""
raise NotImplementedError


@classmethod
def add_dimension(cls, columns, dimension, dim_pos, values, vdim):
"""
Adding value dimensions not currently supported by iris interface.
Adding key dimensions not possible on dense interfaces.
"""
if not vdim:
raise Exception("Cannot add key dimension to a dense representation.")
raise NotImplementedError


@classmethod
def select_to_constraint(cls, selection):
"""
Expand All @@ -232,7 +268,7 @@ def select_to_constraint(cls, selection):
if isinstance(constraint, slice):
constraint = (constraint.start, constraint.stop)
if isinstance(constraint, tuple):
constraint = iris.util.between(*constraint)
constraint = iris.util.between(*constraint, rh_inclusive=False)
constraint_kwargs[dim] = constraint
return iris.Constraint(**constraint_kwargs)

Expand All @@ -244,8 +280,9 @@ def select(cls, dataset, selection_mask=None, **selection):
"""
constraint = cls.select_to_constraint(selection)
pre_dim_coords = [c.name() for c in dataset.data.dim_coords]
indexed = cls.indexed(dataset, selection)
extracted = dataset.data.extract(constraint)
if not extracted.dim_coords:
if indexed and not extracted.dim_coords:
return extracted.data.item()
post_dim_coords = [c.name() for c in extracted.dim_coords]
dropped = [c for c in pre_dim_coords if c not in post_dim_coords]
Expand Down
4 changes: 4 additions & 0 deletions holoviews/core/data/ndelement.py
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
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
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
2 changes: 1 addition & 1 deletion holoviews/element/chart.py
Expand Up @@ -349,7 +349,7 @@ class VectorField(Points):
group = param.String(default='VectorField', constant=True)

vdims = param.List(default=[Dimension('Angle', cyclic=True, range=(0,2*np.pi)),
Dimension('Magnitude')], bounds=(1, 2))
Dimension('Magnitude')], bounds=(1, None))

_null_value = np.array([[], [], [], []]).T # For when data is None
_min_dims = 3 # Minimum number of columns
Expand Down
52 changes: 28 additions & 24 deletions holoviews/interface/collector.py
Expand Up @@ -538,30 +538,34 @@ def __call__(self, attrtree=Layout(), times=[], strict=False):
self._schedule_tasks(times, strict)
(self.fixed, attrtree.fixed) = (False, False)

for i, t in enumerate(np.diff(times)):
interval_hook(float(t))

# An empty attrtree buffer stops analysis repeatedly
# computing results over the entire accumulated map
attrtree_buffer = Layout()
for task in self._scheduled_tasks:
try:
if isinstance(task, Analyze) and task.mapwise:
task(attrtree, self.time_fn(), times)
else:
task(attrtree_buffer, self.time_fn(), times)
attrtree.update(attrtree_buffer)
except Exception as e:
param.main.warning("Task %s at time %s failed with following "
"exception and was skipped:\n%s",
task, self.time_fn(), e)
if update_progress:
interval_hook.percent_range = (completion[i],
completion[i+1])
interval_hook(0)

(self.fixed, attrtree.fixed) = (True, True)
return attrtree
try:
for i, t in enumerate(np.diff(times)):
interval_hook(float(t))

# An empty attrtree buffer stops analysis repeatedly
# computing results over the entire accumulated map
attrtree_buffer = Layout()
for task in self._scheduled_tasks:
try:
if isinstance(task, Analyze) and task.mapwise:
task(attrtree, self.time_fn(), times)
else:
task(attrtree_buffer, self.time_fn(), times)
attrtree.update(attrtree_buffer)
except Exception as e:
param.main.warning("Task %s at time %s failed with following "
"exception and was skipped:\n%s",
task, self.time_fn(), e)
if update_progress:
interval_hook.percent_range = (completion[i],
completion[i+1])
interval_hook(0)

(self.fixed, attrtree.fixed) = (True, True)
return attrtree
except KeyboardInterrupt:
(self.fixed, attrtree.fixed) = (True, True)
return attrtree


def verify_times(self, times, strict=False):
Expand Down
2 changes: 1 addition & 1 deletion holoviews/plotting/mpl/__init__.py
Expand Up @@ -67,7 +67,7 @@ def get_color_cycle():
if mpl_ge_150:
cyl = mpl.rcParams['axes.prop_cycle']
# matplotlib 1.5 verifies that axes.prop_cycle *is* a cycler
# but no garuantee that there's a `color` key.
# but no guarantee that there's a `color` key.
# so users could have a custom rcParmas w/ no color...
try:
return [x['color'] for x in cyl]
Expand Down

0 comments on commit 2763871

Please sign in to comment.