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

Implementing ticklabels keyword in plt.colorbar()#18884

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

Closed
danuo wants to merge13 commits intomatplotlib:masterfromdanuo:colorbar1

Conversation

danuo
Copy link
Contributor

@danuodanuo commentedNov 3, 2020
edited
Loading

PR Summary

This adds the ticklabels keyword to the plt.colorbar() function. Tests and documentation has been added. Please review the documentation, as I am unsure if this has been done correctly.

PR Checklist

  • Has pytest style unit tests (andpytest passes).
  • IsFlake 8 compliant (runflake8 on changed files to check).
  • New features are documented, with examples if plot related.
  • Documentation is sphinx and numpydoc compliant (the docs shouldbuild without error).
  • Conforms to Matplotlib style conventions (installflake8-docstrings and runflake8 --docstring-convention=all).
  • New features have an entry indoc/users/next_whats_new/ (follow instructions in README.rst there).
  • API changes documented indoc/api/next_api_changes/ (follow instructions in README.rst there).

@timhoffm
Copy link
Member

What is the use case for explicitly setting labels? I mainly see categoricals, but that does not make sense for a colorbar.

@danuo
Copy link
ContributorAuthor

danuo commentedNov 5, 2020
edited
Loading

What is the use case for explicitly setting labels? I mainly see categoricals, but that does not make sense for a colorbar.

@timhoffm From my view, it is not us to decide if something makes sense or not. I will still provide some examples for you: May it be that someone wants to visualize the certainty of a neural network that something is part of a category. Or a user is looking for an easy/hacky way of adding units behind the tick numbers. There even is a demo for this, with the following approach:
https://matplotlib.org/3.1.1/gallery/ticks_and_spines/colorbar_tick_labelling_demo.html

cbar=fig.colorbar(cax,ticks=[-1,0,1])cbar.ax.set_yticklabels(['< -1','0','> 1'])

Not only is this complicated, but also frustrating for users. Remember: Most of your users are no experts and, for example, set ticks withplt.xticks(ticks=.., labels=...). Therefore, I see this as an improvement of api consistency.

@jklymak
Copy link
Member

If we are adding API then we need to decide if it makes sense or not.

OTOH this seems pretty useful to me. Imagine a pcolor of phase and wanting to label \pi/2, \pi etc.

@timhoffm
Copy link
Member

timhoffm commentedNov 6, 2020
edited
Loading

I'm a bit hesitant with adding functionality for explicit labels, because it tends to be a can of worms: The ticks positions and labels must be synchronized. If you just pass labels they will end up on the automatically chosen ticks, hence their position is dependent on the data(range) - That e.g. is explicitly forbidden inplt.xticks(), but not in the current state of this PR. Additionally, the attributes are basically public, so users could expect to be able to e.g.cb = plt.colorbar(); cb.ticklabels =['foo', 'bar'], which again can lead inconsistent labeling. Much of this is inherited from a non-ideal design, but that's where we start from and we must be really careful if extensions based on that design will consistently work. Otherwise, the API consistency gets worse overall.

IMHO colorbar_tick_labelling_demo.html should be changed because the <1 / >1 don't actually make sense. But@jklymak's phase example seems like a fair use case, which is why one can consider alabels argument. But if we want to provide this functionality, it needs more consideration per the above.

Concerning frustration: I see your point that it's more cumbersome to work on the axes afterwards as in the example. If one can provide a consistent simplification, that's a benefit for this use case. However the frustration is even larger if a claimed functionality does not work consistently. With alabels parameter we claim that setting colorbar labels is a first-class functionality. If we cannot live up to that, I'd rather not support it and defer to the example (which implies: You can change the labels by working on the internals, but it's also your responsibility to do that consistently).

@jklymak
Copy link
Member

Just to be clear, the last couple of draft PRs I have put in are attempting to make colorbar axes thesame as normal axes as much as is practical. Its a bit far off, maybe even after 3.4, but it is a goal. Then most of the tricks that work for normal axes should carry over.

@danuo
Copy link
ContributorAuthor

