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.14k stars 109 forks source link

hvplot.points generates error when color is mapped to a variable and dataframe contains NaN values for axis variables #1202

Closed ablythed closed 1 month ago

ablythed commented 1 year ago

ALL software version info

hvplot 0.9.0 pandas 2.1.1 notebook 6.5.4

Description of expected behavior and the observed behavior

Expected behavior: Produces a Point plot where each point is colored according to specified variable ("color") in the example. Ignores NaN values for axis and color variables.

Observed behavior: Generates a ValueError. Removing the NaN entries solves the problem.

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

import pandas as pd
import hvplot.pandas

df = pd.DataFrame({"longitude": [-80, None], "latitude": [39, None], "color": [1, 2]})
df.hvplot.points("longitude", "latitude", geo=True, color="color")

Stack traceback and/or browser JavaScript console output

Initial error:

ValueError [Call holoviews.ipython.show_traceback() for details] 
failed to validate Scatter(id='1217', ...).line_color: 
expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), 
Either(String, Instance(Transform), Instance(Expression), Nullable(Color))) or Nullable(Color), 
got dim('color')

Traceback on calling holoviews.ipython.show_traceback():

Traceback (most recent call last):

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/holoviews/plotting/bokeh/element.py", line 1717, in _init_glyphs
    renderer, glyph = self._init_glyph(plot, mapping, properties)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/holoviews/plotting/bokeh/element.py", line 2420, in _init_glyph
    ret = super()._init_glyph(plot, mapping, properties)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/holoviews/plotting/bokeh/element.py", line 1402, in _init_glyph
    renderer = getattr(plot, plot_method)(**dict(properties, **mapping))
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/plotting/glyph_api.py", line 967, in scatter
    return self._scatter(*args, marker=marker_type, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/plotting/_decorators.py", line 86, in wrapped
    return create_renderer(glyphclass, self.plot, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/plotting/_renderer.py", line 116, in create_renderer
    glyph = make_glyph(glyphclass, kwargs, glyph_visuals)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/plotting/_renderer.py", line 145, in make_glyph
    return glyphclass(**kws)
           ^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/model/model.py", line 128, in __init__
    super().__init__(**kwargs)

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/core/has_props.py", line 206, in __init__
    setattr(self, name, value)

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/core/has_props.py", line 230, in __setattr__
    return super().__setattr__(name, value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/core/property/descriptors.py", line 283, in __set__
    value = self.property.prepare_value(obj, self.name, value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/core/property/dataspec.py", line 627, in prepare_value
    return super().prepare_value(cls, name, value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  File "/Users/adavis/opt/anaconda3/envs/gis/lib/python3.11/site-packages/bokeh/core/property/bases.py", line 365, in prepare_value
    raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")

ValueError: failed to validate Scatter(id='1217', ...).line_color: expected an element of either String, Dict(Enum('expr', 'field', 'value', 'transform'), Either(String, Instance(Transform), Instance(Expression), Nullable(Color))) or Nullable(Color), got dim('color')

Screenshots or screencasts of the bug in action

Screenshot 2023-11-13 at 2 40 15 PM

Thanks @Hoxbro for the MRE and for helping me figure out what was going on!

kthyng commented 1 year ago

I met this same problem today! I have been using the same code snippet for plotting for weeks so it's been really driving me crazy that it suddenly didn't work this afternoon. I had already checked for nan's, but it turns out I had an inf in my lon/lat. Also a weird thing is that it works if geo=False. Anyway just thought I'd chime in.

maximlt commented 1 month ago

Closing in favor of the upstream issue.