Skip to content

Commit

Permalink
Merge 1ee04d7 into 66d177a
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Jul 27, 2016
2 parents 66d177a + 1ee04d7 commit 6f86c37
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 79 deletions.
4 changes: 3 additions & 1 deletion holoviews/core/data/__init__.py
Expand Up @@ -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), **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)
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)
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/array.py
Expand Up @@ -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):
Expand Down
11 changes: 7 additions & 4 deletions holoviews/core/data/dictionary.py
Expand Up @@ -56,7 +56,10 @@ 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):
# 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))}
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))
Expand Down Expand Up @@ -84,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
Expand Down Expand Up @@ -132,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])
Expand Down
97 changes: 78 additions & 19 deletions holoviews/core/data/grid.py
Expand Up @@ -37,6 +37,8 @@ class GridInterface(DictInterface):

datatype = 'grid'

gridded = True

@classmethod
def init(cls, eltype, data, kdims, vdims):
if kdims is None:
Expand All @@ -53,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
Expand All @@ -71,7 +73,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}, {}


Expand Down Expand Up @@ -101,19 +103,76 @@ def length(cls, dataset):
return np.product([len(dataset.data[d.name]) for d in dataset.kdims])


@classmethod
def coords(cls, dataset, dim, ordered=False, expanded=False):
"""
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 canonicalize(cls, dataset, data, coord_dims=None):
"""
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.
"""
if coord_dims is None:
coord_dims = dataset.dimensions('key', True)

# Reorient data
invert = False
slices = []
for d in coord_dims:
coords = cls.coords(dataset, d)
if np.all(coords[1:] < coords[:-1]):
slices.append(slice(None, None, -1))
invert = True
else:
slices.append(slice(None))
data = data.__getitem__(slices[::-1]) if invert else data

# Transpose data
dims = [name for name in coord_dims[::-1]
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
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


@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])
idx = dataset.get_dimension_index(dim)
values = prod[idx]
return values.flatten() if flat else values
else:
if dim in dataset.vdims:
dim = dataset.get_dimension(dim)
values = dataset.data.get(dim.name)
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.coords(dataset, dim, expanded=True)
return data.flatten() if flat else data
else:
return cls.coords(dataset, dim, ordered=True)


@classmethod
Expand Down Expand Up @@ -282,17 +341,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

Expand Down
2 changes: 2 additions & 0 deletions holoviews/core/data/interface.py
Expand Up @@ -11,6 +11,8 @@ class Interface(param.Parameterized):

datatype = None

gridded = False

@classmethod
def register(cls, interface):
cls.interfaces[interface.datatype] = interface
Expand Down
34 changes: 18 additions & 16 deletions holoviews/core/data/iris.py
Expand Up @@ -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 = []
Expand All @@ -118,30 +118,32 @@ def validate(cls, dataset):
pass


@classmethod
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]
return data


@classmethod
def values(cls, dataset, dim, expanded=True, flat=True):
"""
Returns an array of the values along the supplied dimension.
"""
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]
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)
else:
data = np.flipud(data)
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
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]
data = cls.coords(dataset, dim, expanded=True)
return data.flatten() if flat else data
else:
data = dataset.data.coords(dim.name)[0].points
return data.flatten() if flat else data
return cls.coords(dataset, dim.name, ordered=True)


@classmethod
Expand Down
65 changes: 38 additions & 27 deletions holoviews/core/data/xarray.py
Expand Up @@ -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 = [vd 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}, {}


Expand Down Expand Up @@ -116,20 +119,28 @@ def groupby(cls, dataset, dimensions, container_type, group_type, **kwargs):
return container_type(data)


@classmethod
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


@classmethod
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)
else:
return data.T.flatten() if flat else data
elif not expanded:
return data
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 expanded:
data = cls.coords(dataset, dim, expanded=True)
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)]
return product.flatten() if flat else product
return cls.coords(dataset, dim, ordered=True)


@classmethod
Expand Down
13 changes: 13 additions & 0 deletions holoviews/core/util.py
Expand Up @@ -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]

0 comments on commit 6f86c37

Please sign in to comment.