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

Skip to content

Commit 33efc48

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".
1 parent a5c0164 commit 33efc48

File tree

3 files changed

+24
-28
lines changed

3 files changed

+24
-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: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,19 @@ 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). So pick a smaller value.
106+
out[a <= 0] = -2 ** 23
107+
return out
104108

105109

106110
class InvertedLogTransformBase(Transform):
@@ -432,18 +436,17 @@ class LogitTransform(Transform):
432436

433437
def __init__(self, nonpos):
434438
Transform.__init__(self)
435-
if nonpos == 'mask':
436-
self._fill_value = np.nan
437-
else:
438-
self._fill_value = 1e-300
439439
self._nonpos = nonpos
440+
self._clip = {"clip": True, "mask": False}[nonpos]
440441

441442
def transform_non_affine(self, a):
442443
"""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))
444+
with np.errstate(divide="ignore", invalid="ignore"):
445+
out = np.log10(a / (1 - a))
446+
if self._clip: # See LogTransform for choice of clip value.
447+
out[a <= 0] = -2 ** 23
448+
out[1 <= a] = 2 ** 23
449+
return out
447450

448451
def inverted(self):
449452
return LogisticTransform(self._nonpos)

0 commit comments

Comments
 (0)