diff --git a/doc/api/api_changes.rst b/doc/api/api_changes.rst index 790a56cb63a6..9e1c267f9c77 100644 --- a/doc/api/api_changes.rst +++ b/doc/api/api_changes.rst @@ -10,9 +10,24 @@ out what caused the breakage and how to fix it by updating your code. For new features that were added to Matplotlib, please see :ref:`whats-new`. +API Changes in 2.1.1 +==================== + +Default behavior of log scales reverted to clip <= 0 values +----------------------------------------------------------- + +The change it 2.1.0 to mask in logscale by default had more disruptive +changes than anticipated and has been reverted, however the clipping is now +done in a way that fixes the issues that motivated changing the default behavior +to ``'mask'``. + +As a side effect of this change, error bars which go negative now work as expected +on log scales. + API Changes in 2.1.0 ==================== + Default behavior of log scales changed to mask <= 0 values ---------------------------------------------------------- diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 2275361aa17c..69d21b1ec732 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -1556,11 +1556,9 @@ def loglog(self, *args, **kwargs): dx = {'basex': kwargs.pop('basex', 10), 'subsx': kwargs.pop('subsx', None), - 'nonposx': kwargs.pop('nonposx', 'mask'), } dy = {'basey': kwargs.pop('basey', 10), 'subsy': kwargs.pop('subsy', None), - 'nonposy': kwargs.pop('nonposy', 'mask'), } self.set_xscale('log', **dx) @@ -2851,11 +2849,6 @@ def errorbar(self, x, y, yerr=None, xerr=None, Valid kwargs for the marker properties are %(Line2D)s - - Notes - ----- - Error bars with negative values will not be shown when plotted on a - logarithmic axis. """ kwargs = cbook.normalize_kwargs(kwargs, _alias_map) # anything that comes in as 'None', drop so the default thing diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 00cc9ee7aa4a..ab07179ba0f2 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2970,11 +2970,6 @@ def set_xscale(self, value, **kwargs): matplotlib.scale.LogisticTransform : logit transform """ - # If the scale is being set to log, mask nonposx to prevent headaches - # around zero - if value.lower() == 'log' and 'nonposx' not in kwargs: - kwargs['nonposx'] = 'mask' - g = self.get_shared_x_axes() for ax in g.get_siblings(self): ax.xaxis._set_scale(value, **kwargs) @@ -3292,11 +3287,6 @@ def set_yscale(self, value, **kwargs): matplotlib.scale.LogisticTransform : logit transform """ - # If the scale is being set to log, mask nonposy to prevent headaches - # around zero - if value.lower() == 'log' and 'nonposy' not in kwargs: - kwargs['nonposy'] = 'mask' - g = self.get_shared_y_axes() for ax in g.get_siblings(self): ax.yaxis._set_scale(value, **kwargs) diff --git a/lib/matplotlib/scale.py b/lib/matplotlib/scale.py index 7b8f224ee221..459c1c0326c5 100644 --- a/lib/matplotlib/scale.py +++ b/lib/matplotlib/scale.py @@ -92,15 +92,24 @@ class LogTransformBase(Transform): def __init__(self, nonpos): Transform.__init__(self) - if nonpos == 'mask': - self._fill_value = np.nan - else: - self._fill_value = 1e-300 + self._clip = {"clip": True, "mask": False}[nonpos] def transform_non_affine(self, a): - with np.errstate(invalid="ignore"): - a = np.where(a <= 0, self._fill_value, a) - return np.divide(np.log(a, out=a), np.log(self.base), out=a) + with np.errstate(divide="ignore", invalid="ignore"): + out = np.log(a) + out /= np.log(self.base) + if self._clip: + # SVG spec says that conforming viewers must support values up + # to 3.4e38 (C float); however experiments suggest that Inkscape + # (which uses cairo for rendering) runs into cairo's 24-bit limit + # (which is apparently shared by Agg). + # Ghostscript (used for pdf rendering appears to overflow even + # earlier, with the max value around 2 ** 15 for the tests to pass. + # On the other hand, in practice, we want to clip beyond + # np.log10(np.nextafter(0, 1)) ~ -323 + # so 1000 seems safe. + out[a <= 0] = -1000 + return out class InvertedLogTransformBase(Transform): @@ -220,11 +229,17 @@ def __init__(self, axis, **kwargs): if axis.axis_name == 'x': base = kwargs.pop('basex', 10.0) subs = kwargs.pop('subsx', None) - nonpos = kwargs.pop('nonposx', 'mask') + nonpos = kwargs.pop('nonposx', 'clip') else: base = kwargs.pop('basey', 10.0) subs = kwargs.pop('subsy', None) - nonpos = kwargs.pop('nonposy', 'mask') + nonpos = kwargs.pop('nonposy', 'clip') + + if len(kwargs): + raise ValueError(("provided too many kwargs, can only pass " + "{'basex', 'subsx', nonposx'} or " + "{'basey', 'subsy', nonposy'}. You passed ") + + "{!r}".format(kwargs)) if nonpos not in ['mask', 'clip']: raise ValueError("nonposx, nonposy kwarg must be 'mask' or 'clip'") @@ -432,18 +447,17 @@ class LogitTransform(Transform): def __init__(self, nonpos): Transform.__init__(self) - if nonpos == 'mask': - self._fill_value = np.nan - else: - self._fill_value = 1e-300 self._nonpos = nonpos + self._clip = {"clip": True, "mask": False}[nonpos] def transform_non_affine(self, a): """logit transform (base 10), masked or clipped""" - with np.errstate(invalid="ignore"): - a = np.select( - [a <= 0, a >= 1], [self._fill_value, 1 - self._fill_value], a) - return np.log10(a / (1 - a)) + with np.errstate(divide="ignore", invalid="ignore"): + out = np.log10(a / (1 - a)) + if self._clip: # See LogTransform for choice of clip value. + out[a <= 0] = -1000 + out[1 <= a] = 1000 + return out def inverted(self): return LogisticTransform(self._nonpos) diff --git a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png index 9e68e62fd75d..e305de1d9ac7 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png and b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg index 596ba00f9b67..0a29c9d0af21 100644 --- a/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg +++ b/lib/matplotlib/tests/baseline_images/test_axes/log_scales.svg @@ -27,7 +27,8 @@ z " style="fill:#ffffff;"/> - pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy