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.extension(compatibility)` does not work? #1378

Open LecrisUT opened 3 months ago

LecrisUT commented 3 months ago

Firstly, I'm building on top of https://github.com/holoviz/holoviews/pull/6331 in order to get holoviews.opts.defaults(backend) to actually work. But then the compatibility does not seem to work anymore. Here's a MWE.

import holoviews
import hvplot

hvplot.extension("bokeh", "matplotlib", compatibility="bokeh")

holoviews.opts.defaults(
    holoviews.opts.Curve(
        linewidth=1,
    ),
    backend="matplotlib",
)
hvplot.output(backend="matplotlib")
curve = holoviews.Curve([0, 1, 2])
curve.opts(line_dash="dashed")
curve

After further debugging with this example, I couldn't get it to work in any combination. The version of hvplot I am working with is 0.10.0

maximlt commented 3 months ago

The compatibility option is a purely hvPlot feature, it has no effect when rendering objects with HoloViews directly.

https://github.com/holoviz/hvplot/blob/feffee5beaf2cfe6f2676a464802a66c431e432f/hvplot/converter.py#L695-L699

You could open a feature request for HoloViews to see if it could be implemented there too.

LecrisUT commented 3 months ago

I am using hvplot. I just used holoviews.Curve here as an example.

        def select_and_plot(_data: xarray.DataArray | xarray.Dataset, _indexers: dict[str | None, Any], backend: str, _plot_opts: dict[str,Any]) -> holoviews.Curve:
            nonlocal x, transform, label
            # Workaround in plot_opts because `compatibility` does not work properly
            apply_plot_opts = _plot_opts.copy()
            # if backend == "matplotlib":
            #     for bokeh_key in list(apply_plot_opts.keys()):
            #         if bokeh_key in MATPLOTLIB_TRANSFORMS["style"]:
            #             opt_transform = MATPLOTLIB_TRANSFORMS["style"][bokeh_key]
            #             bokeh_val = apply_plot_opts.pop(bokeh_key)
            #             if opt_transform != UNSET:
            #                 new_key, new_val = opt_transform(bokeh_key, bokeh_val)
            #                 apply_plot_opts[new_key] = new_val
            hvplot.output(backend=backend)

            plot_data = _data
            if None in _indexers:
                plot_data = plot_data[_indexers[None]]
            plot_data = plot_data.sel(**{k: v for k, v in _indexers.items() if k in _data.dims})
            plot_data = plot_data.dropna(x, how="all")
            curve: holoviews.Curve = plot_data.hvplot(kind=kind, x=x, label=label, **apply_plot_opts)
            return curve
maximlt commented 3 months ago

Sorry, that wasn't clear from the OP. I'm going to re-open, can you please post a minimal reproducible example that includes hvPlot code and that shows the compatibility doesn't work, screenshot appreciated.

LecrisUT commented 3 months ago

Here's my mwe

import hvplot.xarray
import holoviews
import xarray

hvplot.extension("bokeh", "matplotlib", compatibility="bokeh")

data1 = xarray.DataArray([0, 1, 2])
data1.name="Needs a name"
data2 = data1 + 1

hvplot.output(backend="matplotlib")

overlay = holoviews.Overlay()
curve_line1 = data1.hvplot(kind="line")
curve_line2 = data2.hvplot(kind="line", line_dash="dashed")
overlay *= curve_line1
## This one does not seem to work
curve_line1.opts(line_dash="dashed")
overlay *= curve_line2
overlay

image

maximlt commented 3 months ago

Ah ok you're referring to this line curve_line1.opts(line_dash="dashed"), correct? If so, this is not expected to work as the mapping of options from one backend to another is made through an hvplot() call, not directly through HoloViews .opts() calls. In other words, .opts() is not part of hvPlot's API.

LecrisUT commented 3 months ago

Ok, got it, still had some issues but probably all are related to #1380. Initially I did put all of those options inside the hvplot command like I showed I did in this comment, let me thinker with this a bit more and see again what issues I encountered.

Edit: I've tried it again, and indeed it works (except for #1380 and such), I probably was using .opt() but changed sometime in between

LecrisUT commented 3 months ago

Aaah, I remember the issue:

# Note: `hvplot.extension(compatibility)` does not work with `holoviews.opts.defaults(backend)`
hvplot.extension("bokeh", "matplotlib", compatibility="bokeh")
# hvplot.extension("bokeh", "matplotlib")
holoviews.opts.defaults(
    holoviews.opts.Curve(
        fig_size=500,
        linewidth=1,
    ),
    backend="matplotlib",
)

when I use hvplot.extension(compatibility), all of the defaults from holoviews.opts.defaults(backend) get ignored. I used https://github.com/holoviz/holoviews/pull/6331 to patch passing the backend, but it still gets ignored

maximlt commented 3 months ago

Ok. Tbh I'm not sure they get ignored as hvPlot sets some options by default. For example, hvPlot definitely overrides the default width/height of HoloViews, so I'd expect fig_size=500 to be ineffective. We don't have a great story around setting default options between hvplot and HoloViews.

LecrisUT commented 3 months ago

Primarily it's the linewidth=1 that is noticeable, where the default of matplotlib is being used