hvplot icon indicating copy to clipboard operation
hvplot copied to clipboard

Support `pandas.Interval` arrays in `step` plots

Open maximlt opened this issue 3 years ago • 0 comments

xarray support generating step plots from dimensions that consist of array containing pandas Interval objects.

import hvplot.xarray
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import xarray as xr

airtemps = xr.tutorial.open_dataset("air_temperature")
air = airtemps.air - 273.15
air.attrs = airtemps.air.attrs
air.attrs["units"] = "deg C"
air_grp = air.mean(["time", "lon"]).groupby_bins("lat", [0, 23.5, 66.5, 90])
air_mean = air_grp.mean()
air_mean.plot.step()

This is what air_mean.lat_bins.values looks like:

array([Interval(0.0, 23.5, closed='right'),
       Interval(23.5, 66.5, closed='right'),
       Interval(66.5, 90.0, closed='right')], dtype=object)

air_mean.hvplot.step() fails with a TypeError in HoloViews:

TypeError                                 Traceback (most recent call last)
File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/IPython/core/formatters.py:973, in MimeBundleFormatter.__call__(self, obj, include, exclude)
    970     method = get_real_method(obj, self.print_method)
    972     if method is not None:
--> 973         return method(include=include, exclude=exclude)
    974     return None
    975 else:

File ~/work/dev/holoviews/holoviews/core/dimension.py:1294, in Dimensioned._repr_mimebundle_(self, include, exclude)
   1287 def _repr_mimebundle_(self, include=None, exclude=None):
   1288     """
   1289     Resolves the class hierarchy for the class rendering the
   1290     object using any display hooks registered on Store.display
   1291     hooks.  The output of all registered display_hooks is then
   1292     combined and returned.
   1293     """
-> 1294     return Store.render(self)

File ~/work/dev/holoviews/holoviews/core/options.py:1426, in Store.render(cls, obj)
   1424 data, metadata = {}, {}
   1425 for hook in hooks:
-> 1426     ret = hook(obj)
   1427     if ret is None:
   1428         continue

File ~/work/dev/holoviews/holoviews/ipython/display_hooks.py:277, in pprint_display(obj)
    275 if not ip.display_formatter.formatters['text/plain'].pprint:
    276     return None
--> 277 return display(obj, raw_output=True)

File ~/work/dev/holoviews/holoviews/ipython/display_hooks.py:247, in display(obj, raw_output, **kwargs)
    245 elif isinstance(obj, (CompositeOverlay, ViewableElement)):
    246     with option_state(obj):
--> 247         output = element_display(obj)
    248 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    249     with option_state(obj):

File ~/work/dev/holoviews/holoviews/ipython/display_hooks.py:141, in display_hook.<locals>.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 {}, {}

File ~/work/dev/holoviews/holoviews/ipython/display_hooks.py:187, in element_display(element, max_frames)
    184 if type(element) not in Store.registry[backend]:
    185     return None
--> 187 return render(element)

File ~/work/dev/holoviews/holoviews/ipython/display_hooks.py:68, in render(obj, **kwargs)
     65 if renderer.fig == 'pdf':
     66     renderer = renderer.instance(fig='png')
---> 68 return renderer.components(obj, **kwargs)

File ~/work/dev/holoviews/holoviews/plotting/renderer.py:398, in Renderer.components(self, obj, fmt, comm, **kwargs)
    395 embed = (not (dynamic or streams or self.widget_mode == 'live') or config.embed)
    397 if embed or config.comms == 'default':
--> 398     return self._render_panel(plot, embed, comm)
    399 return self._render_ipywidget(plot)

File ~/work/dev/holoviews/holoviews/plotting/renderer.py:405, in Renderer._render_panel(self, plot, embed, comm)
    403 doc = Document()
    404 with config.set(embed=embed):
--> 405     model = plot.layout._render_model(doc, comm)
    406 if embed:
    407     return render_model(model, comm)

File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/panel/viewable.py:505, in Renderable._render_model(self, doc, comm)
    503 if comm is None:
    504     comm = state._comm_manager.get_server_comm()
--> 505 model = self.get_root(doc, comm)
    507 if config.embed:
    508     embed_state(self, model, doc,
    509                 json=config.embed_json,
    510                 json_prefix=config.embed_json_prefix,
    511                 save_path=config.embed_save_path,
    512                 load_path=config.embed_load_path,
    513                 progress=False)

