Holoviews: Support datashading hv.Rectangles

Created on 24 Nov 2020  路  5Comments  路  Source: holoviz/holoviews

ALL software version info

  • python = 3.6.9
  • holoviews = 1.13.5
  • datashader = 0.11.1
  • bokeh = 2.2.0
  • OS: macOS = 10.15.7
  • browser: firefox = 82.0.1
  • jupyter = 1.0.0

Description of expected behavior and the observed behavior

Datashader fails to process Rectangles due to missing xdensity attribute.

Complete, minimal, self-contained example code that reproduces the issue

import holoviews as hv
from holoviews.operation.datashader import datashade
hv.extension('bokeh')
datashade(hv.Rectangles([(0, 0, 1, 1), (2, 3, 4, 6), (0.5, 2, 1.5, 4), (2, 1, 3.5, 2.5)]))

Stack traceback and/or browser JavaScript console output

AttributeError                            Traceback (most recent call last)
/opt/conda/lib/python3.6/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

/opt/conda/lib/python3.6/site-packages/holoviews/core/dimension.py in _repr_mimebundle_(self, include, exclude)
   1310         combined and returned.
   1311         """
-> 1312         return Store.render(self)
   1313 
   1314 

/opt/conda/lib/python3.6/site-packages/holoviews/core/options.py in render(cls, obj)
   1392         data, metadata = {}, {}
   1393         for hook in hooks:
-> 1394             ret = hook(obj)
   1395             if ret is None:
   1396                 continue

/opt/conda/lib/python3.6/site-packages/holoviews/ipython/display_hooks.py in pprint_display(obj)
    280     if not ip.display_formatter.formatters['text/plain'].pprint:
    281         return None
--> 282     return display(obj, raw_output=True)
    283 
    284 

/opt/conda/lib/python3.6/site-packages/holoviews/ipython/display_hooks.py in display(obj, raw_output, **kwargs)
    256     elif isinstance(obj, (HoloMap, DynamicMap)):
    257         with option_state(obj):
--> 258             output = map_display(obj)
    259     elif isinstance(obj, Plot):
    260         output = render(obj)

/opt/conda/lib/python3.6/site-packages/holoviews/ipython/display_hooks.py in wrapped(element)
    144         try:
    145             max_frames = OutputSettings.options['max_frames']
--> 146             mimebundle = fn(element, max_frames=max_frames)
    147             if mimebundle is None:
    148                 return {}, {}

/opt/conda/lib/python3.6/site-packages/holoviews/ipython/display_hooks.py in map_display(vmap, max_frames)
    204         return None
    205 
--> 206     return render(vmap)
    207 
    208 

/opt/conda/lib/python3.6/site-packages/holoviews/ipython/display_hooks.py in render(obj, **kwargs)
     66         renderer = renderer.instance(fig='png')
     67 
---> 68     return renderer.components(obj, **kwargs)
     69 
     70 

/opt/conda/lib/python3.6/site-packages/holoviews/plotting/renderer.py in components(self, obj, fmt, comm, **kwargs)
    393                 doc = Document()
    394                 with config.set(embed=embed):
--> 395                     model = plot.layout._render_model(doc, comm)
    396                 if embed:
    397                     return render_model(model, comm)

/opt/conda/lib/python3.6/site-packages/panel/viewable.py in _render_model(self, doc, comm)
    416         if comm is None:
    417             comm = state._comm_manager.get_server_comm()
--> 418         model = self.get_root(doc, comm)
    419 
    420         if config.embed:

/opt/conda/lib/python3.6/site-packages/panel/viewable.py in get_root(self, doc, comm)
    645         """
    646         doc = doc or _curdoc()
--> 647         root = self._get_model(doc, comm=comm)
    648         self._preprocess(root)
    649         ref = root.ref['id']

/opt/conda/lib/python3.6/site-packages/panel/layout.py in _get_model(self, doc, root, parent, comm)
    118         if root is None:
    119             root = model
--> 120         objects = self._get_objects(model, [], doc, root, comm)
    121         props = dict(self._init_properties(), objects=objects)
    122         model.update(**self._process_param_change(props))

/opt/conda/lib/python3.6/site-packages/panel/layout.py in _get_objects(self, model, old_objects, doc, root, comm)
    108             else:
    109                 try:
--> 110                     child = pane._get_model(doc, root, model, comm)
    111                 except RerenderError:
    112                     return self._get_objects(model, current_objects[:i], doc, root, comm)

/opt/conda/lib/python3.6/site-packages/panel/pane/holoviews.py in _get_model(self, doc, root, parent, comm)
    225             plot = self.object
    226         else:
--> 227             plot = self._render(doc, comm, root)
    228 
    229         plot.pane = self

/opt/conda/lib/python3.6/site-packages/panel/pane/holoviews.py in _render(self, doc, comm, root)
    284             kwargs = {}
    285 
--> 286         return renderer.get_plot(self.object, **kwargs)
    287 
    288     def _cleanup(self, root):

