Skip to content
Closed
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2d90c5a
Added ArbitaryNorm and RootNorm classes in colors, as well as example…
Oct 17, 2016
57aad3d
PEP8 formatting on examples, plotting using the object oriented appro…
Oct 18, 2016
f818aff
Added title/description to the examples
Oct 18, 2016
ffe1b9d
Class attributes are now hidden
Oct 18, 2016
1d22b90
Major update: complete refactorization of code. A much more powerful …
Oct 19, 2016
b5801ea
Corrected lambda function syntax that was not compatible with python …
Oct 19, 2016
e93d82d
Added FuncNorm: now everything inherits from this. Changed the name o…
Oct 20, 2016
3749b0a
Forgot to uncomment an import
Oct 20, 2016
de62491
Improved the auto-tick feature, and corrected some pep8 issues
Oct 20, 2016
d148756
Improved examples, created a new file for generating sample data.'
alvarosg Oct 22, 2016
5373a98
Corrected a double line, and removed a comment
alvarosg Oct 22, 2016
13edeab
Tests for FuncNorm added, and bug corrected in FuncNorm
alvarosg Oct 22, 2016
21d5cd0
Added compatibility for python 3 string check, added tests for Piecew…
alvarosg Oct 22, 2016
d359a4e
Added tests on all classes, including all public methods
alvarosg Oct 22, 2016
4622829
Change type of arrays in tests from int to float
alvarosg Oct 22, 2016
30ff404
Corrected wrong `super()` for RootNorm
alvarosg Oct 22, 2016
df835cb
Solve problem with implicit int to float casting that was not working…
alvarosg Oct 22, 2016
dfaa0f8
Added documentation in the numpydoc format
alvarosg Oct 23, 2016
a386395
Improve style in the examples. Corrected intending problem in the doc…
alvarosg Oct 23, 2016
b9dafb0
Added example in `FuncNorm` docstring
alvarosg Oct 23, 2016
d10be73
Finished with the examples in the docstrings
alvarosg Oct 24, 2016
c85a14c
Implemented clipping behavoir. Refactored _func_parser
alvarosg Oct 26, 2016
7597ddd
It now allows some string functions with parameters. Added a test for…
alvarosg Oct 28, 2016
7fce503
Forgot to add a file...
alvarosg Oct 28, 2016
bcd7dd0
Forgot to add another file...
alvarosg Oct 28, 2016
a71e1e9
Improved tests, documentation, and exceptions
alvarosg Oct 31, 2016
33f57d1
Removed test_colors.py from __init__.py after including parametrize
alvarosg Oct 31, 2016
9687173
Moved the string function parser to its own class in cbook. Added tes…
alvarosg Nov 1, 2016
46395aa
Improved documentation
Nov 2, 2016
63dab61
Added new example
Nov 2, 2016
8abf2c2
Added example for PiecewiseNorm, and MirrorPiecewiseNorm. String in t…
alvarosg Nov 3, 2016
b4ecdb2
Removed sampledata.py no longer necessary, and changed examples in do…
alvarosg Nov 3, 2016
42007ee
Added examples for MirrorRootNorm and RootNorm
alvarosg Nov 3, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Forgot to add a file...
  • Loading branch information
alvarosg committed Oct 28, 2016
commit 7fce503b1cc1594d9d4b8315336a84f0293aff51
121 changes: 78 additions & 43 deletions lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -977,8 +977,10 @@ def __init__(self, f, finv=None, **normalize_kw):
f : callable or string
Function to be used for the normalization receiving a single
parameter, compatible with scalar values, and ndarrays.
Alternatively a string from the list ['linear','quadratic',
'cubic','sqrt','crt','log'] can be used.
Alternatively a string from the list ['linear', 'quadratic',
'cubic', 'sqrt', 'crt','log', 'log10', 'power{a}', 'root{a}',
'log(x+{a})', 'log10(x+{a})'] can be used, replacing 'a' by a
number different than 0 when necessary.
finv : callable, optional
Inverse function of `f` that satisfies finv(f(x))==x. It is
optional in cases where f is provided as a string.
Expand All @@ -991,7 +993,7 @@ def __init__(self, f, finv=None, **normalize_kw):
Creating a logarithmic normalization using the predefined strings:

>>> import matplotlib.colors as colors
>>> norm = colors.FuncNorm(f='log', vmin=0.01, vmax=2)
>>> norm = colors.FuncNorm(f='log10', vmin=0.01, vmax=2)

Or doing it manually:

Expand Down Expand Up @@ -1032,7 +1034,7 @@ def __call__(self, value, clip=None):
result : masked array of floats
Normalized data to the `[0.0, 1.0]` interval. If clip == False,
the values original below vmin or above vmax will be assigned to
-0.1 or 1.1, respectively
-0.1 or 1.1, respectively.

