Skip to content
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

Open
mwaskom opened this issue Aug 28, 2020 · 3 comments
Open

Notes on ongoing legend challenges #2231

mwaskom opened this issue Aug 28, 2020 · 3 comments

Comments

@mwaskom
Copy link
Owner

mwaskom commented Aug 28, 2020

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. Because loc="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 that legend() 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 to legend(). The disadvantage is that calling legend() 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.

@alualex
Copy link

alualex commented May 26, 2021

Changed marker style in sns.scatterplot and the legend did not update the marker style. Any suggestions?

@shivendra90
Copy link

@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 ax.legend method.

mwaskom added a commit that referenced this issue Aug 15, 2021
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.
mwaskom added a commit that referenced this issue Aug 15, 2021
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.
mwaskom added a commit that referenced this issue Aug 15, 2021
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)
@mwaskom mwaskom unpinned this issue Mar 31, 2022
@thuiop
Copy link
Contributor

thuiop commented Sep 18, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants