Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Diverging norm#5054

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
jkseppan wants to merge16 commits intomatplotlib:masterfromjkseppan:diverging-norm
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
16 commits
Select commitHold shift + click to select a range
92bc85b
ENH: Add OffsetNorm and tests
phobsonNov 27, 2014
17d12ca
fix OffsetNorm docstring to numpydoc spec
phobsonMar 30, 2015
a6b5d77
OffsetNorm blurb in whats_new
phobsonMar 30, 2015
8c5e14e
removed OffsetNorm.inverse method
phobsonJun 6, 2015
5a84ad2
attempting to create a baseline image
phobsonJun 6, 2015
68e67db
fix bad indexing when returning a scalar
phobsonJun 8, 2015
1f59b20
the real test image
phobsonJun 8, 2015
67e745b
refactor tests a bit to handle non-invertable Norms
phobsonJun 8, 2015
23f197a
OffsetNorm -> PiecewiseLinearNorm
dopplershiftJul 11, 2015
bb56923
Propagate mask from input data
jkseppanSep 3, 2015
35dec37
Remove unused clip parameter
jkseppanSep 3, 2015
5fc932f
Use process_value in PiecewiseLinearNorm
jkseppanSep 3, 2015
2cdf0a5
Return a scalar when passed in a scalar
jkseppanSep 3, 2015
53ed8ae
Return 0.5 for vcenter == vmax
jkseppanSep 3, 2015
b7f42f4
pep8 fixes
jkseppanSep 3, 2015
9d48773
Rename to DivergingNorm
jkseppanSep 13, 2015
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
7 changes: 7 additions & 0 deletionsdoc/users/whats_new.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,13 @@ Added a :code:`pivot` kwarg to :func:`~mpl_toolkits.mplot3d.Axes3D.quiver`
that controls the pivot point around which the quiver line rotates. This also
determines the placement of the arrow head along the quiver line.

Offset Normalizers for Colormaps
````````````````````````````````
Paul Hobson/Geosyntec Consultants added a new :class:`matplotlib.colors.DivergingNorm`
class with the help of Till Stensitzki. This is particularly useful when using a
diverging colormap on data that are asymetrically centered around a logical value
(e.g., 0 when data range from -2 to 4).

New backend selection
---------------------

Expand Down
83 changes: 83 additions & 0 deletionslib/matplotlib/colors.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -225,6 +225,7 @@ def rgb2hex(rgb):
a = '#%02x%02x%02x' % tuple([int(np.round(val * 255)) for val in rgb[:3]])
return a


hexColorPattern = re.compile("\A#[a-fA-F0-9]{6}\Z")


Expand DownExpand Up@@ -963,6 +964,88 @@ def scaled(self):
return (self.vmin is not None and self.vmax is not None)


class DivergingNorm(Normalize):
"""
A subclass of matplotlib.colors.Normalize.

Normalizes data into the ``[0.0, 1.0]`` interval.
"""
def __init__(self, vmin=None, vcenter=None, vmax=None):
"""Normalize data with an offset midpoint

Useful when mapping data unequally centered around a conceptual
center, e.g., data that range from -2 to 4, with 0 as the midpoint.

Parameters
----------
vmin : float, optional
The data value that defines ``0.0`` in the normalized data.
Defaults to the min value of the dataset.

vcenter : float, optional
The data value that defines ``0.5`` in the normalized data.
Defaults to halfway between *vmin* and *vmax*.

vmax : float, optional
The data value that defines ``1.0`` in the normalized data.
Defaults to the the max value of the dataset.

Examples
--------
>>> import matplotlib.colors as mcolors
>>> offset = mcolors.DivergingNorm(vmin=-2., vcenter=0., vmax=4.)
>>> data = [-2., -1., 0., 1., 2., 3., 4.]
>>> offset(data)
array([0., 0.25, 0.5, 0.625, 0.75, 0.875, 1.0])

"""

self.vmin = vmin
self.vcenter = vcenter
self.vmax = vmax

def __call__(self, value, clip=None):
"""Map value to the interval [0, 1]. The clip argument is unused."""

result, is_scalar = self.process_value(value)

self.autoscale_None(result)
vmin, vcenter, vmax = self.vmin, self.vcenter, self.vmax
if vmin == vmax == vcenter:
result.fill(0)
elif not vmin <= vcenter <= vmax:
raise ValueError("minvalue must be less than or equal to "
"centervalue which must be less than or "
"equal to maxvalue")
else:
vmin = float(vmin)
vcenter = float(vcenter)
vmax = float(vmax)
# in degenerate cases, prefer the center value to the extremes
degen = (result == vcenter) if vcenter == vmax else None

