-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Notes on ongoing legend challenges #2231
Comments
Changed marker style in sns.scatterplot and the legend did not update the marker style. Any suggestions? |
@alualex Same here. Also legends for scatter plots can be customised, but for histplots this is not possible. I'm unable to change font sizes for simple histplots through |
This addresses issues discussed in #2280, along with some of the issues in #2231 It is a somewhat hack-ish solution. Because matplotlib legends don't offer public control over their location, this copies data from an existing legend to a new object, and then removes the original legend. I am hopeful that there will be upstream changes that make legend repositioning more natural, but this is a reasonable stopgap measure to alleviate a common seaborn pain-point.
This addresses issues discussed in #2280, along with some of the issues in #2231 It is a somewhat hack-ish solution. Because matplotlib legends don't offer public control over their location, this copies data from an existing legend to a new object, and then removes the original legend. I am hopeful that there will be upstream changes that make legend repositioning more natural, but this is a reasonable stopgap measure to alleviate a common seaborn pain-point.
This addresses issues discussed in #2280, along with some of the issues in #2231 It is a somewhat hack-ish solution. Because matplotlib legends don't offer public control over their location, this copies data from an existing legend to a new object, and then removes the original legend. I am hopeful that there will be upstream changes that make legend repositioning more natural, but this is a reasonable stopgap measure to alleviate a common seaborn pain-point. (cherry picked from commit f4a5076)
https://matplotlib.org/devdocs/users/next_whats_new/set_loc.html Seems things are moving in the right direction on the matplotlib side. This may at least help with moving around the legend. |
Here are some notes about legends, which are an area of future work.
Challenges with legends include:
Multi-variable legends
When multiple semantic mappings exist (currently only possible in the relational plots), the legends use a hack that adds a legend entry with an invisible artist handle to serve as a "subtitle". This is not ideal. Matplotlib legends do not have any concept of a "subtitle". It's possible to add multiple legends to a plot. But it's not possible to add multiple legends that both use
loc="best"
; they will be placed in the same location. Becauseloc="best"
is determined at draw-time, it's also not possible how to but one legend in the "best" location and then stick the other one next to or under it. It would also be better if it were possible to assign the sub-legends to e.g. different columns). See #1519 for some related work.Numeric semantic mappings
When using a numeric semantic mapping, a set of handles is added to the legend with evenly-spaced labels. This is intended to act like a "colorbar", but it doesn't look that much like a colorbar, and it's an ongoing source of confusion. Ideally, there would be something that looks like a colorbar inside the legend itself.
No consistent approach to legend creation
Most plotting functions add proxy artists with label attributes to the axes and then call
legend()
to pick them up. This has the advantage thatlegend()
can be called again with new kwargs (e.g. a different location) and it will be recreated. But it adds a lot of extraneous artists to the legend, which is confusing when trying to do deep customization. The new distribution plots create proxy artists but just pass them directly tolegend()
. The disadvantage is that callinglegend()
again will erase these artists. And in general, this should be predictable, and users should have access to the legends and their handles should they need to modify them.Legends are not very customizable
For numeric mappings, there's no way to specify exactly which labels to use. And in general, plotting functions don't take a
legend_kws
dict, but probably they should.Seaborn legend creation is a mess
The code that creates legends in seaborn is distributed and complicated, especially in the case of multi-variable legends in the relational module. While the semantic mappers could own the logic of determining what values to show in the legend, the plotting objects/functions know what artist to use and which attributes to modify.
Existing legends don't use matplotlib kwargs to show artists
Most of the existing legends represent the semantic mapping, but don't always correspond directly to the artists in the figure. e.g. if you make a scatterplot and pass
marker="x"
, the legend will still have dots representing the color or size. And not using the linewidth/edgecolor parameters causes problems for how the size variable is represented (#1763, #2355, #2852).Matplotlib legend API has limitations
Some of these problems exist because the matplotlib legend API has some limitations, both for users and for seaborn. Some of these problems would be less painful if it were easier to modify a legend after plotting, e.g. to move it or to add new entries to it. Moving a legend seems basically impossible through the public API, adding entries doesn't seem possible either. Some aspects of the legends can be modified in-place, but not through obvious methods. And the legend code uses a lot of private attributes and functions, so extending it (i.e. through subclassing) is not really possible
Unclear at this point whether the best path forward is to try and get an enhanced legend API from matplotlib or to fork the legend code and adapt to get what we need in seaborn itself.
The text was updated successfully, but these errors were encountered: