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

Support "layer labels" with legend #3046

Closed
mwaskom opened this issue Sep 26, 2022 · 3 comments · Fixed by #3456
Closed

Support "layer labels" with legend #3046

mwaskom opened this issue Sep 26, 2022 · 3 comments · Fixed by #3456

Comments

@mwaskom
Copy link
Owner

mwaskom commented Sep 26, 2022

It should be possible to represent a a layer in the legend, in addition to a mapping. An example usecase would be

(
    so.Plot(data, "x", "y")
    .add(so.Line(color="r"), so.PolyFit(1), label="1st degree")
    .add(so.Line(color="b"), so.Polyfit(2), label="2nd degree")
    .add(so.Dots())
)

We'd like this plot to have a legend with two entries: a red line with the label "1st degree" and a blue line with the label "2nd degree".

Complications!

  • How does this interact with mapped properties? Should this only be allowed if there's no mapping for that layer? Or should we get a legend with the mapped properties set to a "neutral" value (similar to when you have, e.g. two separate legends for color and linewidth)?
  • How should this legend appear by default? Perhaps as a separate legend panel from the variable legends? Should it always be first? If we want it to have a title, where does that happen?
  • How would we refer to this legend in TBD legend customization interface?
  • Implementation-wise, should this expand the functionality of Plot._update_legend_contents, or should we separate out methods for "labeled layer" legends and mapping legends?
  • Is there anything else we might want to (or users might expect us to) use the concept of a "layer label" for?
@EwoutH
Copy link
Contributor

EwoutH commented Oct 30, 2022

As discussed in #3101, I would really love this feature!

@matulad
Copy link

matulad commented Feb 19, 2023

I'd love this feature.

One possible way (related to 3107) to achieve this could be enabling to specify custom string as a 'mapping'.

Example:

data = pd.DataFrame({"x": ..., "y": ...})
(
    so.Plot(data, "x", "y") 
    .add(so.Line(), so.PolyFit(1), color="1st degree")
    .add(so.Line(), so.Polyfit(2), color="2nd degree")
    .add(so.Dots())
    .scale(color={"1st degree": "r", "2nd degree": "b"})
)

Under the hood, the value would be assign to a new column of the layer data and this value would be used in legend in the standard way. Therefore, it's a shortcut for:

   .add(so.Line(), so.PolyFit(1), color="tmp", data=data.assign(tmp="1st order"))

As custom string might be sometimes a bit ambiguous (and to avoid interference with data.columns), specific format of the new label may be needed (backticks?)

This way of "layer labels" answers some of complications you've listed, e.g. how should this apear in legend (the position is chosen by user via aesthetics she picked for the label).

@mwaskom
Copy link
Owner Author

mwaskom commented Aug 31, 2023

Added with #3456; the proposed example now works as expected

mpg = sns.load_dataset("mpg")
(
    so.Plot(mpg, x="weight", y="mpg")
    .add(so.Line(color="r"), so.PolyFit(1), label="1st degree")
    .add(so.Line(color="b"), so.PolyFit(2), label="2nd degree")
    .add(so.Dots(color=".2"))
)

image

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

Successfully merging a pull request may close this issue.

3 participants