Skip to content

Specify custom tick space heuristic in MaxNLocator #11027

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

Closed
wants to merge 1 commit into from

Conversation

egpbos
Copy link
Contributor

@egpbos egpbos commented Apr 11, 2018

PR Summary

Following discussion in #11025, I added an optional argument to MaxNLocator (and hence AutoLocator) to specify a custom heuristic for spacing ticks. It allows people to experiment with their own spacing rules without having to change Matplotlib source code. This way, one can benefit from the automatic spacing introduced in Matplotlib 2 (if I understand correctly), but still customize it if deemed necessary (which was the case for me as discussed in #11025).

PR Checklist

  • Has Pytest style unit tests
  • Code is PEP 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

Copy link
Member

@jklymak jklymak left a comment

Choose a reason for hiding this comment

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

I'm 50/50 on the utility of this, and dubious about adding another kwarg given that the new kwarg is only used if the nbins = 'auto'.

I think this is a pretty obscure use case, but if you think its important, it will only get used if there is an example somewhere...

@@ -1821,6 +1822,12 @@ def __init__(self, *args, **kwargs):
Relax `nbins` and `integer` constraints if necessary to
obtain this minimum number of ticks.

*tick_space_heuristic*
Copy link
Member

Choose a reason for hiding this comment

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

I'd suggest that this just be another option for nbins. i.e. if nbins is callable it is used to determine the heuristic...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a great idea!

@@ -1882,11 +1889,17 @@ def set_params(self, **kwargs):
self._extended_steps = self._staircase(self._steps)
if 'integer' in kwargs:
self._integer = kwargs['integer']
if 'tick_space_heuristic' in kwargs:
tick_space_heuristic = kwargs['tick_space_heuristic']
Copy link
Member

Choose a reason for hiding this comment

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

I think we just use kwarg.pop these days...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's fine by me. I was just following the style used in the rest of the set_params function. Do you think I should convert the entire function then? Seems like something for a different PR, but I don't mind doing it here.

@@ -1760,6 +1760,11 @@ def get_minpos(self):
raise NotImplementedError()


def tick_space_heuristic(axis_length, label_fontsize, label_fontsize_factor):
Copy link
Member

Choose a reason for hiding this comment

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

This should be private I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean prepend the function with an underscore?

Copy link
Member

Choose a reason for hiding this comment

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

Yes.

@tacaswell
Copy link
Member

I am 👎 on this going in as-is. My primary concern is expanding the public API of the Axis classes to serve the needs of one of the Locator classes. Even if I were convinced that adding an extra coupling between the locator and the ticker was the right thing to do, I am not sure that this is the right path. It might be better to ex add a method to the Axis that returns both the length of the axis, the font-size, and the orientation of the font relative to the axis (parallel, particularize, or rotated). If I were convinced that this is a good idea and this is the right API to add, it will need to be much better documented and motivated.

I think a better course path here would be to make it easier to set the default XAxis, YAxis, and tick-related classes used by Matplotlib (it looks like you can currently do it by over-riding a private method on Axes to use your prefered classes and then re-registering your new class Axes class with the projection framework, yikes!).

attn @timhoffm for more ticker-related issues.

Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

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

See my comment for details, concerned about expanding the public API in this way.

@jklymak
Copy link
Member

jklymak commented Apr 11, 2018

@tacaswell. The horse is already out of the barn on that one; axis.get_tick_spacing predates this PR and is explicitly used for MaxNLpcator as far as I can tell

@jklymak jklymak added this to the v3.0 milestone Apr 11, 2018
@egpbos
Copy link
Contributor Author

egpbos commented Apr 12, 2018

@jklymak

I think this is a pretty obscure use case, but if you think its important, it will only get used if there is an example somewhere...

Following #11025 (comment), I can make an example using AxesGrid that shows the problem I have with the ticks and compares to a plot using the custom tick heuristic to make it look nicer.

@egpbos
Copy link
Contributor Author

egpbos commented Apr 12, 2018

@tacaswell: regarding extending public API, what if I follow @jklymak's suggestion of using the nbins parameter (check if it's callable) instead of adding a new parameter to MaxNLocator, would that be acceptable?

As @jklymak said, the coupling between X/YAxis and MaxNLocator was already there, I just added the heuristic parameter as a way to generalize/abstract this method. I can hide that into a private attribute of the Axis if you prefer.

The problem I see with setting different defaults (which I agree would be nicer) is that there is currently no option among the existing Locators to do something that acts like nbins='auto', which is desirable behavior, but with a different way of automatically estimating the tick label size in order to prevent overlapping labels, as the current hard-coded heuristic does, but, imho, does in a way that makes the axis look ugly when the labels are so small, i.e. have an aspect ratio more like 1:1 instead of the 3:1 that the XAxis heuristic assumes. So, since there is currently no such customization option, setting different defaults would still not be sufficient.

@tacaswell
Copy link
Member

We discussed this on the weekly phone call and came to the following suggestion:

  • deprecated get_tick_space
  • replace it with _get_axis_length_and_font_size (name negotiable, maybe make this public?)
  • in the locator look at the Axis object and axis.axis_name to decide what ratio to use and then you can adjust that ratio by sub-classing the Locator and setting your locator onto the axis or possible some API on the Locator.

Notes are at https://paper.dropbox.com/doc/Matplotlib-meeting-agenda-aAmENlkgepgsMeDZtlsYu

@jklymak
Copy link
Member

jklymak commented May 9, 2018

ping @egpbos are you still interested in keeping this alive given the feedback above?

@egpbos
Copy link
Contributor Author

egpbos commented May 14, 2018

@jklymak I'm interested, but currently I have some other priorities, so won't get to it any time soon.

@tacaswell tacaswell modified the milestones: v3.0, v3.1 Jul 7, 2018
@jklymak
Copy link
Member

jklymak commented Feb 7, 2019

OK, I'm going to close this as not having enough support. Maybe something like this can go in an example showing how to subclass MaxNLocator?

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.

3 participants