Repository: plotly/plotly.py

Issue:
Plotly Express strange hover_data ValueError
Scatter plotting with no problems, until I attempted to map a column of my data to hover_data.
Here's what my data looks like. I'm trying to map the `most_similar_kt` column to hover_data.

![image](https://user-images.githubusercontent.com/23055812/61797829-2b358a80-adf6-11e9-8240-00387e858a26.png)

Here's the error message:
`ValueError: Element of value of 'hover_data' is not the name of a column in 'data_frame'. Expected one of ['x', 'y', 'label', 'freq', 'similarity', 'size', 'class', 'most_similar_kt'] but received: m`

And here's my code:
`fig = px.scatter(df, x='x',y='y', color='similarity', size='size', symbol='class',hover_name='label', hover_data='most_similar_kt', width=1500, height=1000, template='plotly_dark', render_mode='webgl')`

Apologies if this has already been addressed; I spent 10 minutes googling for errors and it doesn't appear anyone else had this issue.

If I `remove the hover_data assignment`, then the plot works:
![image](https://user-images.githubusercontent.com/23055812/61798086-aeef7700-adf6-11e9-9bad-8bc878c1c7c6.png)

Thanks for your assistance!
Better error message if passed in a column name instead of a list of column names into `custom_data`
`custom_data` is unlike most other `px` properties in that it accepts a list of columns instead of just a single column.

It would be helpful if we said something like "Looks like you supplied a single column name, you should supply a list of columns."

Here's the current error: 
> ValueError: Value of 'custom_data_0' is not the name of a column in 'data_frame'. Expected one of ['uid', 'from_uid', 'dropout', 'embedding_size', 'epoch', 'lr', 'train f1', 'valid ppl'] but received: 19751736_1



Discussion:
Apologies; I figure out that you are supposed to feed columns inside a list rather than just a string of the column name like the other items. Closing out.
Had the exact same issue. This is indeed a very misleading error message though, and very much seem to be a bug as it says `Expected one of ['x', 'y', 'label', 'freq', 'similarity', 'size', 'class', 'most_similar_kt'] but received: m`, as you passed in `hover_data='most_similar_kt'`.

It would make sense if it says something like "list of column names expected but received a single str".
@zhaomh1998 we have an open issue #2177 to improve the error message, it should be improved soon :-).
Please fix this.
We haven't gotten around to it yet... Contributions are welcome :)
just came here to say these comments helped me.  :)
The answers have helped me as well. For other newbies like myself, to pass all the columns you can do
` hover_data = df.columns.tolist()`
 and to pass just certain columns 
`hover_data = [ 'col1', 'col2']`
Good idea. Same for `hover_data` and maybe a couple of others. We'll have to find a clever workaround though to see at which level to catch the error.
It would also be helpful if we noted _which_ attribute had an error. So, for the error message above, saying something like:
> You supplied a single column name _to the property custom_data_. This property only accepts a list of column names. Try supplying custom_data=["your-column"] instead of custom_data="your-column"
If we let the parser automatically convert "your-column" to ["your-column"], will there be any consequence? 
I'd rather we provide a helpful error message for all the `array_attrables` if what's provided for them is not list-like :)

Resolving patch:
diff --git a/packages/python/plotly/plotly/express/_core.py b/packages/python/plotly/plotly/express/_core.py
--- a/packages/python/plotly/plotly/express/_core.py
+++ b/packages/python/plotly/plotly/express/_core.py
@@ -1295,11 +1295,14 @@ def build_dataframe(args, constructor):
     # make copies of all the fields via dict() and list()
     for field in args:
         if field in array_attrables and args[field] is not None:
-            args[field] = (
-                dict(args[field])
-                if isinstance(args[field], dict)
-                else list(args[field])
-            )
+            if isinstance(args[field], dict):
+                args[field] = dict(args[field])
+            elif field in ["custom_data", "hover_data"] and isinstance(
+                args[field], str
+            ):
+                args[field] = [args[field]]
+            else:
+                args[field] = list(args[field])
 
     # Cast data_frame argument to DataFrame (it could be a numpy array, dict etc.)
     df_provided = args["data_frame"] is not None
diff --git a/packages/python/plotly/plotly/express/_doc.py b/packages/python/plotly/plotly/express/_doc.py
--- a/packages/python/plotly/plotly/express/_doc.py
+++ b/packages/python/plotly/plotly/express/_doc.py
@@ -199,8 +199,8 @@
         "Values from this column or array_like appear in bold in the hover tooltip.",
     ],
     hover_data=[
-        "list of str or int, or Series or array-like, or dict",
-        "Either a list of names of columns in `data_frame`, or pandas Series,",
+        "str, or list of str or int, or Series or array-like, or dict",
+        "Either a name or list of names of columns in `data_frame`, or pandas Series,",
         "or array_like objects",
         "or a dict with column names as keys, with values True (for default formatting)",
         "False (in order to remove this column from hover information),",
@@ -211,8 +211,8 @@
         "Values from these columns appear as extra data in the hover tooltip.",
     ],
     custom_data=[
-        colref_list_type,
-        colref_list_desc,
+        "str, or list of str or int, or Series or array-like",
+        "Either name or list of names of columns in `data_frame`, or pandas Series, or array_like objects",
         "Values from these columns are extra data, to be used in widgets or Dash callbacks for example. This data is not user-visible but is included in events emitted by the figure (lasso selection etc.)",
     ],
     text=[


Test patch:
diff --git a/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_hover.py b/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_hover.py
--- a/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_hover.py
+++ b/packages/python/plotly/plotly/tests/test_optional/test_px/test_px_hover.py
@@ -17,6 +17,17 @@ def test_skip_hover():
     assert fig.data[0].hovertemplate == "species_id=%{marker.size}<extra></extra>"
 
 
+def test_hover_data_string_column():
+    df = px.data.tips()
+    fig = px.scatter(
+        df,
+        x="tip",
+        y="total_bill",
+        hover_data="sex",
+    )
+    assert "sex" in fig.data[0].hovertemplate
+
+
 def test_composite_hover():
     df = px.data.tips()
     hover_dict = OrderedDict(
@@ -89,17 +100,20 @@ def test_formatted_hover_and_labels():
 
 
 def test_fail_wrong_column():
-    with pytest.raises(ValueError) as err_msg:
-        px.scatter(
-            {"a": [1, 2], "b": [3, 4], "c": [2, 1]},
-            x="a",
-            y="b",
-            hover_data={"d": True},
+    # Testing for each of bare string, list, and basic dictionary
+    for hover_data_value in ["d", ["d"], {"d": True}]:
+        with pytest.raises(ValueError) as err_msg:
+            px.scatter(
+                {"a": [1, 2], "b": [3, 4], "c": [2, 1]},
+                x="a",
+                y="b",
+                hover_data=hover_data_value,
+            )
+        assert (
+            "Value of 'hover_data_0' is not the name of a column in 'data_frame'."
+            in str(err_msg.value)
         )
-    assert (
-        "Value of 'hover_data_0' is not the name of a column in 'data_frame'."
-        in str(err_msg.value)
-    )
+    # Testing other dictionary possibilities below
     with pytest.raises(ValueError) as err_msg:
         px.scatter(
             {"a": [1, 2], "b": [3, 4], "c": [2, 1]},