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

JointGrid reference lines #2620

Merged
merged 16 commits into from
Jul 24, 2021
Merged

Conversation

stefmolin
Copy link
Contributor

@stefmolin stefmolin commented Jul 18, 2021

Closes #2249 – Adds new JointGrid.refline() method:

JointGrid.refline(self, *, x=None, y=None, joint=True, marginal=True, **line_kws)

Example usage:

import seaborn as sns

penguins = sns.load_dataset('penguins')
g = sns.jointplot(data=penguins, x='bill_length_mm', y='bill_depth_mm')

# by default plot on both the marginal and joint axes
g.refline(y=16, linestyle='dashed', color='gray')

# only on joint
g.refline(x=45, marginal=False, linestyle='dotted', color='red')

# only on marginal
g.refline(x=55, joint=False, linestyle='dashed', color='blue')

Needs:

  • Test
  • Documentation in the JointGrid and FacetGrid API examples
  • Mention in release notes

seaborn/axisgrid.py Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Jul 18, 2021

Codecov Report

Merging #2620 (37d85d4) into master (445a54a) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2620   +/-   ##
=======================================
  Coverage   97.44%   97.45%           
=======================================
  Files          17       17           
  Lines        6350     6371   +21     
=======================================
+ Hits         6188     6209   +21     
  Misses        162      162           
Impacted Files Coverage Δ
seaborn/axisgrid.py 95.72% <100.00%> (+0.08%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 445a54a...37d85d4. Read the comment docs.

seaborn/axisgrid.py Outdated Show resolved Hide resolved
@stefmolin
Copy link
Contributor Author

Now both joint = marginal = False and x = y = None don't raise any errors and don't do anything:

# x and y missing -- nothing happens
g.refline(linestyle='dashed', color='gray')

# marginal and joint are False -- nothing happens
g.refline(x=45, y=16, marginal=False, joint=False, linestyle='dashed', color='gray')

Passing both x and y will now plot both:

g.refline(x=45, y=16, linestyle='dashed', color='gray')

@mwaskom
Copy link
Owner

mwaskom commented Jul 19, 2021

Great! So, three thoughts now:

  1. Default visual properties. axhline and axvline default to the first color in the color cycle, but I change the color to some grayscale value 100% of the time that I use them. I also usually make them dashed. This is because they typically represent annotations, not data, and I want to visually distinguish them from the other marks on the plot. (This is something the matplotlib devs agree with). I think it would be good to encode defaults like that in the refline signature, e.g. color=".5", linestyle="--". Does that sound good to you?

  2. It occurs to me that this functionality would be pretty useful in FacetGrid too. The implementation will need to be a little different since there's no concept of joint/marginal there, so they can't share the same method, but in a sense it's even simpler. Do you agree and would you be up for adding that as part of this PR?

  3. I edited the PR summary to include a short TODO list of additional tasks that will been to be accomplished before this can be merged. Please let me know if you need any guidance on implementing them.

@stefmolin
Copy link
Contributor Author

  1. I agree with the default style for reference lines – it's typically what I do as well. To confirm, you want to add color and linestyle with the defaults above to the signature? Since these are also technically line_kws, we could also leave that default logic internal to the refline() method without changing the signature. Which do you prefer?

  2. Sure, I'll work on adding this to FacetGrid. I would like to get your thoughts on that first though. When sharex = sharey = True, it's pretty straightforward to add the reference lines, but when they aren't equal, the line may not show up on all subplots. Do we want to allow for multiple x's/y's (one per subplot)?

  3. I'll take a look and let you know if I have any questions.

@mwaskom
Copy link
Owner

mwaskom commented Jul 19, 2021

I agree with the default style for reference lines – it's typically what I do as well. To confirm, you want to add color and linestyle with the defaults above to the signature? Since these are also technically line_kws, we could also leave that default logic internal to the refline() method without changing the signature. Which do you prefer?

Given that the defaults will differ a bit from the analagous matplotlib functions, I think it would be good to have them explicit in the signature. (But the code will be cleaner if you inject them into the line_kws dict before plotting.

Sure, I'll work on adding this to FacetGrid. I would like to get your thoughts on that first though. When sharex = sharey = True, it's pretty straightforward to add the reference lines, but when they aren't equal, the line may not show up on all subplots. Do we want to allow for multiple x's/y's (one per subplot)?

I don't think allowing multiple x/y's is worth the code complexity / thinking about what that API would look like. Let's have FacetGrid.refline with the same x/y on every facet for now.

@stefmolin
Copy link
Contributor Author

I've added the color and linestyle parameters to the method signature. Before I move on to the FacetGrid though, could you give me some feedback on the tests and example for JointGrid.refline()?

Copy link
Owner

@mwaskom mwaskom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One tiny nit in the docstring, otherwise looks perfect!

seaborn/axisgrid.py Outdated Show resolved Hide resolved
@mwaskom
Copy link
Owner

mwaskom commented Jul 21, 2021

Everything looks great so far!

Once implemented in FacetGrid, it would be good to update these two examples to use g.refline instead of mapping plt.axhline:

@stefmolin
Copy link
Contributor Author

Can you confirm that the kde_ridgeplot example is running correctly for you? When I run it (without switching to refline()), the y-axis labels ("Density") show up for each subplot, only by changing g.set(yticks=[]) to g.set(yticks=[], ylabel='') do I get the result in the docs.

@mwaskom
Copy link
Owner

mwaskom commented Jul 22, 2021

Ah, interesting! That does seem to be broken on master (not on v0.11.1).

@mwaskom
Copy link
Owner

mwaskom commented Jul 22, 2021

Probably the result of this: #2583. "Improvement", indeed...

@mwaskom
Copy link
Owner

mwaskom commented Jul 22, 2021

OK thinking about this a bit more, I think that is the desired result of #2583. Using a more standard case:

sns.FacetGrid(tips, col="time").map(sns.kdeplot, "total_bill")

v0.11.1:

image

master:

image

I think master is right (or better) here, FacetGrid shouldn't remove useful axis labels (from the "exterior" axes) unless you ask it too.

It produces an undesirable result in the ridgeplot example because lots of additional tweaking has been done to the plot, but that's ok, all that needs to be done is to add ylabel="" where g.set() is called.

@stefmolin
Copy link
Contributor Author

For the release notes, how should this be tagged? I took a look at the release notes for v0.11.0, which also has some new entries for new methods. Those are tagged "Feature", but since this PR is tagged "Enhancement", I figured I would double check.

@mwaskom
Copy link
Owner

mwaskom commented Jul 23, 2021

Hm good question. Actually there's no feature label on GitHub 🙃.

I think the feature/enhancement boundary can be a little blurry (is this adding a wholly new feature, or enhancing the existing JointGrid/FacetGrid functionality?) but I think Feature probably fits best.

@stefmolin
Copy link
Contributor Author

Agreed. I'll push the rest of the changes up shortly.

Copy link
Owner

@mwaskom mwaskom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of tiny comments, otherwise I think this looks great!

doc/docstrings/FacetGrid.ipynb Outdated Show resolved Hide resolved
seaborn/axisgrid.py Outdated Show resolved Hide resolved
examples/many_facets.py Outdated Show resolved Hide resolved
seaborn/axisgrid.py Outdated Show resolved Hide resolved
@mwaskom mwaskom merged commit 70cb3d9 into mwaskom:master Jul 24, 2021
@mwaskom
Copy link
Owner

mwaskom commented Jul 24, 2021

Thanks @stefmolin!

@stefmolin stefmolin deleted the jointgrid-reflines branch July 25, 2021 21:14
@mwaskom mwaskom modified the milestones: v0.12.0, v0.11.2 Aug 6, 2021
mwaskom pushed a commit that referenced this pull request Aug 6, 2021
* Add method to JointGrid for plotting reference lines.

* Tweak docstring

* Require one of joint/marginal to be True.

* Switch from orient to x/y.

* Add additional input validation.

* Allow both x and y; remove ValueErrors.

* Add color and linestyle params.

* Add JointGrid.refline() tests.

* Add JointGrid.refline() example.

* Update JointGrid.refline() docstring

Co-authored-by: Michael Waskom <[email protected]>

* Add FacetGrid.refline()

* Switch examples to use FacetGrid.refline()

* Use :meth: for method reference.

* Add FacetGrid.refline() test and example.

* Add refline to release notes.

* Address PR comments

Co-authored-by: Michael Waskom <[email protected]>
(cherry picked from commit 70cb3d9)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JointGrid could have axhline/axvline analogues that would plot on joint and marginal axes
2 participants