holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.69k stars 402 forks source link

Support categorical values in operation.datashader #5637

Open hegde-anish opened 1 year ago

hegde-anish commented 1 year ago

ALL software version info

Version info

``` aiobotocore 2.4.2 aiofiles 22.1.0 aiohttp 3.8.4 aioitertools 0.11.0 aiosignal 1.3.1 aiosqlite 0.18.0 anyio 3.6.2 argon2-cffi 21.3.0 argon2-cffi-bindings 21.2.0 async-timeout 4.0.2 asynctest 0.13.0 attrs 22.2.0 aws-cfn-bootstrap 2.0 Babel 2.12.1 backcall 0.2.0 beautifulsoup4 4.9.3 bleach 6.0.0 bokeh 2.4.3 boto 2.49.0 boto3 1.26.84 botocore 1.27.59 certifi 2022.12.7 cffi 1.15.1 charset-normalizer 3.0.1 click 8.1.3 cloudpickle 2.2.1 colorcet 3.0.1 dask 2022.2.0 datashader 0.14.4 datashape 0.5.2 debugpy 1.6.6 decorator 5.1.1 defusedxml 0.7.1 docutils 0.14 entrypoints 0.4 fastjsonschema 2.16.3 frozenlist 1.3.3 fsspec 2023.1.0 holoviews 1.15.4 idna 3.4 importlib-metadata 6.0.0 importlib-resources 5.12.0 ipykernel 6.16.2 ipython 7.34.0 ipython-genutils 0.2.0 ipywidgets 8.0.4 jedi 0.18.2 Jinja2 3.1.2 jmespath 1.0.1 joblib 1.2.0 json5 0.9.11 jsonschema 4.17.3 jupyter-client 7.4.9 jupyter-core 4.12.0 jupyter-events 0.6.3 jupyter-server 1.23.6 jupyter-server-fileid 0.8.0 jupyter-server-ydoc 0.6.1 jupyter-ydoc 0.2.2 jupyterlab 3.6.1 jupyterlab-pygments 0.2.2 jupyterlab-server 2.19.0 jupyterlab-widgets 3.0.5 llvmlite 0.39.1 locket 1.0.0 lockfile 0.11.0 lxml 4.9.1 Markdown 3.4.1 MarkupSafe 2.1.2 matplotlib-inline 0.1.6 mistune 2.0.5 multidict 6.0.4 multipledispatch 0.6.0 mysqlclient 1.4.2 nbclassic 0.5.2 nbclient 0.7.2 nbconvert 7.2.9 nbformat 5.7.3 nest-asyncio 1.5.6 nltk 3.7 nodejs 0.1.1 nose 1.3.4 notebook 6.5.2 notebook-shim 0.2.2 numba 0.56.4 numpy 1.20.0 optional-django 0.1.0 packaging 23.0 pandas 1.3.5 pandocfilters 1.5.0 panel 0.14.4 param 1.12.3 parso 0.8.3 partd 1.3.0 pexpect 4.8.0 pickleshare 0.7.5 Pillow 9.4.0 pip 20.2.2 pkgutil-resolve-name 1.3.10 plotly 5.13.1 prometheus-client 0.16.0 prompt-toolkit 3.0.38 psutil 5.9.4 ptyprocess 0.7.0 py-dateutil 2.2 pyarrow 11.0.0 pycparser 2.21 pyct 0.5.0 Pygments 2.14.0 PyMySQL 1.0.2 pyrsistent 0.19.3 pystache 0.5.4 python-daemon 2.2.3 python-dateutil 2.8.2 python-json-logger 2.0.7 python37-sagemaker-pyspark 1.4.2 pytz 2022.6 pyviz-comms 2.2.1 PyYAML 5.4.1 pyzmq 25.0.0 regex 2021.11.10 requests 2.28.2 rfc3339-validator 0.1.4 rfc3986-validator 0.1.1 s3fs 2023.1.0 s3transfer 0.6.0 scipy 1.7.3 Send2Trash 1.8.0 setuptools 49.1.3 simplejson 3.2.0 six 1.13.0 sniffio 1.3.0 soupsieve 2.4 tenacity 8.2.2 terminado 0.17.1 tinycss2 1.2.1 tomli 2.0.1 toolz 0.12.0 tornado 6.2 tqdm 4.64.1 traitlets 5.9.0 typing-extensions 4.5.0 urllib3 1.26.14 wcwidth 0.2.6 webencodings 0.5.1 websocket-client 1.5.1 widgetsnbextension 4.0.5 windmill 1.6 wrapt 1.15.0 xarray 0.20.2 y-py 0.5.9 yarl 1.8.2 ypy-websocket 0.8.4 zipp 3.15.0 ```

