Skip to content

Commit

Permalink
Added basics of an Image interface
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Nov 23, 2016
1 parent 3ba0b42 commit 3af6cdd
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 125 deletions.
18 changes: 14 additions & 4 deletions holoviews/core/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .array import ArrayInterface
from .dictionary import DictInterface
from .grid import GridInterface
from .image import ImageInterface
from .ndelement import NdElementInterface

datatypes = ['array', 'dictionary', 'grid', 'ndelement']
Expand Down Expand Up @@ -254,11 +255,12 @@ def select(self, selection_specs=None, **selection):
if selection_specs and not any(self.matches(sp) for sp in selection_specs):
return self

data = self.interface.select(self, **selection)
data, kwargs = self.interface.select(self, **selection)

if np.isscalar(data):
return data
else:
return self.clone(data)
return self.clone(data, **kwargs)


def reindex(self, kdims=None, vdims=None):
Expand Down Expand Up @@ -373,11 +375,19 @@ def aggregate(self, dimensions=None, function=None, spreadfn=None, **kwargs):
combined = combined.add_dimension(dim, ndims+i, dvals, True)
return combined

ndims = len(dimensions)
min_d, max_d = self.params('kdims').bounds
if np.isscalar(aggregated):
return aggregated
else:
return self.clone(aggregated, kdims=kdims, vdims=vdims)

new_type = None if ndims >= min_d and ndims <= max_d else Dataset
try:
return self.clone(aggregated, kdims=kdims, vdims=vdims,
new_type=new_type)
except:
datatype = self.params('datatype').default
return self.clone(aggregated, kdims=kdims, vdims=vdims,
new_type=new_type, datatype=datatype)


def groupby(self, dimensions=[], container_type=HoloMap, group_type=None,
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def select(cls, dataset, selection_mask=None, **selection):
data = np.atleast_2d(dataset.data[selection_mask, :])
if len(data) == 1 and indexed:
data = data[0, dataset.ndims]
return data
return data, {}


@classmethod
Expand Down
4 changes: 2 additions & 2 deletions holoviews/core/data/dask.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ def select(cls, columns, selection_mask=None, **selection):
indexed = cls.indexed(columns, selection)
df = df if selection_mask is None else df[selection_mask]
if indexed and len(df) == 1:
return df[columns.vdims[0].name].compute().iloc[0]
return df
return df[columns.vdims[0].name].compute().iloc[0], {}
return df, {}

@classmethod
def groupby(cls, columns, dimensions, container_type, group_type, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion holoviews/core/data/dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def select(cls, dataset, selection_mask=None, **selection):
for k, v in dataset.data.items())
if indexed and len(list(data.values())[0]) == 1:
return data[dataset.vdims[0].name][0]
return data
return data, {}


@classmethod
Expand Down
4 changes: 2 additions & 2 deletions holoviews/core/data/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,9 @@ def select(cls, dataset, selection_mask=None, **selection):
data[vdim.name] = dataset.data[vdim.name][index]

if indexed and len(data[dataset.vdims[0].name]) == 1:
return data[dataset.vdims[0].name][0]
return data[dataset.vdims[0].name][0], {}

return data
return data, {}


@classmethod
Expand Down
148 changes: 148 additions & 0 deletions holoviews/core/data/image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import numpy as np

from ..boundingregion import BoundingRegion, BoundingBox
from ..dimension import Dimension
from ..ndmapping import OrderedDict
from ..sheetcoords import SheetCoordinateSystem, Slice
from .grid import GridInterface
from .interface import Interface


class ImageInterface(GridInterface):
"""
Interface for 2 or 3D arrays representing images
of raw luminance values, RGB values or HSV values.
"""

types = (np.ndarray,)

datatype = 'image'

@classmethod
def init(cls, eltype, data, kdims, vdims):
if kdims is None:
kdims = eltype.kdims
if vdims is None:
vdims = eltype.vdims

dimensions = [d.name if isinstance(d, Dimension) else
d for d in kdims + vdims]
if not isinstance(data, np.ndarray) or data.ndim != 2:
raise ValueError('ImageIntereface expects a 2D array.')

if kdims is None:
kdims = eltype.kdims
if vdims is None:
vdims = eltype.vdims
return data, {'kdims':kdims, 'vdims':vdims}, {}


@classmethod
def validate(cls, dataset):
pass


@classmethod
def range(cls, obj, dim):
dim_idx = obj.get_dimension_index(dim)
if dim_idx in [0, 1] and obj.bounds:
l, b, r, t = obj.bounds.lbrt()
if dim_idx:
drange = (b, t)
else:
drange = (l, r)
elif 1 < dim_idx < len(obj.vdims) + 2:
dim_idx -= 2
data = np.atleast_3d(obj.data)[:, :, dim_idx]
drange = (np.nanmin(data), np.nanmax(data))
else:
drange = (None, None)
return drange


@classmethod
def values(cls, dataset, dim, expanded=True, flat=True):
"""
The set of samples available along a particular dimension.
"""
dim_idx = dataset.get_dimension_index(dim)
if dim_idx in [0, 1]:
l, b, r, t = dataset.bounds.lbrt()
dim2, dim1 = dataset.data.shape[:2]
d1_half_unit = (r - l)/dim1/2.
d2_half_unit = (t - b)/dim2/2.
d1lin = np.linspace(l+d1_half_unit, r-d1_half_unit, dim1)
d2lin = np.linspace(b+d2_half_unit, t-d2_half_unit, dim2)
if expanded:
values = np.meshgrid(d2lin, d1lin)[abs(dim_idx-1)]
return values.flatten() if flat else values
else:
return d2lin if dim_idx else d1lin
elif dim_idx == 2:
# Raster arrays are stored with different orientation
# than expanded column format, reorient before expanding
data = np.flipud(dataset.data)
return data.flatten() if flat else data
else:
return None, None


@classmethod
def select(cls, dataset, selection_mask=None, **selection):
"""
Slice the underlying numpy array in sheet coordinates.
"""
selection = {k: slice(*sel) if isinstance(sel, tuple) else sel
for k, sel in selection.items()}
coords = tuple(selection[kd.name] if kd.name in selection else slice(None)
for kd in dataset.kdims)
if not any([isinstance(el, slice) for el in coords]):
data = dataset.data[dataset.sheet2matrixidx(*coords)]
xidx, yidx = coords
l, b, r, t = dataset.bounds.lbrt()
xunit = (1./dataset.xdensity)
yunit = (1./dataset.ydensity)
if isinstance(xidx, slice):
l = l if xidx.start is None else max(l, xidx.start)
r = r if xidx.stop is None else min(r, xidx.stop)
if isinstance(yidx, slice):
b = b if yidx.start is None else max(b, yidx.start)
t = t if yidx.stop is None else min(t, yidx.stop)
bounds = BoundingBox(points=((l, b), (r, t)))
slc = Slice(bounds, dataset)
data = slc.submatrix(dataset.data)
l, b, r, t = slc.compute_bounds(dataset).lbrt()
if not isinstance(xidx, slice):
xc, _ = dataset.closest_cell_center(xidx, b)
l, r = xc-xunit/2, xc+xunit/2
_, x = dataset.sheet2matrixidx(xidx, b)
data = data[:, x][:, np.newaxis]
elif not isinstance(yidx, slice):
_, yc = dataset.closest_cell_center(l, yidx)
b, t = yc-yunit/2, yc+yunit/2
y, _ = dataset.sheet2matrixidx(l, yidx)
data = data[y, :][np.newaxis, :]
bounds = BoundingBox(points=((l, b), (r, t)))
return data, {'bounds': bounds}


@classmethod
def length(cls, dataset):
return np.product(dataset.data.shape)


@classmethod
def aggregate(cls, dataset, kdims, function, **kwargs):
kdims = [kd.name if isinstance(kd, Dimension) else kd for kd in kdims]
axes = tuple(dataset.ndims-dataset.get_dimension_index(kdim)-1
for kdim in dataset.kdims if kdim not in kdims)

data = np.atleast_1d(function(dataset.data, axis=axes, **kwargs))
if np.isscalar(data):
return data
elif len(axes) == 1:
return {kdims[0]: cls.values(dataset, axes[0], expanded=False),
dataset.vdims[0].name: data}


Interface.register(ImageInterface)
4 changes: 2 additions & 2 deletions holoviews/core/data/iris.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,12 @@ def select(cls, dataset, selection_mask=None, **selection):
indexed = cls.indexed(dataset, selection)
extracted = dataset.data.extract(constraint)
if indexed and not extracted.dim_coords:
return extracted.data.item()
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]
for d in dropped:
extracted = iris.util.new_axis(extracted, d)
return extracted
return extracted, {}


Interface.register(CubeInterface)
4 changes: 2 additions & 2 deletions holoviews/core/data/ndelement.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ def groupby(cls, columns, dimensions, container_type, group_type, **kwargs):
@classmethod
def select(cls, columns, selection_mask=None, **selection):
if selection_mask is None:
return columns.data.select(**selection)
return columns.data.select(**selection), {}
else:
return columns.data[selection_mask]
return columns.data[selection_mask], {}

@classmethod
def sample(cls, columns, samples=[]):
Expand Down
4 changes: 2 additions & 2 deletions holoviews/core/data/pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ def select(cls, columns, selection_mask=None, **selection):
indexed = cls.indexed(columns, selection)
df = df.ix[selection_mask]
if indexed and len(df) == 1:
return df[columns.vdims[0].name].iloc[0]
return df
return df[columns.vdims[0].name].iloc[0], {}
return df, {}


@classmethod
Expand Down
4 changes: 2 additions & 2 deletions holoviews/core/data/xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ def select(cls, dataset, selection_mask=None, **selection):
indexed = cls.indexed(dataset, selection)
if (indexed and len(data.data_vars) == 1 and
len(data[dataset.vdims[0].name].shape) == 0):
return data[dataset.vdims[0].name].item()
return data
return data[dataset.vdims[0].name].item(), {}
return data, {}

@classmethod
def length(cls, dataset):
Expand Down
Loading

0 comments on commit 3af6cdd

Please sign in to comment.