x, y = [vmin, vcenter, vmax], [0, 0.5, 1]
result = ma.masked_array(np.interp(result, x, y),
mask=ma.getmask(result))
if degen is not None:
result[degen] = 0.5

if is_scalar:
result = np.atleast_1d(result)[0]
return result

def autoscale_None(self, A):
' autoscale only None-valued vmin or vmax'
if self.vmin is None and np.size(A) > 0:
self.vmin = ma.min(A)

if self.vmax is None and np.size(A) > 0:
self.vmax = ma.max(A)

if self.vcenter is None:
self.vcenter = (self.vmax + self.vmin) * 0.5


class LogNorm(Normalize):
"""
Normalize a given value to the 0-1 range on a log scale
Expand Down
View file
Open in desktop
Loading
Sorry, something went wrong.Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
211 changes: 209 additions & 2 deletionslib/matplotlib/tests/test_colors.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,7 +5,8 @@
import itertools
from distutils.version import LooseVersion as V

from nose.tools import assert_raises, assert_equal, assert_true
from nose.tools import assert_raises, assert_equal, assert_true, \
assert_false, raises

import numpy as np
from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
Expand DownExpand Up@@ -163,6 +164,207 @@ def test_Normalize():
_mask_tester(norm, vals)


class BaseNormMixin(object):
def test_call(self):
normed_vals = self.norm(self.vals)
assert_array_almost_equal(normed_vals, self.expected)

def test_inverse(self):
if self.test_inverse:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I am a bit confused about how I expect this to behave...

_inverse_tester(self.norm, self.vals)
else:
pass

def test_scalar(self):
_scalar_tester(self.norm, self.vals)

def test_mask(self):
_mask_tester(self.norm, self.vals)

def test_autoscale(self):
norm = self.normclass()
norm.autoscale([10, 20, 30, 40])
assert_equal(norm.vmin, 10.)
assert_equal(norm.vmax, 40.)

def test_autoscale_None_vmin(self):
norm = self.normclass(vmin=0, vmax=None)
norm.autoscale_None([1, 2, 3, 4, 5])
assert_equal(norm.vmin, 0.)
assert_equal(norm.vmax, 5.)

def test_autoscale_None_vmax(self):
norm = self.normclass(vmin=None, vmax=10)
norm.autoscale_None([1, 2, 3, 4, 5])
assert_equal(norm.vmin, 1.)
assert_equal(norm.vmax, 10.)

def test_scale(self):
norm = self.normclass()
assert_false(norm.scaled())

norm([1, 2, 3, 4])
assert_true(norm.scaled())

def test_process_value_scalar(self):
res, is_scalar = mcolors.Normalize.process_value(5)
assert_true(is_scalar)
assert_array_equal(res, np.array([5.]))

def test_process_value_list(self):
res, is_scalar = mcolors.Normalize.process_value([5, 10])
assert_false(is_scalar)
assert_array_equal(res, np.array([5., 10.]))

def test_process_value_tuple(self):
res, is_scalar = mcolors.Normalize.process_value((5, 10))
assert_false(is_scalar)
assert_array_equal(res, np.array([5., 10.]))

def test_process_value_array(self):
res, is_scalar = mcolors.Normalize.process_value(np.array([5, 10]))
assert_false(is_scalar)
assert_array_equal(res, np.array([5., 10.]))


class BaseDivergingNorm(BaseNormMixin):
normclass = mcolors.DivergingNorm
test_inverse = False


class test_DivergingNorm_Even(BaseDivergingNorm):
def setup(self):
self.norm = self.normclass(vmin=-1, vcenter=0, vmax=4)
self.vals = np.array([-1.0, -0.5, 0.0, 1.0, 2.0, 3.0, 4.0])
self.expected = np.array([0.0, 0.25, 0.5, 0.625, 0.75, 0.875, 1.0])


class test_DivergingNorm_Odd(BaseDivergingNorm):
def setup(self):
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=-2, vcenter=0, vmax=5)
self.vals = np.array([-2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
self.expected = np.array([0.0, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])


class test_DivergingNorm_AllNegative(BaseDivergingNorm):
def setup(self):
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=-10, vcenter=-8, vmax=-2)
self.vals = np.array([-10., -9., -8., -6., -4., -2.])
self.expected = np.array([0.0, 0.25, 0.5, 0.666667, 0.833333, 1.0])


class test_DivergingNorm_AllPositive(BaseDivergingNorm):
def setup(self):
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=0, vcenter=3, vmax=9)
self.vals = np.array([0., 1.5, 3., 4.5, 6.0, 7.5, 9.])
self.expected = np.array([0.0, 0.25, 0.5, 0.625, 0.75, 0.875, 1.0])


class test_DivergingNorm_NoVs(BaseDivergingNorm):
def setup(self):
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=None, vcenter=None, vmax=None)
self.vals = np.array([-2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0])
self.expected = np.array([0., 0.16666667, 0.33333333,
0.5, 0.66666667, 0.83333333, 1.0])
self.expected_vmin = -2
self.expected_vcenter = 1
self.expected_vmax = 4

def test_vmin(self):
assert_true(self.norm.vmin is None)
self.norm(self.vals)
assert_equal(self.norm.vmin, self.expected_vmin)

def test_vcenter(self):
assert_true(self.norm.vcenter is None)
self.norm(self.vals)
assert_equal(self.norm.vcenter, self.expected_vcenter)

def test_vmax(self):
assert_true(self.norm.vmax is None)
self.norm(self.vals)
assert_equal(self.norm.vmax, self.expected_vmax)


class test_DivergingNorm_VminEqualsVcenter(BaseDivergingNorm):
def setup(self):
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=-2, vcenter=-2, vmax=2)
self.vals = np.array([-2.0, -1.0, 0.0, 1.0, 2.0])
self.expected = np.array([0.5, 0.625, 0.75, 0.875, 1.0])


class test_DivergingNorm_VmaxEqualsVcenter(BaseDivergingNorm):
def setup(self):
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=-2, vcenter=2, vmax=2)
self.vals = np.array([-2.0, -1.0, 0.0, 1.0, 2.0])
self.expected = np.array([0.0, 0.125, 0.25, 0.375, 0.5])


class test_DivergingNorm_VsAllEqual(BaseDivergingNorm):
def setup(self):
self.v = 10
self.normclass = mcolors.DivergingNorm
self.norm = self.normclass(vmin=self.v, vcenter=self.v, vmax=self.v)
self.vals = np.array([-2.0, -1.0, 0.0, 1.0, 2.0])
self.expected = np.array([0.0, 0.0, 0.0, 0.0, 0.0])
self.expected_inv = self.expected + self.v

def test_inverse(self):
assert_array_almost_equal(
self.norm.inverse(self.norm(self.vals)),
self.expected_inv
)


class test_DivergingNorm_Errors(object):
def setup(self):
self.vals = np.arange(50)

@raises(ValueError)
def test_VminGTVcenter(self):
norm = mcolors.DivergingNorm(vmin=10, vcenter=0, vmax=20)
norm(self.vals)

@raises(ValueError)
def test_VminGTVmax(self):
norm = mcolors.DivergingNorm(vmin=10, vcenter=0, vmax=5)
norm(self.vals)

@raises(ValueError)
def test_VcenterGTVmax(self):
norm = mcolors.DivergingNorm(vmin=10, vcenter=25, vmax=20)
norm(self.vals)

@raises(ValueError)
def test_premature_scaling(self):
norm = mcolors.DivergingNorm()
norm.inverse(np.array([0.1, 0.5, 0.9]))


@image_comparison(baseline_images=['test_offset_norm'], extensions=['png'])
def test_offset_norm_img():
x = np.linspace(-2, 7)
y = np.linspace(-1*np.pi, np.pi)
X, Y = np.meshgrid(x, y)
Z = x * np.sin(Y)**2

fig, (ax1, ax2) = plt.subplots(ncols=2)
cmap = plt.cm.coolwarm
norm = mcolors.DivergingNorm(vmin=-2, vcenter=0, vmax=7)

img1 = ax1.imshow(Z, cmap=cmap, norm=None)
cbar1 = fig.colorbar(img1, ax=ax1)

img2 = ax2.imshow(Z, cmap=cmap, norm=norm)
cbar2 = fig.colorbar(img2, ax=ax2)


def test_SymLogNorm():
"""
Test SymLogNorm behavior
Expand DownExpand Up@@ -281,7 +483,12 @@ def test_cmap_and_norm_from_levels_and_colors2():
'Wih extend={0!r} and data '
'value={1!r}'.format(extend, d_val))

assert_raises(ValueError, mcolors.from_levels_and_colors, levels, colors)
assert_raises(
ValueError,
mcolors.from_levels_and_colors,
levels,
colors
)


def test_rgb_hsv_round_trip():
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp