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

Proposed change to default log scale tick formatting #5161

Merged
merged 9 commits into from Aug 22, 2016

Conversation

zblz
Copy link
Member

@zblz zblz commented Oct 1, 2015

Following #4730 and #4867, this is a proposal to change the defaults for the upcoming 2.0 release regarding the default tick label formatting for log-scaled axes. This is not a finished PR, but mean to initiate discussion on whether this is desired as default (if not I still think a similar thing could be worth having as an option).

  • A new default formatter (LogFormatterSciNotation) is used as default for all log-scaled axes. This formatter always prints all labels for the bases of the log scale (major ticks) and adds labels to the minor ticks as the axis view limits are made smaller (with a similar logic to LogLocator with subs=None):
    • For less than 3 decades, (1, 3) * base**i are labeled.
    • For less than 2 decades, (1, 2, 5) * base**i are labeled.
    • For less than 1 decade, (1, 2, 4, 7) * base**i are labeled.
  • The minor ticks are formatted in scientific notation: $ 3 \times 10^{-2} $
  • The base and exponent are omitted if the exponent is between -1 and 1: i.e., 0.1, 0.7, 1, 8, will be formatted as shown (as in [WIP] Proposed improvement in default log formatting #4730, this could be split as an option for all LogFormatter).

Example of this formatter in action:
log_labels

The main motivation is to improve readability of the axis ticks when log-scaled axis are zoomed-in to less than a few decades, which under the current Formatters leaves only very few labels in the axis. Note that this does not change the formatting whenever more than 3 decades are spanned by the axis view limits.

@jenshnielsen jenshnielsen added this to the next major release (2.0) milestone Oct 1, 2015
@mdboom
Copy link
Member

mdboom commented Oct 6, 2015

It seems like you've combined the logic of whether to display a tick (which is usually the job of a Locator subclass) with the logic of how to display a tick (which is usually the job of a Formatter subclass). Any reason why you didn't separate concerns like all the other Locator/Formatters?

Other than that, I see this as very useful, though we'll have to determine whether or not it's a good idea to change the default behavior.

@zblz
Copy link
Member Author

zblz commented Oct 6, 2015

@mdboom: For the case of log-scaled axis, it is nice to show all minor ticks to make it clear that the scaling is logarithmic, but we obviously do not want to label all minor ticks. The current default is show all minor ticks and label none. I had to combine them so that all minor ticks are shown, but not all of them labeled. I understand its a departure from how other Locator/Formatter work, but I did not see a different way to do it.

EDIT: I could not find it, but is there any other Locator/Formatter where this is done (i.e., only some of the shown minor or major ticks are labeled)?

@mdboom
Copy link
Member

mdboom commented Oct 6, 2015

@zblz: I understand the problem better now. I think it could lead to confusion that the formatter chooses not to format some ticks, particularly if using an explicit locator or something. However, as you say, I can't think of a better way right now. That's all the more argument to keep the default for log scales as something that formats everything. Let me think on it for a bit -- maybe there is a better way, perhaps by adding the ability to have separate locators for ticks and text...

@zblz
Copy link
Member Author

zblz commented Oct 6, 2015

Separate locators would be ideal for this case, but it would increase the complexity of the tick logic in Axis. In addition, the process to change tick format and location is not the easiest thing for a user (needing additional imports from ticker, and discovering the names and options of the Locator/Formatter a bit difficult), so adding another Locator might complicate things even further. I don't really have a better option, though.

By the way, I adapted this logic of not showing some labels from the only way I knew how to produce this previously, which was to use horrible, horrible ad-hoc labels like this:

ax.xaxis.set_ticklabels([r'$2\times10^4$','','',r'$5\times10^4$','','','','', 
        r'$2\times 10^4$','','',r'$5\times10^4$','','','','',
        r'$2\times 10^5$','','',r'$5\times10^5$','','','','',
        ],minor=True)

@WeatherGod
Copy link
Member

What if a locator could signal whether the location is intended for
tickline, ticklabel, or both (default)?

On Tue, Oct 6, 2015 at 1:58 PM, Victor Zabalza notifications@github.com
wrote:

Separate locators would be ideal for this case, but it would increase the
complexity of the tick logic in Axis and might be quite confusing for the
user. In addition, the process to change tick format and location is not
the easiest thing for a user (needing additional imports from ticker, and
discovering the names and options of the Locator/Formatter a bit
difficult), so adding another Locator might complicate things even further.
I don't really have a better option, though.


Reply to this email directly or view it on GitHub
#5161 (comment)
.

@mdboom
Copy link
Member

mdboom commented Oct 6, 2015

@WeatherGod: I like that idea.

@zblz
Copy link
Member Author

zblz commented Oct 6, 2015

@WeatherGod: It sounds good, but I have no idea of how to even begin to implement that from a glance at ticker.py and axis.py. In Axis all ticks have an associated location and label, so if we dissociate the tick location and the label location we might need a lot of refactoring.

@WeatherGod
Copy link
Member

If one works from the assumption that all ticklabels must have a tickline,
then it is possible to simply have a blank ticklabel where a location is
only intended for a tickline. Then, it just becomes a matter of how to
signal that from the locator to Axis. That is the hard part, but probably
doable.

On Tue, Oct 6, 2015 at 2:15 PM, Victor Zabalza notifications@github.com
wrote:

@WeatherGod https://github.com/WeatherGod: It sounds good, but I have
no idea of how to even begin to implement that from a glance at ticker.py
and axis.py. In Axis all ticks have an associated location and label, so
if we dissociate the tick location and the label location we might need a
lot of refactoring.


Reply to this email directly or view it on GitHub
#5161 (comment)
.

@tacaswell
Copy link
Member

The Formatter objects are called with the signature (tick_location, tick_number) where iirc tick_number is either None or the integer count of ticks along the axis (with possibly some off-by-one bugs due to a clipped left most tick). It may be possible to write a clever enough Formatter to take this into account.

@zblz
Copy link
Member Author

zblz commented Oct 6, 2015

@tacaswell: But if whether we return a label depends on tick_number then we have the same problem as looking at the parent axis: the decision of whether to show a label is left to the Formatter rather than the Locator, which is what @mdboom objected to initially.

@zblz
Copy link
Member Author

zblz commented Oct 7, 2015

Ok, I tried to move the print logic from the formatter to the locator following @WeatherGod's suggestion, and now the locator must return two arrays: the location for the ticks and whether each tick should be labeled. This means a change to all the __call__ functions of the locators: I have modified it only for LogLocator as a proof-of-concept, but they could also be wrapped so that if they only return one array, the second defaults to all True.

@tacaswell tacaswell modified the milestones: proposed next point release (2.1), next major release (2.0) Oct 7, 2015
@tacaswell
Copy link
Member

re-milestoned this for 2.1 as this is not just changing some parameters.

@zblz
Copy link
Member Author

zblz commented Oct 7, 2015

Change of mind right after pushing: I moved the logic on whether to show the labels to an different function than the __call__ so that this new function (show_tick_label) can be defined in the parent Locator class. In this way, locator classes that want labels on all their ticks don't have to be modified.

@zblz
Copy link
Member Author

zblz commented Oct 8, 2015

To keep the focus of this PR, I have removed the functionality to plot (1e-1, 1e0, 1e1) as 0.1 1 10, because this should be covered by #4730 in LogFormatterMathtext. This allows LogFormatterSciNotation to be a subclass of LogFormatterMathtext with only the non-decade ticks formatted differently (and any changes to LogFormatterMathtext in #4730 will propagate here).

@mdboom
Copy link
Member

mdboom commented Oct 9, 2015

Thanks. I think the separation of Locator/Formatter is much better now. One further refinement though -- I don't think we necessarily require that a Locator inherits from LocatorBase (matplotlib's built-in ones all do, but third-party ones may not). So we should be robust to the possibility that show_tick_label may not be present and handle the default behavior accordingly.

@mdboom
Copy link
Member

mdboom commented Oct 15, 2015

Thanks. I'm happy to merge this once the test failure is resolved.

@zblz zblz force-pushed the log-ticks branch 2 times, most recently from 03aa5bc to 4c2f76e Compare October 15, 2015 18:18
@efiring
Copy link
Member

efiring commented Oct 15, 2015

  1. I presume this needs a "what's new" entry.
  2. Is this still a default change, or is it now just a new Formatter that one can choose? I think it is the latter. Can the PR title be changed accordingly, before or after a merge?
  3. I can see the usefulness of this, particularly for interactive use when zooming, as noted at the start of the PR, but it could also cause clutter and messiness, particularly when using multiple subplots. One thing that might help would be to make the thresholds for labeling minor ticks into settable parameters.
    Suggestion 3 need not hold up this PR; it could be a future improvement, if needed. I suspect it would fit in with the move to traitlets.

@zblz
Copy link
Member Author

zblz commented Oct 16, 2015

@efiring:

  1. It is both a new Formatter and a change in how Locators can specify where to put labels. I think it would make a good default, but if you think it should be considered separately, I can make this PR only about adding the functionality and another PR about whether to make it default (i.e., chaning the default minor tick formatter from NullFormatter to LogFormatterSciNotation) when setting a log scale.
  2. Yes, it does add a bit of horizontal width to the y-axis labels as compared to labeling only the bases, so could be a problem in close multiple plots. It can always be avoided by setting the minor formatter to NullFormatter.

@mdboom
Copy link
Member

mdboom commented Oct 19, 2015

We could consider the default change as part of 2.0. Not promising anything, but that's a good window of opportunity for it.

@anntzer
Copy link
Contributor

anntzer commented Jun 26, 2016

I would like to suggest re-milestoning this (or the original issue, #4867), for 2.0. When round-number limits were the default, a log-scale axis would by default be expanded to initially cover at least one (integer) decade, and thus have at least two labeled ticks (at the two ends). With the switch to margins-based limits, it is now possible for axes to default to having no labels at all (e.g. semilogy([.2, .9])) which is (IMO) very bad.

Edit: Changed the milestone, but feel free to discuss if you disagree.

@anntzer anntzer added this to the 2.0 (style change major release) milestone Jun 27, 2016
@zblz
Copy link
Member Author

zblz commented Aug 2, 2016

I have moved the logic back into the formatter classes, choosing to add it into LogFormatter rather than LogFormatterSciNotation as all log formatters could benefit from it (note though that all of them except for LogFormatterSciNotation have labelOnlyBase=True as default, so this change will not affect them if used with default parameters).

I have also added back LogFormatterSciNotation as the default formatter for log-scaled axes.

@WeatherGod
Copy link
Member

Getting a bunch of errors. Somehow, zeros are getting down into math.log(). Got some image comparison failures, too.

@zblz
Copy link
Member Author

zblz commented Aug 3, 2016

I have fixed the error on symlog axes, which where sending values vmin <= 0 to math.log. I also updated the baseline images for the test_axes.log_scale test.

@zblz
Copy link
Member Author

zblz commented Aug 3, 2016

The remaining error on matplotlib.tests.test_backend_ps.test_savefig_to_stringio_eps_afm will be fixed by PR #6898, so that should be merged before this PR.

@WeatherGod
Copy link
Member

power-cycling to test against the latest master.

@WeatherGod
Copy link
Member

This is passing and is milestoned for v2.0. I don't know who is the one to approve the remaining style changes.

@tacaswell tacaswell merged commit 947e6eb into matplotlib:master Aug 22, 2016
tacaswell added a commit that referenced this pull request Aug 22, 2016
API: Changes to default log scale tick formatting
@tacaswell
Copy link
Member

backported to v2.x as fb45c4a

tacaswell added a commit to tacaswell/matplotlib that referenced this pull request Aug 29, 2016
Reverts part of matplotlib#5161

original commit merged to master as 947e6eb
the merge was backported to v2.x as fb45c4a
efiring added a commit to efiring/matplotlib that referenced this pull request Nov 7, 2016
This partly restores the functionality that was added in
PR matplotlib#5161 and partly removed in matplotlib#7000.  The "partly" is because
now the labeling of minor log ticks is turned on only when
numdecs (the axis range in powers of the log base) is less
than or equal to one, rather than 3.

This also fixes a bug that was causing double labeling with a
base of 2; minor ticks were coinciding with major ticks, and
both were being labeled.
@QuLogic QuLogic changed the title [WIP] Proposed change to default log scale tick formatting Proposed change to default log scale tick formatting Jan 26, 2017
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.

None yet

7 participants