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

Skip to content

Commit 73ae1d3

Browse files
committed
Re-write sym-log-norm
1 parent b47fc9d commit 73ae1d3

File tree

2 files changed

+40
-38
lines changed

2 files changed

+40
-38
lines changed

lib/matplotlib/colors.py

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,9 +1231,10 @@ def __init__(self, linthresh, linscale=1.0,
12311231
"""
12321232
Normalize.__init__(self, vmin, vmax, clip)
12331233
self.linthresh = float(linthresh)
1234-
self._linscale_adj = (linscale / (1.0 - np.e ** -1))
1235-
if vmin is not None and vmax is not None:
1236-
self._transform_vmin_vmax()
1234+
# Number of decades in the log region
1235+
ndec = np.log10(vmax / linthresh)
1236+
# Size of the linear region from 0 to linthresh in the transformed space
1237+
self.linear_size = 1 / (1 + linscale / ndec)
12371238

12381239
def __call__(self, value, clip=None):
12391240
if clip is None:
@@ -1254,57 +1255,52 @@ def __call__(self, value, clip=None):
12541255
mask=mask)
12551256
# in-place equivalent of above can be much faster
12561257
resdat = self._transform(result.data)
1257-
resdat -= self._lower
1258-
resdat /= (self._upper - self._lower)
12591258

12601259
if is_scalar:
12611260
result = result[0]
12621261
return result
12631262

12641263
def _transform(self, a):
1265-
"""Inplace transformation."""
1264+
"""In-place mapping from *a* to [0, 1]"""
12661265
with np.errstate(invalid="ignore"):
1267-
masked = np.abs(a) > self.linthresh
1268-
sign = np.sign(a[masked])
1269-
log = (self._linscale_adj + np.log(np.abs(a[masked]) / self.linthresh))
1270-
log *= sign * self.linthresh
1271-
a[masked] = log
1272-
a[~masked] *= self._linscale_adj
1266+
logregion = np.abs(a) > self.linthresh
1267+
1268+
# Transform log value
1269+
sign = np.sign(a[logregion])
1270+
log = (1 - self.linear_size) * np.log10(np.abs(a[logregion])) + self.linear_size
1271+
a[logregion] = log * sign
1272+
1273+
# Transform linear values
1274+
a[~logregion] *= self.linear_size / self.linthresh
1275+
1276+
# Transform from [-1, 1] to [0, 1]
1277+
a += 1
1278+
a /= 2
12731279
return a
12741280

12751281
def _inv_transform(self, a):
12761282
"""Inverse inplace Transformation."""
1277-
masked = np.abs(a) > (self.linthresh * self._linscale_adj)
1278-
sign = np.sign(a[masked])
1279-
exp = np.exp(sign * a[masked] / self.linthresh - self._linscale_adj)
1280-
exp *= sign * self.linthresh
1281-
a[masked] = exp
1282-
a[~masked] /= self._linscale_adj
1283+
# Transform from [0, 1] to [-1, 1]
1284+
a *= 2
1285+
a -= 1
1286+
1287+
# Transform back log values
1288+
logregion = np.abs(a) > self.linear_size
1289+
sign = np.sign(a[logregion])
1290+
exp = 10**((np.abs(a[logregion]) - self.linear_size) /
1291+
(1 - self.linear_size))
1292+
a[logregion] = exp * sign
1293+
1294+
# Transform back linear values
1295+
a[~logregion] /= self.linear_size / self.linthresh
12831296
return a
12841297

1285-
def _transform_vmin_vmax(self):
1286-
"""Calculates vmin and vmax in the transformed system."""
1287-
vmin, vmax = self.vmin, self.vmax
1288-
arr = np.array([vmax, vmin]).astype(float)
1289-
self._upper, self._lower = self._transform(arr)
1290-
12911298
def inverse(self, value):
12921299
if not self.scaled():
12931300
raise ValueError("Not invertible until scaled")
12941301
val = np.ma.asarray(value)
1295-
val = val * (self._upper - self._lower) + self._lower
12961302
return self._inv_transform(val)
12971303

1298-
def autoscale(self, A):
1299-
# docstring inherited.
1300-
super().autoscale(A)
1301-
self._transform_vmin_vmax()
1302-
1303-
def autoscale_None(self, A):
1304-
# docstring inherited.
1305-
super().autoscale_None(A)
1306-
self._transform_vmin_vmax()
1307-
13081304

13091305
class PowerNorm(Normalize):
13101306
"""

lib/matplotlib/tests/test_colors.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,7 @@ def test_TwoSlopeNorm_premature_scaling():
395395

396396

397397
def test_SymLogNorm():
398-
"""
399-
Test SymLogNorm behavior
400-
"""
398+
# Test SymLogNorm behavior
401399
norm = mcolors.SymLogNorm(3, vmax=5, linscale=1.2)
402400
vals = np.array([-30, -1, 2, 6], dtype=float)
403401
normed_vals = norm(vals)
@@ -413,6 +411,14 @@ def test_SymLogNorm():
413411
assert_array_almost_equal(normed_vals, expected)
414412

415413

414+
@pytest.mark.parametrize('val,normed',
415+
([10, 1], [1, 0.75], [0, 0.5], [-1, 0.25], [-10, 0]))
416+
def test_symlognorm_vals(val, normed):
417+
norm = mcolors.SymLogNorm(linthresh=1, vmin=-10, vmax=10, linscale=1)
418+
assert_array_almost_equal(norm(val), normed)
419+
assert_array_almost_equal(norm.inverse(norm(val)), val)
420+
421+
416422
def test_SymLogNorm_colorbar():
417423
"""
418424
Test un-called SymLogNorm in a colorbar.

0 commit comments

Comments
 (0)