Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Refactor color parsing of Axes.scatter#11663

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

timhoffm
Copy link
Member

@timhoffmtimhoffm commentedJul 15, 2018
edited
Loading

PR Summary

As discussed in#11383, it would be helpful to extract the color parameter parsing ofAxes.scatter() to a separate method. This helps in managing the complexity of the code: separted from other stuff inscatter(), easier and faster to test because one does need a figure to test the parameter parsing.

Some things that can be improved in the future, but are beyond the scope of this PR:

  • I did only some simple refactorings so far. I'm quite sure this function can be untangled more. This will become easier after this PR, because we can now add a further tests.
  • The tests fromTestScatter::test_scatter_c should be moved to simple tests on theAxes._parse_scatter_color_args later on.
  • Ideally,Axes._parse_scatter_color_args would be a static method. So far, it still has a singleself reference for obtainingself._get_patches_for_fill.get_next_color(). We cannot easily get rid of that.
    Passing the fallback color in is not an option, because it's only evaluated conditionally and evaluation has the side effect of advancing the color cycle. On the other hand, we cannot return a 'DUMMY_FALLBACK_COLOR' and evaluate it outside the method, because the result is further processed within the method. Good ideas how to handle this are welcome.

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant

@jklymak
Copy link
Member

OK, so for this PR, no functionality changes? Its just a refactor? I'm 👍 on the refactor....

@timhoffm
Copy link
MemberAuthor

timhoffm commentedSep 6, 2018
edited
Loading

Jep, no functional changes. This just extracts the function and regroups independent parts of its logic.

And of course, added some tests. 😄.

Copy link
Member

@jklymakjklymak left a comment

Choose a reason for hiding this comment

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

Looks better to me. Thanks for all the tests too....

@timhoffmtimhoffmforce-pushed thescatter-parameter-parsing branch from05516df to07c1c62CompareNovember 1, 2018 21:42
@timhoffm
Copy link
MemberAuthor

Rebased and updated to handle merge conflict due to#12431.

@timhoffm
Copy link
MemberAuthor

Rebased to handle merge conflict due to#12673

if edgecolors is None and not rcParams['_internal.classic_mode']:
edgecolors = 'face'

c_is_none = c is None
Copy link
Contributor

Choose a reason for hiding this comment

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

"c_was_none"? (otherwise the next line reads a bit weird)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Done.


c_is_none = c is None
if c is None:
if facecolors is not None:
Copy link
Contributor

Choose a reason for hiding this comment

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

would tend to merge the conditionals:

c = (facecolors if facecolors is not None     else "b" if classic_mode     else self._get_patches_for_fill.get_next_color())

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Done.

try: # First, does 'c' look suitable for value-mapping?
c_array = np.asanyarray(c, dtype=float)
n_elem = c_array.shape[0]
if c_array.shape in [xshape, yshape]:
Copy link
Contributor

@anntzeranntzerNov 4, 2018
edited
Loading

Choose a reason for hiding this comment

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

Something funny is going on here (I know it was already there before, but still seems worth pointing out): we take x and y of any shapes and flatten them (they just need to have the same size), butc has to match either the shape ofx ory, not just the size.

In other words the following "work" (i.e. perform an implicit ravel()):

scatter(np.arange(12).reshape((3, 4)), np.arange(12).reshape((4, 3)), c=np.arange(12).reshape((3, 4)))scatter(np.arange(12).reshape((3, 4)), np.arange(12).reshape((4, 3)), c=np.arange(12).reshape((4, 3)))

but the following fail:

scatter(np.arange(12).reshape((3, 4)), np.arange(12).reshape((4, 3)), c=np.arange(12).reshape((6, 2)))# and evenscatter(np.arange(12).reshape((3, 4)), np.arange(12).reshape((4, 3)), c=np.arange(12))

Of course that last one has the best error message (irony intended):

ValueError: 'c' argument has 12 elements, which is not acceptable for use with 'x' with size 12, 'y' with size 12.

Copy link
MemberAuthor

@timhoffmtimhoffmNov 4, 2018
edited
Loading

Choose a reason for hiding this comment

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

Well observed. I prefer not to change functionality as part of this refactoring, but I'm happy to open an issue/pr for that. - Also not sure what the intended behavior should be (should we modifyc to be as permissive as withx/y, or should we be more strict aboutx/y shape?

Edit: Issue added#12735

Choose a reason for hiding this comment

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

ValueError: 'c' argument has 12 elements, which is not acceptable for use with 'x' with size 12, 'y' with size 12.

is a really awful error. Has the intuitive functionality that was present before been restored?

Copy link
Member

Choose a reason for hiding this comment

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

Please continue this conversation at#12735. I think a PR that gave a better error for this case would be most welcome.

)
# Both the mapping *and* the RGBA conversion failed: pretty
# severe failure => one may appreciate a verbose feedback.
raise ValueError(
Copy link
Contributor

Choose a reason for hiding this comment

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

else: (goes together with theif: above)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Done.

@timhoffmtimhoffmforce-pushed thescatter-parameter-parsing branch 2 times, most recently from892b72f to99cae22CompareNovember 4, 2018 12:09
"'c' argument has {nc} elements, which is not "
"acceptable for use with 'x' with size {xs}, "
"'y' with size {ys}."
.format(nc=n_elem, xs=xsize, ys=ysize)
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of tracking n_elem I think you can just donc=np.shape(c)[0] here?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

You can have edge cases in whichnp.shape(c)[0] will raise here. E.g.

plt.scatter([1,2], [1,2], c='rgb')

...ValueError:During handling of the above exception, another exception occurred:~/dev/matplotlib/lib/matplotlib/axes/_axes.py in _parse_scatter_color_args(self, c, edgecolors, kwargs, xshape, yshape)   4144                         "acceptable for use with 'x' with size {xs}, "   4145                         "'y' with size {ys}."-> 4146                             .format(nc=np.shape(c)[0], xs=xsize, ys=ysize)   4147                     )   4148                 else:IndexError: tuple index out of range

Makes shure the intended message is printed, even if we cannot determine the shape of c. Even if it says -1 there it's better than just "tuple index out of range".

Copy link
Contributor

Choose a reason for hiding this comment

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

fair enough

raise ValueError(
"'c' argument must either be valid as mpl color(s) "
"or as numbers to be mapped to colors. "
"Here c = {}." # <- beware, could be long depending on c.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the standard wording would be something like

"'c' must be either this or that, not\n{}".format(c)

(the newline being there only to handle cases of very long c's)

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Message rewritten. I don't think that exception messages should contain explicit newlines.

"with a single row if you really want to specify "
"the same RGB or RGBA value for all points.")
# Wrong size; it must not be intended for mapping.
valid_shape = False
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I have a mild preference for moving the invalid shape error message up and just doraise ValueError(invalid_shape.format(...)) here and below instead of keeping thevalid_shape andn_elem tracking variables.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

I feel that the overall structure is not very clear and that it can be written much more concise, but that's beyond this PR. Therefore I'd like to defer any further code changes within the function. As written in the original PR message:

I'm quite sure this function can be untangled more. This will become easier after this PR, because we can now add a further tests.

So the strategy is:

  1. Get this PR in without changing more than neccesary of the original code.
  2. Move the tests to directly testingAxes._parse_scatter_color_args, which is significantly faster than creating a figure each time.
  3. This allows adding more tests an thus be surer that code changes do not modify the functionality.

anntzer reacted with thumbs up emoji
else:
# Both the mapping *and* the RGBA conversion failed: pretty
# severe failure => one may appreciate a verbose feedback.
raise ValueError(
Copy link
Contributor

Choose a reason for hiding this comment

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

At that point the only error we can get is the one thrown by to_rgba_array which I think is just fine to let through (Invalid RGBA argument: ...).

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

As above would like to defer this to future PRs.

@timhoffmtimhoffmforce-pushed thescatter-parameter-parsing branch from0466d49 to1b68e69CompareNovember 4, 2018 14:46
@anntzeranntzer merged commitac0525e intomatplotlib:masterNov 4, 2018
@timhoffmtimhoffm deleted the scatter-parameter-parsing branchNovember 4, 2018 16:57
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@jklymakjklymakjklymak approved these changes

@mathematicalmichaelmathematicalmichaelmathematicalmichael left review comments

@anntzeranntzeranntzer approved these changes

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
v3.1.0
Development

Successfully merging this pull request may close these issues.

7 participants
@timhoffm@jklymak@anntzer@mathematicalmichael@tacaswell@QuLogic@dstansby

[8]ページ先頭

©2009-2025 Movatter.jp