Description of expected behavior and the observed behavior

I'm trying to plot categorical values on y-axis and timestamp on x-axis. hv.Scatter returns the expected graph but when I pass it through datashade it throws below error TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

If I were to plot numerical values in y-axis, then both hv.scatter and datashade applied on top of hv.scatter returns expected graphs

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

import pandas as pd
import numpy as np
from holoviews.operation.datashader import datashade, dynspread, aggregate
import holoviews as hv
import datashader as ds
hv.extension('bokeh')
dates = pd.date_range(start='2022-01-01', end='2022-01-31', freq='D')
df = pd.DataFrame({'x': np.random.choice(dates, 1000),
                   'y': np.random.choice(['A', 'B', 'C', 'D'], 1000),
                   'z': np.random.choice([1, 2, 3, 5], 1000)})

scatter_categorical = hv.Scatter(df, kdims=['x'], vdims=['y']).opts(title="Scatter Categorical", width=800)
datashade_categorical = datashade(scatter_categorical).opts(title="Datashader Categorical", width=800)
scatter_numerical = hv.Scatter(df, kdims=['x'], vdims=['z']).opts(title="Scatter Numerical", width=800)
datashade_numerical = datashade(scatter_numerical).opts(title="Datashader Numerical", width=800)

display((scatter_numerical+datashade_numerical+scatter_categorical).cols(2))
display(datashade_categorical)

Stack traceback and/or browser JavaScript console output

WARNING:param.dynamic_operation: Callable raised "TypeError("ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''")".
Invoked as dynamic_operation(height=400, scale=1.0, width=400, x_range=None, y_range=None)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/.local/lib/python3.7/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:

~/.local/lib/python3.7/site-packages/holoviews/core/dimension.py in _repr_mimebundle_(self, include, exclude)
   1288         combined and returned.
   1289         """
-> 1290         return Store.render(self)
   1291 
   1292 

~/.local/lib/python3.7/site-packages/holoviews/core/options.py in render(cls, obj)
   1423         data, metadata = {}, {}
   1424         for hook in hooks:
-> 1425             ret = hook(obj)
   1426             if ret is None:
   1427                 continue

~/.local/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in pprint_display(obj)
    277     if not ip.display_formatter.formatters['text/plain'].pprint:
    278         return None
--> 279     return display(obj, raw_output=True)
    280 
    281 

~/.local/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in display(obj, raw_output, **kwargs)
    251     elif isinstance(obj, (HoloMap, DynamicMap)):
    252         with option_state(obj):
--> 253             output = map_display(obj)
    254     elif isinstance(obj, Plot):
    255         output = render(obj)

~/.local/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in wrapped(element)
    139         try:
    140             max_frames = OutputSettings.options['max_frames']
--> 141             mimebundle = fn(element, max_frames=max_frames)
    142             if mimebundle is None:
    143                 return {}, {}

~/.local/lib/python3.7/site-packages/holoviews/ipython/display_hooks.py in map_display(vmap, max_frames)
    199         return None
    200 
--> 201     return render(vmap)
    202 
    203 

~/.local/lib/python3.7/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 

~/.local/lib/python3.7/site-packages/holoviews/plotting/renderer.py in components(self, obj, fmt, comm, **kwargs)
    392 
    393         if embed or config.comms == 'default':
--> 394             return self._render_panel(plot, embed, comm)
    395         return self._render_ipywidget(plot)
    396 

~/.local/lib/python3.7/site-packages/holoviews/plotting/renderer.py in _render_panel(self, plot, embed, comm)
    399         doc = Document()
    400         with config.set(embed=embed):
--> 401             model = plot.layout._render_model(doc, comm)
    402         if embed:
    403             return render_model(model, comm)

~/.local/lib/python3.7/site-packages/panel/viewable.py in _render_model(self, doc, comm)
    507         if comm is None:
    508             comm = state._comm_manager.get_server_comm()
--> 509         model = self.get_root(doc, comm)
    510 
    511         if config.embed:

~/.local/lib/python3.7/site-packages/panel/viewable.py in get_root(self, doc, comm, preprocess)
    558         """
    559         doc = init_doc(doc)
--> 560         root = self._get_model(doc, comm=comm)
    561         if preprocess:
    562             self._preprocess(root)

~/.local/lib/python3.7/site-packages/panel/layout/base.py in _get_model(self, doc, root, parent, comm)
    144         if root is None:
    145             root = model
--> 146         objects = self._get_objects(model, [], doc, root, comm)
    147         props = dict(self._init_params(), objects=objects)
    148         model.update(**self._process_param_change(props))

~/.local/lib/python3.7/site-packages/panel/layout/base.py in _get_objects(self, model, old_objects, doc, root, comm)
    129             else:
    130                 try:
--> 131                     child = pane._get_model(doc, root, model, comm)
    132                 except RerenderError:
    133                     return self._get_objects(model, current_objects[:i], doc, root, comm)

~/.local/lib/python3.7/site-packages/panel/pane/holoviews.py in _get_model(self, doc, root, parent, comm)
    365             plot = self.object
    366         else:
--> 367             plot = self._render(doc, comm, root)
    368 
    369         plot.pane = self

~/.local/lib/python3.7/site-packages/panel/pane/holoviews.py in _render(self, doc, comm, root)
    440                 kwargs['comm'] = comm
    441 
--> 442         return renderer.get_plot(self.object, **kwargs)
    443 
    444     def _cleanup(self, root: Model | None = None) -> None:

