Repository: holoviz/hvplot

Issue:
Adding label to xarray plot produces KeyError
#### ALL software version info

- hvplot 0.7.3
- holoviews 1.14.6
- xarray 0.19.0


#### Description of expected behavior and the observed behavior

I want to plot an Xarray DataArray with a specified label. This does not work unless the DataArray has a name. The error raised is not helpful and the behavior is confusing / inconsistent. Passing `label=` should always work, no matter what.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import xarray as xr
import numpy as np
import hvplot.xarray

ny, nx = (30, 20)
x = np.linspace(0, 1, nx)
y = np.linspace(0, 2, ny)
coords={'y': ('y', y), 'x': ('x', x)}
foo = xr.DataArray(np.random.rand(ny, nx), dims=['y', 'x'], coords=coords)

# works
foo.hvplot('x', 'y')
       
# Key Error
foo.hvplot('x', 'y', label='Foo') 
```

This can be resolved by giving the array a name
```python
foo.rename('bar').hvplot('x', 'y', label='Foo')
```

or by specifying a clim manually

```python
foo.hvplot('x', 'y', label='Foo', clim=(0, 1)) 
```

Neither of these workarounds makes sense conceptually.

#### Stack traceback and/or browser JavaScript console output

```
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/core/dataset.py in _construct_dataarray(self, name)
   1397         try:
-> 1398             variable = self._variables[name]
   1399         except KeyError:

KeyError: 'value'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
/tmp/ipykernel_926/56029127.py in <module>
     13 
     14 # Key Error
---> 15 foo.hvplot('x', 'y', label='Foo')

/srv/conda/envs/notebook/lib/python3.8/site-packages/hvplot/plotting/core.py in __call__(self, x, y, kind, **kwds)
     77                 return pn.panel(plot, **panel_dict)
     78 
---> 79         return self._get_converter(x, y, kind, **kwds)(kind, x, y)
     80 
     81     def _get_converter(self, x=None, y=None, kind=None, **kwds):

/srv/conda/envs/notebook/lib/python3.8/site-packages/hvplot/plotting/core.py in _get_converter(self, x, y, kind, **kwds)
     84         y = y or params.pop('y', None)
     85         kind = kind or params.pop('kind', None)
---> 86         return HoloViewsConverter(
     87             self._data, x, y, kind=kind, **params
     88         )

/srv/conda/envs/notebook/lib/python3.8/site-packages/hvplot/converter.py in __init__(self, data, x, y, kind, by, use_index, group_label, value_label, backlog, persist, use_dask, crs, fields, groupby, dynamic, grid, legend, rot, title, xlim, ylim, clim, symmetric, logx, logy, loglog, hover, subplots, label, invert, stacked, colorbar, datashade, rasterize, row, col, figsize, debug, framewise, aggregator, projection, global_extent, geo, precompute, flip_xaxis, flip_yaxis, dynspread, hover_cols, x_sampling, y_sampling, project, tools, attr_labels, coastline, tiles, sort_date, check_symmetric_max, transforms, stream, cnorm, features, **kwds)
    504             try:
    505                 if not use_dask:
--> 506                     symmetric = self._process_symmetric(symmetric, clim, check_symmetric_max)
    507 
    508                 if self._style_opts.get('cmap') is None:

/srv/conda/envs/notebook/lib/python3.8/site-packages/hvplot/converter.py in _process_symmetric(self, symmetric, clim, check_symmetric_max)
    547             if self.data.chunks:
    548                 return False
--> 549             data = self.data[self.z]
    550             if is_xarray_dataarray(data):
    551                 if data.size > check_symmetric_max:

/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/core/dataset.py in __getitem__(self, key)
   1500 
   1501         if hashable(key):
-> 1502             return self._construct_dataarray(key)
   1503         else:
   1504             return self._copy_listed(key)

/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/core/dataset.py in _construct_dataarray(self, name)
   1398             variable = self._variables[name]
   1399         except KeyError:
-> 1400             _, name, variable = _get_virtual_variable(
   1401                 self._variables, name, self._level_coords, self.dims
   1402             )

/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/core/dataset.py in _get_virtual_variable(variables, key, level_vars, dim_sizes)
    171         ref_var = dim_var.to_index_variable().get_level_variable(ref_name)
    172     else:
--> 173         ref_var = variables[ref_name]
    174 
    175     if var_name is None:

KeyError: 'value'
```


Discussion:
I agree, that behavior doesn't look reasonable at all! @jlstevens should be able to address that.
Sorry for letting this languish! I'll definitely have a proper look for 0.8.1.

Resolving patch:
diff --git a/hvplot/converter.py b/hvplot/converter.py
--- a/hvplot/converter.py
+++ b/hvplot/converter.py
@@ -747,7 +747,7 @@ def _process_data(self, kind, data, x, y, by, groupby, row, col,
                 if isinstance(data, xr.Dataset):
                     z = list(data.data_vars)[0]
                 else:
-                    z = data.name or 'value'
+                    z = data.name or label or value_label
             if gridded and isinstance(data, xr.Dataset) and not isinstance(z, list):
                 data = data[z]
             self.z = z


Test patch:
diff --git a/hvplot/tests/testgridplots.py b/hvplot/tests/testgridplots.py
--- a/hvplot/tests/testgridplots.py
+++ b/hvplot/tests/testgridplots.py
@@ -227,3 +227,19 @@ def test_symmetric_dataset_in_memory(self):
         plot_opts = Store.lookup_options('bokeh', plot.last, 'plot')
         # This DataArray happens to be symmetric.
         assert plot_opts.kwargs['symmetric']
+
+    def test_dataarray_unnamed_label(self):
+        plot = self.da_rgb.sel(band=1).hvplot.image(label='test')
+        assert plot.vdims[0].name == 'test'
+
+    def test_dataarray_unnamed_value_label(self):
+        plot = self.da_rgb.sel(band=1).hvplot.image(value_label='test')
+        assert plot.vdims[0].name == 'test'
+
+    def test_dataarray_label_precedence(self):
+        # name > label > value_label
+        plot = self.da_rgb.sel(band=1).rename('a').hvplot.image(label='b')
+        assert plot.vdims[0].name == 'a'
+
+        plot = self.da_rgb.sel(band=1).hvplot.image(label='b', value_label='c')
+        assert plot.vdims[0].name == 'b'