From 3e1fcd4a7b034bfa2e030d2766736ccfaf0954d0 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sat, 10 Jun 2017 02:27:06 +0530 Subject: [PATCH 01/26] Initailize class 2D norm --- lib/matplotlib/colors.py | 54 +++++++++++++++++++++++++++++++++++++ lib/matplotlib/tests/test_colors.py | 43 +++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 4cef8be06b6..1a5136c016b 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1346,6 +1346,60 @@ def __call__(self, value, clip=None): def inverse(self, value): return value +class Norm2d: + """ + Normalize a list of two values corresponding to two 1D normalizers + """ + def __init__(self, norm_instances=None): + """ + Parameters + ---------- + norm_instances : + A list of length two having instances of 1D normalizers + """ + if norm_instances is None: + self.norm_instances = [Normalize(), Normalize()] + else: + self.norm_instances = norm_instances + + def __call__(self, values, clip=None): + """ + Parameters + ---------- + values : array-like + A list of two values to be normalized + clip : list of bools, None, optional + A list of two bools corresponding to value in values. + If clip is None then clip is set according to corresponding + normalizers. + + Returns + ------- + A list of two normalized values according to corresponding 1D + normalizers. + """ + norm_one, norm_two = self.norm_instances + + if clip is None: + clip = [norm_one.clip, norm_two.clip] + + return [norm_one(values[0], clip=clip[0]), + norm_two(values[1], clip=clip[1])] + + def inverse(self, values): + """ + Parameters + ---------- + values : array-like + A list of two values to be inverted + + Returns + ------- + A list of two unnormalized values + """ + norm_one, norm_two = self.norm_instances + + return [norm_one.inverse(values[0]), norm_two.inverse(values[1])] def rgb_to_hsv(arr): """ diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 721813e62f8..d18bb64ff95 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -272,6 +272,49 @@ def _mask_tester(norm_instance, vals): masked_array[0] = np.ma.masked assert_array_equal(masked_array.mask, norm_instance(masked_array).mask) +@pytest.mark.parametrize( + 'norm_instances, values, expected, clip', [ + ( + None, + [[0.2, 0.4, 0.5], [5, 7, 9, 10]], + [[0, 0.666667, 1], [0, 0.4, 0.8, 1]], + None + ), + ( + [mcolors.LogNorm(clip=True, vmax=5), mcolors.Normalize()], + [[1, 6], [5, 7, 9, 10]], + [[0, 1.0], [0, 0.4, 0.8, 1]], + None + ), + ( + [mcolors.PowerNorm(2, vmin=0, vmax=8, clip=None), + mcolors.PowerNorm(2, vmin=2, vmax=8, clip=True)], + [np.array([-0.5, 0, 2, 4, 8], dtype=float), + np.array([-0.5, 0, 1, 8, 16], dtype=float)], + [[0, 0, 1/16, 1/4, 1], [0, 0, 0, 1, 1]], + None + ), + ( + [mcolors.SymLogNorm(3, vmin=-30, vmax=5, linscale=1.2), + mcolors.PowerNorm(2, vmin=2, vmax=8, clip=False)], + [np.array([-30, -1, 2, 6], dtype=float), + np.array([-0.5, 0, 1, 8, 16], dtype=float)], + [[0., 0.53980074, 0.826991, 1.02758204], [0, 0, 0, 1, 1]], + [False, True] + ) + ], ids=[ + 'norm_is_None', + 'LogNorm_and_LinearNorm', + 'PowerNorm_clip_None_and_True', + 'SymLogNorm_and_PowerNorm_clip_in_call' + ] +) +def test_norm2d(norm_instances, values, expected, clip): + norm = mcolors.Norm2d(norm_instances=norm_instances) + ans1, ans2 = norm(values=values, clip=clip) + assert_array_almost_equal(ans1, expected[0]) + assert_array_almost_equal(ans2, expected[1]) + @image_comparison(baseline_images=['levels_and_colors'], extensions=['png']) From 4420d6cb8d31679aff6c61ce97649402aebd5fab Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sat, 10 Jun 2017 04:20:13 +0530 Subject: [PATCH 02/26] Change name of class to BivariateNorm, take normalizer instances separately --- lib/matplotlib/colors.py | 32 +++++++++++++++++--------------- lib/matplotlib/tests/test_colors.py | 18 ++++++++++-------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 1a5136c016b..4228005186a 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1346,21 +1346,27 @@ def __call__(self, value, clip=None): def inverse(self, value): return value -class Norm2d: +class BivariateNorm: """ Normalize a list of two values corresponding to two 1D normalizers """ - def __init__(self, norm_instances=None): + def __init__(self, norm1=None, norm2=None): """ Parameters ---------- - norm_instances : - A list of length two having instances of 1D normalizers + norm1 : + An instance of 1D normalizers + norm2 : + An instance of 1D normalizers """ - if norm_instances is None: - self.norm_instances = [Normalize(), Normalize()] + if norm1 is None: + self.norm1 = Normalize() else: - self.norm_instances = norm_instances + self.norm1 = norm1 + if norm2 is None: + self.norm2 = Normalize() + else: + self.norm2 = norm2 def __call__(self, values, clip=None): """ @@ -1378,13 +1384,11 @@ def __call__(self, values, clip=None): A list of two normalized values according to corresponding 1D normalizers. """ - norm_one, norm_two = self.norm_instances - if clip is None: - clip = [norm_one.clip, norm_two.clip] + clip = [self.norm1.clip, self.norm2.clip] - return [norm_one(values[0], clip=clip[0]), - norm_two(values[1], clip=clip[1])] + return [self.norm1(values[0], clip=clip[0]), + self.norm2(values[1], clip=clip[1])] def inverse(self, values): """ @@ -1397,9 +1401,7 @@ def inverse(self, values): ------- A list of two unnormalized values """ - norm_one, norm_two = self.norm_instances - - return [norm_one.inverse(values[0]), norm_two.inverse(values[1])] + return [self.norm1.inverse(values[0]), self.norm.inverse(values[1])] def rgb_to_hsv(arr): """ diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index d18bb64ff95..e93f7257c61 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -273,30 +273,32 @@ def _mask_tester(norm_instance, vals): assert_array_equal(masked_array.mask, norm_instance(masked_array).mask) @pytest.mark.parametrize( - 'norm_instances, values, expected, clip', [ + 'norm1, norm2, values, expected, clip', [ ( None, + None, [[0.2, 0.4, 0.5], [5, 7, 9, 10]], [[0, 0.666667, 1], [0, 0.4, 0.8, 1]], None ), ( - [mcolors.LogNorm(clip=True, vmax=5), mcolors.Normalize()], + mcolors.LogNorm(clip=True, vmax=5), + None, [[1, 6], [5, 7, 9, 10]], [[0, 1.0], [0, 0.4, 0.8, 1]], None ), ( - [mcolors.PowerNorm(2, vmin=0, vmax=8, clip=None), - mcolors.PowerNorm(2, vmin=2, vmax=8, clip=True)], + mcolors.PowerNorm(2, vmin=0, vmax=8, clip=None), + mcolors.PowerNorm(2, vmin=2, vmax=8, clip=True), [np.array([-0.5, 0, 2, 4, 8], dtype=float), np.array([-0.5, 0, 1, 8, 16], dtype=float)], [[0, 0, 1/16, 1/4, 1], [0, 0, 0, 1, 1]], None ), ( - [mcolors.SymLogNorm(3, vmin=-30, vmax=5, linscale=1.2), - mcolors.PowerNorm(2, vmin=2, vmax=8, clip=False)], + mcolors.SymLogNorm(3, vmin=-30, vmax=5, linscale=1.2), + mcolors.PowerNorm(2, vmin=2, vmax=8, clip=False), [np.array([-30, -1, 2, 6], dtype=float), np.array([-0.5, 0, 1, 8, 16], dtype=float)], [[0., 0.53980074, 0.826991, 1.02758204], [0, 0, 0, 1, 1]], @@ -309,8 +311,8 @@ def _mask_tester(norm_instance, vals): 'SymLogNorm_and_PowerNorm_clip_in_call' ] ) -def test_norm2d(norm_instances, values, expected, clip): - norm = mcolors.Norm2d(norm_instances=norm_instances) +def test_BivariateNorm(norm1, norm2, values, expected, clip): + norm = mcolors.BivariateNorm(norm1=norm1, norm2=norm2) ans1, ans2 = norm(values=values, clip=clip) assert_array_almost_equal(ans1, expected[0]) assert_array_almost_equal(ans2, expected[1]) From acc2fad2270437264795ca76c1f475c1dbee3faa Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 3 Jul 2017 15:45:27 +0530 Subject: [PATCH 03/26] Bivariate works with imshow, pcolor, pcolormesh, pcolorfast --- lib/matplotlib/axes/_axes.py | 96 +++++++++++++++++++++++++++++++++++++++----- lib/matplotlib/colors.py | 31 +++++++++++++- 2 files changed, 115 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index e2610703489..19ad5a112b1 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -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'" 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) @@ -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 @@ -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) + 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)) @@ -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( @@ -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) @@ -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' " \ + "or 'mcolors.BivariateNorm'" raise ValueError(msg) + collection.set_cmap(cmap) collection.set_norm(norm) collection.set_clim(vmin, vmax) @@ -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) @@ -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" diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 4228005186a..909da3715d4 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -855,6 +855,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) + + 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() + + class Normalize(object): """ A class which, when called, can normalize data into @@ -1387,8 +1413,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): """ @@ -1403,6 +1429,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 From d4753f97e2310cdc695d45a57990f2bb9f39474b Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 10 Jul 2017 01:55:10 +0530 Subject: [PATCH 04/26] add blank line between bivariate classes --- lib/matplotlib/colors.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 909da3715d4..d67a282fcb9 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1372,6 +1372,7 @@ def __call__(self, value, clip=None): def inverse(self, value): return value + class BivariateNorm: """ Normalize a list of two values corresponding to two 1D normalizers From 6b9833bbbcbb9b0f3cd773dd8f60856f96283347 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 10 Jul 2017 03:51:33 +0530 Subject: [PATCH 05/26] Add support for bivariate in scatter --- lib/matplotlib/axes/_axes.py | 17 +++++++++++++++-- lib/matplotlib/colors.py | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 19ad5a112b1..51e58000779 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3979,6 +3979,17 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, else: try: c_array = np.asanyarray(c, dtype=float) + if c_array.ndim > 1: + if cmap is None: + cmap = mcolors.BivariateColormap() + if norm is None: + norm = mcolors.BivariateNorm() + c_array = norm(c_array) + c_array[0] = c_array[0] * (cmap.N-1) + c_array[1] = c_array[1] * (cmap.N-1) + c_array = c_array.astype(int) + c_array = c_array[0] + cmap.N * c_array[1] + norm = mcolors.NoNorm() if c_array.shape in xy_shape: c = np.ma.ravel(c_array) else: @@ -4043,8 +4054,10 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, collection.update(kwargs) if colors is 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) collection.set_array(np.asarray(c)) collection.set_cmap(cmap) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index d67a282fcb9..258b4fa439e 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -856,7 +856,7 @@ def reversed(self, name=None): class BivariateColormap(Colormap): - def __init__(self, name, N=256): + def __init__(self, name='bivariate', N=256): Colormap.__init__(self, name, N) def _init(self): From dda25d580ec4189687dd02fe0055dd3656025e18 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Tue, 11 Jul 2017 23:37:04 +0530 Subject: [PATCH 06/26] Fix missing norm2 in BivaraiteNorm --- lib/matplotlib/colors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 258b4fa439e..eefe0b8dfa2 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1428,7 +1428,8 @@ def inverse(self, values): ------- A list of two unnormalized values """ - return [self.norm1.inverse(values[0]), self.norm.inverse(values[1])] + return np.asarray([self.norm1.inverse(values[0]), + self.norm2.inverse(values[1])]) def rgb_to_hsv(arr): From 1fce3e9b8a72bcfc1d7b0c2bbaee2f3e90079524 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Thu, 13 Jul 2017 00:06:59 +0530 Subject: [PATCH 07/26] Fixed bug in if condition bivariate mapping --- lib/matplotlib/axes/_axes.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 51e58000779..34fc461b924 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5165,8 +5165,9 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, self.set_aspect(aspect) temp = np.asarray(X) - if (temp.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)): + isBivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (temp.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() if norm is None: @@ -5222,8 +5223,9 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 1: C = np.asanyarray(args[0]) - if (C.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)): + isBivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() if norm is None: @@ -5246,8 +5248,9 @@ 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)): + isBivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() if norm is None: @@ -5803,8 +5806,9 @@ def pcolorfast(self, *args, **kwargs): C = np.asarray(args[-1]) - if (C.ndim == 3 and isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)): + isBivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() if norm is None: From 5b6ecf1b04867db50593426e818c1c6eaf99bc71 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Thu, 13 Jul 2017 12:26:21 +0530 Subject: [PATCH 08/26] Add autoscale, autoscale_None, scaled methods for BivariateNorm --- lib/matplotlib/colors.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index eefe0b8dfa2..24646432237 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1414,7 +1414,7 @@ def __call__(self, values, clip=None): if clip is None: clip = [self.norm1.clip, self.norm2.clip] - return np.array([self.norm1(values[0], clip=clip[0]), + return np.asarray([self.norm1(values[0], clip=clip[0]), self.norm2(values[1], clip=clip[1])]) def inverse(self, values): @@ -1431,6 +1431,22 @@ def inverse(self, values): return np.asarray([self.norm1.inverse(values[0]), self.norm2.inverse(values[1])]) + def autoscale(self, A): + """ + Set *vmin*, *vmax* to min, max of *A*. + """ + self.norm1.autoscale(A[0]) + self.norm2.autoscale(A[1]) + + def autoscale_None(self, A): + 'autoscale only None-valued vmin or vmax' + self.norm1.autoscale_None(A[0]) + self.norm2.autoscale_None(A[1]) + + def scaled(self): + 'return true if vmin and vmax set for both normalizers' + return self.norm1.scaled() and self.norm2.scaled() + def rgb_to_hsv(arr): """ From 061ecc9349012869f36cf06242e47f784d278988 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 17 Jul 2017 01:38:00 +0530 Subject: [PATCH 09/26] trying to show ticks and labels on both x and y axis --- lib/matplotlib/cm.py | 3 +- lib/matplotlib/colorbar.py | 560 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 560 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index bdf3e157565..b9b59bbbdd1 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -201,7 +201,8 @@ def __init__(self, norm=None, cmap=None): #: The Normalization instance of this ScalarMappable. self.norm = norm #: The Colormap instance of this ScalarMappable. - self.cmap = get_cmap(cmap) + # self.cmap = get_cmap(cmap) + self.cmap = cmap #: The last colorbar associated with this ScalarMappable. May be None. self.colorbar = None self.update_dict = {'array': False} diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 0fb4e3b47c1..a0d2b18c219 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -900,7 +900,562 @@ def remove(self): fig.delaxes(self.ax) -class Colorbar(ColorbarBase): +class Colorsquare(ColorbarBase): + def __init__(self, ax, cmap=None, + norm=None, + alpha=None, + values=None, + boundaries=None, + ticklocation='auto', + extend='neither', + spacing='uniform', # uniform or proportional + ticks=None, + format=None, + drawedges=False, + filled=True, + extendfrac=None, + extendrect=False, + xlabel='', + ylabel='', + ): + #: The axes that this colorbar lives in. + self.ax = ax + self._patch_ax() + if cmap is None: + cmap = colors.BivariateColormap() + if norm is None: + norm = colors.BivariateNorm() + self.alpha = alpha + cm.ScalarMappable.__init__(self, cmap=cmap, norm=norm) + self.values = values + self.boundaries = boundaries + self.extend = extend + self._inside = self._slice_dict[extend] + self.spacing = spacing + self.drawedges = drawedges + self.filled = filled + self.extendfrac = extendfrac + self.extendrect = extendrect + self.solids = None + self.lines = list() + self.outline = None + self.patch = None + self.dividers = None + + self.set_label(xlabel, ylabel) + if cbook.iterable(ticks): + self.locator = ticker.FixedLocator(ticks, nbins=len(ticks)) + else: + self.locator = ticks # Handle default in _ticker() + if format is None: + if isinstance(self.norm, colors.LogNorm): + self.formatter = ticker.LogFormatterSciNotation() + elif isinstance(self.norm, colors.SymLogNorm): + self.formatter = ticker.LogFormatterSciNotation( + linthresh=self.norm.linthresh) + else: + self.formatter = ticker.ScalarFormatter() + elif isinstance(format, six.string_types): + self.formatter = ticker.FormatStrFormatter(format) + else: + self.formatter = format # Assume it is a Formatter + # The rest is in a method so we can recalculate when clim changes. + self.config_axis() + self.draw_all() + + def draw_all(self): + ''' + Calculate any free parameters based on the current cmap and norm, + and do all the drawing. + ''' + + self._process_values() + self._find_range() + X, Y = self._mesh() + C = self._values[:, np.newaxis] + self._config_axes(X, Y) + if self.filled: + self._add_solids(X, Y, C) + + def config_axis(self): + ax = self.ax + + ax.yaxis.set_label_position('right') + ax.yaxis.set_ticks_position('right') + + ax.xaxis.set_label_position('bottom') + ax.xaxis.set_ticks_position('bottom') + + self._set_label() + + def update_ticks(self): + """ + Force the update of the ticks and ticklabels. This must be + called whenever the tick locator and/or tick formatter changes. + """ + ax = self.ax + # xticks, yticks, xticklabels, yticklabels, offset_string = self._ticker() + yticks, yticklabels, offset_string = self._ticker() + xticks = [] + xticklabels = '' + ax.yaxis.set_ticks(yticks) + ax.set_yticklabels(yticklabels) + ax.yaxis.get_major_formatter().set_offset_string(offset_string) + ax.xaxis.set_ticks(xticks) + ax.set_xticklabels(xticklabels) + ax.xaxis.get_major_formatter().set_offset_string(offset_string) + + def set_ticks(self, xticks, yticks, update_ticks=True): + """ + Set tick locations. + + Parameters + ---------- + ticks : {None, sequence, :class:`~matplotlib.ticker.Locator` instance} + If None, a default Locator will be used. + + update_ticks : {True, False}, optional + If True, tick locations are updated immediately. If False, + use :meth:`update_ticks` to manually update the ticks. + + """ + if cbook.iterable(xticks): + self.xlocator = ticker.FixedLocator(xticks, nbins=len(ticks)) + else: + self.xlocator = xticks + + if cbook.iterable(yticks): + self.ylocator = ticker.FixedLocator(yticks, nbins=len(ticks)) + else: + self.ylocator = yticks + + if update_ticks: + self.update_ticks() + self.stale = True + + def get_ticks(self, minor=False): + """Return the x ticks as a list of locations""" + return self._tick_data_values + + def set_ticklabels(self, ticklabels, update_ticks=True): + """ + set tick labels. Tick labels are updated immediately unless + update_ticks is *False*. To manually update the ticks, call + *update_ticks* method explicitly. + """ + if isinstance(self.locator, ticker.FixedLocator): + self.formatter = ticker.FixedFormatter(ticklabels) + if update_ticks: + self.update_ticks() + else: + warnings.warn("set_ticks() must have been called.") + self.stale = True + + def _config_axes(self, X, Y): + ''' + Make an axes patch and outline. + ''' + ax = self.ax + ax.set_frame_on(False) + ax.set_navigate(False) + xy = self._outline(X, Y) + ax.update_datalim(xy) + ax.set_xlim(*ax.dataLim.intervalx) + ax.set_ylim(*ax.dataLim.intervaly) + if self.outline is not None: + self.outline.remove() + self.outline = mpatches.Polygon( + xy, edgecolor=mpl.rcParams['axes.edgecolor'], + facecolor='none', + linewidth=mpl.rcParams['axes.linewidth'], + closed=True, + zorder=2) + ax.add_artist(self.outline) + self.outline.set_clip_box(None) + self.outline.set_clip_path(None) + c = mpl.rcParams['axes.facecolor'] + if self.patch is not None: + self.patch.remove() + self.patch = mpatches.Polygon(xy, edgecolor=c, + facecolor=c, + linewidth=0.01, + zorder=-1) + ax.add_artist(self.patch) + + self.update_ticks() + + def _set_label(self): + self.ax.set_ylabel(self._ylabel, **self._labelkw) + self.ax.set_xlabel(self._xlabel, **self._labelkw) + self.stale = True + + def set_label(self, xlabel, ylabel, **kw): + ''' + Label the long axis of the colorbar + ''' + self._xlabel = '%s' % (xlabel, ) + self._ylabel = '%s' % (ylabel, ) + self._labelkw = kw + self._set_label() + + def _outline(self, X, Y): + ''' + Return *x*, *y* arrays of colorbar bounding polygon, + taking orientation into account. + ''' + N = X.shape[0] + ii = [0, 1, N - 2, N - 1, 2 * N - 1, 2 * N - 2, N + 1, N, 0] + x = np.take(np.ravel(np.transpose(X)), ii) + y = np.take(np.ravel(np.transpose(Y)), ii) + x = x.reshape((len(x), 1)) + y = y.reshape((len(y), 1)) + # if self.orientation == 'horizontal': + # return np.hstack((y, x)) + return np.hstack((x, y)) + + def _edges(self, X, Y): + ''' + Return the separator line segments; helper for _add_solids. + ''' + pass + + def _add_solids(self, X, Y, C): + ''' + Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`; + optionally add separators. + ''' + # if self.orientation == 'vertical': + args = (X, Y, C) + # else: + # args = (np.transpose(Y), np.transpose(X), np.transpose(C)) + kw = dict(cmap=self.cmap, + norm=self.norm, + alpha=self.alpha, + edgecolors='None') + # Save, set, and restore hold state to keep pcolor from + # clearing the axes. Ordinarily this will not be needed, + # since the axes object should already have hold set. + _hold = self.ax._hold + self.ax._hold = True + col = self.ax.pcolormesh(*args, **kw) + self.ax._hold = _hold + #self.add_observer(col) # We should observe, not be observed... + + if self.solids is not None: + self.solids.remove() + self.solids = col + if self.dividers is not None: + self.dividers.remove() + self.dividers = None + if self.drawedges: + linewidths = (0.5 * mpl.rcParams['axes.linewidth'],) + """ + self.dividers = collections.LineCollection(self._edges(X, Y), + colors=(mpl.rcParams['axes.edgecolor'],), + linewidths=linewidths) + self.ax.add_collection(self.dividers) + """ + elif len(self._y) >= self.n_rasterize: + self.solids.set_rasterized(True) + + def add_lines(self, levels, colors, linewidths, erase=True): + ''' + Draw lines on the colorbar. + + *colors* and *linewidths* must be scalars or + sequences the same length as *levels*. + + Set *erase* to False to add lines without first + removing any previously added lines. + ''' + y = self._locate(levels) + igood = (y < 1.001) & (y > -0.001) + y = y[igood] + if cbook.iterable(colors): + colors = np.asarray(colors)[igood] + if cbook.iterable(linewidths): + linewidths = np.asarray(linewidths)[igood] + N = len(y) + x = np.array([0.0, 1.0]) + X, Y = np.meshgrid(x, y) + if self.orientation == 'vertical': + xy = [list(zip(X[i], Y[i])) for i in xrange(N)] + else: + xy = [list(zip(Y[i], X[i])) for i in xrange(N)] + col = collections.LineCollection(xy, linewidths=linewidths) + + if erase and self.lines: + for lc in self.lines: + lc.remove() + self.lines = [] + self.lines.append(col) + col.set_color(colors) + self.ax.add_collection(col) + self.stale = True + + def _ticker(self): + ''' + Return the sequence of ticks (colorbar data locations), + ticklabels (strings), and the corresponding offset string. + ''' + locator = self.locator + formatter = self.formatter + if locator is None: + if self.boundaries is None: + if isinstance(self.norm, colors.NoNorm): + nv = len(self._values) + base = 1 + int(nv / 10) + locator = ticker.IndexLocator(base=base, offset=0) + elif isinstance(self.norm, colors.BoundaryNorm): + b = self.norm.boundaries + locator = ticker.FixedLocator(b, nbins=10) + elif isinstance(self.norm, colors.LogNorm): + locator = ticker.LogLocator(subs='all') + elif isinstance(self.norm, colors.SymLogNorm): + # The subs setting here should be replaced + # by logic in the locator. + locator = ticker.SymmetricalLogLocator( + subs=np.arange(1, 10), + linthresh=self.norm.linthresh, + base=10) + else: + if mpl.rcParams['_internal.classic_mode']: + locator = ticker.MaxNLocator() + else: + locator = ticker.AutoLocator() + else: + b = self._boundaries[self._inside] + locator = ticker.FixedLocator(b, nbins=10) + if isinstance(self.norm, colors.NoNorm) and self.boundaries is None: + intv = self._values[0], self._values[-1] + else: + intv = self.vmin, self.vmax + locator.create_dummy_axis(minpos=intv[0]) + formatter.create_dummy_axis(minpos=intv[0]) + locator.set_view_interval(*intv) + locator.set_data_interval(*intv) + formatter.set_view_interval(*intv) + formatter.set_data_interval(*intv) + + b = np.array(locator()) + if isinstance(locator, ticker.LogLocator): + eps = 1e-10 + b = b[(b <= intv[1] * (1 + eps)) & (b >= intv[0] * (1 - eps))] + else: + eps = (intv[1] - intv[0]) * 1e-10 + b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)] + self._tick_data_values = b + ticks = self._locate(b) + formatter.set_locs(b) + ticklabels = [formatter(t, i) for i, t in enumerate(b)] + offset_string = formatter.get_offset() + return ticks, ticklabels, offset_string + + def _process_values(self, b=None): + ''' + Set the :attr:`_boundaries` and :attr:`_values` attributes + based on the input boundaries and values. Input boundaries + can be *self.boundaries* or the argument *b*. + ''' + if b is None: + b = self.boundaries + if b is not None: + self._boundaries = np.asarray(b, dtype=float) + if self.values is None: + self._values = 0.5 * (self._boundaries[:-1] + + self._boundaries[1:]) + if isinstance(self.norm, colors.NoNorm): + self._values = (self._values + 0.00001).astype(np.int16) + return + self._values = np.array(self.values) + return + if self.values is not None: + self._values = np.array(self.values) + if self.boundaries is None: + b = np.zeros(len(self.values) + 1, 'd') + b[1:-1] = 0.5 * (self._values[:-1] - self._values[1:]) + b[0] = 2.0 * b[1] - b[2] + b[-1] = 2.0 * b[-2] - b[-3] + self._boundaries = b + return + self._boundaries = np.array(self.boundaries) + return + # Neither boundaries nor values are specified; + # make reasonable ones based on cmap and norm. + if isinstance(self.norm, colors.NoNorm): + b = self._uniform_y(self.cmap.N + 1) * self.cmap.N - 0.5 + v = np.zeros((len(b) - 1,), dtype=np.int16) + v[self._inside] = np.arange(self.cmap.N, dtype=np.int16) + if self._extend_lower(): + v[0] = -1 + if self._extend_upper(): + v[-1] = self.cmap.N + self._boundaries = b + self._values = v + return + elif isinstance(self.norm, colors.BoundaryNorm): + b = list(self.norm.boundaries) + if self._extend_lower(): + b = [b[0] - 1] + b + if self._extend_upper(): + b = b + [b[-1] + 1] + b = np.array(b) + v = np.zeros((len(b) - 1,), dtype=float) + bi = self.norm.boundaries + v[self._inside] = 0.5 * (bi[:-1] + bi[1:]) + if self._extend_lower(): + v[0] = b[0] - 1 + if self._extend_upper(): + v[-1] = b[-1] + 1 + self._boundaries = b + self._values = v + return + else: + if not self.norm.scaled(): + self.norm.vmin = 0 + self.norm.vmax = 1 + + self.norm.vmin, self.norm.vmax = mtransforms.nonsingular( + self.norm.vmin, + self.norm.vmax, + expander=0.1) + + b = self.norm.inverse(self._uniform_y(self.cmap.N + 1)) + + if isinstance(self.norm, colors.LogNorm): + # If using a lognorm, ensure extensions don't go negative + if self._extend_lower(): + b[0] = 0.9 * b[0] + if self._extend_upper(): + b[-1] = 1.1 * b[-1] + else: + if self._extend_lower(): + b[0] = b[0] - 1 + if self._extend_upper(): + b[-1] = b[-1] + 1 + self._process_values(b) + + def _find_range(self): + ''' + Set :attr:`vmin` and :attr:`vmax` attributes to the first and + last boundary excluding extended end boundaries. + ''' + b = self._boundaries[self._inside] + self.vmin = b[0] + self.vmax = b[-1] + + def _central_N(self): + '''number of boundaries **before** extension of ends''' + nb = len(self._boundaries) + if self.extend == 'both': + nb -= 2 + elif self.extend in ('min', 'max'): + nb -= 1 + return nb + + def _extended_N(self): + ''' + Based on the colormap and extend variable, return the + number of boundaries. + ''' + N = self.cmap.N + 1 + if self.extend == 'both': + N += 2 + elif self.extend in ('min', 'max'): + N += 1 + return N + + def _proportional_y(self): + ''' + Return colorbar data coordinates for the boundaries of + a proportional colorbar. + ''' + if isinstance(self.norm, colors.BoundaryNorm): + y = (self._boundaries - self._boundaries[0]) + y = y / (self._boundaries[-1] - self._boundaries[0]) + else: + y = self.norm(self._boundaries.copy()) + y = np.ma.filled(y, np.nan) + if self.extend == 'min': + # Exclude leftmost interval of y. + clen = y[-1] - y[1] + automin = (y[2] - y[1]) / clen + automax = (y[-1] - y[-2]) / clen + elif self.extend == 'max': + # Exclude rightmost interval in y. + clen = y[-2] - y[0] + automin = (y[1] - y[0]) / clen + automax = (y[-2] - y[-3]) / clen + elif self.extend == 'both': + # Exclude leftmost and rightmost intervals in y. + clen = y[-2] - y[1] + automin = (y[2] - y[1]) / clen + automax = (y[-2] - y[-3]) / clen + if self.extend in ('both', 'min', 'max'): + extendlength = self._get_extension_lengths(self.extendfrac, + automin, automax, + default=0.05) + if self.extend in ('both', 'min'): + y[0] = 0. - extendlength[0] + if self.extend in ('both', 'max'): + y[-1] = 1. + extendlength[1] + yi = y[self._inside] + norm = colors.Normalize(yi[0], yi[-1]) + y[self._inside] = np.ma.filled(norm(yi), np.nan) + return y + + def _mesh(self): + ''' + Return X,Y, the coordinate arrays for the colorbar pcolormesh. + These are suitable for a vertical colorbar; swapping and + transposition for a horizontal colorbar are done outside + this function. + ''' + x = np.array([0.0, 1.0]) + if self.spacing == 'uniform': + y = self._uniform_y(self._central_N()) + else: + y = self._proportional_y() + self._y = y + X, Y = np.meshgrid(x, y) + if self._extend_lower() and not self.extendrect: + X[0, :] = 0.5 + if self._extend_upper() and not self.extendrect: + X[-1, :] = 0.5 + return X, Y + + def _locate(self, x): + ''' + Given a set of color data values, return their + corresponding colorbar data coordinates. + ''' + if isinstance(self.norm, (colors.NoNorm, colors.BoundaryNorm)): + b = self._boundaries + xn = x + else: + # Do calculations using normalized coordinates so + # as to make the interpolation more accurate. + b = self.norm(self._boundaries, clip=False).filled() + xn = self.norm(x, clip=False).filled() + + # The rest is linear interpolation with extrapolation at ends. + ii = np.searchsorted(b, xn) + i0 = ii - 1 + itop = (ii == len(b)) + ibot = (ii == 0) + i0[itop] -= 1 + ii[itop] -= 1 + i0[ibot] += 1 + ii[ibot] += 1 + + db = np.take(b, ii) - np.take(b, i0) + y = self._y + dy = np.take(y, ii) - np.take(y, i0) + z = np.take(y, i0) + (xn - np.take(b, i0)) * dy / db + return z + + +class Colorbar(Colorsquare): """ This class connects a :class:`ColorbarBase` to a :class:`~matplotlib.cm.ScalarMappable` such as a @@ -940,7 +1495,7 @@ def __init__(self, ax, mappable, **kw): if isinstance(mappable, martist.Artist): kw['alpha'] = mappable.get_alpha() - ColorbarBase.__init__(self, ax, **kw) + Colorsquare.__init__(self, ax, **kw) def on_mappable_changed(self, mappable): """ @@ -1362,6 +1917,7 @@ def colorbar_factory(cax, mappable, **kwargs): and any([hatch is not None for hatch in mappable.hatches])): cb = ColorbarPatch(cax, mappable, **kwargs) else: + temp = kwargs.pop('orientation', 'vert') cb = Colorbar(cax, mappable, **kwargs) cid = mappable.callbacksSM.connect('changed', cb.on_mappable_changed) From dcb030ff521651d49a270045f154d34ba0d55cf2 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 17 Jul 2017 12:47:55 +0530 Subject: [PATCH 10/26] commented outline code, changed gridspace to show sqaure colorbar --- lib/matplotlib/colorbar.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index a0d2b18c219..62445db3363 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1020,12 +1020,12 @@ def set_ticks(self, xticks, yticks, update_ticks=True): """ if cbook.iterable(xticks): - self.xlocator = ticker.FixedLocator(xticks, nbins=len(ticks)) + self.xlocator = ticker.FixedLocator(xticks, nbins=len(xticks)) else: self.xlocator = xticks if cbook.iterable(yticks): - self.ylocator = ticker.FixedLocator(yticks, nbins=len(ticks)) + self.ylocator = ticker.FixedLocator(yticks, nbins=len(yticks)) else: self.ylocator = yticks @@ -1037,14 +1037,15 @@ def get_ticks(self, minor=False): """Return the x ticks as a list of locations""" return self._tick_data_values - def set_ticklabels(self, ticklabels, update_ticks=True): + def set_ticklabels(self, xticklabels, yticklabels, update_ticks=True): """ set tick labels. Tick labels are updated immediately unless update_ticks is *False*. To manually update the ticks, call *update_ticks* method explicitly. """ if isinstance(self.locator, ticker.FixedLocator): - self.formatter = ticker.FixedFormatter(ticklabels) + self.xformatter = ticker.FixedFormatter(xticklabels) + self.yformatter = ticker.FixedFormatter(yticklabels) if update_ticks: self.update_ticks() else: @@ -1056,14 +1057,15 @@ def _config_axes(self, X, Y): Make an axes patch and outline. ''' ax = self.ax - ax.set_frame_on(False) + # ax.set_frame_on(False) ax.set_navigate(False) - xy = self._outline(X, Y) - ax.update_datalim(xy) - ax.set_xlim(*ax.dataLim.intervalx) - ax.set_ylim(*ax.dataLim.intervaly) - if self.outline is not None: - self.outline.remove() + # xy = self._outline(X, Y) + # ax.update_datalim(xy) + # ax.set_xlim(*ax.dataLim.intervalx) + # ax.set_ylim(*ax.dataLim.intervaly) + # if self.outline is not None: + # self.outline.remove() + """ self.outline = mpatches.Polygon( xy, edgecolor=mpl.rcParams['axes.edgecolor'], facecolor='none', @@ -1073,6 +1075,7 @@ def _config_axes(self, X, Y): ax.add_artist(self.outline) self.outline.set_clip_box(None) self.outline.set_clip_path(None) + c = mpl.rcParams['axes.facecolor'] if self.patch is not None: self.patch.remove() @@ -1081,7 +1084,7 @@ def _config_axes(self, X, Y): linewidth=0.01, zorder=-1) ax.add_artist(self.patch) - + """ self.update_ticks() def _set_label(self): @@ -1768,9 +1771,9 @@ def make_axes_gridspec(parent, **kw): orientation = kw.setdefault('orientation', 'vertical') kw['ticklocation'] = 'auto' - fraction = kw.pop('fraction', 0.15) + fraction = kw.pop('fraction', 0.35) shrink = kw.pop('shrink', 1.0) - aspect = kw.pop('aspect', 20) + aspect = kw.pop('aspect', 1) x1 = 1.0 - fraction From e84fa25e714a8e0830104d845d2b77d66c914545 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 17 Jul 2017 15:06:46 +0530 Subject: [PATCH 11/26] 2d colorbar printing now --- lib/matplotlib/colorbar.py | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 62445db3363..74c0bae946f 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -972,7 +972,8 @@ def draw_all(self): self._process_values() self._find_range() X, Y = self._mesh() - C = self._values[:, np.newaxis] + C = np.array(X * 256 * 256 + Y * 256, dtype='int64') + # C = self._values[:, np.newaxis] self._config_axes(X, Y) if self.filled: self._add_solids(X, Y, C) @@ -1101,21 +1102,6 @@ def set_label(self, xlabel, ylabel, **kw): self._labelkw = kw self._set_label() - def _outline(self, X, Y): - ''' - Return *x*, *y* arrays of colorbar bounding polygon, - taking orientation into account. - ''' - N = X.shape[0] - ii = [0, 1, N - 2, N - 1, 2 * N - 1, 2 * N - 2, N + 1, N, 0] - x = np.take(np.ravel(np.transpose(X)), ii) - y = np.take(np.ravel(np.transpose(Y)), ii) - x = x.reshape((len(x), 1)) - y = y.reshape((len(y), 1)) - # if self.orientation == 'horizontal': - # return np.hstack((y, x)) - return np.hstack((x, y)) - def _edges(self, X, Y): ''' Return the separator line segments; helper for _add_solids. @@ -1414,11 +1400,11 @@ def _mesh(self): transposition for a horizontal colorbar are done outside this function. ''' - x = np.array([0.0, 1.0]) if self.spacing == 'uniform': - y = self._uniform_y(self._central_N()) + x = y = self._uniform_y(self._central_N()) else: - y = self._proportional_y() + x = y = self._proportional_y() + self._x = x self._y = y X, Y = np.meshgrid(x, y) if self._extend_lower() and not self.extendrect: From 4608baae0c103def280b32749c3150972c456382 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sun, 23 Jul 2017 03:14:48 +0530 Subject: [PATCH 12/26] Add code for Colorsquare --- lib/matplotlib/colorbar.py | 591 ++++++++++++++++++++------------------------- 1 file changed, 262 insertions(+), 329 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 74c0bae946f..dd567e439e4 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -900,21 +900,21 @@ def remove(self): fig.delaxes(self.ax) -class Colorsquare(ColorbarBase): +class ColorsquareBase(cm.ScalarMappable): + n_rasterize = 50 # rasterize solids if number of colors >= n_rasterize def __init__(self, ax, cmap=None, norm=None, alpha=None, - values=None, - boundaries=None, - ticklocation='auto', - extend='neither', - spacing='uniform', # uniform or proportional - ticks=None, - format=None, + xvalues=None, + yvalues=None, + xboundaries=None, + yboundaries=None, + xticks=None, + yticks=None, + xformat=None, + yformat=None, drawedges=False, filled=True, - extendfrac=None, - extendrect=False, xlabel='', ylabel='', ): @@ -927,59 +927,78 @@ def __init__(self, ax, cmap=None, norm = colors.BivariateNorm() self.alpha = alpha cm.ScalarMappable.__init__(self, cmap=cmap, norm=norm) - self.values = values - self.boundaries = boundaries - self.extend = extend - self._inside = self._slice_dict[extend] - self.spacing = spacing + self.xvalues = xvalues + self.yvalues = yvalues + self.xboundaries = xboundaries + self.yboundaries = yboundaries + self._inside = slice(0, None) self.drawedges = drawedges self.filled = filled - self.extendfrac = extendfrac - self.extendrect = extendrect self.solids = None self.lines = list() - self.outline = None - self.patch = None self.dividers = None - self.set_label(xlabel, ylabel) - if cbook.iterable(ticks): - self.locator = ticker.FixedLocator(ticks, nbins=len(ticks)) + + if cbook.iterable(xticks): + self.xlocator = ticker.FixedLocator(xticks, nbins=len(xticks)) else: - self.locator = ticks # Handle default in _ticker() - if format is None: - if isinstance(self.norm, colors.LogNorm): - self.formatter = ticker.LogFormatterSciNotation() - elif isinstance(self.norm, colors.SymLogNorm): - self.formatter = ticker.LogFormatterSciNotation( - linthresh=self.norm.linthresh) + self.xlocator = xticks # Handle default in _ticker() + + if cbook.iterable(yticks): + self.ylocator = ticker.FixedLocator(yticks, nbins=len(yticks)) + else: + self.ylocator = yticks + + if xformat is None: + if isinstance(self.norm.norm1, colors.LogNorm): + self.xformatter = ticker.LogFormatterSciNotation() + elif isinstance(self.norm.norm1, colors.SymLogNorm): + self.xformatter = ticker.LogFormatterSciNotation( + linthresh=self.norm.norm1.linthresh) else: - self.formatter = ticker.ScalarFormatter() - elif isinstance(format, six.string_types): - self.formatter = ticker.FormatStrFormatter(format) + self.xformatter = ticker.ScalarFormatter() + elif isinstance(xformat, six.string_types): + self.xformatter = ticker.FormatStrFormatter(xformat) else: - self.formatter = format # Assume it is a Formatter + self.xformatter = xformat # Assume it is a Formatter + + if yformat is None: + if isinstance(self.norm.norm2, colors.LogNorm): + self.yformatter = ticker.LogFormatterSciNotation() + elif isinstance(self.norm.norm2, colors.SymLogNorm): + self.yformatter = ticker.LogFormatterSciNotation( + linthresh=self.norm.norm2.linthresh) + else: + self.yformatter = ticker.ScalarFormatter() + elif isinstance(yformat, six.string_types): + self.yformatter = ticker.FormatStrFormatter(yformat) + else: + self.yformatter = yformat # Assume it is a Formatter + # The rest is in a method so we can recalculate when clim changes. self.config_axis() self.draw_all() - def draw_all(self): - ''' - Calculate any free parameters based on the current cmap and norm, - and do all the drawing. - ''' + def _patch_ax(self): + # bind some methods to the axes to warn users + # against using those methods. + self.ax.set_xticks = _set_ticks_on_axis_warn + self.ax.set_yticks = _set_ticks_on_axis_warn - self._process_values() - self._find_range() + def draw_all(self): + normx = self.norm.norm1 + normy = self.norm.norm2 + self._xvalues, self._xboundaries = self._process_values(norm=normx) + self._yvalues, self._yboundaries = self._process_values(norm=normy) X, Y = self._mesh() - C = np.array(X * 256 * 256 + Y * 256, dtype='int64') - # C = self._values[:, np.newaxis] - self._config_axes(X, Y) + CX, CY = np.meshgrid(self._xvalues, self._yvalues) + self.update_ticks() if self.filled: - self._add_solids(X, Y, C) + self._add_solids(X, Y, [CX, CY]) def config_axis(self): ax = self.ax + ax.set_navigate(False) ax.yaxis.set_label_position('right') ax.yaxis.set_ticks_position('right') @@ -995,31 +1014,18 @@ def update_ticks(self): called whenever the tick locator and/or tick formatter changes. """ ax = self.ax - # xticks, yticks, xticklabels, yticklabels, offset_string = self._ticker() - yticks, yticklabels, offset_string = self._ticker() - xticks = [] - xticklabels = '' - ax.yaxis.set_ticks(yticks) - ax.set_yticklabels(yticklabels) - ax.yaxis.get_major_formatter().set_offset_string(offset_string) + xticks, xticklabels, xoffset_string = self._ticker(self.norm.norm1) + yticks, yticklabels, yoffset_string = self._ticker(self.norm.norm2) + ax.xaxis.set_ticks(xticks) ax.set_xticklabels(xticklabels) - ax.xaxis.get_major_formatter().set_offset_string(offset_string) - - def set_ticks(self, xticks, yticks, update_ticks=True): - """ - Set tick locations. - - Parameters - ---------- - ticks : {None, sequence, :class:`~matplotlib.ticker.Locator` instance} - If None, a default Locator will be used. + ax.xaxis.get_major_formatter().set_offset_string(xoffset_string) - update_ticks : {True, False}, optional - If True, tick locations are updated immediately. If False, - use :meth:`update_ticks` to manually update the ticks. + ax.yaxis.set_ticks(yticks) + ax.set_yticklabels(yticklabels) + ax.yaxis.get_major_formatter().set_offset_string(yoffset_string) - """ + def set_ticks(self, xticks, yticks, update_ticks=True): if cbook.iterable(xticks): self.xlocator = ticker.FixedLocator(xticks, nbins=len(xticks)) else: @@ -1034,59 +1040,28 @@ def set_ticks(self, xticks, yticks, update_ticks=True): self.update_ticks() self.stale = True - def get_ticks(self, minor=False): - """Return the x ticks as a list of locations""" - return self._tick_data_values - - def set_ticklabels(self, xticklabels, yticklabels, update_ticks=True): + def set_ticklabels(self, xticklabels=None, yticklabels=None, + update_ticks=True): """ set tick labels. Tick labels are updated immediately unless update_ticks is *False*. To manually update the ticks, call *update_ticks* method explicitly. """ - if isinstance(self.locator, ticker.FixedLocator): - self.xformatter = ticker.FixedFormatter(xticklabels) - self.yformatter = ticker.FixedFormatter(yticklabels) + if xticklabels is not None or yticklabels is not None: + if isinstance(self.xlocator, ticker.FixedLocator): + self.xformatter = ticker.FixedFormatter(xticklabels) + + if isinstance(self.ylocator, ticker.FixedLocator): + self.yformatter = ticker.FixedFormatter(yticklabels) + if update_ticks: self.update_ticks() - else: - warnings.warn("set_ticks() must have been called.") - self.stale = True - def _config_axes(self, X, Y): - ''' - Make an axes patch and outline. - ''' - ax = self.ax - # ax.set_frame_on(False) - ax.set_navigate(False) - # xy = self._outline(X, Y) - # ax.update_datalim(xy) - # ax.set_xlim(*ax.dataLim.intervalx) - # ax.set_ylim(*ax.dataLim.intervaly) - # if self.outline is not None: - # self.outline.remove() - """ - self.outline = mpatches.Polygon( - xy, edgecolor=mpl.rcParams['axes.edgecolor'], - facecolor='none', - linewidth=mpl.rcParams['axes.linewidth'], - closed=True, - zorder=2) - ax.add_artist(self.outline) - self.outline.set_clip_box(None) - self.outline.set_clip_path(None) + self.stale = True + return - c = mpl.rcParams['axes.facecolor'] - if self.patch is not None: - self.patch.remove() - self.patch = mpatches.Polygon(xy, edgecolor=c, - facecolor=c, - linewidth=0.01, - zorder=-1) - ax.add_artist(self.patch) - """ - self.update_ticks() + warnings.warn("set_ticks() must have been called.") + self.stale = True def _set_label(self): self.ax.set_ylabel(self._ylabel, **self._labelkw) @@ -1095,7 +1070,7 @@ def _set_label(self): def set_label(self, xlabel, ylabel, **kw): ''' - Label the long axis of the colorbar + Label the axes of the colorbar ''' self._xlabel = '%s' % (xlabel, ) self._ylabel = '%s' % (ylabel, ) @@ -1106,17 +1081,16 @@ def _edges(self, X, Y): ''' Return the separator line segments; helper for _add_solids. ''' - pass + N = X.shape[0]. + return [list(zip(X[i], Y[i])) for i in xrange(1, N - 1)] + + [list(zip(Y[i], X[i])) for i in xrange(1, N - 1)] def _add_solids(self, X, Y, C): ''' Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`; optionally add separators. ''' - # if self.orientation == 'vertical': args = (X, Y, C) - # else: - # args = (np.transpose(Y), np.transpose(X), np.transpose(C)) kw = dict(cmap=self.cmap, norm=self.norm, alpha=self.alpha, @@ -1128,7 +1102,6 @@ def _add_solids(self, X, Y, C): self.ax._hold = True col = self.ax.pcolormesh(*args, **kw) self.ax._hold = _hold - #self.add_observer(col) # We should observe, not be observed... if self.solids is not None: self.solids.remove() @@ -1138,74 +1111,49 @@ def _add_solids(self, X, Y, C): self.dividers = None if self.drawedges: linewidths = (0.5 * mpl.rcParams['axes.linewidth'],) - """ self.dividers = collections.LineCollection(self._edges(X, Y), colors=(mpl.rcParams['axes.edgecolor'],), linewidths=linewidths) self.ax.add_collection(self.dividers) - """ - elif len(self._y) >= self.n_rasterize: + elif(len(self._y) >= self.n_rasterize + or len(self._x) >= self.n_rasterize): self.solids.set_rasterized(True) - def add_lines(self, levels, colors, linewidths, erase=True): - ''' - Draw lines on the colorbar. - - *colors* and *linewidths* must be scalars or - sequences the same length as *levels*. - - Set *erase* to False to add lines without first - removing any previously added lines. - ''' - y = self._locate(levels) - igood = (y < 1.001) & (y > -0.001) - y = y[igood] - if cbook.iterable(colors): - colors = np.asarray(colors)[igood] - if cbook.iterable(linewidths): - linewidths = np.asarray(linewidths)[igood] - N = len(y) - x = np.array([0.0, 1.0]) - X, Y = np.meshgrid(x, y) - if self.orientation == 'vertical': - xy = [list(zip(X[i], Y[i])) for i in xrange(N)] - else: - xy = [list(zip(Y[i], X[i])) for i in xrange(N)] - col = collections.LineCollection(xy, linewidths=linewidths) - - if erase and self.lines: - for lc in self.lines: - lc.remove() - self.lines = [] - self.lines.append(col) - col.set_color(colors) - self.ax.add_collection(col) - self.stale = True - - def _ticker(self): + def _ticker(self, norm): ''' Return the sequence of ticks (colorbar data locations), ticklabels (strings), and the corresponding offset string. ''' - locator = self.locator - formatter = self.formatter + if norm is self.norm.norm1: + _values = self._xvalues + _boundaries = self._xboundaries + boundaries = self.xboundaries + locator = self.xlocator + formatter = self.xformatter + else: + _values = self._yvalues + _boundaries = self._yboundaries + boundaries = self.yboundaries + locator = self.ylocator + formatter = self.yformatter + if locator is None: - if self.boundaries is None: - if isinstance(self.norm, colors.NoNorm): - nv = len(self._values) + if boundaries is None: + if isinstance(norm, colors.NoNorm): + nv = len(_values) base = 1 + int(nv / 10) locator = ticker.IndexLocator(base=base, offset=0) - elif isinstance(self.norm, colors.BoundaryNorm): - b = self.norm.boundaries + elif isinstance(norm, colors.BoundaryNorm): + b = norm.boundaries locator = ticker.FixedLocator(b, nbins=10) - elif isinstance(self.norm, colors.LogNorm): + elif isinstance(norm, colors.LogNorm): locator = ticker.LogLocator(subs='all') - elif isinstance(self.norm, colors.SymLogNorm): + elif isinstance(norm, colors.SymLogNorm): # The subs setting here should be replaced # by logic in the locator. locator = ticker.SymmetricalLogLocator( subs=np.arange(1, 10), - linthresh=self.norm.linthresh, + linthresh=norm.linthresh, base=10) else: if mpl.rcParams['_internal.classic_mode']: @@ -1213,12 +1161,13 @@ def _ticker(self): else: locator = ticker.AutoLocator() else: - b = self._boundaries[self._inside] + b = _boundaries[self._inside] locator = ticker.FixedLocator(b, nbins=10) - if isinstance(self.norm, colors.NoNorm) and self.boundaries is None: - intv = self._values[0], self._values[-1] + if isinstance(norm, colors.NoNorm) and boundaries is None: + intv = _values[0], _values[-1] else: - intv = self.vmin, self.vmax + b = _boundaries[self._inside] + intv = b[0], b[-1] locator.create_dummy_axis(minpos=intv[0]) formatter.create_dummy_axis(minpos=intv[0]) locator.set_view_interval(*intv) @@ -1233,199 +1182,96 @@ def _ticker(self): else: eps = (intv[1] - intv[0]) * 1e-10 b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)] - self._tick_data_values = b - ticks = self._locate(b) + # self._tick_data_values = b + ticks = self._locate(b, norm) formatter.set_locs(b) ticklabels = [formatter(t, i) for i, t in enumerate(b)] offset_string = formatter.get_offset() return ticks, ticklabels, offset_string - def _process_values(self, b=None): - ''' - Set the :attr:`_boundaries` and :attr:`_values` attributes - based on the input boundaries and values. Input boundaries - can be *self.boundaries* or the argument *b*. - ''' + def _process_values(self, b=None, norm=None): + if norm is self.norm.norm1: + boundaries = self.xboundaries + values = self.xvalues + else: + boundaries = self.yboundaries + values = self.yvalues if b is None: - b = self.boundaries + b = boundaries if b is not None: - self._boundaries = np.asarray(b, dtype=float) - if self.values is None: - self._values = 0.5 * (self._boundaries[:-1] - + self._boundaries[1:]) - if isinstance(self.norm, colors.NoNorm): - self._values = (self._values + 0.00001).astype(np.int16) - return - self._values = np.array(self.values) - return - if self.values is not None: - self._values = np.array(self.values) - if self.boundaries is None: - b = np.zeros(len(self.values) + 1, 'd') - b[1:-1] = 0.5 * (self._values[:-1] - self._values[1:]) + b = np.asarray(b, dtype=float) + if values is None: + v = 0.5 * (b[:-1] + b[1:]) + if isinstance(norm, colors.NoNorm): + v = (v + 0.00001).astype(np.int16) + return v, b + v = np.array(self.values) + return v, b + if values is not None: + v = np.array(values) + if boundaries is None: + b = np.zeros(len(values) + 1, 'd') + b[1:-1] = 0.5 * (v[:-1] - v[1:]) b[0] = 2.0 * b[1] - b[2] b[-1] = 2.0 * b[-2] - b[-3] - self._boundaries = b - return - self._boundaries = np.array(self.boundaries) - return + return v, b + b = np.array(boundaries) + return v, b # Neither boundaries nor values are specified; # make reasonable ones based on cmap and norm. - if isinstance(self.norm, colors.NoNorm): - b = self._uniform_y(self.cmap.N + 1) * self.cmap.N - 0.5 + N = self.cmap.N + if isinstance(norm, colors.NoNorm): + b = np.linspace(0, 1, np.sqrt(N) + 1) * np.sqrt(N) - 0.5 v = np.zeros((len(b) - 1,), dtype=np.int16) - v[self._inside] = np.arange(self.cmap.N, dtype=np.int16) - if self._extend_lower(): - v[0] = -1 - if self._extend_upper(): - v[-1] = self.cmap.N - self._boundaries = b - self._values = v - return - elif isinstance(self.norm, colors.BoundaryNorm): - b = list(self.norm.boundaries) - if self._extend_lower(): - b = [b[0] - 1] + b - if self._extend_upper(): - b = b + [b[-1] + 1] + v[self._inside] = np.arange(np.sqrt(N), dtype=np.int16) + return v, b + elif isinstance(norm, colors.BoundaryNorm): + b = list(norm.boundaries) b = np.array(b) v = np.zeros((len(b) - 1,), dtype=float) - bi = self.norm.boundaries + bi = norm.boundaries v[self._inside] = 0.5 * (bi[:-1] + bi[1:]) - if self._extend_lower(): - v[0] = b[0] - 1 - if self._extend_upper(): - v[-1] = b[-1] + 1 - self._boundaries = b - self._values = v - return + return v, b else: - if not self.norm.scaled(): - self.norm.vmin = 0 - self.norm.vmax = 1 + if not norm.scaled(): + norm.vmin = 0 + norm.norm1.vmax = 1 - self.norm.vmin, self.norm.vmax = mtransforms.nonsingular( - self.norm.vmin, - self.norm.vmax, - expander=0.1) + norm.vmin, norm.vmax = mtransforms.nonsingular( + norm.vmin, norm.vmax, expander=0.1) - b = self.norm.inverse(self._uniform_y(self.cmap.N + 1)) + b = norm.inverse(np.linspace(0, 1, np.sqrt(N) + 1)) - if isinstance(self.norm, colors.LogNorm): - # If using a lognorm, ensure extensions don't go negative - if self._extend_lower(): - b[0] = 0.9 * b[0] - if self._extend_upper(): - b[-1] = 1.1 * b[-1] - else: - if self._extend_lower(): - b[0] = b[0] - 1 - if self._extend_upper(): - b[-1] = b[-1] + 1 - self._process_values(b) - - def _find_range(self): - ''' - Set :attr:`vmin` and :attr:`vmax` attributes to the first and - last boundary excluding extended end boundaries. - ''' - b = self._boundaries[self._inside] - self.vmin = b[0] - self.vmax = b[-1] - - def _central_N(self): - '''number of boundaries **before** extension of ends''' - nb = len(self._boundaries) - if self.extend == 'both': - nb -= 2 - elif self.extend in ('min', 'max'): - nb -= 1 - return nb - - def _extended_N(self): - ''' - Based on the colormap and extend variable, return the - number of boundaries. - ''' - N = self.cmap.N + 1 - if self.extend == 'both': - N += 2 - elif self.extend in ('min', 'max'): - N += 1 - return N - - def _proportional_y(self): - ''' - Return colorbar data coordinates for the boundaries of - a proportional colorbar. - ''' - if isinstance(self.norm, colors.BoundaryNorm): - y = (self._boundaries - self._boundaries[0]) - y = y / (self._boundaries[-1] - self._boundaries[0]) - else: - y = self.norm(self._boundaries.copy()) - y = np.ma.filled(y, np.nan) - if self.extend == 'min': - # Exclude leftmost interval of y. - clen = y[-1] - y[1] - automin = (y[2] - y[1]) / clen - automax = (y[-1] - y[-2]) / clen - elif self.extend == 'max': - # Exclude rightmost interval in y. - clen = y[-2] - y[0] - automin = (y[1] - y[0]) / clen - automax = (y[-2] - y[-3]) / clen - elif self.extend == 'both': - # Exclude leftmost and rightmost intervals in y. - clen = y[-2] - y[1] - automin = (y[2] - y[1]) / clen - automax = (y[-2] - y[-3]) / clen - if self.extend in ('both', 'min', 'max'): - extendlength = self._get_extension_lengths(self.extendfrac, - automin, automax, - default=0.05) - if self.extend in ('both', 'min'): - y[0] = 0. - extendlength[0] - if self.extend in ('both', 'max'): - y[-1] = 1. + extendlength[1] - yi = y[self._inside] - norm = colors.Normalize(yi[0], yi[-1]) - y[self._inside] = np.ma.filled(norm(yi), np.nan) - return y + return self._process_values(b=b, norm=norm) def _mesh(self): ''' Return X,Y, the coordinate arrays for the colorbar pcolormesh. - These are suitable for a vertical colorbar; swapping and - transposition for a horizontal colorbar are done outside - this function. ''' - if self.spacing == 'uniform': - x = y = self._uniform_y(self._central_N()) - else: - x = y = self._proportional_y() + x = np.linspace(0, 1, len(self._xboundaries)) + y = np.linspace(0, 1, len(self._yboundaries)) self._x = x self._y = y X, Y = np.meshgrid(x, y) - if self._extend_lower() and not self.extendrect: - X[0, :] = 0.5 - if self._extend_upper() and not self.extendrect: - X[-1, :] = 0.5 return X, Y - def _locate(self, x): + def _locate(self, x, norm): ''' Given a set of color data values, return their corresponding colorbar data coordinates. ''' - if isinstance(self.norm, (colors.NoNorm, colors.BoundaryNorm)): - b = self._boundaries + if norm is self.norm.norm1: + boundaries = self._xboundaries + else: + boundaries = self._yboundaries + if isinstance(norm, (colors.NoNorm, colors.BoundaryNorm)): + b = boundaries xn = x else: # Do calculations using normalized coordinates so # as to make the interpolation more accurate. - b = self.norm(self._boundaries, clip=False).filled() - xn = self.norm(x, clip=False).filled() + b = norm(boundaries, clip=False).filled() + xn = norm(x, clip=False).filled() # The rest is linear interpolation with extrapolation at ends. ii = np.searchsorted(b, xn) @@ -1443,8 +1289,17 @@ def _locate(self, x): z = np.take(y, i0) + (xn - np.take(b, i0)) * dy / db return z + def set_alpha(self, alpha): + self.alpha = alpha -class Colorbar(Colorsquare): + def remove(self): + """ + Remove this colorsquare from the figure + """ + fig = self.ax.figure + fig.delaxes(self.ax) + +class Colorbar(ColorbarBase): """ This class connects a :class:`ColorbarBase` to a :class:`~matplotlib.cm.ScalarMappable` such as a @@ -1484,7 +1339,7 @@ def __init__(self, ax, mappable, **kw): if isinstance(mappable, martist.Artist): kw['alpha'] = mappable.get_alpha() - Colorsquare.__init__(self, ax, **kw) + ColorbarBase.__init__(self, ax, **kw) def on_mappable_changed(self, mappable): """ @@ -1599,6 +1454,81 @@ def remove(self): ax.set_subplotspec(subplotspec) +class Colorsquare(ColorsquareBase): + """ + This class connects a :class:`Colorbarsquare` to a + :class:`~matplotlib.cm.ScalarMappable` such as a + :class:`~matplotlib.image.AxesImage` generated via + :meth:`~matplotlib.axes.Axes.imshow`. + + It is not intended to be instantiated directly; instead, + use :meth:`~matplotlib.figure.Figure.colorbar` or + :func:`~matplotlib.pyplot.colorbar` to make your colorsquare. + + """ + def __init__(self, ax, mappable, **kw): + # Ensure the given mappable's norm has appropriate vmin and vmax set + # even if mappable.draw has not yet been called. + mappable.autoscale_None() + + self.mappable = mappable + kw['cmap'] = cmap = mappable.cmap + kw['norm'] = norm = mappable.norm + + if isinstance(mappable, martist.Artist): + kw['alpha'] = mappable.get_alpha() + + ColorsquareBase.__init__(self, ax, **kw) + + def on_mappable_changed(self, mappable): + """ + Updates this colorsquare to match the mappable's properties. + + Typically this is automatically registered as an event handler + by :func:`colorbar_factory` and should not be called manually. + + """ + self.set_cmap(mappable.get_cmap()) + self.set_clim(mappable.get_clim()) + self.update_normal(mappable) + + def update_normal(self, mappable): + ''' + update solid, lines, etc. Unlike update_bruteforce, it does + not clear the axes. This is meant to be called when the image + or contour plot to which this colorsquare belongs is changed. + ''' + self.draw_all() + self.stale = True + + def remove(self): + """ + Remove this colorsquare from the figure. If the colorsquare was + created with ``use_gridspec=True`` then restore the gridspec to its + previous value. + """ + Colorbarsquare.remove(self) + self.mappable.callbacksSM.disconnect(self.mappable.colorbar_cid) + self.mappable.colorbar = None + self.mappable.colorbar_cid = None + + try: + ax = self.mappable.axes + except AttributeError: + return + + try: + gs = ax.get_subplotspec().get_gridspec() + subplotspec = gs.get_topmost_subplotspec() + except AttributeError: + # use_gridspec was False + pos = ax.get_position(original=True) + ax.set_position(pos) + else: + # use_gridspec was True + ax.set_subplotspec(subplotspec) + + @docstring.Substitution(make_axes_kw_doc) def make_axes(parents, location=None, orientation=None, fraction=0.15, shrink=1.0, aspect=20, **kw): @@ -1757,9 +1687,9 @@ def make_axes_gridspec(parent, **kw): orientation = kw.setdefault('orientation', 'vertical') kw['ticklocation'] = 'auto' - fraction = kw.pop('fraction', 0.35) + fraction = kw.pop('fraction', 0.15) shrink = kw.pop('shrink', 1.0) - aspect = kw.pop('aspect', 1) + aspect = kw.pop('aspect', 20) x1 = 1.0 - fraction @@ -1905,8 +1835,11 @@ def colorbar_factory(cax, mappable, **kwargs): if (isinstance(mappable, contour.ContourSet) and any([hatch is not None for hatch in mappable.hatches])): cb = ColorbarPatch(cax, mappable, **kwargs) + elif (isinstance(mappable.norm, colors.BivariateNorm)): + kwargs.pop('orientation', None) + kwargs.pop('ticklocation', None) + cb = Colorsquare(cax, mappable, **kwargs) else: - temp = kwargs.pop('orientation', 'vert') cb = Colorbar(cax, mappable, **kwargs) cid = mappable.callbacksSM.connect('changed', cb.on_mappable_changed) From 6e502e392844c11bc46951735362a37caee8f204 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sun, 23 Jul 2017 03:31:45 +0530 Subject: [PATCH 13/26] Modify axis aspect and fraction for colorsquare --- lib/matplotlib/figure.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 4c6b042be5a..db644b6f38a 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -35,6 +35,7 @@ from matplotlib.image import FigureImage import matplotlib.colorbar as cbar +import matplotlib.colors as mcolors from matplotlib.axes import Axes, SubplotBase, subplot_class_factory from matplotlib.blocking_input import BlockingMouseInput, BlockingKeyMouseInput @@ -1833,6 +1834,9 @@ def colorbar(self, mappable, cax=None, ax=None, use_gridspec=True, **kw): # Store the value of gca so that we can set it back later on. current_ax = self.gca() + if isinstance(mappable.norm, mcolors.BivariateNorm): + kw['fraction'] = 0.30 + kw['aspect'] = 1 if cax is None: if use_gridspec and isinstance(ax, SubplotBase): cax, kw = cbar.make_axes_gridspec(ax, **kw) From 3a6468b39e906a9ac989a8e7e4c29c2d327a6f83 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sun, 23 Jul 2017 03:47:28 +0530 Subject: [PATCH 14/26] fix failing commits --- lib/matplotlib/axes/_axes.py | 66 +++++++------------------------------ lib/matplotlib/cm.py | 3 +- lib/matplotlib/colorbar.py | 5 +-- lib/matplotlib/colors.py | 21 ++++-------- lib/matplotlib/tests/test_colors.py | 45 ------------------------- 5 files changed, 22 insertions(+), 118 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 34fc461b924..575e1a6e871 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -3979,17 +3979,6 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, else: try: c_array = np.asanyarray(c, dtype=float) - if c_array.ndim > 1: - if cmap is None: - cmap = mcolors.BivariateColormap() - if norm is None: - norm = mcolors.BivariateNorm() - c_array = norm(c_array) - c_array[0] = c_array[0] * (cmap.N-1) - c_array[1] = c_array[1] * (cmap.N-1) - c_array = c_array.astype(int) - c_array = c_array[0] + cmap.N * c_array[1] - norm = mcolors.NoNorm() if c_array.shape in xy_shape: c = np.ma.ravel(c_array) else: @@ -4054,10 +4043,8 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, collection.update(kwargs) if colors is None: - 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'" + if norm is not None and not isinstance(norm, mcolors.Normalize): + msg = "'norm' must be an instance of 'mcolors.Normalize'" raise ValueError(msg) collection.set_array(np.asarray(c)) collection.set_cmap(cmap) @@ -5151,6 +5138,11 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, """ + temp = np.asarray(X) + if temp.ndim == 3 and isinstance(norm, mcolors.BivariateNorm): + temp = norm(temp) + X = cmap(temp, alpha=self.get_alpha(), bytes=True) + if not self._hold: self.cla() @@ -5164,21 +5156,6 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, aspect = rcParams['image.aspect'] self.set_aspect(aspect) - temp = np.asarray(X) - isBivari = (isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)) - if (temp.ndim == 3 and isBivari): - 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) @@ -5219,10 +5196,10 @@ def _pcolorargs(funcname, *args, **kw): allmatch = kw.pop("allmatch", False) norm = kw.pop("norm", None) cmap = kw.pop("cmap", None) + alpha = kw.pop("alpha", 1) if len(args) == 1: C = np.asanyarray(args[0]) - isBivari = (isinstance(norm, mcolors.BivariateNorm) or isinstance(cmap, mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): @@ -5231,12 +5208,6 @@ def _pcolorargs(funcname, *args, **kw): 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 if allmatch: X, Y = np.meshgrid(np.arange(numCols), np.arange(numRows)) @@ -5256,11 +5227,6 @@ def _pcolorargs(funcname, *args, **kw): 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( @@ -5809,19 +5775,11 @@ def pcolorfast(self, *args, **kwargs): isBivari = (isinstance(norm, mcolors.BivariateNorm) or isinstance(cmap, mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): - 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 + nr, nc = C.shape + C = cmap(C, alpha=alpha, bytes=True) + else: + nr, nc = C.shape if len(args) == 1: style = "image" x = [0, nc] diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index b9b59bbbdd1..bdf3e157565 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -201,8 +201,7 @@ def __init__(self, norm=None, cmap=None): #: The Normalization instance of this ScalarMappable. self.norm = norm #: The Colormap instance of this ScalarMappable. - # self.cmap = get_cmap(cmap) - self.cmap = cmap + self.cmap = get_cmap(cmap) #: The last colorbar associated with this ScalarMappable. May be None. self.colorbar = None self.update_dict = {'array': False} diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index dd567e439e4..ae3d7efbd53 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1081,7 +1081,7 @@ def _edges(self, X, Y): ''' Return the separator line segments; helper for _add_solids. ''' - N = X.shape[0]. + N = X.shape[0] return [list(zip(X[i], Y[i])) for i in xrange(1, N - 1)] + [list(zip(Y[i], X[i])) for i in xrange(1, N - 1)] @@ -1159,7 +1159,8 @@ def _ticker(self, norm): if mpl.rcParams['_internal.classic_mode']: locator = ticker.MaxNLocator() else: - locator = ticker.AutoLocator() + # locator = ticker.AutoLocator() + locator = ticker.MaxNLocator(nbins=5) else: b = _boundaries[self._inside] locator = ticker.FixedLocator(b, nbins=10) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 24646432237..e98c2d197f3 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -1414,22 +1414,13 @@ def __call__(self, values, clip=None): if clip is None: clip = [self.norm1.clip, self.norm2.clip] - return np.asarray([self.norm1(values[0], clip=clip[0]), - self.norm2(values[1], clip=clip[1])]) + temp = np.asarray([self.norm1(values[0], clip=clip[0]), + self.norm2(values[1], clip=clip[1])]) - def inverse(self, values): - """ - Parameters - ---------- - values : array-like - A list of two values to be inverted - - Returns - ------- - A list of two unnormalized values - """ - return np.asarray([self.norm1.inverse(values[0]), - self.norm2.inverse(values[1])]) + temp[0] = temp[0] * (256) + temp[1] = temp[1] * (256) + temp = temp.astype(int) + return temp[0] + temp[1] * 256 def autoscale(self, A): """ diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index e93f7257c61..721813e62f8 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -272,51 +272,6 @@ def _mask_tester(norm_instance, vals): masked_array[0] = np.ma.masked assert_array_equal(masked_array.mask, norm_instance(masked_array).mask) -@pytest.mark.parametrize( - 'norm1, norm2, values, expected, clip', [ - ( - None, - None, - [[0.2, 0.4, 0.5], [5, 7, 9, 10]], - [[0, 0.666667, 1], [0, 0.4, 0.8, 1]], - None - ), - ( - mcolors.LogNorm(clip=True, vmax=5), - None, - [[1, 6], [5, 7, 9, 10]], - [[0, 1.0], [0, 0.4, 0.8, 1]], - None - ), - ( - mcolors.PowerNorm(2, vmin=0, vmax=8, clip=None), - mcolors.PowerNorm(2, vmin=2, vmax=8, clip=True), - [np.array([-0.5, 0, 2, 4, 8], dtype=float), - np.array([-0.5, 0, 1, 8, 16], dtype=float)], - [[0, 0, 1/16, 1/4, 1], [0, 0, 0, 1, 1]], - None - ), - ( - mcolors.SymLogNorm(3, vmin=-30, vmax=5, linscale=1.2), - mcolors.PowerNorm(2, vmin=2, vmax=8, clip=False), - [np.array([-30, -1, 2, 6], dtype=float), - np.array([-0.5, 0, 1, 8, 16], dtype=float)], - [[0., 0.53980074, 0.826991, 1.02758204], [0, 0, 0, 1, 1]], - [False, True] - ) - ], ids=[ - 'norm_is_None', - 'LogNorm_and_LinearNorm', - 'PowerNorm_clip_None_and_True', - 'SymLogNorm_and_PowerNorm_clip_in_call' - ] -) -def test_BivariateNorm(norm1, norm2, values, expected, clip): - norm = mcolors.BivariateNorm(norm1=norm1, norm2=norm2) - ans1, ans2 = norm(values=values, clip=clip) - assert_array_almost_equal(ans1, expected[0]) - assert_array_almost_equal(ans2, expected[1]) - @image_comparison(baseline_images=['levels_and_colors'], extensions=['png']) From 9fd6f0dd584d926d2cf71a7b502af6d777c92986 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Thu, 27 Jul 2017 19:44:57 +0530 Subject: [PATCH 15/26] Change PEP8 violations --- lib/matplotlib/axes/_axes.py | 12 ++++++------ lib/matplotlib/colorbar.py | 23 +++++++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 575e1a6e871..7b81312f5ad 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5200,8 +5200,8 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 1: C = np.asanyarray(args[0]) - isBivari = (isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)) + isBivari = isinstance(norm, (mcolors.BivariateNorm, + mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() @@ -5219,8 +5219,8 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 3: X, Y, C = [np.asanyarray(a) for a in args] - isBivari = (isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)) + isBivari = isinstance(norm, (mcolors.BivariateNorm, + mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() @@ -5772,8 +5772,8 @@ def pcolorfast(self, *args, **kwargs): C = np.asarray(args[-1]) - isBivari = (isinstance(norm, mcolors.BivariateNorm) or - isinstance(cmap, mcolors.BivariateColormap)) + isBivari = isinstance(norm, (mcolors.BivariateNorm, + mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): C = norm(C) nr, nc = C.shape diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index ae3d7efbd53..eb1d7f88a1d 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -901,7 +901,9 @@ def remove(self): class ColorsquareBase(cm.ScalarMappable): + n_rasterize = 50 # rasterize solids if number of colors >= n_rasterize + def __init__(self, ax, cmap=None, norm=None, alpha=None, @@ -1069,9 +1071,9 @@ def _set_label(self): self.stale = True def set_label(self, xlabel, ylabel, **kw): - ''' + """ Label the axes of the colorbar - ''' + """ self._xlabel = '%s' % (xlabel, ) self._ylabel = '%s' % (ylabel, ) self._labelkw = kw @@ -1086,10 +1088,10 @@ def _edges(self, X, Y): + [list(zip(Y[i], X[i])) for i in xrange(1, N - 1)] def _add_solids(self, X, Y, C): - ''' + """ Draw the colors using :meth:`~matplotlib.axes.Axes.pcolormesh`; optionally add separators. - ''' + """ args = (X, Y, C) kw = dict(cmap=self.cmap, norm=self.norm, @@ -1120,10 +1122,10 @@ def _add_solids(self, X, Y, C): self.solids.set_rasterized(True) def _ticker(self, norm): - ''' + """ Return the sequence of ticks (colorbar data locations), ticklabels (strings), and the corresponding offset string. - ''' + """ if norm is self.norm.norm1: _values = self._xvalues _boundaries = self._xboundaries @@ -1246,9 +1248,9 @@ def _process_values(self, b=None, norm=None): return self._process_values(b=b, norm=norm) def _mesh(self): - ''' + """ Return X,Y, the coordinate arrays for the colorbar pcolormesh. - ''' + """ x = np.linspace(0, 1, len(self._xboundaries)) y = np.linspace(0, 1, len(self._yboundaries)) self._x = x @@ -1257,10 +1259,10 @@ def _mesh(self): return X, Y def _locate(self, x, norm): - ''' + """ Given a set of color data values, return their corresponding colorbar data coordinates. - ''' + """ if norm is self.norm.norm1: boundaries = self._xboundaries else: @@ -1300,6 +1302,7 @@ def remove(self): fig = self.ax.figure fig.delaxes(self.ax) + class Colorbar(ColorbarBase): """ This class connects a :class:`ColorbarBase` to a From 3d6fa0df3b81bb6b0f5bc403d6ce58fb6b9d9135 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sun, 30 Jul 2017 00:06:45 +0530 Subject: [PATCH 16/26] Revert isBivari, remove classic locator --- lib/matplotlib/axes/_axes.py | 12 ++++++------ lib/matplotlib/colorbar.py | 3 --- lib/matplotlib/colors.py | 3 ++- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 7b81312f5ad..c2ccd0fb748 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5200,8 +5200,8 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 1: C = np.asanyarray(args[0]) - isBivari = isinstance(norm, (mcolors.BivariateNorm, - mcolors.BivariateColormap)) + isBivari = (isinstance(norm, mcolors.BivariateNorm) + or isinstance(cmap, mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() @@ -5219,8 +5219,8 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 3: X, Y, C = [np.asanyarray(a) for a in args] - isBivari = isinstance(norm, (mcolors.BivariateNorm, - mcolors.BivariateColormap)) + isBivari = (isinstance(norm, mcolors.BivariateNorm) + or isinstance(cmap, mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): if cmap is None: cmap = mcolors.BivariateColormap() @@ -5772,8 +5772,8 @@ def pcolorfast(self, *args, **kwargs): C = np.asarray(args[-1]) - isBivari = isinstance(norm, (mcolors.BivariateNorm, - mcolors.BivariateColormap)) + isBivari = (isinstance(norm, mcolors.BivariateNorm) + or isinstance(cmap, mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): C = norm(C) nr, nc = C.shape diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index eb1d7f88a1d..af4cde18648 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1158,9 +1158,6 @@ def _ticker(self, norm): linthresh=norm.linthresh, base=10) else: - if mpl.rcParams['_internal.classic_mode']: - locator = ticker.MaxNLocator() - else: # locator = ticker.AutoLocator() locator = ticker.MaxNLocator(nbins=5) else: diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index e98c2d197f3..d500f4051cc 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -865,7 +865,8 @@ def _init(self): 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)) + 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 From 1b03f5a1b739edf6f65d8ea4544f52c22c482ee1 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Wed, 2 Aug 2017 14:41:53 +0530 Subject: [PATCH 17/26] Subclass normalizers from Abstract Base Class --- lib/matplotlib/axes/_axes.py | 10 ++++------ lib/matplotlib/colors.py | 13 ++++++++++--- lib/matplotlib/tests/test_colors.py | 12 ++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index c2ccd0fb748..21e3e93c625 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5146,8 +5146,7 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, if not self._hold: self.cla() - isNorm = isinstance(norm, (mcolors.Normalize, mcolors.BivariateNorm)) - if norm is not None and not isNorm: + if norm is not None and not isinstance(norm, mcolors.Norms): msg = "'norm' must be an instance of 'mcolors.Normalize' " \ "or 'mcolors.BivariateNorm'" raise ValueError(msg) @@ -5483,8 +5482,7 @@ def pcolor(self, *args, **kwargs): collection.set_alpha(alpha) collection.set_array(C) - isNorm = isinstance(norm, (mcolors.Normalize, mcolors.BivariateNorm)) - if norm is not None and not isNorm: + if norm is not None and not isinstance(norm, mcolors.Norms): msg = "'norm' must be an instance of 'mcolors.Normalize' " \ "or 'mcolors.BivariateNorm'" raise ValueError(msg) @@ -5764,8 +5762,8 @@ def pcolorfast(self, *args, **kwargs): cmap = kwargs.pop('cmap', None) vmin = kwargs.pop('vmin', None) vmax = kwargs.pop('vmax', None) - isNorm = isinstance(norm, (mcolors.Normalize, mcolors.BivariateNorm)) - if norm is not None and not isNorm: + + if norm is not None and not isinstance(norm, mcolors.Norms): msg = "'norm' must be an instance of 'mcolors.Normalize' " \ "or 'mcolors.BivariateNorm'" raise ValueError(msg) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index d500f4051cc..a3ba8c72eca 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -68,7 +68,7 @@ import numpy as np import matplotlib.cbook as cbook from ._color_data import BASE_COLORS, TABLEAU_COLORS, CSS4_COLORS, XKCD_COLORS - +from abc import ABCMeta class _ColorMapping(dict): def __init__(self, mapping): @@ -882,7 +882,14 @@ def reversed(self, name=None): raise NotImplementedError() -class Normalize(object): +class Norms: + """ + Abstract Base Class to group `Normalize` and `BivariateNorm` + """ + __metaclass__ = ABCMeta + pass + +class Normalize(Norms): """ A class which, when called, can normalize data into the ``[0.0, 1.0]`` interval. @@ -1374,7 +1381,7 @@ def inverse(self, value): return value -class BivariateNorm: +class BivariateNorm(Norms): """ Normalize a list of two values corresponding to two 1D normalizers """ diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 721813e62f8..fbcad244014 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -707,3 +707,15 @@ def __add__(self, other): mcolors.SymLogNorm(3, vmax=5, linscale=1), mcolors.PowerNorm(1)]: assert_array_equal(norm(data.view(MyArray)), norm(data)) + + +@pytest.mark.parametrize('norm', [ + mcolors.Normalize(), mcolors.LogNorm(), mcolors.BivariateNorm() + ] +) +def test_abstract_base_class_norms(norm): + """ + Test that all types of normalizers subclasses Abstract Base class + `colors.Norms` + """ + assert isinstance(norm, mcolors.Norms) \ No newline at end of file From 51d352cf286559438fbfa6e49cfa4bc7a5f28994 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Wed, 2 Aug 2017 14:59:57 +0530 Subject: [PATCH 18/26] move _ticker function inside update_ticks --- lib/matplotlib/colorbar.py | 142 ++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index af4cde18648..4884eec2e11 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -944,7 +944,7 @@ def __init__(self, ax, cmap=None, if cbook.iterable(xticks): self.xlocator = ticker.FixedLocator(xticks, nbins=len(xticks)) else: - self.xlocator = xticks # Handle default in _ticker() + self.xlocator = xticks if cbook.iterable(yticks): self.ylocator = ticker.FixedLocator(yticks, nbins=len(yticks)) @@ -1015,9 +1015,77 @@ def update_ticks(self): Force the update of the ticks and ticklabels. This must be called whenever the tick locator and/or tick formatter changes. """ + def _make_ticker(norm): + """ + Return the sequence of ticks (colorbar data locations), + ticklabels (strings), and the corresponding offset string. + """ + if norm is self.norm.norm1: + _values = self._xvalues + _boundaries = self._xboundaries + boundaries = self.xboundaries + locator = self.xlocator + formatter = self.xformatter + else: + _values = self._yvalues + _boundaries = self._yboundaries + boundaries = self.yboundaries + locator = self.ylocator + formatter = self.yformatter + + if locator is None: + if boundaries is None: + if isinstance(norm, colors.NoNorm): + nv = len(_values) + base = 1 + int(nv / 10) + locator = ticker.IndexLocator(base=base, offset=0) + elif isinstance(norm, colors.BoundaryNorm): + b = norm.boundaries + locator = ticker.FixedLocator(b, nbins=10) + elif isinstance(norm, colors.LogNorm): + locator = ticker.LogLocator(subs='all') + elif isinstance(norm, colors.SymLogNorm): + # The subs setting here should be replaced + # by logic in the locator. + locator = ticker.SymmetricalLogLocator( + subs=np.arange(1, 10), + linthresh=norm.linthresh, + base=10) + else: + # locator = ticker.AutoLocator() + locator = ticker.MaxNLocator(nbins=5) + else: + b = _boundaries[self._inside] + locator = ticker.FixedLocator(b, nbins=10) + if isinstance(norm, colors.NoNorm) and boundaries is None: + intv = _values[0], _values[-1] + else: + b = _boundaries[self._inside] + intv = b[0], b[-1] + locator.create_dummy_axis(minpos=intv[0]) + formatter.create_dummy_axis(minpos=intv[0]) + locator.set_view_interval(*intv) + locator.set_data_interval(*intv) + formatter.set_view_interval(*intv) + formatter.set_data_interval(*intv) + + b = np.array(locator()) + if isinstance(locator, ticker.LogLocator): + eps = 1e-10 + b = b[(b <= intv[1] * (1 + eps)) & (b >= intv[0] * (1 - eps))] + else: + eps = (intv[1] - intv[0]) * 1e-10 + b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)] + # self._tick_data_values = b + ticks = self._locate(b, norm) + formatter.set_locs(b) + ticklabels = [formatter(t, i) for i, t in enumerate(b)] + offset_string = formatter.get_offset() + return ticks, ticklabels, offset_string + ax = self.ax - xticks, xticklabels, xoffset_string = self._ticker(self.norm.norm1) - yticks, yticklabels, yoffset_string = self._ticker(self.norm.norm2) + xticks, xticklabels, xoffset_string = _make_ticker(self.norm.norm1) + yticks, yticklabels, yoffset_string = _make_ticker(self.norm.norm2) ax.xaxis.set_ticks(xticks) ax.set_xticklabels(xticklabels) @@ -1121,74 +1189,6 @@ def _add_solids(self, X, Y, C): or len(self._x) >= self.n_rasterize): self.solids.set_rasterized(True) - def _ticker(self, norm): - """ - Return the sequence of ticks (colorbar data locations), - ticklabels (strings), and the corresponding offset string. - """ - if norm is self.norm.norm1: - _values = self._xvalues - _boundaries = self._xboundaries - boundaries = self.xboundaries - locator = self.xlocator - formatter = self.xformatter - else: - _values = self._yvalues - _boundaries = self._yboundaries - boundaries = self.yboundaries - locator = self.ylocator - formatter = self.yformatter - - if locator is None: - if boundaries is None: - if isinstance(norm, colors.NoNorm): - nv = len(_values) - base = 1 + int(nv / 10) - locator = ticker.IndexLocator(base=base, offset=0) - elif isinstance(norm, colors.BoundaryNorm): - b = norm.boundaries - locator = ticker.FixedLocator(b, nbins=10) - elif isinstance(norm, colors.LogNorm): - locator = ticker.LogLocator(subs='all') - elif isinstance(norm, colors.SymLogNorm): - # The subs setting here should be replaced - # by logic in the locator. - locator = ticker.SymmetricalLogLocator( - subs=np.arange(1, 10), - linthresh=norm.linthresh, - base=10) - else: - # locator = ticker.AutoLocator() - locator = ticker.MaxNLocator(nbins=5) - else: - b = _boundaries[self._inside] - locator = ticker.FixedLocator(b, nbins=10) - if isinstance(norm, colors.NoNorm) and boundaries is None: - intv = _values[0], _values[-1] - else: - b = _boundaries[self._inside] - intv = b[0], b[-1] - locator.create_dummy_axis(minpos=intv[0]) - formatter.create_dummy_axis(minpos=intv[0]) - locator.set_view_interval(*intv) - locator.set_data_interval(*intv) - formatter.set_view_interval(*intv) - formatter.set_data_interval(*intv) - - b = np.array(locator()) - if isinstance(locator, ticker.LogLocator): - eps = 1e-10 - b = b[(b <= intv[1] * (1 + eps)) & (b >= intv[0] * (1 - eps))] - else: - eps = (intv[1] - intv[0]) * 1e-10 - b = b[(b <= intv[1] + eps) & (b >= intv[0] - eps)] - # self._tick_data_values = b - ticks = self._locate(b, norm) - formatter.set_locs(b) - ticklabels = [formatter(t, i) for i, t in enumerate(b)] - offset_string = formatter.get_offset() - return ticks, ticklabels, offset_string - def _process_values(self, b=None, norm=None): if norm is self.norm.norm1: boundaries = self.xboundaries From 41ac0cef9358ad865788277b2eea6d89858f8289 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Wed, 2 Aug 2017 14:59:57 +0530 Subject: [PATCH 19/26] move _ticker function inside update_ticks --- lib/matplotlib/colors.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index a3ba8c72eca..2942e0cb39b 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -70,6 +70,7 @@ from ._color_data import BASE_COLORS, TABLEAU_COLORS, CSS4_COLORS, XKCD_COLORS from abc import ABCMeta + class _ColorMapping(dict): def __init__(self, mapping): super(_ColorMapping, self).__init__(mapping) @@ -889,6 +890,7 @@ class Norms: __metaclass__ = ABCMeta pass + class Normalize(Norms): """ A class which, when called, can normalize data into From 3c14cfb4384e82a4841a4bf819d7cc1b79e0e441 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sat, 5 Aug 2017 15:14:25 +0530 Subject: [PATCH 20/26] remove cmap in imshow and pcolorfast --- lib/matplotlib/axes/_axes.py | 4 +--- lib/matplotlib/colors.py | 6 +++--- lib/matplotlib/image.py | 3 ++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 21e3e93c625..1939100df0b 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5140,8 +5140,7 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, temp = np.asarray(X) if temp.ndim == 3 and isinstance(norm, mcolors.BivariateNorm): - temp = norm(temp) - X = cmap(temp, alpha=self.get_alpha(), bytes=True) + X = norm(temp) if not self._hold: self.cla() @@ -5775,7 +5774,6 @@ def pcolorfast(self, *args, **kwargs): if (C.ndim == 3 and isBivari): C = norm(C) nr, nc = C.shape - C = cmap(C, alpha=alpha, bytes=True) else: nr, nc = C.shape if len(args) == 1: diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index 2942e0cb39b..e3320178eb0 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -859,10 +859,11 @@ def reversed(self, name=None): class BivariateColormap(Colormap): def __init__(self, name='bivariate', N=256): Colormap.__init__(self, name, N) + self.N = self.N * self.N def _init(self): - red = np.linspace(0, 1, self.N) - green = np.linspace(0, 1, self.N) + red = np.linspace(0, 1, np.sqrt(self.N)) + green = np.linspace(0, 1, np.sqrt(self.N)) red_mesh, green_mesh = np.meshgrid(red, green) blue_mesh = np.zeros_like(red_mesh) alpha_mesh = np.ones_like(red_mesh) @@ -870,7 +871,6 @@ def _init(self): alpha_mesh)) self._lut = np.vstack(bivariate_cmap) self._isinit = True - self.N = self.N * self.N self._set_extremes() def _resample(self, lutsize): diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index bfcce87f4f4..d1b60cd2687 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -362,7 +362,8 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, raise ValueError("Invalid dimensions, got {}".format(A.shape)) if A.ndim == 2: - A = self.norm(A) + if not isinstance(self.norm, mcolors.BivariateNorm): + A = self.norm(A) if A.dtype.kind == 'f': # If the image is greyscale, convert to RGBA and # use the extra channels for resizing the over, From 3256f09d5d794325cd38cef2cdc2bd5fd028e917 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 7 Aug 2017 18:14:47 +0530 Subject: [PATCH 21/26] Defer call to norm and cmap in imshow --- lib/matplotlib/axes/_axes.py | 5 ----- lib/matplotlib/axes/_subplots.py | 1 - lib/matplotlib/image.py | 34 +++++++++++++++++++++++----------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 1939100df0b..8ad80f73ba5 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5137,11 +5137,6 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, of pixel (0, 0). """ - - temp = np.asarray(X) - if temp.ndim == 3 and isinstance(norm, mcolors.BivariateNorm): - X = norm(temp) - if not self._hold: self.cla() diff --git a/lib/matplotlib/axes/_subplots.py b/lib/matplotlib/axes/_subplots.py index 90d55d21cc4..3d806106d4c 100644 --- a/lib/matplotlib/axes/_subplots.py +++ b/lib/matplotlib/axes/_subplots.py @@ -177,7 +177,6 @@ def subplot_class_factory(axes_class=None): (SubplotBase, axes_class), {'_axes_class': axes_class}) _subplot_classes[axes_class] = new_class - return new_class # This is provided for backward compatibility diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index d1b60cd2687..56c56741c70 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -251,7 +251,11 @@ def get_size(self): if self._A is None: raise RuntimeError('You must first set the image array') - return self._A.shape[:2] + if isinstance(self.norm, mcolors.BivariateNorm): + imshape = self._A.shape[1:] + else: + imshape = self._A.shape[:2] + return imshape def set_alpha(self, alpha): """ @@ -298,6 +302,12 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, `trans` is the affine transformation from the image to pixel space. """ + if isinstance(self.norm, mcolors.BivariateNorm): + imwidth = A.shape[1] + imheight = A.shape[2] + else: + imwidth = A.shape[0] + imheight = A.shape[1] if A is None: raise RuntimeError('You must first set the image ' 'array or the image attribute') @@ -321,15 +331,15 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # Flip the input image using a transform. This avoids the # problem with flipping the array, which results in a copy # when it is converted to contiguous in the C wrapper - t0 = Affine2D().translate(0, -A.shape[0]).scale(1, -1) + t0 = Affine2D().translate(0, -imwidth).scale(1, -1) else: t0 = IdentityTransform() t0 += ( Affine2D() .scale( - in_bbox.width / A.shape[1], - in_bbox.height / A.shape[0]) + in_bbox.width / imheight, + in_bbox.height / imwidth) .translate(in_bbox.x0, in_bbox.y0) + self.get_transform()) @@ -361,9 +371,9 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, if A.ndim not in (2, 3): raise ValueError("Invalid dimensions, got {}".format(A.shape)) - if A.ndim == 2: - if not isinstance(self.norm, mcolors.BivariateNorm): - A = self.norm(A) + if A.ndim == 2 or (A.ndim == 3 and + isinstance(self.norm, mcolors.BivariateNorm)): + A = self.norm(A) if A.dtype.kind == 'f': # If the image is greyscale, convert to RGBA and # use the extra channels for resizing the over, @@ -371,7 +381,7 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # Agg's resampler is very aggressive about # clipping to [0, 1] and we use out-of-bounds # values to carry the over/under/bad information - rgba = np.empty((A.shape[0], A.shape[1], 4), dtype=A.dtype) + rgba = np.empty((imwidth, imheight, 4), dtype=A.dtype) rgba[..., 0] = A # normalized data # this is to work around spurious warnings coming # out of masked arrays. @@ -410,9 +420,10 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, if not created_rgba_mask: # Always convert to RGBA, even if only RGB input + isBivari = (A.ndim == 2 and A.shape[0] == 2) if A.shape[2] == 3: A = _rgb_to_rgba(A) - elif A.shape[2] != 4: + elif A.shape[2] != 4 and not isBivari: raise ValueError("Invalid dimensions, got %s" % (A.shape,)) output = np.zeros((out_height, out_width, 4), dtype=A.dtype) @@ -595,8 +606,9 @@ def set_data(self, A): not np.can_cast(self._A.dtype, float, "same_kind")): raise TypeError("Image data cannot be converted to float") - if not (self._A.ndim == 2 - or self._A.ndim == 3 and self._A.shape[-1] in [3, 4]): + isRGB = (self._A.ndim == 3 and self._A.shape[-1] in [3, 4]) + isBivari = (self._A.ndim == 3 and self._A.shape[0] == 2) + if not (self._A.ndim == 2 or isRGB or isBivari): raise TypeError("Invalid dimensions for image data") self._imcache = None From 6330260af6c0e3c3c9575fdd136eb14e82a5825e Mon Sep 17 00:00:00 2001 From: patniharshit Date: Tue, 15 Aug 2017 20:33:49 +0530 Subject: [PATCH 22/26] Fix bivariate handling with pcolormesh --- lib/matplotlib/axes/_axes.py | 24 +++++++++++++----------- lib/matplotlib/cm.py | 2 +- lib/matplotlib/collections.py | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 8ad80f73ba5..0929da99bc2 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5200,8 +5200,9 @@ def _pcolorargs(funcname, *args, **kw): cmap = mcolors.BivariateColormap() if norm is None: norm = mcolors.BivariateNorm() - C = norm(C) - numRows, numCols = C.shape + numRows, numCols = C.shape[1:] + else: + numRows, numCols = C.shape if allmatch: X, Y = np.meshgrid(np.arange(numCols), np.arange(numRows)) else: @@ -5219,8 +5220,9 @@ def _pcolorargs(funcname, *args, **kw): cmap = mcolors.BivariateColormap() if norm is None: norm = mcolors.BivariateNorm() - C = norm(C) - numRows, numCols = C.shape + numRows, numCols = C.shape[1:] + else: + numRows, numCols = C.shape else: raise TypeError( 'Illegal arguments to %s; see help(%s)' % (funcname, funcname)) @@ -5614,17 +5616,17 @@ def pcolormesh(self, *args, **kwargs): 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) Y = self.convert_yunits(Y) - # convert to one dimensional arrays - C = C.ravel() + # convert to one dimensional arrays if univariate + if isinstance(norm, mcolors.BivariateNorm): + C = np.asarray([C[0].ravel(), C[1].ravel()]) + else: + C = C.ravel() + coords = np.column_stack((X.flat, Y.flat)).astype(float, copy=False) collection = mcoll.QuadMesh(Nx - 1, Ny - 1, coords, @@ -5632,7 +5634,7 @@ def pcolormesh(self, *args, **kwargs): **kwargs) collection.set_alpha(alpha) collection.set_array(C) - if norm is not None and not isinstance(norm, mcolors.Normalize): + if norm is not None and not isinstance(norm, mcolors.Norms): msg = "'norm' must be an instance of 'mcolors.Normalize'" raise ValueError(msg) collection.set_cmap(cmap) diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index bdf3e157565..0ff3b56d9a4 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -238,7 +238,7 @@ def to_rgba(self, x, alpha=None, bytes=False, norm=True): """ # First check for special case, image input: try: - if x.ndim == 3: + if x.ndim == 3 and (x.shape[-1] == 3 or x.shape[-1] == 4): if x.shape[2] == 3: if alpha is None: alpha = 1 diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 66a6a58d94a..de57fea37a4 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -730,7 +730,8 @@ def update_scalarmappable(self): """ if self._A is None: return - if self._A.ndim > 1: + if (self._A.ndim > 1 and + not isinstance(self.norm, mcolors.BivariateNorm)): raise ValueError('Collections can only map rank 1 arrays') if not self.check_update("array"): return From 75af60ed26a2cb210da251f427ffd6eb927ba0f1 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Wed, 16 Aug 2017 11:56:00 +0530 Subject: [PATCH 23/26] Make pcolor work with bivariate --- lib/matplotlib/axes/_axes.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 0929da99bc2..0202dd1af2e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5408,10 +5408,6 @@ def pcolor(self, *args, **kwargs): 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) @@ -5426,7 +5422,10 @@ def pcolor(self, *args, **kwargs): xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] + mask[0:-1, 1:] + mask[1:, 0:-1]) # don't plot if C or any of the surrounding vertices are masked. - mask = ma.getmaskarray(C) + xymask + if isinstance(norm, mcolors.BivariateNorm): + mask = ma.getmaskarray(C[0]) + ma.getmaskarray(C[1]) + xymask + else: + mask = ma.getmaskarray(C) + xymask newaxis = np.newaxis compress = np.compress @@ -5450,7 +5449,14 @@ def pcolor(self, *args, **kwargs): axis=1) verts = xy.reshape((npoly, 5, 2)) - C = compress(ravelmask, ma.filled(C[0:Ny - 1, 0:Nx - 1]).ravel()) + if isinstance(norm, mcolors.BivariateNorm): + C0 = C[0] + C1 = C[1] + C0 = compress(ravelmask, ma.filled(C0[0:Ny - 1, 0:Nx - 1]).ravel()) + C1 = compress(ravelmask, ma.filled(C1[0:Ny - 1, 0:Nx - 1]).ravel()) + C = np.array([C0, C1]) + else: + C = compress(ravelmask, ma.filled(C[0:Ny - 1, 0:Nx - 1]).ravel()) linewidths = (0.25,) if 'linewidth' in kwargs: @@ -5769,8 +5775,7 @@ def pcolorfast(self, *args, **kwargs): isBivari = (isinstance(norm, mcolors.BivariateNorm) or isinstance(cmap, mcolors.BivariateColormap)) if (C.ndim == 3 and isBivari): - C = norm(C) - nr, nc = C.shape + nr, nc = C.shape[1:] else: nr, nc = C.shape if len(args) == 1: From 060842ad722ff428cd203653eb8020de4fe181db Mon Sep 17 00:00:00 2001 From: patniharshit Date: Sun, 20 Aug 2017 23:42:39 +0530 Subject: [PATCH 24/26] Do 2D->1D mapping in call method of colormap --- lib/matplotlib/colors.py | 12 ++++++------ lib/matplotlib/image.py | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index e3320178eb0..c199dfa5e10 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -470,6 +470,11 @@ def __call__(self, X, alpha=None, bytes=False): xa = np.array([X]) else: vtype = 'array' + if isinstance(self, BivariateColormap): + X[0] = X[0] * 256 + X[1] = X[1] * 256 + X = X.astype(int) + X = X[0] + X[1] * 256 xma = np.ma.array(X, copy=True) # Copy here to avoid side effects. mask_bad = xma.mask # Mask will be used below. xa = xma.filled() # Fill to avoid infs, etc. @@ -1424,14 +1429,9 @@ def __call__(self, values, clip=None): if clip is None: clip = [self.norm1.clip, self.norm2.clip] - temp = np.asarray([self.norm1(values[0], clip=clip[0]), + return np.asarray([self.norm1(values[0], clip=clip[0]), self.norm2(values[1], clip=clip[1])]) - temp[0] = temp[0] * (256) - temp[1] = temp[1] * (256) - temp = temp.astype(int) - return temp[0] + temp[1] * 256 - def autoscale(self, A): """ Set *vmin*, *vmax* to min, max of *A*. diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 56c56741c70..8304d5fdad4 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -374,7 +374,8 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, if A.ndim == 2 or (A.ndim == 3 and isinstance(self.norm, mcolors.BivariateNorm)): A = self.norm(A) - if A.dtype.kind == 'f': + if (A.dtype.kind == 'f' and + not isinstance(self.norm, mcolors.BivariateNorm)): # If the image is greyscale, convert to RGBA and # use the extra channels for resizing the over, # under, and bad pixels. This is needed because @@ -420,7 +421,7 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, if not created_rgba_mask: # Always convert to RGBA, even if only RGB input - isBivari = (A.ndim == 2 and A.shape[0] == 2) + isBivari = (A.ndim == 3 and A.shape[0] == 2) if A.shape[2] == 3: A = _rgb_to_rgba(A) elif A.shape[2] != 4 and not isBivari: From 590f8739d27fc177e18c00580051a60f8d298756 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Mon, 21 Aug 2017 02:44:48 +0530 Subject: [PATCH 25/26] Use list comprehensions to ravel --- lib/matplotlib/axes/_axes.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 0202dd1af2e..71e2a11ed2b 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -5450,11 +5450,12 @@ def pcolor(self, *args, **kwargs): verts = xy.reshape((npoly, 5, 2)) if isinstance(norm, mcolors.BivariateNorm): - C0 = C[0] - C1 = C[1] - C0 = compress(ravelmask, ma.filled(C0[0:Ny - 1, 0:Nx - 1]).ravel()) - C1 = compress(ravelmask, ma.filled(C1[0:Ny - 1, 0:Nx - 1]).ravel()) - C = np.array([C0, C1]) + C = np.array([ + compress( + ravelmask, + ma.filled(c[0:Ny - 1, 0:Nx - 1]).ravel() + ) for c in C + ]) else: C = compress(ravelmask, ma.filled(C[0:Ny - 1, 0:Nx - 1]).ravel()) @@ -5629,7 +5630,7 @@ def pcolormesh(self, *args, **kwargs): # convert to one dimensional arrays if univariate if isinstance(norm, mcolors.BivariateNorm): - C = np.asarray([C[0].ravel(), C[1].ravel()]) + C = np.asarray([c.ravel() for c in C]) else: C = C.ravel() From 40265f32b9b69b5e6c438a93a2ab3be68fd06606 Mon Sep 17 00:00:00 2001 From: patniharshit Date: Tue, 22 Aug 2017 03:19:57 +0530 Subject: [PATCH 26/26] Reflect on reviews --- lib/matplotlib/axes/_axes.py | 39 ++++++++++++++++++++----------------- lib/matplotlib/colorbar.py | 4 ++-- lib/matplotlib/colors.py | 4 ++-- lib/matplotlib/tests/test_colors.py | 2 +- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 71e2a11ed2b..4ce84d18776 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -4044,7 +4044,8 @@ def scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, if colors is None: if norm is not None and not isinstance(norm, mcolors.Normalize): - msg = "'norm' must be an instance of 'mcolors.Normalize'" + msg = ("'norm' must be an instance of 'mcolors.Normalize' or " + "'mcolors.BivariateNorm'") raise ValueError(msg) collection.set_array(np.asarray(c)) collection.set_cmap(cmap) @@ -4403,7 +4404,8 @@ def hexbin(self, x, y, C=None, gridsize=100, bins=None, accum = bins.searchsorted(accum) if norm is not None and not isinstance(norm, mcolors.Normalize): - msg = "'norm' must be an instance of 'mcolors.Normalize'" + msg = ("'norm' must be an instance of 'mcolors.Normalize' or " + "'mcolors.BivariateNorm'") raise ValueError(msg) collection.set_array(accum) collection.set_cmap(cmap) @@ -5141,8 +5143,8 @@ def imshow(self, X, cmap=None, norm=None, aspect=None, self.cla() if norm is not None and not isinstance(norm, mcolors.Norms): - msg = "'norm' must be an instance of 'mcolors.Normalize' " \ - "or 'mcolors.BivariateNorm'" + msg = ("'norm' must be an instance of 'mcolors.Normalize' or " + "'mcolors.BivariateNorm'") raise ValueError(msg) if aspect is None: @@ -5193,9 +5195,9 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 1: C = np.asanyarray(args[0]) - isBivari = (isinstance(norm, mcolors.BivariateNorm) - or isinstance(cmap, mcolors.BivariateColormap)) - if (C.ndim == 3 and isBivari): + is_bivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (C.ndim == 3 and is_bivari): if cmap is None: cmap = mcolors.BivariateColormap() if norm is None: @@ -5213,9 +5215,9 @@ def _pcolorargs(funcname, *args, **kw): if len(args) == 3: X, Y, C = [np.asanyarray(a) for a in args] - isBivari = (isinstance(norm, mcolors.BivariateNorm) - or isinstance(cmap, mcolors.BivariateColormap)) - if (C.ndim == 3 and isBivari): + is_bivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (C.ndim == 3 and is_bivari): if cmap is None: cmap = mcolors.BivariateColormap() if norm is None: @@ -5486,8 +5488,8 @@ def pcolor(self, *args, **kwargs): collection.set_array(C) if norm is not None and not isinstance(norm, mcolors.Norms): - msg = "'norm' must be an instance of 'mcolors.Normalize' " \ - "or 'mcolors.BivariateNorm'" + msg = ("'norm' must be an instance of 'mcolors.Normalize' or " + "'mcolors.BivariateNorm'") raise ValueError(msg) collection.set_cmap(cmap) @@ -5642,7 +5644,8 @@ def pcolormesh(self, *args, **kwargs): collection.set_alpha(alpha) collection.set_array(C) if norm is not None and not isinstance(norm, mcolors.Norms): - msg = "'norm' must be an instance of 'mcolors.Normalize'" + msg = ("'norm' must be an instance of 'mcolors.Normalize' or " + "'mcolors.BivariateNorm'") raise ValueError(msg) collection.set_cmap(cmap) collection.set_norm(norm) @@ -5767,15 +5770,15 @@ def pcolorfast(self, *args, **kwargs): vmax = kwargs.pop('vmax', None) if norm is not None and not isinstance(norm, mcolors.Norms): - msg = "'norm' must be an instance of 'mcolors.Normalize' " \ - "or 'mcolors.BivariateNorm'" + msg = ("'norm' must be an instance of 'mcolors.Normalize' or " + "'mcolors.BivariateNorm'") raise ValueError(msg) C = np.asarray(args[-1]) - isBivari = (isinstance(norm, mcolors.BivariateNorm) - or isinstance(cmap, mcolors.BivariateColormap)) - if (C.ndim == 3 and isBivari): + is_bivari = (isinstance(norm, mcolors.BivariateNorm) or + isinstance(cmap, mcolors.BivariateColormap)) + if (C.ndim == 3 and is_bivari): nr, nc = C.shape[1:] else: nr, nc = C.shape diff --git a/lib/matplotlib/colorbar.py b/lib/matplotlib/colorbar.py index 4884eec2e11..7e4d1c9ff21 100644 --- a/lib/matplotlib/colorbar.py +++ b/lib/matplotlib/colorbar.py @@ -1152,8 +1152,8 @@ def _edges(self, X, Y): Return the separator line segments; helper for _add_solids. ''' N = X.shape[0] - return [list(zip(X[i], Y[i])) for i in xrange(1, N - 1)] - + [list(zip(Y[i], X[i])) for i in xrange(1, N - 1)] + return ([list(zip(X[i], Y[i])) for i in xrange(1, N - 1)] + + [list(zip(Y[i], X[i])) for i in xrange(1, N - 1)]) def _add_solids(self, X, Y, C): """ diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py index c199dfa5e10..bb778f480b8 100644 --- a/lib/matplotlib/colors.py +++ b/lib/matplotlib/colors.py @@ -885,14 +885,14 @@ def _resample(self, lutsize): return BivariateColormap(self.name, lutsize) def reversed(self, name=None): - raise NotImplementedError() + raise NotImplementedError +@six.add_metaclass(ABCMeta) class Norms: """ Abstract Base Class to group `Normalize` and `BivariateNorm` """ - __metaclass__ = ABCMeta pass diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index fbcad244014..d18ab41ecf1 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -718,4 +718,4 @@ def test_abstract_base_class_norms(norm): Test that all types of normalizers subclasses Abstract Base class `colors.Norms` """ - assert isinstance(norm, mcolors.Norms) \ No newline at end of file + assert isinstance(norm, mcolors.Norms)