hvplot ignores default legend_position
ALL software version info
bokeh 2.3.0 holoviews 1.14.2 hvplot 0.7.1
Description of expected behavior and the observed behavior
Hello, when setting default values for the legend position in holoviews, I expect it to be also respected by hvplot, if not specified otherwise in the hvplot call
df.hvplot()
but the code above plots the legend at the standard hvplot position on the right and not on the specified default position.
Complete, minimal, self-contained example code that reproduces the issue
import holoviews as hv
import hvplot.pandas
import pandas as pd
from holoviews import opts
import pandas.util.testing as tm
opts.defaults(opts.NdOverlay(show_legend=True, legend_position='top_left', legend_cols=False))
df = tm.makeDataFrame()
plt = df.hvplot()
hv.save(plt,"test.html")
Sadly most HoloViews defaults are currently ignored. Whether to use HoloViews defaults or have a separate defaults management system is also up for debate.
I would vote against adding some separate defaults management system.
The problem with that is that hvPlot defaults are already very different from HoloViews defaults, so if we want to unify the defaults management we'd probably have to apply the hvPlot defaults to HoloViews on import.
Having HoloViews change all of its defaults when hvPlot is imported would be very surprising to me, but it might possibly be useful, because someone who gets e.g. an hvPlot scatterplot then builds a HoloViews scatterplot by hand might well want the styling of the HoloViews plot to match their default hvPlot outputs. Even so, I think I'd be uncomfortable applying those defaults to HoloViews automatically on hvPlot import. Instead it would be nice for a user to be able to say explicitly "use hvPlot defaults for native HoloViews plots", and to have that mentioned as a potentially useful thing to do in an advanced section of hvPlot docs. But of course making that work would require extracting hvPlot's defaults into something declarative, when I assume they are currently embedded into the hvPlot code.
With that in mind, it seems to me like hvPlot and HoloViews could share the same defaults management system without actually sharing the same defaults. E.g. hv.config and hvplot.config might be the same code but with different sets of defaults, ideally with hvplot.opts.defaults falling back to hv.opts.default values for anything not overridden in hvplot.opts.defaults. To me it seems like that would make things work the most naturally, with users able to set any default they want and make them match if they need to, but it does sound like a good bit of work.
I suppose apart from the huge amount of effort I just don't see how that would work for a rather large number of reasons:
- If they don't share the same defaults then they by definition cannot be set in the same way, there would have to be two different APIs the set them (which kind of defeats the point).
- hvPlot users shouldn't have to know about HoloViews element names, e.g. to set defaults for an hvPlot
lineplot a user shouldn't have to sethv.Curvedefaults. - hvPlot does not neatly have to map to one particular HoloViews element, e.g. there might be composite objects returned by hvPlot which require very different defaults from the elements that make up the composite object.
- The hvPlot options deliberately do not map directly to HoloViews option names, so to set defaults via a shared system a user would have to learn what the HoloViews options are called, which is different from what they would use in an hvPlot call.
- There is no way for hvPlot to maintain a separate options tree like
hv.opts.defaultsbecause, once the object is returned by hvPlot, there is no way to tell HoloViews that it should query a different set of defaults.
So overall I see a shared system not only technically challenging but also resulting in a seriously sub-optimal user experience.
I suppose apart from the huge amount of effort I just don't see how that would work for a rather large number of reasons:
Definitely, the amount of work involved is probably prohibitive. Still, it's worth having even quite difficult roadmap items if they are desirable, because at the moment there's no straightforward way for a user to configure hvPlot defaults at all, which doesn't seem sustainable in the long term. Plus, as we want to add a Matplotlib backend for hvPlot, I it seems like something of this nature would be needed anyway. Plus making and maintaining a separate defaults management system is also a huge amount of effort, in the long run. So let's try to discuss what would be desirable separately from the difficulty of doing it.
- If they don't share the same defaults then they by definition cannot be set in the same way, there would have to be two different APIs the set them (which kind of defeats the point).
I don't think that's true; can't they be set the same way with hvplot.opts.defaults() to set the ones specifically for hvPlot, and hv.opts.defaults() setting ones that apply either to both libraries (for options not already overridden in hvplot.opts.defaults either by hvPlot itself or by a user's changes) or specifically to HoloViews. That's the same API, but acting on different objects.
- hvPlot users shouldn't have to know about HoloViews element names, e.g. to set defaults for an hvPlot line plot a user shouldn't have to set hv.Curve defaults.
I agree that's annoying and confusing, but it seems like you'd have to have not just a new defaults management system but also a new options management system to avoid that. Advanced hvPlot users already have to know about HoloViews elements and .opts if they want to change any of the options on a created object, and that seems like something they would typically learn first, before ever messing with hv.opts.defaults at all. Only once they've applied .opts a lot would they ever be interested in changing the global defaults, and by that point, they've already made such a mental mapping. We could add some aliases to the options system to help smooth over such differences (accept opts.Line or opts.line for opts.Curve, etc.), but I don't have a strong opinion whether that's a good idea. In any case I agree that it's a shame there will be this impedance mismatch between hvPlot's method names and HoloView's ELement names, but I don't see a practical way to avoid that gap existing somewhere.
- hvPlot does not neatly have to map to one particular HoloViews element, e.g. there might be composite objects returned by hvPlot which require very different defaults from the elements that make up the composite object.
Sure. In that case we'd presumably want to make a dummy hvPlot-specific element to hold such default values, even if it has no plotting implementation. We could even consider whether the implementation for such objects should be in a HoloViews element in general, so that HoloViews users could access that functionality in a natural way. In any case we don't necessarily have to solve such cases; they currently aren't configurable in a defaults system anyway, and so they would be no worse in my proposed unified defaults system.
- The hvPlot options deliberately do not map directly to HoloViews option names, so to set defaults via a shared system a user would have to learn what the HoloViews options are called, which is different from what they would use in an hvPlot call.
Again, unless you are proposing not just a new defaults system but a new options system, I don't see how that's relevant. Yes, advanced or picky hvPlot users do have to figure out that hvPlot returns HoloViews objects and that such objects have different option names etc., but that's true already and is not affected by a defaults management system. And I think there are far more options that don't exist at all in hvPlot calls than that do, and so a default system that only addresses the hvPlot syntax will be severely limited. People would still need to reach for HoloViews options at that point, so again it seems to me that a separate defaults system is just going to make things worse.
In any case, despite strongly pushing hvPlot as our primary point of entry for users, I still would vote against adding another separate options management system, for the same reasons as voting against a separate defaults management system. I think users are better served by having solid, well documented functionality in hvPlot, along with a clearly demarcated boundary where they fall back to the underlying HoloViews way of doing things. Adding yet more complexity trying to hide the underlying system seems unworkable to me.
5.There is no way for hvPlot to maintain a separate options tree like hv.opts.defaults because, once the object is returned by hvPlot, there is no way to tell HoloViews that it should query a different set of defaults.
There are surely lots of ways! hvPlot could make a call before it returns the objects, looking up the hvPlot-specific options from hvplot.opts.defaults and applying them to the object before returning it. The result would be a pure HoloViews object with hvPlot's options applied. Or, HoloViews objects could have a pointer to HoloView's root options tree, while hvPlot would return objects pointing to a different root, so that they work exactly the same but with different option values. There are probably other approaches I haven't thought of, and definitely some way to make that work!
Anyway, my main concern here is that we need systems that we can maintain and defend and that users can understand. Adding some partial, always limited new defaults management system for hvPlot will make some things easier for some class of hvPlot users and some tasks. But we'd still either have continual pressure to make it cover more cases (inventing new hvPlot versions of all the HoloViews options that don't currently show up as hvPlot method arguments), or else users would have to drop down to HoloViews option and defaults management anyway. My vote is for having a clean break: hvPlot lets you build plots the hvPlot way, using function arguments. Anything else is done the HoloViews way, both for options and for defaults, even if we might do some aliasing in such cases to help hvPlot users make the mapping from hvPlot to HoloViews.
Hello,
I also think an additional options system is more confusing, as already stated an advanced user already knows about the connection to holoviews. The confusion for me comes more from the fact, that some default options are ignored. I basicly use hvplot to easily create plots from dataframes and then compose them further or add interactive functionality using the returned holoviews objects.
The defaults from hvplot are basicly set implicitly by the handling of the input arguments in the hvplot call.
For me the most intuitive way would be if an argument is provided to the hvplot call it is used and if its not, hvplot would check if there are user defaults set for that parameter and use that instead of the hvplot default.
This could either be achieved when handling the parameters in hvplot or afterwards with applying the options.
For setting defaults directly from hvplot, I could think of an api using the hvplot names, that applies the options to the corresponding holoviews options tree. e.g hvplot.opts.default.line
Parameters like legend_position, that apply to line but not to Curve can than be applied NdOverlay for example.