Repository: holoviz/hvplot

Issue:
Improve handling of arraylike as input for hvplot
#### ALL software version info
``` 
pandas    1.3.5
hvplot    0.8.0a11.post9+gfbcc04e
```

#### Description of expected behavior and the observed behavior
When passing an array type like `df.columns` into `hvplot` it raises `ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()`. 

I think there is two solution for this problem; 
* one is either to support array-like formats like `numpy` or `pandas` as input for `hvplot`, 
* the other options is to raise a better error message when passing in a array-like type. 

#### Complete, minimal, self-contained example code that reproduces the issue

``` python
import pandas as pd
import hvplot.pandas

df = pd._testing.makeDataFrame()
df.hvplot(y=df.columns)  # Raises ValueError
df.hvplot(y=list(df.columns))  # Works fine
```


Discussion:
I'd vote for automatically converting array-like formats into lists where appropriate. Not sure if it also makes sense to accept iterators or generators as well.

Resolving patch:
diff --git a/hvplot/plotting/core.py b/hvplot/plotting/core.py
--- a/hvplot/plotting/core.py
+++ b/hvplot/plotting/core.py
@@ -8,7 +8,7 @@
     panel_available = False
 
 from ..converter import HoloViewsConverter
-from ..util import process_dynamic_args
+from ..util import is_list_like, process_dynamic_args
 
 
 class hvPlotBase:
@@ -33,8 +33,8 @@ def __call__(self, x=None, y=None, kind=None, **kwds):
 
         Parameters
         ----------
-        x, y : string, optional
-            Field name in the data to draw x- and y-positions from
+        x, y : string, list, or array-like, optional
+            Field name(s) in the data to draw x- and y-positions from
         kind : string, optional
             The kind of plot to generate, e.g. 'line', 'scatter', etc.
         **kwds : optional
@@ -44,6 +44,10 @@ def __call__(self, x=None, y=None, kind=None, **kwds):
         -------
         HoloViews object: Object representing the requested visualization
         """
+        # Convert an array-like to a list
+        x = list(x) if is_list_like(x) else x
+        y = list(y) if is_list_like(y) else y
+
         if isinstance(kind, str) and kind not in self.__all__:
             raise NotImplementedError("kind='{kind}' for data of type {type}".format(
                 kind=kind, type=type(self._data)))
diff --git a/hvplot/util.py b/hvplot/util.py
--- a/hvplot/util.py
+++ b/hvplot/util.py
@@ -8,6 +8,7 @@
 from packaging.version import Version
 from types import FunctionType
 
+import numpy as np
 import pandas as pd
 import holoviews as hv
 import param
@@ -252,6 +253,21 @@ def process_crs(crs):
         raise ValueError("Projection must be defined as a EPSG code, proj4 string, cartopy CRS or pyproj.Proj.")
     return crs
 
+
+def is_list_like(obj):
+    """
+    Adapted from pandas' is_list_like cython function.
+    """
+    return (
+        # equiv: `isinstance(obj, abc.Iterable)`
+        hasattr(obj, "__iter__") and not isinstance(obj, type)
+        # we do not count strings/unicode/bytes as list-like
+        and not isinstance(obj, (str, bytes))
+        # exclude zero-dimensional numpy arrays, effectively scalars
+        and not (isinstance(obj, np.ndarray) and obj.ndim == 0)
+    )
+
+
 def is_tabular(data):
     if check_library(data, ['dask', 'streamz', 'pandas', 'geopandas', 'cudf']):
         return True


Test patch:
diff --git a/hvplot/tests/plotting/testcore.py b/hvplot/tests/plotting/testcore.py
new file mode 100644
--- /dev/null
+++ b/hvplot/tests/plotting/testcore.py
@@ -0,0 +1,21 @@
+import numpy as np
+import pandas as pd
+import hvplot.pandas  # noqa
+
+import pytest
+
+@pytest.mark.parametrize("y", (
+    ["A", "B", "C", "D"],
+    ("A", "B", "C", "D"),
+    {"A", "B", "C", "D"},
+    np.array(["A", "B", "C", "D"]),
+    pd.Index(["A", "B", "C", "D"]),
+    pd.Series(["A", "B", "C", "D"]),
+    ))
+def test_diffent_input_types(y):
+    df = pd._testing.makeDataFrame()
+    types = {t for t in dir(df.hvplot) if not t.startswith("_")}
+    ignore_types = {'bivariate', 'heatmap', 'hexbin', 'labels', 'vectorfield'}
+
+    for t in types - ignore_types:
+        df.hvplot(y=y, kind=t)
diff --git a/hvplot/tests/testutil.py b/hvplot/tests/testutil.py
--- a/hvplot/tests/testutil.py
+++ b/hvplot/tests/testutil.py
@@ -4,11 +4,12 @@
 import sys
 
 import numpy as np
+import pandas as pd
 import pytest
 
 from unittest import TestCase, SkipTest
 
-from hvplot.util import check_crs, process_xarray
+from hvplot.util import check_crs, is_list_like, process_xarray
 
 
 class TestProcessXarray(TestCase):
@@ -275,3 +276,15 @@ def test_check_crs():
     assert p.srs == '+proj=utm +zone=15 +datum=NAD83 +units=m +no_defs'
     p = check_crs('wrong')
     assert p is None
+
+
+def test_is_list_like():
+    assert not is_list_like(0)
+    assert not is_list_like('string')
+    assert not is_list_like(np.array('a'))
+    assert is_list_like(['a', 'b'])
+    assert is_list_like(('a', 'b'))
+    assert is_list_like({'a', 'b'})
+    assert is_list_like(pd.Series(['a', 'b']))
+    assert is_list_like(pd.Index(['a', 'b']))
+    assert is_list_like(np.array(['a', 'b']))