Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0f89c30

Browse files
committed
In LogTransform, clip after log, not before.
When clipping before taking the log, the clip value in the output array cannot be beyond log(epsilon) ~ -300. When clipping after taking the log, the clip value can be much further (see comments regarding choice of value). This allows setting the default nonpos mode back to "clip". Some test images had to be updated as this effectively moves the position of the "clip point", slightly changing the slope of the line that goes to that point and thus the rasterization (by a tiny amount).
1 parent a5c0164 commit 0f89c30

File tree

5 files changed

+29
-28
lines changed

5 files changed

+29
-28
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,7 @@ def semilogx(self, *args, **kwargs):
16181618
self.cla()
16191619
d = {'basex': kwargs.pop('basex', 10),
16201620
'subsx': kwargs.pop('subsx', None),
1621+
'nonposx': kwargs.pop('nonposx', 'clip'),
16211622
}
16221623

16231624
self.set_xscale('log', **d)
@@ -1673,6 +1674,7 @@ def semilogy(self, *args, **kwargs):
16731674
self.cla()
16741675
d = {'basey': kwargs.pop('basey', 10),
16751676
'subsy': kwargs.pop('subsy', None),
1677+
'nonposy': kwargs.pop('nonposy', 'mask'),
16761678
}
16771679
self.set_yscale('log', **d)
16781680
b = self._hold
@@ -2851,11 +2853,6 @@ def errorbar(self, x, y, yerr=None, xerr=None,
28512853
Valid kwargs for the marker properties are
28522854
28532855
%(Line2D)s
2854-
2855-
Notes
2856-
-----
2857-
Error bars with negative values will not be shown when plotted on a
2858-
logarithmic axis.
28592856
"""
28602857
kwargs = cbook.normalize_kwargs(kwargs, _alias_map)
28612858
# anything that comes in as 'None', drop so the default thing

lib/matplotlib/axes/_base.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2970,10 +2970,8 @@ def set_xscale(self, value, **kwargs):
29702970
29712971
matplotlib.scale.LogisticTransform : logit transform
29722972
"""
2973-
# If the scale is being set to log, mask nonposx to prevent headaches
2974-
# around zero
2975-
if value.lower() == 'log' and 'nonposx' not in kwargs:
2976-
kwargs['nonposx'] = 'mask'
2973+
if value.lower() == "log": # Avoid headaches around zero.
2974+
kwargs.setdefault("nonposx", "clip")
29772975

29782976
g = self.get_shared_x_axes()
29792977
for ax in g.get_siblings(self):
@@ -3292,10 +3290,8 @@ def set_yscale(self, value, **kwargs):
32923290
32933291
matplotlib.scale.LogisticTransform : logit transform
32943292
"""
3295-
# If the scale is being set to log, mask nonposy to prevent headaches
3296-
# around zero
3297-
if value.lower() == 'log' and 'nonposy' not in kwargs:
3298-
kwargs['nonposy'] = 'mask'
3293+
if value.lower() == "log": # Avoid headaches around zero.
3294+
kwargs.setdefault("nonposy", "clip")
32993295

33003296
g = self.get_shared_y_axes()
33013297
for ax in g.get_siblings(self):

lib/matplotlib/scale.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,24 @@ class LogTransformBase(Transform):
9292

9393
def __init__(self, nonpos):
9494
Transform.__init__(self)
95-
if nonpos == 'mask':
96-
self._fill_value = np.nan
97-
else:
98-
self._fill_value = 1e-300
95+
self._clip = {"clip": True, "mask": False}[nonpos]
9996

10097
def transform_non_affine(self, a):
101-
with np.errstate(invalid="ignore"):
102-
a = np.where(a <= 0, self._fill_value, a)
103-
return np.divide(np.log(a, out=a), np.log(self.base), out=a)
98+
with np.errstate(divide="ignore", invalid="ignore"):
99+
out = np.log(a)
100+
out /= np.log(self.base)
101+
if self._clip:
102+
# SVG spec says that conforming viewers must support values up
103+
# to 3.4e38 (C float); however experiments suggest that Inkscape
104+
# (which uses cairo for rendering) runs into cairo's 24-bit limit
105+
# (which is apparently shared by Agg).
106+
# Ghostscript (used for pdf rendering appears to overflow even
107+
# earlier, with the max value around 2 ** 15 for the tests to pass.
108+
# On the other hand, in practice, we want to clip beyond
109+
# np.log10(np.nextafter(0, 1)) ~ -323
110+
# so 1000 seems safe.
111+
out[a <= 0] = -1000
112+
return out
104113

105114

106115
class InvertedLogTransformBase(Transform):
@@ -432,18 +441,17 @@ class LogitTransform(Transform):
432441

433442
def __init__(self, nonpos):
434443
Transform.__init__(self)
435-
if nonpos == 'mask':
436-
self._fill_value = np.nan
437-
else:
438-
self._fill_value = 1e-300
439444
self._nonpos = nonpos
445+
self._clip = {"clip": True, "mask": False}[nonpos]
440446

441447
def transform_non_affine(self, a):
442448
"""logit transform (base 10), masked or clipped"""
443-
with np.errstate(invalid="ignore"):
444-
a = np.select(
445-
[a <= 0, a >= 1], [self._fill_value, 1 - self._fill_value], a)
446-
return np.log10(a / (1 - a))
449+
with np.errstate(divide="ignore", invalid="ignore"):
450+
out = np.log10(a / (1 - a))
451+
if self._clip: # See LogTransform for choice of clip value.
452+
out[a <= 0] = -1000
453+
out[1 <= a] = 1000
454+
return out
447455

448456
def inverted(self):
449457
return LogisticTransform(self._nonpos)

0 commit comments

Comments
 (0)