~/.local/lib/python3.7/site-packages/holoviews/plotting/bokeh/renderer.py in get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     68         combining the bokeh model with another plot.
     69         """
---> 70         plot = super().get_plot(obj, doc, renderer, **kwargs)
     71         if plot.document is None:
     72             plot.document = Document() if self_or_cls.notebook_context else curdoc()

~/.local/lib/python3.7/site-packages/holoviews/plotting/renderer.py in get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    211 
    212         # Initialize DynamicMaps with first data item
--> 213         initialize_dynamic(obj)
    214 
    215         if not renderer:

~/.local/lib/python3.7/site-packages/holoviews/plotting/util.py in initialize_dynamic(obj)
    255             continue
    256         if not len(dmap):
--> 257             dmap[dmap._initial_key()]
    258 
    259 

~/.local/lib/python3.7/site-packages/holoviews/core/spaces.py in __getitem__(self, key)
   1211         # Not a cross product and nothing cached so compute element.
   1212         if cache is not None: return cache
-> 1213         val = self._execute_callback(*tuple_key)
   1214         if data_slice:
   1215             val = self._dataslice(val, data_slice)

~/.local/lib/python3.7/site-packages/holoviews/core/spaces.py in _execute_callback(self, *args)
    978 
    979         with dynamicmap_memoization(self.callback, self.streams):
--> 980             retval = self.callback(*args, **kwargs)
    981         return self._style(retval)
    982 

~/.local/lib/python3.7/site-packages/holoviews/core/spaces.py in __call__(self, *args, **kwargs)
    578 
    579         try:
--> 580             ret = self.callable(*args, **kwargs)
    581         except KeyError:
    582             # KeyError is caught separately because it is used to signal

~/.local/lib/python3.7/site-packages/holoviews/util/__init__.py in dynamic_operation(*key, **kwargs)
   1009         def dynamic_operation(*key, **kwargs):
   1010             key, obj = resolve(key, kwargs)
-> 1011             return apply(obj, *key, **kwargs)
   1012 
   1013         operation = self.p.operation

~/.local/lib/python3.7/site-packages/holoviews/util/__init__.py in apply(element, *key, **kwargs)
   1001         def apply(element, *key, **kwargs):
   1002             kwargs = dict(util.resolve_dependent_kwargs(self.p.kwargs), **kwargs)
-> 1003             processed = self._process(element, key, kwargs)
   1004             if (self.p.link_dataset and isinstance(element, Dataset) and
   1005                 isinstance(processed, Dataset) and processed._dataset is None):

~/.local/lib/python3.7/site-packages/holoviews/util/__init__.py in _process(self, element, key, kwargs)
    983         elif isinstance(self.p.operation, Operation):
    984             kwargs = {k: v for k, v in kwargs.items() if k in self.p.operation.param}
--> 985             return self.p.operation.process_element(element, key, **kwargs)
    986         else:
    987             return self.p.operation(element, **kwargs)

~/.local/lib/python3.7/site-packages/holoviews/core/operation.py in process_element(self, element, key, **params)
    192             self.p = param.ParamOverrides(self, params,
    193                                           allow_extra_keywords=self._allow_extra_keywords)
--> 194         return self._apply(element, key)
    195 
    196 

~/.local/lib/python3.7/site-packages/holoviews/core/operation.py in _apply(self, element, key)
    139             if not in_method:
    140                 element._in_method = True
--> 141         ret = self._process(element, key)
    142         if hasattr(element, '_in_method') and not in_method:
    143             element._in_method = in_method

~/.local/lib/python3.7/site-packages/holoviews/operation/datashader.py in _process(self, element, key)
   1592 
   1593     def _process(self, element, key=None):
-> 1594         agg = rasterize._process(self, element, key)
   1595         shaded = shade._process(self, agg, key)
   1596         return shaded

~/.local/lib/python3.7/site-packages/holoviews/operation/datashader.py in _process(self, element, key)
   1571                                        if k in transform.param})
   1572             op._precomputed = self._precomputed
-> 1573             element = element.map(op, predicate)
   1574             self._precomputed = op._precomputed
   1575 

~/.local/lib/python3.7/site-packages/holoviews/core/data/__init__.py in pipelined_fn(*args, **kwargs)
    197 
    198             try:
--> 199                 result = method_fn(*args, **kwargs)
    200                 if PipelineMeta.disable:
    201                     return result

~/.local/lib/python3.7/site-packages/holoviews/core/data/__init__.py in map(self, *args, **kwargs)
   1205     @wraps(LabelledData.map)
   1206     def map(self, *args, **kwargs):
-> 1207         return super().map(*args, **kwargs)
   1208 
   1209     @wraps(LabelledData.relabel)

~/.local/lib/python3.7/site-packages/holoviews/core/dimension.py in map(self, map_fn, specs, clone)
    698             return deep_mapped
    699         else:
--> 700             return map_fn(self) if applies else self
    701 
    702 

~/.local/lib/python3.7/site-packages/holoviews/core/operation.py in __call__(self, element, **kwargs)
    212             elif ((self._per_element and isinstance(element, Element)) or
    213                   (not self._per_element and isinstance(element, ViewableElement))):
--> 214                 return self._apply(element)
    215         elif 'streams' not in kwargs:
    216             kwargs['streams'] = self.p.streams

~/.local/lib/python3.7/site-packages/holoviews/core/operation.py in _apply(self, element, key)
    139             if not in_method:
    140                 element._in_method = True
--> 141         ret = self._process(element, key)
    142         if hasattr(element, '_in_method') and not in_method:
    143             element._in_method = in_method

~/.local/lib/python3.7/site-packages/holoviews/operation/datashader.py in _process(self, element, key)
    493         if self.p.precompute:
    494             self._precomputed[element._plot_id] = x, y, data, glyph
--> 495         (x_range, y_range), (xs, ys), (width, height), (xtype, ytype) = self._get_sampling(element, x, y)
    496         ((x0, x1), (y0, y1)), (xs, ys) = self._dt_transform(x_range, y_range, xs, ys, xtype, ytype)
    497 

~/.local/lib/python3.7/site-packages/holoviews/operation/datashader.py in _get_sampling(self, element, x, y, ndim, default)
    188             ystart, yend = dt_to_int(ystart, 'ns'), dt_to_int(yend, 'ns')
    189             ytype = 'datetime'
--> 190         elif not np.isfinite(ystart) and not np.isfinite(yend):
    191             ystart, yend = 0, 0
    192             if y and element.get_dimension_type(y[0]) in datetime_types:

TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
:DynamicMap   []

Screenshots or screencasts of the bug in action

datashader_bug

hoxbro commented 1 year ago

Categorical data is not currently supported in Datashader. I have added a PR https://github.com/holoviz/holoviews/pull/5643, giving a better error message.

There is no plan to add this, but I will label this issue as a feature request.