Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Datashader operations #894

Merged
merged 23 commits into from Oct 5, 2016
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dfa037d
Added support for DataArrays in xarray interface
philippjfr Oct 4, 2016
c6422b7
Added Datashader Aggregate and Shade operations
philippjfr Oct 4, 2016
e0874c0
Refactored and added docstrings to datashader operations
philippjfr Oct 4, 2016
ba30243
Added support for attaching streams to ElementOperations
philippjfr Oct 4, 2016
40f1153
Range streams suppress updating of ranges
philippjfr Oct 4, 2016
345c413
Renamed DynamicFunction to Dynamic
philippjfr Oct 4, 2016
08ef8e0
Set Range stream defaults to None
philippjfr Oct 4, 2016
d5fc734
Added Datashade operation
philippjfr Oct 4, 2016
b4d3fdf
Renamed DynamicFunction and moved it to hv.util
philippjfr Oct 4, 2016
0488c52
Reverted change to ElementOperation.process_element
philippjfr Oct 4, 2016
5ce5bab
Cleaned up datashader operations
philippjfr Oct 4, 2016
83acee2
Fixed datashader sampling options
philippjfr Oct 5, 2016
189a231
Expanded on Shade docstring
philippjfr Oct 5, 2016
4dee22f
Added datashader to travis
philippjfr Oct 5, 2016
97381b2
Tweaked datashader operation parameters
philippjfr Oct 5, 2016
6046f9e
Expanded on dynamic ElementOperation features
philippjfr Oct 5, 2016
088a1b7
Initialize stream parameters in Dynamic
philippjfr Oct 5, 2016
d23d873
Fixed dynamic operation tests
philippjfr Oct 5, 2016
ac04550
Fixed docstring
philippjfr Oct 5, 2016
7b8a92a
Expanded on datashader operation docstrings
philippjfr Oct 5, 2016
10ea44e
Exposed Aliases in hv.util
philippjfr Oct 5, 2016
d602fde
Small docstring fix
philippjfr Oct 5, 2016
e151dce
Small Dynamic docstring fix
philippjfr Oct 5, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -24,7 +24,7 @@ install:
- conda update -q conda
# Useful for debugging any issues with conda
- conda info -a
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION scipy numpy freetype nose bokeh pandas jupyter ipython=4.2.0 param matplotlib=1.5.1 xarray
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION scipy numpy freetype nose bokeh pandas jupyter ipython=4.2.0 param matplotlib=1.5.1 xarray datashader
- source activate test-environment
- conda install -c conda-forge -c scitools iris sip=4.18 plotly
- if [[ "$TRAVIS_PYTHON_VERSION" == "3.4" ]]; then
Expand Down
12 changes: 11 additions & 1 deletion holoviews/core/data/xarray.py
Expand Up @@ -37,7 +37,17 @@ def init(cls, eltype, data, kdims, vdims):
kdim_param = element_params['kdims']
vdim_param = element_params['vdims']

if not isinstance(data, xr.Dataset):
if isinstance (data, xr.DataArray):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment would be helpful above this block explaining what the purpose is. Maybe "Extract dimensions from xarray data structure"?

if data.name:
vdim = Dimension(data.name)
elif vdims:
vdim = vdims[0]
elif len(vdim_param.default) == 1:
vdim = vdim_param.default[0]
vdims = [vdim]
kdims = [Dimension(d) for d in data.dims[::-1]]
data = xr.Dataset({vdim.name: data})
elif not isinstance(data, xr.Dataset):
if kdims is None:
kdims = kdim_param.default
if vdims is None:
Expand Down
92 changes: 13 additions & 79 deletions holoviews/core/operation.py
Expand Up @@ -70,80 +70,6 @@ def get_overlay_bounds(cls, overlay):
raise ValueError("Extents across the overlay are inconsistent")


class DynamicOperation(Operation):
"""
Dynamically applies an operation to the elements of a HoloMap
or DynamicMap. Will return a DynamicMap wrapping the original
map object, which will lazily evaluate when a key is requested.
The _process method should be overridden in subclasses to apply
a specific operation, DynamicOperation itself applies a no-op,
making the DynamicOperation baseclass useful for converting
existing HoloMaps to a DynamicMap.
"""

def __call__(self, map_obj, **params):
self.p = param.ParamOverrides(self, params)
callback = self._dynamic_operation(map_obj)
if isinstance(map_obj, DynamicMap):
return map_obj.clone(callback=callback, shared_data=False)
else:
return self._make_dynamic(map_obj, callback)


def _process(self, element):
return element