/opt/conda/lib/python3.6/site-packages/holoviews/plotting/bokeh/renderer.py in get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     71         combining the bokeh model with another plot.
     72         """
---> 73         plot = super(BokehRenderer, self_or_cls).get_plot(obj, doc, renderer, **kwargs)
     74         if plot.document is None:
     75             plot.document = Document() if self_or_cls.notebook_context else curdoc()

/opt/conda/lib/python3.6/site-packages/holoviews/plotting/renderer.py in get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    217 
    218         # Initialize DynamicMaps with first data item
--> 219         initialize_dynamic(obj)
    220 
    221         if not renderer:

/opt/conda/lib/python3.6/site-packages/holoviews/plotting/util.py in initialize_dynamic(obj)
    249             continue
    250         if not len(dmap):
--> 251             dmap[dmap._initial_key()]
    252 
    253 

/opt/conda/lib/python3.6/site-packages/holoviews/core/spaces.py in __getitem__(self, key)
   1329         # Not a cross product and nothing cached so compute element.
   1330         if cache is not None: return cache
-> 1331         val = self._execute_callback(*tuple_key)
   1332         if data_slice:
   1333             val = self._dataslice(val, data_slice)

/opt/conda/lib/python3.6/site-packages/holoviews/core/spaces.py in _execute_callback(self, *args)
   1098 
   1099         with dynamicmap_memoization(self.callback, self.streams):
-> 1100             retval = self.callback(*args, **kwargs)
   1101         return self._style(retval)
   1102 

/opt/conda/lib/python3.6/site-packages/holoviews/core/spaces.py in __call__(self, *args, **kwargs)
    712 
    713         try:
--> 714             ret = self.callable(*args, **kwargs)
    715         except KeyError:
    716             # KeyError is caught separately because it is used to signal

/opt/conda/lib/python3.6/site-packages/holoviews/util/__init__.py in dynamic_operation(*key, **kwargs)
   1017         def dynamic_operation(*key, **kwargs):
   1018             key, obj = resolve(key, kwargs)
-> 1019             return apply(obj, *key, **kwargs)
   1020 
   1021         operation = self.p.operation

/opt/conda/lib/python3.6/site-packages/holoviews/util/__init__.py in apply(element, *key, **kwargs)
   1009         def apply(element, *key, **kwargs):
   1010             kwargs = dict(util.resolve_dependent_kwargs(self.p.kwargs), **kwargs)
-> 1011             processed = self._process(element, key, kwargs)
   1012             if (self.p.link_dataset and isinstance(element, Dataset) and
   1013                 isinstance(processed, Dataset) and processed._dataset is None):

/opt/conda/lib/python3.6/site-packages/holoviews/util/__init__.py in _process(self, element, key, kwargs)
    991         elif isinstance(self.p.operation, Operation):
    992             kwargs = {k: v for k, v in kwargs.items() if k in self.p.operation.param}
--> 993             return self.p.operation.process_element(element, key, **kwargs)
    994         else:
    995             return self.p.operation(element, **kwargs)

/opt/conda/lib/python3.6/site-packages/holoviews/core/operation.py in process_element(self, element, key, **params)
    179             self.p = param.ParamOverrides(self, params,
    180                                           allow_extra_keywords=self._allow_extra_keywords)
--> 181         return self._apply(element, key)
    182 
    183 

/opt/conda/lib/python3.6/site-packages/holoviews/core/operation.py in _apply(self, element, key)
    135             if not in_method:
    136                 element._in_method = True
--> 137         ret = self._process(element, key)
    138         if hasattr(element, '_in_method') and not in_method:
    139             element._in_method = in_method

/opt/conda/lib/python3.6/site-packages/holoviews/operation/datashader.py in _process(self, element, key)
   1443     def _process(self, element, key=None):
   1444         agg = rasterize._process(self, element, key)
-> 1445         shaded = shade._process(self, agg, key)
   1446         return shaded
   1447 

/opt/conda/lib/python3.6/site-packages/holoviews/operation/datashader.py in _process(self, element, key)
   1222             return element.map(partial(shade._process, self), [Element])
   1223         else:
-> 1224             xdensity = element.xdensity
   1225             ydensity = element.ydensity
   1226             bounds = element.bounds

AttributeError: 'Rectangles' object has no attribute 'xdensity'

Most helpful comment

Implemented in #4701

All 5 comments

Note that hv.Rectangles creation can be vectorized hv.Rectangles(df, ['x_low', 'y_low', 'x_high', 'y_high']) (unlike hvBox) and in cases with thousands/millions of rectangles it would be great to also render them efficiently with datashade

Yes, datashading Rectangles would make good sense and could help render some interesting plot types, but more work would need to be done in HoloViews to support it than simply computing xdensity. I think Datashader's Area plotting would work efficiently for this case, but someone would need to write code issuing the appropriate Datashader calls to render each Rectangle as an Area. Probably not too hard if you follow the existing examples in holoviews/operation/datashader.py , e.g. Spikes and Area, seeing how they map from HoloViews objects to Datashader plots. Seems like a good medium-sized project for somebody!

Implemented in #4701

Wow, thanks @philippjfr !

awesome thanks !

Was this page helpful?
0 / 5 - 0 ratings