@timhoffm
Ifcolorbar() is called with ticklabels, but without ticks, there actually is a userwarning in the state of this PR:

plt.colorbar(ticks=None,ticklabels=['foo','bar'])**output:**UserWarning:set_ticks()musthavebeencalled.

I believe that addresses your main concern. Regarding your second argument: Would it be possible to callself.set_ticklabels(self.ticklabels) when the figure is finalized, i.e. whenplt.show() or something similar is executed?

@jklymak
Copy link
Member

I think as long asticklabels is forced to be called withticks and they are forced to be the same, we should be fine, though you may not want to use the lower level warning.

Regarding your second argument: Would it be possible to call self.set_ticklabels(self.ticklabels) when the figure is finalized

I am not sure what you are suggesting here. The way that ticks and tick labels get decided at draw time is via Locators and Formatters. All thatshould happen when you callset_ticklabels(labels) is the axis's Formatter is changed to a FixedFormatter. I've not actually looked at what you have done.

@danuo
Copy link
ContributorAuthor

@jklymak My second thought was related to Tim's concern, that directly settingcb = plt.colorbar(); cb.ticklabels =['foo', 'bar'] would have no effect on the colorbar. After reading your response and giving it a second thought, I believe my suggestion is not feasible.

I am not sure what you are suggesting here. The way that ticks and tick labels get decided at draw time is via Locators and Formatters. All that should happen when you call set_ticklabels(labels) is the axis's Formatter is changed to a FixedFormatter. I've not actually looked at what you have done.

I guess that is exactly what my PR is doing. I tried to utilize as much of existing code as possible, and indeed the kwarg ticklabels is handed over to set_ticklabels(ticklabels) which then creates an FixedFormatter.

@timhoffm
Copy link
Member

To clarify: I'm conceptually ok with adding aticklabels keyword arguement tocolorbar(). However, I consider the proposed implementation too ad-hoc. At least the follow issues arise:

  • passing labels without an explicit list of ticks must raise incolorbar() with an appropriate message. The warning from the lower level is too weak and too imprecise.
  • passing labels and format are incompatible and must raise.
  • The existing public attributes are an API nightmare, I prefer not to add more of those, in particular if they can be overlap.
    Speaking of which, it may be reasonable to store the tick labels informat using aFixedFormatter instad of creating a new attribute. That'd only slighly worsen the situation (FixedFormatter and FixedLocator should be used together and there's still the possibility of overwriting each via the existing public attributes).

I've not completely thought this through, so there may be more.

@jklymak Will there still be public attributes and adraw_all() in your Colorbar rewrite?

@jklymak
Copy link
Member

I agree with all the above. For sureticklabels should not be a public attribute, though we could add aset_ticklabels.

@jklymak Will there still be public attributes and a draw_all() in your Colorbar rewrite?

For sure. Nothing I'm working on will change the API (much, I hope). Its low level with how we draw the solids and the extend triangles. OTOH, I'm all for cleaning up the API. The changes I am making may make more API easier. ie.set_scale orset_lims will actually be possible, and could do what you expect without changing the underlying norm. But none of that is necessary..

@danuo
Copy link
ContributorAuthor

@timhoffm@jklymak Please have a look at the latest commit. Now, ticks are only accepted with they arenp.iterable or isinstance(ticker.Locator). This applies for ticks keyword argument inplt.colorbar() and forcolorbar.set_ticks(). If they are not, ticks are not used and a clear warning warning is given. Ticklabels set through keyword argument inplt.colorbar() or through colorbar.set_ticklabels() are only accepted, if valid and explicit ticks have been supplied earlier. Anything going wrong will throw a clear warning. The attributes are both private now.

@timhoffm
Copy link
Member

Something is broken here. Tests are failing withAttributeError: 'Colorbar' object has no attribute 'vmin'.

@danuo
Copy link
ContributorAuthor

@timhoffm
github matplotlib

I have investigated the issue and found the bug. All existing tests are now passing again. I have also implemented additional tests for the new ticklables keyword. Can you explain, why the last test of mine fails? Why doesn't the error match?

Failed: DID NOT WARN. No warnings of type (<class 'UserWarning'>,) matching ('To set explicit ticklabels, call colorbar() with ticks keyword or use set_ticks() method first.') was emitted. The list of emitted warnings is: [UserWarning('To set explicit ticklabels, call colorbar() with ticks keyword or use set_ticks() method first.')].

(I have aligned the warnings so one can see that the strings are exactly the same)

@timhoffm
Copy link
Member

Why doesn't the error match?

Thematch parameter is interpreted as a regular expression, so you have to escape the brackets.

@danuo
Copy link
ContributorAuthor

Why doesn't the error match?

Thematch parameter is interpreted as a regular expression, so you have to escape the brackets.

@timhoffm Thank you, that worked. Can you also point me in the right direction regarding the failed build of the documentation? I do not find a meaningful error message.

@timhoffm
Copy link
Member

From CI log:

/home/circleci/project/lib/mpl_toolkits/axes_grid1/colorbar.py:docstring of mpl_toolkits.axes_grid1.colorbar.colorbar:68: WARNING: Malformed table.Text in column margin in table line 15.===========   ====================================================Property      Description===========   ====================================================*extend*      [ 'neither' | 'both' | 'min' | 'max' ]              If not 'neither', make pointed end(s) for out-of-              range values.  These are set for a given colormap              using the colormap set_under and set_over methods.*spacing*     [ 'uniform' | 'proportional' ]              Uniform spacing gives each discrete color the same              space; proportional makes the space proportional to              the data interval.*ticks*       [ None | list of ticks | Locator object ]              If None, ticks are determined automatically from the              input.*ticklabels*  sequence of str              List of texts for tick labels; must include values              for non-visible labels.*format*      [ None | format string | Formatter object ]              If None, the              :class:`~matplotlib.ticker.ScalarFormatter` is used.              If a format string is given, e.g., '%.3f', that is              used. An alternative              :class:`~matplotlib.ticker.Formatter` object may be              given instead.*drawedges*   bool              Whether to draw lines at color boundaries.===========   ====================================================

*ticklabels* is one char wider than the column (specified by===========).

@danuo
Copy link
ContributorAuthor

@timhoffm Thank you tim, it's all fixed now.

@danuo
Copy link
ContributorAuthor

@jklymak please review

'length as the ticks.')
# check if objects in list have valid type
elif not all(type(item) in [str, float, int] for item in ticklabels):
cbook._warn_external('ticklabels need to be a list of str')
Copy link
Member

Choose a reason for hiding this comment

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

It would be good to check these warnings and the next one if you have time for that....

Copy link
Member

Choose a reason for hiding this comment

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

Actually, w/o reading the discussion, do we need all these warning checks? I don't think we usually check for mismatched type, for instance...

@timhoffm
Copy link
Member

Given#19016 (comment), I'm wondering if we should accept a dict/list of tuples for theticks parameter instead of a newticklabels parameter. Unfortunately, the above idea is not yet completely thought through.

@jklymak
Copy link
Member

OK, sorry for letting this languish.

This should just pass through tocax._long_axis.set_ticks andcax._long_axis.set_ticklabels. Ifset_ticks ends up with more features, or a different API, this should just inherit that. That is a slight behaviour change, but that is constant with the changes in#18900 which is trying to make colorbars more like normal axes.

@jklymak
Copy link
Member

@danuo, I'm going to close this, because how colorbar axes are handled is pretty different now that#20054 is in. If you wanted to refactor based on master, that would be great. However, you should also simply be able to docb.ax.set_yticklabels like you can for any axes, so perhaps this is not necessary and/or much easier to implement (you just need to determine the long axes and call the set_xticklabels or set_yticklabels.

Thanks for your work on this!

danuo reacted with thumbs up emoji

@jklymakjklymak closed thisJun 3, 2021
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@jklymakjklymakjklymak left review comments

Assignees
No one assigned
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

3 participants
@danuo@timhoffm@jklymak

[8]ページ先頭

©2009-2025 Movatter.jp