-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Non-sensical negative radial scale minimum autoset in polar plot #13292
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
Comments
I agree the behavior is less than optimal. Perhaps polar plots should just be created with 0 as explicit lower r-lim? (I think negative r-lims should be explicitly requested.) |
I think the issue here is the autoscaling is including a bit of the plot with |
Unfortunately, I think right now there's no mechanism to disable autoscaling on one of the two bounds (r=0) while keeping it on the other (upper r bound) :/ |
Can it be handled in |
I doubt so. In fact, Locator.autoscale() seems completely unused right now (since 5964da2, hey you wrote that :)). (Aside re: autoscale(): it seems like it got superseded by view_limits() (for "round" autoscaling mode), except that dates.py has never been updated so date locators still define the unused autoscale() but don't define view_limits()?) However, the use of sticky_edges in #13444 gave me another idea: we can slightly change the semantics of sticky edges to mean "an autoscale call cannot move a limit beyond a sticky edge through margins application" (including when the limit is already touching the sticky edge, which is the case in all use cases so far), then keep the sticky edge at zero and set the lower datalim to zero. |
Looks like the following patch implements the strategy above (of slightly changing the semantics of sticky_edges to fix this issue): diff --git i/lib/matplotlib/axes/_base.py w/lib/matplotlib/axes/_base.py
index 9515e03e5..8d02a0e68 100644
--- i/lib/matplotlib/axes/_base.py
+++ w/lib/matplotlib/axes/_base.py
@@ -2387,8 +2387,8 @@ class _AxesBase(martist.Artist):
(self._xmargin and scalex and self._autoscaleXon) or
(self._ymargin and scaley and self._autoscaleYon)):
stickies = [artist.sticky_edges for artist in self.get_children()]
- x_stickies = np.array([x for sticky in stickies for x in sticky.x])
- y_stickies = np.array([y for sticky in stickies for y in sticky.y])
+ x_stickies = np.sort([x for sticky in stickies for x in sticky.x])
+ y_stickies = np.sort([y for sticky in stickies for y in sticky.y])
if self.get_xscale().lower() == 'log':
x_stickies = x_stickies[x_stickies > 0]
if self.get_yscale().lower() == 'log':
@@ -2421,7 +2421,7 @@ class _AxesBase(martist.Artist):
dl.extend(y_finite)
bb = mtransforms.BboxBase.union(dl)
- x0, x1 = getattr(bb, interval)
+ x0orig, x1orig = x0, x1 = getattr(bb, interval)
locator = axis.get_major_locator()
x0, x1 = locator.nonsingular(x0, x1)
@@ -2430,10 +2430,6 @@ class _AxesBase(martist.Artist):
minpos = getattr(bb, minpos)
transform = axis.get_transform()
inverse_trans = transform.inverted()
- # We cannot use exact equality due to floating point issues e.g.
- # with streamplot.
- do_lower_margin = not np.any(np.isclose(x0, stickies))
- do_upper_margin = not np.any(np.isclose(x1, stickies))
x0, x1 = axis._scale.limit_range_for_scale(x0, x1, minpos)
x0t, x1t = transform.transform([x0, x1])
@@ -2443,12 +2439,23 @@ class _AxesBase(martist.Artist):
# If at least one bound isn't finite, set margin to zero
delta = 0
- if do_lower_margin:
- x0t -= delta
- if do_upper_margin:
- x1t += delta
+ x0t -= delta
+ x1t += delta
+
x0, x1 = inverse_trans.transform([x0t, x1t])
+ # We cannot use exact equality due to floating point issues e.g.
+ # with streamplot. The tolerances come from isclose().
+ stickies_minus_tol = stickies - 1e-5 * np.abs(stickies) - 1e-8
+ stickies_plus_tol = stickies + 1e-5 * np.abs(stickies) + 1e-8
+
+ i0orig, i0 = stickies_minus_tol.searchsorted([x0orig, x0])
+ if i0orig != i0: # Crossed a sticky boundary.
+ x0 = stickies[i0orig - 1] # Go back to sticky boundary.
+ i1orig, i1 = stickies_plus_tol.searchsorted([x1orig, x1])
+ if i1orig != i1:
+ x1 = stickies[i1orig]
+
if not self._tight:
x0, x1 = locator.view_limits(x0, x1)
set_bound(x0, x1) Haven't tested if this breaks other stuff. Also needs changelog. |
Uh oh!
There was an error while loading. Please reload this page.
When plotting a set of data on a polar plot, the default bottom y_limit might not be zero unexpectedly from the perspective of the user, resulting in confusion about the meaning of the plot, especially for a person (like me) unfamiliar with the concept of a polar plot where r=0 is not at the very center point of the plot.
In a Jupyter Notebook
I ran across this when plotting data and wondering if I had a bug in my analysis somewhere that was giving me a hole around the origin. It took me some time to figure out that the problem was simply in the axis scaling as I expected the version on the left (which seems sensible to me as the default) and not the version on the right which has a hole in the middle.
Matplotlib version
print(matplotlib.get_backend())
): inlineThe text was updated successfully, but these errors were encountered: