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

Appearance settings

2D Normalization & Colormapping for ScalerMappables #8738

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
201dc2a
Initailize class 2D norm
patniharshit Jun 9, 2017
7217536
Change name of class to BivariateNorm, take normalizer instances sepa…
patniharshit Jun 9, 2017
0178730
Bivariate works with imshow, pcolor, pcolormesh, pcolorfast
patniharshit Jul 3, 2017
575a244
add blank line between bivariate classes
patniharshit Jul 9, 2017
8425637
Add support for bivariate in scatter
patniharshit Jul 9, 2017
4a5831d
Fix missing norm2 in BivaraiteNorm
patniharshit Jul 11, 2017
85df736
Fixed bug in if condition bivariate mapping
patniharshit Jul 12, 2017
8e1bc63
Add autoscale, autoscale_None, scaled methods for BivariateNorm
patniharshit Jul 13, 2017
a9aace3
trying to show ticks and labels on both x and y axis
patniharshit Jul 16, 2017
55a7a68
commented outline code, changed gridspace to show sqaure colorbar
patniharshit Jul 17, 2017
c2eb617
2d colorbar printing now
patniharshit Jul 17, 2017
daef751
Add code for Colorsquare
patniharshit Jul 22, 2017
96af3d3
Modify axis aspect and fraction for colorsquare
patniharshit Jul 22, 2017
9285372
fix failing commits
patniharshit Jul 22, 2017
01b5932
Change PEP8 violations
patniharshit Jul 27, 2017
28288e2
Revert isBivari, remove classic locator
patniharshit Jul 29, 2017
ca2b111
Subclass normalizers from Abstract Base Class
patniharshit Aug 2, 2017
75b4eba
move _ticker function inside update_ticks
patniharshit Aug 2, 2017
bf65847
move _ticker function inside update_ticks
patniharshit Aug 2, 2017
f9dc4b1
remove cmap in imshow and pcolorfast
patniharshit Aug 5, 2017
8a985f0
Defer call to norm and cmap in imshow
patniharshit Aug 7, 2017
8f6928d
Fix bivariate handling with pcolormesh
patniharshit Aug 15, 2017
d54cb44
Make pcolor work with bivariate
patniharshit Aug 16, 2017
4d03552
Do 2D->1D mapping in call method of colormap
patniharshit Aug 20, 2017
2573610
Use list comprehensions to ravel
patniharshit Aug 20, 2017
9228480
Reflect on reviews
patniharshit Aug 21, 2017
5c8dc65
Update docstrings
patniharshit Aug 25, 2017
21ea65f
Add bivariate example
patniharshit Aug 25, 2017
b8d43a9
Treat one as slightly less than one
patniharshit Aug 25, 2017
7681d14
Change conditions for bivar
patniharshit Aug 25, 2017
59f56af
Add image comparison test for bivariate
patniharshit Aug 25, 2017
f98978f
Update is_bivar condition to expect either BivariateNorm or Bivariate…
patniharshit Aug 26, 2017
75a289f
Add line continuation in docstrings
patniharshit Aug 27, 2017
7803974
Debug doc build fail
patniharshit Aug 28, 2017
e40c87b
Add missing symbols in docs
patniharshit Aug 28, 2017
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
Bivariate works with imshow, pcolor, pcolormesh, pcolorfast
  • Loading branch information
patniharshit committed Aug 27, 2017
commit 01787306fe5fcfc9dd986c3186d55d066bb3b820
96 changes: 86 additions & 10 deletions 96 lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5141,12 +5141,30 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,
if not self._hold:
self.cla()

if norm is not None and not isinstance(norm, mcolors.Normalize):
msg = "'norm' must be an instance of 'mcolors.Normalize'"
isNorm = isinstance(norm, (mcolors.Normalize, mcolors.BivariateNorm))
if norm is not None and not isNorm:
msg = "'norm' must be an instance of 'mcolors.Normalize' " \
"or 'mcolors.BivariateNorm'"
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't BivariateNorm subclass Normalize? 'specially since now it's normalizing down to a 1d space?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

BivariateNorm does not inherit anything from Normalize so I thought it should not subclass it.

Copy link
Member

Choose a reason for hiding this comment

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

With the new design, BivariateNorm should havel all the same classes as Normalize...and I think subclassing it so it's registered generically as a Norm is preferable to having to treat it as a special case when it doesn't need to be.

Copy link
Member

Choose a reason for hiding this comment

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

This can be done with an ABC https://docs.python.org/3/library/abc.html#abc.ABCMeta.register which both Normalize and BivariateNorm register with.

Copy link
Member

Choose a reason for hiding this comment

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