File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/panel/viewable.py:556, in Renderable.get_root(self, doc, comm, preprocess)
    539 """
    540 Returns the root model and applies pre-processing hooks
    541 
   (...)
    553 Returns the bokeh model corresponding to this panel object
    554 """
    555 doc = init_doc(doc)
--> 556 root = self._get_model(doc, comm=comm)
    557 if preprocess:
    558     self._preprocess(root)

File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/panel/layout/base.py:146, in Panel._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))

File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/panel/layout/base.py:131, in Panel._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)

File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/panel/pane/holoviews.py:265, in HoloViews._get_model(self, doc, root, parent, comm)
    263     plot = self.object
    264 else:
--> 265     plot = self._render(doc, comm, root)
    267 plot.pane = self
    268 backend = plot.renderer.backend

File ~/miniconda3/envs/hv-dev38/lib/python3.8/site-packages/panel/pane/holoviews.py:342, in HoloViews._render(self, doc, comm, root)
    339     if comm:
    340         kwargs['comm'] = comm
--> 342 return renderer.get_plot(self.object, **kwargs)

File ~/work/dev/holoviews/holoviews/plotting/bokeh/renderer.py:70, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     63 @bothmethod
     64 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
     65     """
     66     Given a HoloViews Viewable return a corresponding plot instance.
     67     Allows supplying a document attach the plot to, useful when
     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()

File ~/work/dev/holoviews/holoviews/plotting/renderer.py:240, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    237     defaults = [kd.default for kd in plot.dimensions]
    238     init_key = tuple(v if d is None else d for v, d in
    239                      zip(plot.keys[0], defaults))
--> 240     plot.update(init_key)
    241 else:
    242     plot = obj

File ~/work/dev/holoviews/holoviews/plotting/plot.py:948, in DimensionedPlot.update(self, key)
    946 def update(self, key):
    947     if len(self) == 1 and ((key == 0) or (key == self.keys[0])) and not self.drawn:
--> 948         return self.initialize_plot()
    949     item = self.__getitem__(key)
    950     self.traverse(lambda x: setattr(x, '_updated', True))

File ~/work/dev/holoviews/holoviews/plotting/bokeh/element.py:1382, in ElementPlot.initialize_plot(self, ranges, plot, plots, source)
   1380     element = self.hmap.last
   1381 key = util.wrap_tuple(self.hmap.last_key)
-> 1382 ranges = self.compute_ranges(self.hmap, key, ranges)
   1383 self.current_ranges = ranges
   1384 self.current_frame = element

File ~/work/dev/holoviews/holoviews/plotting/plot.py:607, in DimensionedPlot.compute_ranges(self, obj, key, ranges)
    603     # Only compute ranges if not axiswise on a composite plot
    604     # or not framewise on a Overlay or ElementPlot
    605     if (not (axiswise and not isinstance(obj, HoloMap)) or
    606         (not framewise and isinstance(obj, HoloMap))):
--> 607         self._compute_group_range(group, elements, ranges, framewise,
    608                                   axiswise, robust, self.top_level,
    609                                   prev_frame)
    610 self.ranges.update(ranges)
    611 return ranges

File ~/work/dev/holoviews/holoviews/plotting/plot.py:824, in DimensionedPlot._compute_group_range(cls, group, elements, ranges, framewise, axiswise, robust, top_level, prev_frame)
    821     if t in ('factors', 'id'):
    822         continue
    823     matching &= (
--> 824         len({'date' if isinstance(v, util.datetime_types) else 'number'
    825              for rng in rs for v in rng if util.isfinite(v)}) < 2
    826     )
    827 if matching:
    828     group_dim_ranges[gdim] = values

File ~/work/dev/holoviews/holoviews/plotting/plot.py:825, in <setcomp>(.0)
    821     if t in ('factors', 'id'):
    822         continue
    823     matching &= (
    824         len({'date' if isinstance(v, util.datetime_types) else 'number'
--> 825              for rng in rs for v in rng if util.isfinite(v)}) < 2
    826     )
    827 if matching:
    828     group_dim_ranges[gdim] = values

File ~/work/dev/holoviews/holoviews/core/util.py:886, in isfinite(val)
    884 elif isinstance(val, (str, bytes)):
    885     return True
--> 886 finite = np.isfinite(val)
    887 if pd and pandas_version >= LooseVersion('1.0.0'):
    888     if finite is pd.NA:

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''

I'm not sure whether it's up to hvPlot or HoloViews to add support to this data type. Any opinion @jlstevens ?

maximlt avatar Oct 28 '22 09:10 maximlt