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

Skip to content

Commit 1b9fc6c

Browse files
committed
colors: Add power-law normalization
1 parent ddd5899 commit 1b9fc6c

4 files changed

Lines changed: 99 additions & 0 deletions

File tree

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
all pyplot.tri* methods) and mlab.griddata. Deprecated
1919
matplotlib.delaunay module. - IMT
2020

21+
2013-11-05 Add power-law normalization method. This is useful for,
22+
e.g., showing small populations in a "hist2d" histogram.
23+
2124
2013-10-27 Added get_rlabel_position and set_rlabel_position methods to
2225
PolarAxes to control angular position of radial tick labels.
2326

doc/users/whats_new.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ Phil Elson rewrote of the documentation and userguide for both Legend and PathEf
3232
New plotting features
3333
---------------------
3434

35+
Power-law normalization
36+
```````````````````````
37+
Ben Gamari added a power-law normalization method,
38+
:class:`~matplotlib.colors.PowerNorm`. This class maps a range of
39+
values to the interval [0,1] with power-law scaling with the exponent
40+
provided by the constructor's `gamma` argument. Power law normalization
41+
can be useful for, e.g., emphasizing small populations in a histogram.
42+
3543
Fully customizable boxplots
3644
````````````````````````````
3745
Paul Hobson overhauled the :func:`~matplotlib.pyplot.boxplot` method such

lib/matplotlib/colors.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import six
5353
from six.moves import map, zip
5454

55+
import warnings
5556
import re
5657
import numpy as np
5758
from numpy import ma
@@ -1148,6 +1149,80 @@ def autoscale_None(self, A):
11481149
self._transform_vmin_vmax()
11491150

11501151

1152+
class PowerNorm(Normalize):
1153+
"""
1154+
Normalize a given value to the ``[0, 1]`` interval with a power-law
1155+
scaling. This will clip any negative data points to 0.
1156+
"""
1157+
def __init__(self, gamma, vmin=None, vmax=None, clip=False):
1158+
Normalize.__init__(self, vmin, vmax, clip)
1159+
self.gamma = gamma
1160+
1161+
def __call__(self, value, clip=None):
1162+
if clip is None:
1163+
clip = self.clip
1164+
1165+
result, is_scalar = self.process_value(value)
1166+
1167+
self.autoscale_None(result)
1168+
gamma = self.gamma
1169+
vmin, vmax = self.vmin, self.vmax
1170+
if vmin > vmax:
1171+
raise ValueError("minvalue must be less than or equal to maxvalue")
1172+
elif vmin == vmax:
1173+
result.fill(0)
1174+
else:
1175+
if clip:
1176+
mask = ma.getmask(result)
1177+
val = ma.array(np.clip(result.filled(vmax), vmin, vmax),
1178+
mask=mask)
1179+
resdat = result.data
1180+
resdat -= vmin
1181+
np.power(resdat, gamma, resdat)
1182+
resdat /= (vmax - vmin) ** gamma
1183+
result = np.ma.array(resdat, mask=result.mask, copy=False)
1184+
result[value < 0] = 0
1185+
if is_scalar:
1186+
result = result[0]
1187+
return result
1188+
1189+
def inverse(self, value):
1190+
if not self.scaled():
1191+
raise ValueError("Not invertible until scaled")
1192+
gamma = self.gamma
1193+
vmin, vmax = self.vmin, self.vmax
1194+
1195+
if cbook.iterable(value):
1196+
val = ma.asarray(value)
1197+
return ma.power(value, 1. / gamma) * (vmax - vmin) + vmin
1198+
else:
1199+
return pow(value, 1. / gamma) * (vmax - vmin) + vmin
1200+
1201+
def autoscale(self, A):
1202+
"""
1203+
Set *vmin*, *vmax* to min, max of *A*.
1204+
"""
1205+
self.vmin = ma.min(A)
1206+
if self.vmin < 0:
1207+
self.vmin = 0
1208+
warnings.warn("Power-law scaling on negative values is "
1209+
"ill-defined, clamping to 0.")
1210+
1211+
self.vmax = ma.max(A)
1212+
1213+
def autoscale_None(self, A):
1214+
' autoscale only None-valued vmin or vmax'
1215+
if self.vmin is None and np.size(A) > 0:
1216+
self.vmin = ma.min(A)
1217+
if self.vmin < 0:
1218+
self.vmin = 0
1219+
warnings.warn("Power-law scaling on negative values is "
1220+
"ill-defined, clamping to 0.")
1221+
1222+
if self.vmax is None and np.size(A) > 0:
1223+
self.vmax = ma.max(A)
1224+
1225+
11511226
class BoundaryNorm(Normalize):
11521227
"""
11531228
Generate a colormap index based on discrete intervals.

lib/matplotlib/tests/test_colors.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ def test_LogNorm():
5555
assert_array_equal(ln([1, 6]), [0, 1.0])
5656

5757

58+
def test_PowerNorm():
59+
a = np.array([0, 0.5, 1, 1.5], dtype=np.float)
60+
pnorm = mcolors.PowerNorm(1)
61+
norm = mcolors.Normalize()
62+
assert_array_almost_equal(norm(a), pnorm(a))
63+
64+
a = np.array([-0.5, 0, 2, 4, 8], dtype=np.float)
65+
expected = [0, 0, 1./16, 1./4, 1]
66+
pnorm = mcolors.PowerNorm(2, vmin=0, vmax=8)
67+
assert_array_almost_equal(pnorm(a), expected)
68+
assert_array_almost_equal(a[1:], pnorm.inverse(pnorm(a))[1:])
69+
70+
5871
def test_Normalize():
5972
norm = mcolors.Normalize()
6073
vals = np.arange(-10, 10, 1, dtype=np.float)

0 commit comments

Comments
 (0)