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
Improve multiple semantic legend titles #1440
Comments
Had my eye on this one for a few days now, and I just got some time to look into it, so here are some thoughts.
This was my thought at first too, however even when you specify
|
That's a nice idea but unfortunately it won't work because we can't touch the private |
It's occurred to me that any tricks we do with the legend drawn by the axes-level plots won't propagate to the legends drawn by FacetGrid/PairGrid, so I'm starting to come to terms with adding labels for the semantics but just leaving then left-justified in the right column. Perhaps there could be a special glyph to use that will identify what is a section title rather than an entry? Not sure what, though. |
Agreed. However since you end up building the legend by hand and adding it in for the user for both your axes level plots and your FacetGrids could we supply a post-hoc way of centering labels?
You could build the legend regularly, adding in labels with |
That looks great. I'm a little worried about unintended consequences of simply grabbing all the vpackers, but it seems robust to trying a few things. One thing that doesn't work, unfortunately, is that it's not robust to multiple calls to As for interfacing with FacetGrid, the basic approach there has been to make that as general as possible and to not make any assumptions about the functions that are being passed to it to draw. But there will be a higher-level function to handle important bookkeeping when faceting Ideally the best solution would be official support for "sectioned" legends in matplotlib but seaborn wouldn't be able to take advantage of that at this point anyway. |
Yeah I was worried about this as well. But, as long as the user hasn't meddled with the legend packing (I'm not sure if anyone does, because it would mean drawing up their own ax.legend() function essentially) this should work.
Yep. Maybe we could expose the centering function to users and they could use it if needed? Definitely not an ideal way of setting it up though. I did come up with a more robust solution, but it involves monkey patching the legend handlers which I'm not too keen on. I'll post it here so you want to take a look at it.
But as you'll notice, we're gaining robustness at the cost of mucking around with matplotlib's internals which will cause the unintended side effect of users wondering why all of their markers with
Yeah, it'd be a lot of code refactoring :( |
That is clever, but illegal :) However, is there no way to get something similar to work with the |
I did have a working function where you would pass it the handles you wanted to be subtitles and it would return a However, thinking about it- I may have gotten another idea. I'll do some testing and post back. |
Instead of changing the defaults to check for Only downside, is the user will need to supply the
My only other idea, which I'm not even 100% sure on how to implement is to dynamically subclass the Handles themselves to be a |
This last approach is definitely the most sophisticated, but I think if both options require user involvement, "call this function and it will left-align labels for non-visible legend entries" is a bit easier to understand than "pass this handler map when you call |
I’m in agreement here. I like the nonmagicalness of the center_legend function. However I’m not 100% a fan of how it relies on detecting invisible legend entries to determine if a label becomes a subtitle. Seems like a roundabout way and not as explicit as I would like. Just bouncing ideas here, but what if the center_legend also had an argument where the user could pass a dictionary to restructure the legend ordering. Something like
Where legend_order would be a user generated dictionary of subtitles they with to add, and the labels that would appear under the given subtitle like so:
Then we can go through the legend handles/labels, pull out the handles that match the inputted labels and add a subtitle to them. Then submit that ordering to a call of ax.legend(handles, labels). I’ll write up a function to better demonstrate what I’m talking about when I get some time today. The only down side I see here is that a label under one subtitle can’t be the same as a label under another subtitle. |
I think rather than checking for 0 alpha/size, we can use legend artist visibility as a proxy for whether the label should be left-aligned (which I actually think looks a bit better than centered). |
Ah I just saw your comment as I was coming to post this. I think checking on the legend artist visibility wouldn't be too hard and be a more appropriate proxy than alpha because we can set the visibility of the handle afterwards. What I was just thinking is that instead of even adding the subtitles to the Axes at all (right now, we're calling
|
So to summarize, there are two good options:
I think both functions are useful and are complimentary, since they do more or less different things. I would lean towards (1) as being most useful for a user who needs to quickly regenerate a legend, since they won't necessarily have access to the ordering that seaborn originally used. But (2) is a very nice utility function itself and I think would be nice to have ... it also is probably the most robust option to use inside of seaborn itself. If you agree, can you please open a PR adding these functions to |
I agree that these options are good. I think they can be wrapped into a single function that changes how it works depending whether or not a My only qualm with this approach now is that we're planning on
The legend will have odd right adjusted subtitle of "hello". Which won't be what the user expects. We could use an extra function to hide the subtitle legend entries (prefix the label with "_") from |
To be clear, A user might want to call |
I see, so What if by default the subtitles were hidden from the legend? So we had something that works like this:
This flexibility gives the user a choice on whether or not they want subtitles in the first place, determine if they want to redraw the legend to update some argument in **kwargs, or entirely change up the ordering of the subtitles/labels via the
This is a little more of a complex set up on the backend, but I think this set up makes it magical when it needs to be " But if we want to keep it simple we can definitely implement a function that centers legend entries with |
seaborn's default behavior in most contexts is to add semantic labels where they exist, and would be here too. |
Fair enough, I'll get started on a PR implementing the two aforementioned functions.
|
A few initial suggestions:
This would be better as
I think we agreed that |
|
Also not something that needs to be in the first pass but it would be nice if So to the extent that there are design decisions with tradeoffs, try to avoid making this impossible to add in the future (unclear if this will be a concern). |
Yes, but we can't pass |
Currently the legends for lineplot (#1285) and scatterplot (#1436) don't indicate what variable each of the levels represent. I punted on this but it needs a solution. One option is to cram everything into the legend title but that's not ideal. I think doing something with legend entries that have invisible handles and and then bumping the legend text so it looks centered would work:
But getting this to be really robust is going to be tricky.
The text was updated successfully, but these errors were encountered: