Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
MNT: simplify valid tick logic#12158
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
Uh oh!
There was an error while loading.Please reload this page.
Conversation
157b0e1
tof49d364
ComparePing@efiring |
if tick is None: | ||
continue | ||
# NB: always update labels and position to avoid issues like #9397 | ||
tick.update_position(loc) | ||
tick.set_label1(label) | ||
tick.set_label2(label) | ||
if not mtransforms.interval_contains(interval_expanded, loc): | ||
try: | ||
loct = self.get_transform().transform(loc) |
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.
Not sure if it really makes a performance difference, but AFAICStransform()
works on numpy arrays. So this could be pulled out of the loop and calculated in one go. Well, except for theAssertionError
- when does that occur?
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 still have to do the loop, so can probably keep as-is for now.
I think the Assertion Error happened in CI, possibly get_transform returned None for some wacky axis.
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.
Yeah its from the doc build:gallery/scales/custom_scale.py
.
Exception occurred: File "/home/circleci/project/lib/matplotlib/transforms.py", line 1436, in transform assert not np.ma.is_masked(res) # just to be on the safe sideAssertionError
The custom scale uses masks, and transforms.transform doesn't like it.
The previous implementation didn't call the transform, but this one does so that we can do the tolerance checking in log-scale rather than data space.
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.
Are we sure that there are still any ticks on that example when we hit this?
lib/matplotlib/transforms.py Outdated
@@ -2892,7 +2892,7 @@ def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): | |||
return vmin, vmax | |||
def interval_contains(interval, val): | |||
def interval_contains(interval, val, rtol=1e-10): |
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 feel like so as to not break downstream users relying on this being exact, the default should bertol=0
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.
@eric-wieser you probably understand the numerical issues better than me, but is this comparison ever "exact"?
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.
Yes, comparison is always exact. The danger is when you make assumptions about rounding in operations prior to the comparison, but that's for the caller to decide.
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.
OK, done...
e2a1a63
to61ea269
CompareUh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
lib/matplotlib/axis.py Outdated
except AssertionError: | ||
loct = None | ||
continue | ||
if ((loct is None) or |
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.
Cantransform()
returnNone
? Otherwise we do not need theloct is None
check here.
98f1560
to2ee3e6d
Compare2ee3e6d
to3d4976b
Comparelib/matplotlib/transforms.py Outdated
@@ -2906,10 +2906,39 @@ def interval_contains(interval, val): | |||
Returns | |||
------- | |||
bool | |||
Returns true if given val is within the interval. | |||
Returns true if given val is within the interval (with tolerance) |
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 one says nothing about tolerance
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.
Fixed!
Uh oh!
There was an error while loading.Please reload this page.
c6df889
to227bed5
CompareWe should make sure this does not break wcsaxes (attn@astrofrog ) |
lib/matplotlib/axis.py Outdated
# some scales might make them, so we need this try/except. | ||
loct = None | ||
continue | ||
if not mtransforms.interval_contains_close(inter, loct): |
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.
Do you have examples where we needinterval_contains_close
and notinterval_contains
here? Why couldn't thelocators be in charge of making sure whatever they return is in the interval and adjusting the values if needed (except for those outside the interval needed for classic mode)?
I don't really like the idea of hardcoding the 1e-10 tolerance here when it's something that can clearly(?) be avoided.
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.
Hah, we currently have a 1/2-pixel tolerance! Surely1e-10 * interval
is preferable?
The problem with limits is when people compute them.
On my machine:
3.51-1.51 == 2.0FalseIn [17]: abs(3.51-1.51- 2.0) < 1e-10Out[17]: True
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 guess it doesn't make things worse, but at least let's keep interval_contains_close private?
The point is that the Locator API should say something like the following to implementers (including 3rd party locators):
You may return tick values inside or outside of the axis bounds, but any value outside of the bounds (with no tolerance) will be dropped (except in "classic" mode, where we will keep the first outside value on either side).
and the locator classes are the one which should do something like
xmin, xmax = ...ticks = ...if xmin - tol < ticks[0] < xmin: ticks[0] = xmin
(or ticks[1] if that's the one corresponding to xmin).
i.e. the locator displaces the tick to match xmin if it's just below xmin.
But I guess that can wait.
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.
The issue with locators is that some uses want the bracketing ticks, unfortunately, for contour for instance... We could fix/change that as well, but beyond this PR.
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.
Agreed it's beyond this PR.
The design above does not preclude bracketing ticks.
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.
@anntzer did you really wantinterval_contains_close
to be private? I actually think its useful to take into account floating point slop, but don't feel strongly about it.
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.
yes, it feels like a hack while waiting for locators to be fixed, but I'm not going to insist on it.
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.
Okey doke - done!
FIX: make contains_close private
227bed5
to01d7455
Compare
timhoffm left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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 looks ok by itself.
Not merging yet because of@tacaswell's concern with respect to wcsaxes. Can someone confirm, that this is not breaking them?
I'm checking it and will report back shortly |
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 works fine with WCSAxes (all tests, including image tests, pass), in part because we do all the tick drawing ourselves.
timhoffm commentedJan 8, 2019 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@astrofrog Thanks for testing. |
Yes, thanks all! |
Uh oh!
There was an error while loading.Please reload this page.
PR Summary
This is a much scaled back first-step PR for#12105.
The logic for deciding whether or not to draw a tick was rather convoluted, and depended on the size of a pixel. This PR simply changes the test for whether a tick is in the view limit to one that depends on a relative tolerance to take up floating point slack (like
np.allclose
, but for an interval). The relevant code is intransforms.inverval_contains
.See also#11004
This passes all previous testsexcept for the
test_axes/specgram_*
tests.These had a tick at 0 before becuase 0 was within half a pixel of the minimimum view limit of 0.2. Clearly this tick should not be drawn, and the half a pixel slop was far too large. Worse, if the figure was widened in interactive mode, the tick would disappear:
With this PR, the tick is never drawn (because 0<0.2).
Another minimal example (w/o resizing)
Before
100 dpi
200 dpi
After
100 dpi
200 dpi
As above...
PR Checklist