"""
if clip is None:
Expand All @@ -1047,8 +1049,7 @@ def __call__(self, value, clip=None):
self._update_f(vmin, vmax)

if clip:
result[result >= vmax] = vmax
result[result <= vmin] = vmin
result = np.clip(result, vmin, vmax)
resultnorm = (self._f(result) - self._f(vmin)) / \
(self._f(vmax) - self._f(vmin))
else:
Expand Down Expand Up @@ -1090,24 +1091,58 @@ def inverse(self, value):
return value

@staticmethod
def _func_parser(funcsin):
def _func_parser(funcsin, onlybounded=False):
if hasattr(funcsin[0], '__call__'):
return funcsin
# Each element has the direct, the inverse, and and interval indicating
# wether the function is bounded in the interval 0-1
funcs = {'linear': (lambda x: x, lambda x: x, True),
'quadratic': (lambda x: x**2, lambda x: x**(1. / 2), True),
'cubic': (lambda x: x**3, lambda x: x**(1. / 3), True),
'sqrt': (lambda x: x**(1. / 2), lambda x: x**2, True),
'crt': (lambda x: x**(1. / 3), lambda x: x**3, True),
'log10': (lambda x: np.log10(x), lambda x: (10**(x)), False),
'log': (lambda x: np.log(x), lambda x: (np.exp(x)), False),
'power{a}': (lambda x, a: x**a,
lambda x, a: x**(1. / a), True),
'root{a}': (lambda x, a: x**(1. / a),
lambda x, a: x**a, True),
'log10(x+{a})': (lambda x, a: np.log10(x + a),
lambda x, a: 10**x - a, True),
'log(x+{a})': (lambda x, a: np.log(x + a),
lambda x, a: np.exp(x) - a, True)}

# Checking if it comes with a parameter
param = None
regex = '\{(.*?)\}'
search = re.search(regex, funcsin[0])
if search is not None:
parstring = search.group(1)

try:
param = float(parstring)
except:
raise ValueError("'a' in parametric function strings must be "
"replaced by a number different than 0, "
"e.g. 'log10(x+{0.1})'.")
if param == 0:
raise ValueError("'a' in parametric function strings must be "
"replaced by a number different than 0.")
funcsin[0] = re.sub(regex, '{a}', funcsin[0])

flog = 2000
funcs = {'linear': (lambda x: x, lambda x: x),
'quadratic': (lambda x: x**2, lambda x: x**(1. / 2)),
'cubic': (lambda x: x**3, lambda x: x**(1. / 3)),
'sqrt': (lambda x: x**(1. / 2), lambda x: x**2),
'crt': (lambda x: x**(1. / 3), lambda x: x**3),
'log(x+1)': (lambda x: (np.log10(x * flog + 1) /
np.log10(flog + 1),
lambda x: ((10**(np.log10(flog + 1) * x) - 1) /
flog))),
'log': (lambda x: np.log10(x),
lambda x: (10**(x)))}
try:
return funcs[six.text_type(funcsin[0])]
output = funcs[six.text_type(funcsin[0])]
if onlybounded and not output[2]:
raise ValueError("Only functions bounded in the (0, 1)"
"domain are allowed: %s" %
[key for key in funcs.keys() if funcs[key][2]]
)

if param is not None:
output = (lambda x, output=output: output[0](x, param),
lambda x, output=output: output[1](x, param),
output[2])
return output[0:2]
except KeyError:
raise ValueError("%s: invalid function. The only strings "
"recognized as functions are %s." %
Expand All @@ -1126,23 +1161,23 @@ def _fun_normalizer(fun):
else:
return (lambda x: (fun(x) - fun(0.)) / (fun(1.) - fun(0.)))

def ticks(self, N=13):
def ticks(self, nticks=13):
"""
Returns an automatic list of `N` points in the data space to be used
as ticks in the colorbar.
Returns an automatic list of `nticks` points in the data space
to be used as ticks in the colorbar.

Parameters
----------
N : integer, optional
nticks : integer, optional
Number of ticks to be returned. Default 13.

Returns
-------
ticks : ndarray
1d array of length `N` with the proposed tick locations.
1d array of length `nticks` with the proposed tick locations.

"""
ticks = self.inverse(np.linspace(0, 1, N))
ticks = self.inverse(np.linspace(0, 1, nticks))
finalticks = np.zeros(ticks.shape, dtype=np.bool)
finalticks[0] = True
ticks = FuncNorm._round_ticks(ticks, finalticks)
Expand Down Expand Up @@ -1378,47 +1413,47 @@ def _update_f(self, vmin, vmax):
self._finv = self._build_finv()
return

def ticks(self, N=None):
def ticks(self, nticks=None):
"""
Returns an automatic list of *N* points in the data space to be used
as ticks in the colorbar.
Returns an automatic list of `nticks` points in the data space
to be used as ticks in the colorbar.

Parameters
----------
N : integer, optional
nticks : integer, optional
Number of ticks to be returned. Default 13.

Returns
-------
ticks : ndarray
1d array of length *N* with the proposed tick locations.
1d array of length `nticks` with the proposed tick locations.

"""
rp_cm = self._refpoints_cm
widths_cm = np.diff(rp_cm)

if N is None:
N = max([13, len(rp_cm)])
if nticks is None:
nticks = max([13, len(rp_cm)])

if N < len(rp_cm):
if nticks < len(rp_cm):
ValueError(
"the number of ticks must me larger "
"that the number or intervals +1")

ticks = rp_cm.copy()

available_ticks = N - len(-rp_cm)
available_ticks = nticks - len(-rp_cm)
distribution = widths_cm * (available_ticks) / widths_cm.sum()
nticks = np.floor(distribution)
nticks_each = np.floor(distribution)

while(nticks.sum() < available_ticks):
ind = np.argmax((distribution - nticks))
nticks[ind] += 1
while(nticks_each.sum() < available_ticks):
ind = np.argmax((distribution - nticks_each))
nticks_each[ind] += 1

for i in range(len(nticks)):
if nticks[i] > 0:
N = nticks[i]
auxticks = np.linspace(rp_cm[i], rp_cm[i + 1], N + 2)
for i in range(len(nticks_each)):
if nticks_each[i] > 0:
nticks_this = nticks_each[i]
auxticks = np.linspace(rp_cm[i], rp_cm[i + 1], nticks_this + 2)
ticks = np.concatenate([ticks, auxticks[1:-1]])

finalticks = np.zeros(ticks.shape, dtype=np.bool)
Expand Down