def _dynamic_operation(self, map_obj):
"""
Generate function to dynamically apply the operation.
Wraps an existing HoloMap or DynamicMap.
"""
if not isinstance(map_obj, DynamicMap):
def dynamic_operation(*key):
return self._process(map_obj[key])
return dynamic_operation

def dynamic_operation(*key):
key = key[0] if map_obj.mode == 'open' else key
_, el = util.get_dynamic_item(map_obj, map_obj.kdims, key)
return self._process(el)

return dynamic_operation


def _make_dynamic(self, hmap, dynamic_fn):
"""
Accepts a HoloMap and a dynamic callback function creating
an equivalent DynamicMap from the HoloMap.
"""
dim_values = zip(*hmap.data.keys())
params = util.get_param_values(hmap)
kdims = [d(values=list(set(values))) for d, values in
zip(hmap.kdims, dim_values)]
return DynamicMap(dynamic_fn, **dict(params, kdims=kdims))



class DynamicFunction(DynamicOperation):
"""
Dynamically applies a function to the Elements in a DynamicMap
or HoloMap. Must supply a HoloMap or DynamicMap type and will
return another DynamicMap type, which will apply the supplied
function with the supplied kwargs whenever a value is requested
from the map.
"""

function = param.Callable(default=lambda x: x, doc="""
Function to apply to DynamicMap items dynamically.""")

kwargs = param.Dict(default={}, doc="""
Keyword arguments passed to the function.""")

def _process(self, element):
return self.p.function(element, **self.p.kwargs)



class ElementOperation(Operation):
"""
Expand All @@ -152,6 +78,12 @@ class ElementOperation(Operation):
input, a processed holomap is returned as output where the
individual elements have been transformed accordingly. An
ElementOperation may turn overlays in new elements or vice versa.

An ElementOperation can be set to be dynamic, which will return a
DynamicMap with a callback that will apply the operation
dynamically. An ElementOperation may also supply a list of Stream
classes on the streams attribute, which can allow dynamic control
over the parameters on the operation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have said 'on a streams parameter' instead of 'on the streams attribute'...

"""

dynamic = param.ObjectSelector(default='default',
Expand All @@ -171,7 +103,6 @@ class ElementOperation(Operation):
first component is a Normalization.ranges list and the second
component is Normalization.keys. """)


def _process(self, view, key=None):
"""
Process a single input element and outputs new single element
Expand All @@ -197,16 +128,19 @@ def __call__(self, element, **params):
isinstance(element, DynamicMap))
or self.p.dynamic is True)

if isinstance(element, ViewableElement):
processed = self._process(element)
elif isinstance(element, GridSpace):
if isinstance(element, GridSpace):
# Initialize an empty axis layout
grid_data = ((pos, self(cell, **params))
for pos, cell in element.items())
processed = GridSpace(grid_data, label=element.label,
kdims=element.kdims)
elif dynamic:
processed = DynamicFunction(element, function=self, kwargs=params)
from ..util import Dynamic
streams = getattr(self, 'streams', [])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feature probably needs mentioning in the docstring wherever dynamic is mentioned.

processed = Dynamic(element, streams=streams,
operation=self, kwargs=params)
elif isinstance(element, ViewableElement):
processed = self._process(element)
elif isinstance(element, DynamicMap):
if any((not d.values) for d in element.kdims):
raise ValueError('Applying a non-dynamic operation requires '
Expand Down
4 changes: 2 additions & 2 deletions holoviews/core/overlay.py
Expand Up @@ -24,10 +24,10 @@ class Overlayable(object):

def __mul__(self, other):
if type(other).__name__ == 'DynamicMap':
from .operation import DynamicFunction
from ..util import Dynamic
def dynamic_mul(element):
return self * element
return DynamicFunction(other, function=dynamic_mul)
return Dynamic(other, operation=dynamic_mul)
if isinstance(other, UniformNdMapping) and not isinstance(other, CompositeOverlay):
items = [(k, self * v) for (k, v) in other.items()]
return other.clone(items)
Expand Down
4 changes: 2 additions & 2 deletions holoviews/core/spaces.py
Expand Up @@ -204,10 +204,10 @@ def __mul__(self, other):
return self.clone(items, kdims=dimensions, label=self._label, group=self._group)
elif isinstance(other, self.data_type):
if isinstance(self, DynamicMap):
from .operation import DynamicFunction
from ..util import Dynamic
def dynamic_mul(element):
return element * other
return DynamicFunction(self, function=dynamic_mul)
return Dynamic(self, operation=dynamic_mul)
items = [(k, v * other) for (k, v) in self.data.items()]
return self.clone(items, label=self._label, group=self._group)
else:
Expand Down