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

Skip to content

Commit 0959f01

Browse files
committed
expose multivariate plotting functionality to top level functions imshow, pcolor, pcolormesh, and Collection
1 parent 60383ac commit 0959f01

19 files changed

Lines changed: 417 additions & 85 deletions

doc/api/colors_api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Multivariate Colormaps
5555
BivarColormap
5656
SegmentedBivarColormap
5757
BivarColormapFromImage
58+
MultivarColormap
5859

5960
Other classes
6061
-------------

lib/matplotlib/axes/_axes.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6175,6 +6175,7 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
61756175
- (M, N): an image with scalar data. The values are mapped to
61766176
colors using normalization and a colormap. See parameters *norm*,
61776177
*cmap*, *vmin*, *vmax*.
6178+
- (v, M, N): if coupled with a cmap that supports v scalars
61786179
- (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
61796180
- (M, N, 4): an image with RGBA values (0-1 float or 0-255 int),
61806181
i.e. including transparency.
@@ -6184,15 +6185,16 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
61846185
61856186
Out-of-range RGB(A) values are clipped.
61866187
6187-
%(cmap_doc)s
6188+
6189+
%(multi_cmap_doc)s
61886190
61896191
This parameter is ignored if *X* is RGB(A).
61906192
6191-
%(norm_doc)s
6193+
%(multi_norm_doc)s
61926194
61936195
This parameter is ignored if *X* is RGB(A).
61946196
6195-
%(vmin_vmax_doc)s
6197+
%(multi_vmin_vmax_doc)s
61966198
61976199
This parameter is ignored if *X* is RGB(A).
61986200
@@ -6271,6 +6273,9 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
62716273
See :doc:`/gallery/images_contours_and_fields/image_antialiasing` for
62726274
a discussion of image antialiasing.
62736275
6276+
Only 'data' is available when using `~matplotlib.colors.BivarColormap`
6277+
or `~matplotlib.colors.MultivarColormap`
6278+
62746279
alpha : float or array-like, optional
62756280
The alpha blending value, between 0 (transparent) and 1 (opaque).
62766281
If *alpha* is an array, the alpha blending values are applied pixel
@@ -6376,6 +6381,7 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
63766381
if aspect is not None:
63776382
self.set_aspect(aspect)
63786383

6384+
X = mcolorizer._ensure_multivariate_data(X, im.norm.n_components)
63796385
im.set_data(X)
63806386
im.set_alpha(alpha)
63816387
if im.get_clip_path() is None:
@@ -6531,9 +6537,10 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
65316537
65326538
Parameters
65336539
----------
6534-
C : 2D array-like
6540+
C : 2D or 3D array-like
65356541
The color-mapped values. Color-mapping is controlled by *cmap*,
6536-
*norm*, *vmin*, and *vmax*.
6542+
*norm*, *vmin*, and *vmax*. 3D arrays are supported only if the
6543+
cmap supports v channels, where v is the size along the first axis.
65376544
65386545
X, Y : array-like, optional
65396546
The coordinates of the corners of quadrilaterals of a pcolormesh::
@@ -6578,11 +6585,11 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
65786585
See :doc:`/gallery/images_contours_and_fields/pcolormesh_grids`
65796586
for more description.
65806587
6581-
%(cmap_doc)s
6588+
%(multi_cmap_doc)s
65826589
6583-
%(norm_doc)s
6590+
%(multi_norm_doc)s
65846591
6585-
%(vmin_vmax_doc)s
6592+
%(multi_vmin_vmax_doc)s
65866593
65876594
%(colorizer_doc)s
65886595
@@ -6657,8 +6664,17 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
66576664
if shading is None:
66586665
shading = mpl.rcParams['pcolor.shading']
66596666
shading = shading.lower()
6660-
X, Y, C, shading = self._pcolorargs('pcolor', *args, shading=shading,
6661-
kwargs=kwargs)
6667+
6668+
if colorizer is None:
6669+
cmap = mcolorizer._ensure_cmap(cmap, accept_multivariate=True)
6670+
C = mcolorizer._ensure_multivariate_data(args[-1], cmap.n_variates)
6671+
else:
6672+
C = mcolorizer._ensure_multivariate_data(args[-1],
6673+
colorizer.cmap.n_variates)
6674+
6675+
X, Y, C, shading = self._pcolorargs('pcolor', *args[:-1], C,
6676+
shading=shading, kwargs=kwargs)
6677+
66626678
linewidths = (0.25,)
66636679
if 'linewidth' in kwargs:
66646680
kwargs['linewidths'] = kwargs.pop('linewidth')
@@ -6733,6 +6749,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
67336749
- (M, N) or M*N: a mesh with scalar data. The values are mapped to
67346750
colors using normalization and a colormap. See parameters *norm*,
67356751
*cmap*, *vmin*, *vmax*.
6752+
- (v, M, N): if coupled with a cmap that supports v scalars
67366753
- (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
67376754
- (M, N, 4): an image with RGBA values (0-1 float or 0-255 int),
67386755
i.e. including transparency.
@@ -6767,11 +6784,11 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
67676784
expanded as needed into the appropriate 2D arrays, making a
67686785
rectangular grid.
67696786
6770-
%(cmap_doc)s
6787+
%(multi_cmap_doc)s
67716788
6772-
%(norm_doc)s
6789+
%(multi_norm_doc)s
67736790
6774-
%(vmin_vmax_doc)s
6791+
%(multi_vmin_vmax_doc)s
67756792
67766793
%(colorizer_doc)s
67776794
@@ -6897,7 +6914,15 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
68976914
shading = mpl._val_or_rc(shading, 'pcolor.shading').lower()
68986915
kwargs.setdefault('edgecolors', 'none')
68996916

6900-
X, Y, C, shading = self._pcolorargs('pcolormesh', *args,
6917+
if colorizer is None:
6918+
cmap = mcolorizer._ensure_cmap(cmap, accept_multivariate=True)
6919+
C = mcolorizer._ensure_multivariate_data(args[-1], cmap.n_variates)
6920+
else:
6921+
C = mcolorizer._ensure_multivariate_data(args[-1],
6922+
colorizer.cmap.n_variates)
6923+
6924+
6925+
X, Y, C, shading = self._pcolorargs('pcolormesh', *args[:-1], C,
69016926
shading=shading, kwargs=kwargs)
69026927
coords = np.stack([X, Y], axis=-1)
69036928

lib/matplotlib/axes/_axes.pyi

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ from matplotlib.collections import (
1212
QuadMesh,
1313
)
1414
from matplotlib.colorizer import Colorizer
15-
from matplotlib.colors import Colormap, Normalize
15+
from matplotlib.colors import (
16+
Colormap,
17+
BivarColormap,
18+
MultivarColormap,
19+
Norm,
20+
Normalize,
21+
)
1622
from matplotlib.container import (
1723
BarContainer, PieContainer, ErrorbarContainer, StemContainer)
1824
from matplotlib.contour import ContourSet, QuadContourSet
@@ -500,14 +506,14 @@ class Axes(_AxesBase):
500506
def imshow(
501507
self,
502508
X: ArrayLike | PIL.Image.Image,
503-
cmap: str | Colormap | None = ...,
504-
norm: str | Normalize | None = ...,
509+
cmap: str | Colormap | BivarColormap | MultivarColormap | None = ...,
510+
norm: str | Norm | None = ...,
505511
*,
506512
aspect: Literal["equal", "auto"] | float | None = ...,
507513
interpolation: str | None = ...,
508514
alpha: float | ArrayLike | None = ...,
509-
vmin: float | None = ...,
510-
vmax: float | None = ...,
515+
vmin: float | tuple[float] | None = ...,
516+
vmax: float | tuple[float] | None = ...,
511517
colorizer: Colorizer | None = ...,
512518
origin: Literal["upper", "lower"] | None = ...,
513519
extent: tuple[float, float, float, float] | None = ...,
@@ -524,10 +530,10 @@ class Axes(_AxesBase):
524530
*args: ArrayLike,
525531
shading: Literal["flat", "nearest", "auto"] | None = ...,
526532
alpha: float | None = ...,
527-
norm: str | Normalize | None = ...,
528-
cmap: str | Colormap | None = ...,
529-
vmin: float | None = ...,
530-
vmax: float | None = ...,
533+
norm: str | Norm | None = ...,
534+
cmap: str | Colormap | BivarColormap | MultivarColormap | None = ...,
535+
vmin: float | tuple[float] | None = ...,
536+
vmax: float | tuple[float] | None = ...,
531537
colorizer: Colorizer | None = ...,
532538
data=...,
533539
**kwargs
@@ -536,10 +542,10 @@ class Axes(_AxesBase):
536542
self,
537543
*args: ArrayLike,
538544
alpha: float | None = ...,
539-
norm: str | Normalize | None = ...,
540-
cmap: str | Colormap | None = ...,
541-
vmin: float | None = ...,
542-
vmax: float | None = ...,
545+
norm: str | Norm | None = ...,
546+
cmap: str | Colormap | BivarColormap | MultivarColormap | None = ...,
547+
vmin: float | tuple[float] | None = ...,
548+
vmax: float | tuple[float] | None = ...,
543549
colorizer: Colorizer | None = ...,
544550
shading: Literal["flat", "nearest", "gouraud", "auto"] | None = ...,
545551
antialiased: bool = ...,

lib/matplotlib/collections.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2386,6 +2386,8 @@ def set_array(self, A):
23862386
h, w = height, width
23872387
ok_shapes = [(h, w, 3), (h, w, 4), (h, w), (h * w,)]
23882388
if A is not None:
2389+
if hasattr(self, 'norm'):
2390+
A = mcolorizer._ensure_multivariate_data(A, self.norm.n_components)
23892391
shape = np.shape(A)
23902392
if shape not in ok_shapes:
23912393
raise ValueError(
@@ -2643,7 +2645,7 @@ def _get_unmasked_polys(self):
26432645
mask = (mask[0:-1, 0:-1] | mask[1:, 1:] | mask[0:-1, 1:] | mask[1:, 0:-1])
26442646
arr = self.get_array()
26452647
if arr is not None:
2646-
arr = np.ma.getmaskarray(arr)
2648+
arr = self._getmaskarray(arr)
26472649
if arr.ndim == 3:
26482650
# RGB(A) case
26492651
mask |= np.any(arr, axis=-1)

lib/matplotlib/collections.pyi

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ from numpy.typing import ArrayLike, NDArray
77
from . import colorizer, transforms
88
from .backend_bases import MouseEvent
99
from .artist import Artist
10-
from .colors import Normalize, Colormap
10+
from .colors import (
11+
Colormap,
12+
BivarColormap,
13+
MultivarColormap,
14+
Norm,
15+
)
1116
from .lines import Line2D
1217
from .path import Path
1318
from .patches import Patch
@@ -29,8 +34,8 @@ class Collection(colorizer.ColorizingArtist):
2934
antialiaseds: bool | Sequence[bool] | None = ...,
3035
offsets: tuple[float, float] | Sequence[tuple[float, float]] | None = ...,
3136
offset_transform: transforms.Transform | None = ...,
32-
norm: Normalize | None = ...,
33-
cmap: Colormap | None = ...,
37+
norm: Norm | None = ...,
38+
cmap: Colormap | BivarColormap | MultivarColormap | None = ...,
3439
colorizer: colorizer.Colorizer | None = ...,
3540
pickradius: float = ...,
3641
hatch: str | None = ...,

lib/matplotlib/colorizer.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,18 @@ def get_array(self):
600600
"""
601601
return self._A
602602

603+
def _getmaskarray(self, A):
604+
"""
605+
Similar to np.ma.getmaskarray but also handles the case where
606+
the data has multiple fields.
607+
608+
The return array always has the same shape as the input, and dtype bool
609+
"""
610+
mask = np.ma.getmaskarray(A)
611+
if isinstance(self.norm, colors.MultiNorm):
612+
mask = np.any(mask.view('bool').reshape((*A.shape, -1)), axis=-1)
613+
return mask
614+
603615
def changed(self):
604616
"""
605617
Call this whenever the mappable is changed to notify all the

lib/matplotlib/colorizer.pyi

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ class Colorizer:
99
callbacks: cbook.CallbackRegistry
1010
def __init__(
1111
self,
12-
cmap: str | colors.Colormap | None = ...,
12+
cmap: str | colors.Colormap | colors.BivarColormap | colors.MultivarColormap | None = ...,
1313
norm: str | colors.Norm | None = ...,
1414
) -> None: ...
1515
@property
1616
def norm(self) -> colors.Norm: ...
1717
@norm.setter
18-
def norm(self, norm: colors.Norm | str | None) -> None: ...
18+
def norm(self, norm: colors.Norm | str | tuple[str] | None) -> None: ...
1919
def to_rgba(
2020
self,
2121
x: np.ndarray,
@@ -26,28 +26,28 @@ class Colorizer:
2626
def autoscale(self, A: ArrayLike) -> None: ...
2727
def autoscale_None(self, A: ArrayLike) -> None: ...
2828
@property
29-
def cmap(self) -> colors.Colormap: ...
29+
def cmap(self) -> colors.Colormap | colors.BivarColormap | colors.MultivarColormap: ...
3030
@cmap.setter
31-
def cmap(self, cmap: colors.Colormap | str | None) -> None: ...
31+
def cmap(self, cmap: colors.Colormap | colors.BivarColormap | colors.MultivarColormap | str | None) -> None: ...
3232
def get_clim(self) -> tuple[float, float]: ...
3333
def set_clim(self, vmin: float | tuple[float, float] | None = ..., vmax: float | None = ...) -> None: ...
3434
def changed(self) -> None: ...
3535
@property
36-
def vmin(self) -> float | None: ...
36+
def vmin(self) -> float | tuple[float] | None: ...
3737
@vmin.setter
38-
def vmin(self, value: float | None) -> None: ...
38+
def vmin(self, value: float | tuple[float] | None) -> None: ...
3939
@property
40-
def vmax(self) -> float | None: ...
40+
def vmax(self) -> float | tuple[float] | None: ...
4141
@vmax.setter
42-
def vmax(self, value: float | None) -> None: ...
42+
def vmax(self, value: float | tuple[float] | None) -> None: ...
4343
@property
44-
def clip(self) -> bool: ...
44+
def clip(self) -> bool | tuple[bool, ...]: ...
4545
@clip.setter
46-
def clip(self, value: bool) -> None: ...
46+
def clip(self, value: ArrayLike | bool) -> None: ...
4747

4848

4949
class _ColorizerInterface:
50-
cmap: colors.Colormap
50+
cmap: colors.Colormap | colors.BivarColormap | colors.MultivarColormap
5151
colorbar: colorbar.Colorbar | None
5252
callbacks: cbook.CallbackRegistry
5353
def to_rgba(
@@ -60,8 +60,8 @@ class _ColorizerInterface:
6060
def get_clim(self) -> tuple[float, float]: ...
6161
def set_clim(self, vmin: float | tuple[float, float] | None = ..., vmax: float | None = ...) -> None: ...
6262
def get_alpha(self) -> float | None: ...
63-
def get_cmap(self) -> colors.Colormap: ...
64-
def set_cmap(self, cmap: str | colors.Colormap) -> None: ...
63+
def get_cmap(self) -> colors.Colormap | colors.BivarColormap | colors.MultivarColormap: ...
64+
def set_cmap(self, cmap: str | colors.Colormap | colors.BivarColormap | colors.MultivarColormap) -> None: ...
6565
@property
6666
def norm(self) -> colors.Norm: ...
6767
@norm.setter
@@ -75,7 +75,7 @@ class _ScalarMappable(_ColorizerInterface):
7575
def __init__(
7676
self,
7777
norm: colors.Norm | None = ...,
78-
cmap: str | colors.Colormap | None = ...,
78+
cmap: str | colors.Colormap | colors.BivarColormap | colors.MultivarColormap | None = ...,
7979
*,
8080
colorizer: Colorizer | None = ...,
8181
**kwargs

lib/matplotlib/colors.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,9 +1432,9 @@ def __init__(self, colormaps, combination_mode, name='multivariate colormap'):
14321432
Describe how colormaps are combined in sRGB space
14331433
14341434
- If 'sRGB_add' -> Mixing produces brighter colors
1435-
`sRGB = sum(colors)`
1435+
``sRGB = sum(colors)``
14361436
- If 'sRGB_sub' -> Mixing produces darker colors
1437-
`sRGB = 1 - sum(1 - colors)`
1437+
``sRGB = 1 - sum(1 - colors)``
14381438
name : str, optional
14391439
The name of the colormap family.
14401440
"""
@@ -1610,11 +1610,11 @@ def with_extremes(self, *, bad=None, under=None, over=None):
16101610
If Matplotlib color, the bad value is set accordingly in the copy
16111611
16121612
under tuple of :mpltype:`color`, default: None
1613-
If tuple, the `under` value of each component is set with the values
1613+
If tuple, the 'under' value of each component is set with the values
16141614
from the tuple.
16151615
16161616
over tuple of :mpltype:`color`, default: None
1617-
If tuple, the `over` value of each component is set with the values
1617+
If tuple, the 'over' value of each component is set with the values
16181618
from the tuple.
16191619
16201620
Returns

0 commit comments

Comments
 (0)