Sure, but I don't see a reason why BivariateNorm shouldn't be subclassing Norm as it has the same architecture as any other norm...take data (scaler or vector)->map to 1D lut value ->get color, and color ->lut->scaler/vector.

Copy link
Member

Choose a reason for hiding this comment

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

If the code paths are completely different, why force the sub-class? In general, sub-classing is only worth it when you can share significant amounts of implementation details, otherwise duck-typing is better.

Copy link
Member

@story645 story645 Jul 28, 2017

Choose a reason for hiding this comment

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

My point is mostly that the code paths shouldn't end up being all that different and more to the point in mpl land there's probably a ton of code that checks if things are norms and if this code works in those instances it doesn't make sense to change all that code to cover both cases when this is still fundamentally a norm. But I think I'm on the same page as you on the solution maybe being a "Norm" metaclass.

raise ValueError(msg)

if aspect is None:
aspect = rcParams['image.aspect']
self.set_aspect(aspect)

temp = np.asarray(X)
if (temp.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or
isinstance(cmap, mcolors.BivariateColormap)):
if cmap is None:
cmap = mcolors.BivariateColormap()
if norm is None:
norm = mcolors.BivariateNorm()
temp = norm(temp)
temp[0] = temp[0] * (cmap.N-1)
temp[1] = temp[1] * (cmap.N-1)
temp = temp.astype(int)
X = temp[0] + cmap.N * temp[1]
norm = mcolors.NoNorm()

im = mimage.AxesImage(self, cmap, norm, interpolation, origin, extent,
filternorm=filternorm, filterrad=filterrad,
resample=resample, **kwargs)
Expand All @@ -5173,7 +5191,6 @@ def imshow(self, X, cmap=None, norm=None, aspect=None,

@staticmethod
def _pcolorargs(funcname, *args, **kw):
# This takes one kwarg, allmatch.
# If allmatch is True, then the incoming X, Y, C must
# have matching dimensions, taking into account that
# X and Y can be 1-D rather than 2-D. This perfect
Expand All @@ -5186,9 +5203,25 @@ def _pcolorargs(funcname, *args, **kw):
# is False.

allmatch = kw.pop("allmatch", False)
norm = kw.pop("norm", None)
cmap = kw.pop("cmap", None)

if len(args) == 1:
C = np.asanyarray(args[0])

if (C.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or
isinstance(cmap, mcolors.BivariateColormap)):
if cmap is None:
cmap = mcolors.BivariateColormap()
if norm is None:
norm = mcolors.BivariateNorm()
C = norm(C)
Copy link
Member

Choose a reason for hiding this comment

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

Why do this up-front? defering all of this to _make_image means the colormap and norm and be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check now.

C[0] = C[0] * (cmap.N-1)
C[1] = C[1] * (cmap.N-1)
C = C.astype(int)
C = C[0] + cmap.N * C[1]
C = np.array(C)

numRows, numCols = C.shape
if allmatch:
X, Y = np.meshgrid(np.arange(numCols), np.arange(numRows))
Expand All @@ -5200,6 +5233,18 @@ def _pcolorargs(funcname, *args, **kw):

if len(args) == 3:
X, Y, C = [np.asanyarray(a) for a in args]
if (C.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or
isinstance(cmap, mcolors.BivariateColormap)):
if cmap is None:
cmap = mcolors.BivariateColormap()
if norm is None:
norm = mcolors.BivariateNorm()
C = norm(C)
C[0] = C[0] * (cmap.N-1)
C[1] = C[1] * (cmap.N-1)
C = C.astype(int)
C = C[0] + cmap.N * C[1]
C = np.array(C)
numRows, numCols = C.shape
else:
raise TypeError(
Expand Down Expand Up @@ -5382,9 +5427,14 @@ def pcolor(self, *args, **kwargs):
vmin = kwargs.pop('vmin', None)
vmax = kwargs.pop('vmax', None)

X, Y, C = self._pcolorargs('pcolor', *args, allmatch=False)
kw = {'norm': norm, 'cmap': cmap, 'allmatch': False}
X, Y, C = self._pcolorargs('pcolor', *args, **kw)
Ny, Nx = X.shape

if (isinstance(norm, mcolors.BivariateNorm) or
isinstance(cmap, mcolors.BivariateColormap)):
norm = mcolors.NoNorm()

# unit conversion allows e.g. datetime objects as axis values
self._process_unit_info(xdata=X, ydata=Y, kwargs=kwargs)
X = self.convert_xunits(X)
Expand Down Expand Up @@ -5450,9 +5500,13 @@ def pcolor(self, *args, **kwargs):

collection.set_alpha(alpha)
collection.set_array(C)
if norm is not None and not isinstance(norm, mcolors.Normalize):
msg = "'norm' must be an instance of 'mcolors.Normalize'"

isNorm = isinstance(norm, (mcolors.Normalize, mcolors.BivariateNorm))
if norm is not None and not isNorm:
msg = "'norm' must be an instance of 'mcolors.Normalize' " \
Copy link
Member

Choose a reason for hiding this comment

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

We have a style preference for using () for continuation rather than \ so

msg = ('...' +
       '...')

Copy link
Contributor

Choose a reason for hiding this comment

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

The + is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

"or 'mcolors.BivariateNorm'"
raise ValueError(msg)

collection.set_cmap(cmap)
collection.set_norm(norm)
collection.set_clim(vmin, vmax)
Expand Down Expand Up @@ -5582,9 +5636,14 @@ def pcolormesh(self, *args, **kwargs):

allmatch = (shading == 'gouraud')

X, Y, C = self._pcolorargs('pcolormesh', *args, allmatch=allmatch)
kw = {'norm': norm, 'cmap': cmap, 'allmatch': allmatch}
X, Y, C = self._pcolorargs('pcolormesh', *args, **kw)
Ny, Nx = X.shape

if (isinstance(norm, mcolors.BivariateNorm) or
isinstance(cmap, mcolors.BivariateColormap)):
norm = mcolors.NoNorm()

# unit conversion allows e.g. datetime objects as axis values
self._process_unit_info(xdata=X, ydata=Y, kwargs=kwargs)
X = self.convert_xunits(X)
Expand Down Expand Up @@ -5723,11 +5782,28 @@ def pcolorfast(self, *args, **kwargs):
cmap = kwargs.pop('cmap', None)
vmin = kwargs.pop('vmin', None)
vmax = kwargs.pop('vmax', None)
if norm is not None and not isinstance(norm, mcolors.Normalize):
msg = "'norm' must be an instance of 'mcolors.Normalize'"
isNorm = isinstance(norm, (mcolors.Normalize, mcolors.BivariateNorm))
if norm is not None and not isNorm:
msg = "'norm' must be an instance of 'mcolors.Normalize' " \
"or 'mcolors.BivariateNorm'"
raise ValueError(msg)

C = args[-1]
C = np.asarray(args[-1])

if (C.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or
isinstance(cmap, mcolors.BivariateColormap)):
if cmap is None:
cmap = mcolors.BivariateColormap()
if norm is None:
norm = mcolors.BivariateNorm()
C = norm(C)
C[0] = C[0] * (cmap.N-1)
C[1] = C[1] * (cmap.N-1)
C = C.astype(int)
C = C[0] + cmap.N * C[1]
C = np.array(C)
norm = mcolors.NoNorm()

nr, nc = C.shape
if len(args) == 1:
style = "image"
Expand Down
31 changes: 29 additions & 2 deletions 31 lib/matplotlib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,32 @@ def reversed(self, name=None):
return ListedColormap(colors_r, name=name, N=self.N)


class BivariateColormap(Colormap):
def __init__(self, name, N=256):
Colormap.__init__(self, name, N)
Copy link
Contributor

Choose a reason for hiding this comment

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

would Colormap.init(self, name, N**2) work?


def _init(self):
red = np.linspace(0, 1, self.N)
green = np.linspace(0, 1, self.N)
red_mesh, green_mesh = np.meshgrid(red, green)
blue_mesh = np.zeros_like(red_mesh)
alpha_mesh = np.ones_like(red_mesh)
bivariate_cmap = np.dstack((red_mesh, green_mesh, blue_mesh, alpha_mesh))
self._lut = np.vstack(bivariate_cmap)
self._isinit = True
self.N = self.N * self.N
self._set_extremes()

def _resample(self, lutsize):
"""
Return a new color map with *lutsize x lutsize* entries.
"""
return BivariateColormap(self.name, lutsize)

def reversed(self, name=None):
raise NotImplementedError()
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for the parentheses.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.



class Normalize(object):
"""
A class which, when called, can normalize data into
Expand Down Expand Up @@ -1390,8 +1416,8 @@ def __call__(self, values, clip=None):
if clip is None:
clip = [self.norm1.clip, self.norm2.clip]

return [self.norm1(values[0], clip=clip[0]),
self.norm2(values[1], clip=clip[1])]
return np.array([self.norm1(values[0], clip=clip[0]),
self.norm2(values[1], clip=clip[1])])

def inverse(self, values):
"""
Expand All @@ -1406,6 +1432,7 @@ def inverse(self, values):
"""
return [self.norm1.inverse(values[0]), self.norm.inverse(values[1])]


def rgb_to_hsv(arr):
"""
convert float rgb values (in the range [0, 1]), in a numpy array to hsv
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.