-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix inability to use empty markers in scatter #25593
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for opening your first PR into Matplotlib!
If you have not heard from us in a while, please feel free to ping @matplotlib/developers
or anyone who has commented on the PR. Most of our reviewers are volunteers and sometimes things fall through the cracks.
You can also join us on gitter for real-time discussion.
For details on testing, writing docs, and our review process, please see the developer guide
We strive to be a welcoming and open project. Please follow our Code of Conduct.
lib/matplotlib/axes/_axes.py
Outdated
if (colors is None or edgecolors is None) and not mcolors.is_color_like(c)\ | ||
and not type(c) == np.ndarray: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm going to get the indentation wrong in the GH UI)
if (colors is None or edgecolors is None) and not mcolors.is_color_like(c)\ | |
and not type(c) == np.ndarray: | |
if ((colors is None or edgecolors is None) and not mcolors.is_color_like(c) | |
and not type(c) == np.ndarray): |
we prefer using ()
for multi-line expressions over using line-continuation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I say that and then see a whole bunch of it in the code right above this 🤦🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
lib/matplotlib/tests/test_axes.py
Outdated
@@ -2760,7 +2762,10 @@ def get_next_color(): | |||
_, _, result_edgecolors = \ | |||
mpl.axes.Axes._parse_scatter_color_args( | |||
c, edgecolors, kwargs, xsize=2, get_next_color_func=get_next_color) | |||
assert result_edgecolors == expected_edgecolors | |||
if type(expected_edgecolors) == np.ndarray: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isinstance
here as well please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
Thank you for this work @maramiulescu ! I left a few minor style comments. I feel strongly about the Would you be willing to write a whats new for this? I think that this feature is best explained with an image and is impactful enough we want to be sure to highlight it. |
Thanks @tacaswell for reviewing! I'm working together with @ewpardijs on this, we will implement your feedback and write a what's new soon. |
We recently widened our linewidth to 88 (from 80) so you can be a bit more relaxed on that front for any new code you add and code immediately around it, but please do not reflow all of the code in this file. |
@maramiulescu FYI, a slight tweak to how you word the PR description will automatically link the PR to close that issue: (e.g. saying "Fixes #XXXXX" instead of "Fix issue #XXXXX") I was going to just link this one manually, but the search box is not letting me select #24404 for some reason... |
( I edited the description before seeing @ksunden's comment, so now it is linked...) |
I edited it to say "Fixes ..." too, but it didn't get linked. Could it be that only a maintainer can link a PR after it's been created? |
I don't think the linking in the message is restricted to maintainers. I'd also suggest a better title and description. Even should I click through to the issue the number won't mean anything to me in a week, whereas knowing what it fixed will. |
Hi, I am a new contributor and I was working on this issue when the pull request was submitted. I would still like to contribute in some way if possible, I noticed when looking at the files changed there was a warning that "Added lines #L4623 - L4624 were not covered by tests". Is this something that I could add? If not, if there is any other way I could help contribute, please let me know as soon as possible! |
@erinproc please have a look at https://github.com/matplotlib/matplotlib/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%22 especially those that does not have a PR associated with them. That is probably a better starting point. But, yes, you are correct that ideally all lines should be covered in a PR. |
Updated the title and the description |
@oscargus thank you for your advice. I was working on this issue for an assignment in my software engineering course which is due soon, so I don't really have time to pivot to a new issue, but I'd still like to contribute to this pull request if possible. I have written some new tests and I was wondering what is the best way to submit these to be added to the current pull request? |
@erinproc Wait until this is merged and then contribute them I'd say. You can also add your commits on top of this, put that may still require rebasing later on so probably better to wait. |
@oscargus thanks for all of your help and advice, I really appreciate it. Out of curiosity, how long might it take to merge this pull request? I'm unfamiliar with the timeline of these types of things, and I'd just like to have a general idea. |
I'd say between one hour and, well, the oldest we have is six years... I'm not really up to date with this one, but hopefully shouldn't be long. |
@tacaswell What's new added, could you have a look? |
Enable configuration of empty markers in `~matplotlib.axes.Axes.scatter` | ||
------------------------------------------------------------------------ | ||
|
||
`~matplotlib.axes.Axes.scatter` can now be configured to plot empty markers without additional code. Setting ``facecolors`` to *'none'* and defining ``c`` now draws only the edge colors for fillable markers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should more explicitly highlight the "with colormap" part of this change, it has always been possible to have empty single color markers with facecolor='none', edgecolor='C0'
, e.g.
Admittedly it is a bit surprising that specifying edgecolor
explicitly was required, but that is due to the default being to follow "face"
Does this PR do anything to make that behavior change (without a cmap?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've changed the text in 39cfd56, hopefully now it's more fitting. If I understand your question correctly, yes, this PR does change that behavior: for the value of edgecolor
, c
now takes precedence over face
@@ -4338,6 +4340,7 @@ def _parse_scatter_color_args(c, edgecolors, kwargs, xsize, | |||
- kwargs['edgecolor'] | |||
- edgecolors (is an explicit kw argument in scatter()) | |||
- kwargs['color'] (==kwcolor) | |||
- c (if not None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is only true if facecolors
is "none"
. Otherwise I would expect the test case (dict(c='b'), None),
from the edited test to instead be (dict(c='b'), np.array([[0, 0, 1, 1]]))
, similar to the last test case added... I think, at least?
x, y, s, linewidths = \ | ||
cbook._combine_masks(x, y, s, linewidths) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x, y, s, linewidths = \ | |
cbook._combine_masks(x, y, s, linewidths) | |
x, y, s, linewidths = cbook._combine_masks(x, y, s, linewidths) |
c = np.ma.masked_invalid(c) | ||
x, y, s, colors, linewidths = \ | ||
cbook._combine_masks(x, y, s, colors, linewidths) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c = np.ma.masked_invalid(c) | |
x, y, s, colors, linewidths = \ | |
cbook._combine_masks(x, y, s, colors, linewidths) | |
c = np.ma.masked_invalid(c) | |
x, y, s, colors, linewidths = cbook._combine_masks(x, y, s, colors, linewidths) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs a test case that will cover this line.
if colors is None: | ||
|
||
if ((colors is None or edgecolors is None) and not mcolors.is_color_like(c) and | ||
(not isinstance(c, np.ndarray) or isinstance(c, np.ma.MaskedArray))): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we gating on the type here? Will our normalization above ensure other iterables will be converted to numpy arrays?
@@ -2749,6 +2749,8 @@ def get_next_color(): | |||
(dict(c='b', edgecolor='r', edgecolors='g'), 'r'), | |||
(dict(color='r'), 'r'), | |||
(dict(color='r', edgecolor='g'), 'g'), | |||
(dict(facecolors='none'), None), | |||
(dict(c='b', facecolors='none'), np.array([[0, 0, 1, 1]])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to cover the case of colormapping + empty here as well.
@maramiulescu Sorry this fell off our radar. |
I'd be interested in taking this over if @maramiulescu has no objections. It's been bugging me for ages and it would be nice to just have it finally work. |
It would be great if you could take over @claytharrison |
PR Summary
Fixes #24404
Modifies the
scatter
function to allow users to plot using empty'o'
markers without any additional code.PR Checklist
Documentation and Tests
pytest
passes)Release Notes
.. versionadded::
directive in the docstring and documented indoc/users/next_whats_new/
.. versionchanged::
directive in the docstring and documented indoc/api/next_api_changes/
next_whats_new/README.rst
ornext_api_changes/README.rst