From 668ff586c12a3c577ddd985a375030381e80539c Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 25 Oct 2017 13:57:47 -0400 Subject: [PATCH] Backport PR #9477: In LogTransform, clip after log, not before. --- doc/api/api_changes.rst | 15 ++++++ lib/matplotlib/axes/_axes.py | 7 --- lib/matplotlib/axes/_base.py | 10 ---- lib/matplotlib/scale.py | 48 +++++++++++------- .../baseline_images/test_axes/log_scales.png | Bin 13712 -> 10312 bytes .../baseline_images/test_axes/log_scales.svg | 3 +- .../test_path/semi_log_with_zero.png | Bin 29487 -> 24929 bytes .../test_scale/logscale_mask.png | Bin 12163 -> 9503 bytes .../test_scale/logscale_nonpos_values.png | Bin 0 -> 17034 bytes lib/matplotlib/tests/test_path.py | 6 ++- lib/matplotlib/tests/test_scale.py | 34 +++++++++++++ 11 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_scale/logscale_nonpos_values.png 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 c49327a58723..4064b339016f 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 87a9b08f08d1..5c3ca1ceec1d 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 9e68e62fd75dd87bffee3f4ab5d97f63fcaa2f09..e305de1d9ac7b55b59b28fa5f8d7d36e191619bb 100644 GIT binary patch literal 10312 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 literal 13712 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 - 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