holoviz / hvplot

A high-level plotting API for pandas, dask, xarray, and networkx built on HoloViews
https://hvplot.holoviz.org
BSD 3-Clause "New" or "Revised" License
1.13k stars 108 forks source link

hvplot.image requires coordinate and dimension names to match #1212

Open yt87 opened 10 months ago

yt87 commented 10 months ago

Thanks for contacting us! Please read and follow these instructions carefully, then delete this introductory text to keep your issue easy to read. Note that the issue tracker is NOT the place for usage questions and technical assistance; post those at Discourse instead. Issues without the required information below may be closed immediately.

ALL software version info

(this library, plus any other relevant software, e.g. bokeh, python, notebook, OS, browser, etc)

python 3.11
holoviews 1.18.1
hvplot 0.9.0

Description of expected behavior and the observed behavior

hvplot.image fails when coordinate name and its dimension name differ. Changing coordinate name to fcsttime_bins resolves the issue. The da.plot() line works correctly in both cases.

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

# code goes here between backticks
import hvplot.xarray
import numpy as np

import xarray as xr

month = np.arange(1, 13)
fcsttime = np.arange(0, 45, 5)
z = np.ones((fcsttime.size, month.size))
da = xr.DataArray(
    z,
    coords={"fcsttime": ('fcsttime_bins', fcsttime), "month": month},
    dims=['fcsttime_bins', 'month'],
    name="histogram_observations_forecasts",
)
da.plot()
da.hvplot.image()

Stack traceback and/or browser JavaScript console output

ValueError                                Traceback (most recent call last)
Cell In[1], line 16
      9 da = xr.DataArray(
     10     z,
     11     coords={"fcsttime": ('fcsttime_bins', fcsttime), "month": month},
     12     dims=['fcsttime_bins', 'month'],
     13     name="histogram_observations_forecasts",
     14 )
     15 da.plot()
---> 16 da.hvplot.image()

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/hvplot/plotting/core.py:2019, in hvPlot.image(self, x, y, z, colorbar, **kwds)
   1967 def image(self, x=None, y=None, z=None, colorbar=True, **kwds):
   1968     """
   1969     Image plot
   1970 
   (...)
   2017     - Plotly: https://plotly.com/python/imshow/
   2018     """
-> 2019     return self(x, y, z=z, kind="image", colorbar=colorbar, **kwds)

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/hvplot/plotting/core.py:95, in hvPlotBase.__call__(self, x, y, kind, **kwds)
     92         plot = self._get_converter(x, y, kind, **kwds)(kind, x, y)
     93         return pn.panel(plot, **panel_dict)
---> 95 return self._get_converter(x, y, kind, **kwds)(kind, x, y)

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/hvplot/converter.py:1275, in HoloViewsConverter.__call__(self, kind, x, y)
   1272                 dataset = Dataset(data)
   1273             dataset = dataset.redim(**self._redim)
-> 1275         obj = method(x, y)
   1276         obj._dataset = dataset
   1278 if self.crs and self.project:
   1279     # Apply projection before rasterizing

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/hvplot/converter.py:2184, in HoloViewsConverter.image(self, x, y, z, data)
   2182 element = self._get_element('image')
   2183 if self.geo: params['crs'] = self.crs
-> 2184 return (element(data, [x, y], z, **params).redim(**redim)
   2185         .apply(self._set_backends_opts, cur_opts=cur_opts, compat_opts=compat_opts))

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/core/accessors.py:44, in AccessorPipelineMeta.pipelined.<locals>.pipelined_call(*args, **kwargs)
     41     inst._obj._in_method = True
     43 try:
---> 44     result = __call__(*args, **kwargs)
     46     if not in_method:
     47         init_op = factory.instance(
     48             output_type=type(inst),
     49             kwargs={'mode': getattr(inst, 'mode', None)},
     50         )

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/core/accessors.py:435, in Redim.__call__(self, specs, **dimensions)
    433 transform = self._create_expression_transform(kdims, vdims, list(renames.values()))
    434 transforms = obj._transforms + [transform]
--> 435 clone = obj.clone(data, kdims=kdims, vdims=vdims, transforms=transforms)
    436 if self._obj.dimensions(label='name') == clone.dimensions(label='name'):
    437     # Ensure that plot_id is inherited as long as dimension
    438     # name does not change
    439     clone._plot_id = self._obj._plot_id

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/element/raster.py:400, in Image.clone(self, data, shared_data, new_type, link, *args, **overrides)
    397     sheet_params = dict(bounds=self.bounds, xdensity=self.xdensity,
    398                         ydensity=self.ydensity)
    399     overrides = dict(sheet_params, **overrides)
--> 400 return super().clone(data, shared_data, new_type, link,
    401                                 *args, **overrides)

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/core/data/__init__.py:1203, in Dataset.clone(self, data, shared_data, new_type, link, *args, **overrides)
   1200 elif self._in_method and 'dataset' not in overrides:
   1201     overrides['dataset'] = self.dataset
-> 1203 return super().clone(data, shared_data, new_type, *args, **overrides)

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/core/dimension.py:563, in LabelledData.clone(self, data, shared_data, new_type, link, *args, **overrides)
    561 # Apply name mangling for __ attribute
    562 pos_args = getattr(self, '_' + type(self).__name__ + '__pos_params', [])
--> 563 return clone_type(data, *args, **{k:v for k,v in settings.items()
    564                                   if k not in pos_args})

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/element/raster.py:312, in Image.__init__(self, data, kdims, vdims, bounds, extents, xdensity, ydensity, rtol, **params)
    310     xdensity = xdensity if xdensity else util.compute_density(l, r, dim1, self._time_unit)
    311     ydensity = ydensity if ydensity else util.compute_density(b, t, dim2, self._time_unit)
--> 312 SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
    313 if non_finite:
    314    self.bounds = BoundingBox(points=((np.nan, np.nan), (np.nan, np.nan)))

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/core/sheetcoords.py:166, in SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
    162 self.__set_ydensity(ydensity or xdensity)
    164 self.lbrt = np.array(bounds.lbrt())
--> 166 r1,r2,c1,c2 = Slice._boundsspec2slicespec(self.lbrt,self)
    167 self.__shape = (r2-r1,c2-c1)

File ~/mambaforge/envs/icec/lib/python3.11/site-packages/holoviews/core/sheetcoords.py:519, in Slice._boundsspec2slicespec(boundsspec, scs)
    516 b_m,r_m = scs.sheet2matrix(r,b)
    518 l_idx = int(np.ceil(l_m-0.5))
--> 519 t_idx = int(np.ceil(t_m-0.5))
    520 r_idx = int(np.floor(r_m+0.5))
    521 b_idx = int(np.floor(b_m+0.5))

ValueError: cannot convert float NaN to integer

# code goes here between backticks
import hvplot.xarray
import numpy as np

import xarray as xr

month = np.arange(1, 13)
fcsttime = np.arange(0, 45, 5)
z = np.ones((fcsttime.size, month.size))
da = xr.DataArray(
    z,
    coords={"fcsttime": ('fcsttime_bins', fcsttime), "month": month},
    dims=['fcsttime_bins', 'month'],
    name="histogram_observations_forecasts",
)
da.plot()
da.hvplot.image()
#### Screenshots or screencasts of the bug in action

- [ ] I may be interested in making a pull request to address this
maximlt commented 10 months ago

Thanks for the bug report @yt87, I can reproduce it. PRs are always welcome if you feel like trying to fix this one :)