Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

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

Provide feedback

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

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Multivar imshow#30597

New issue

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

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

Already on GitHub?Sign in to your account

Open
trygvrad wants to merge4 commits intomatplotlib:main
base:main
Choose a base branch
Loading
fromtrygvrad:multivar_imshow
Open
Show file tree
Hide file tree
Changes from1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
NextNext commit
expose multivariate plotting functionality to top level functions ims…
…how, pcolor, pcolormesh, and Collection
  • Loading branch information
@trygvrad
trygvrad committedDec 1, 2025
commit50f55a8da12700287361a59dcad6b3c7be82b619
1 change: 1 addition & 0 deletionsdoc/api/colors_api.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -55,6 +55,7 @@ Multivariate Colormaps
BivarColormap
SegmentedBivarColormap
BivarColormapFromImage
MultivarColormap

Other classes
-------------
Expand Down
53 changes: 39 additions & 14 deletionslib/matplotlib/axes/_axes.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -6150,6 +6150,7 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
- (M, N): an image with scalar data. The values are mapped to
colors using normalization and a colormap. See parameters *norm*,
*cmap*, *vmin*, *vmax*.
- (v, M, N): if coupled with a cmap that supports v scalars
- (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
- (M, N, 4): an image with RGBA values (0-1 float or 0-255 int),
i.e. including transparency.
Expand All@@ -6159,15 +6160,16 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,

Out-of-range RGB(A) values are clipped.

%(cmap_doc)s

%(multi_cmap_doc)s

This parameter is ignored if *X* is RGB(A).

%(norm_doc)s
%(multi_norm_doc)s

This parameter is ignored if *X* is RGB(A).

%(vmin_vmax_doc)s
%(multi_vmin_vmax_doc)s

This parameter is ignored if *X* is RGB(A).

Expand DownExpand Up@@ -6246,6 +6248,9 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
See :doc:`/gallery/images_contours_and_fields/image_antialiasing` for
a discussion of image antialiasing.

Only 'data' is available when using `~matplotlib.colors.BivarColormap`
or `~matplotlib.colors.MultivarColormap`
Copy link
Member

Choose a reason for hiding this comment

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

what do you mean by data?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

'data' refers to the keyword argumentinterpolation_stage

        interpolation_stage : {'auto', 'data', 'rgba'}, default: 'auto'            Supported values:            - 'data': Interpolation is carried out on the data provided by the user              This is useful if interpolating between pixels during upsampling.            - 'rgba': The interpolation is carried out in RGBA-space after the              color-mapping has been applied. This is useful if downsampling and              combining pixels visually.            - 'auto': Select a suitable interpolation stage automatically. This uses              'rgba' when downsampling, or upsampling at a rate less than 3, and              'data' when upsampling at a higher rate.            See :doc:`/gallery/images_contours_and_fields/image_antialiasing` for            a discussion of image antialiasing.            Only 'data' is available when using `~matplotlib.colors.BivarColormap`            or `~matplotlib.colors.MultivarColormap`

I'm changing the last lines to:

            When using a `~matplotlib.colors.BivarColormap` or             `~matplotlib.colors.MultivarColormap`, 'data' is the only valid            interpolation_stage.

in an attempt to increase clarity


alpha : float or array-like, optional
The alpha blending value, between 0 (transparent) and 1 (opaque).
If *alpha* is an array, the alpha blending values are applied pixel
Expand DownExpand Up@@ -6351,6 +6356,7 @@ def imshow(self, X, cmap=None, norm=None, *, aspect=None,
if aspect is not None:
self.set_aspect(aspect)

X = mcolorizer._ensure_multivariate_data(X, im.norm.n_components)
im.set_data(X)
im.set_alpha(alpha)
if im.get_clip_path() is None:
Expand DownExpand Up@@ -6506,9 +6512,10 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,

Parameters
----------
C : 2D array-like
C : 2Dor 3Darray-like
The color-mapped values. Color-mapping is controlled by *cmap*,
*norm*, *vmin*, and *vmax*.
*norm*, *vmin*, and *vmax*. 3D arrays are supported only if the
cmap supports v channels, where v is the size along the first axis.

Copy link
Member

Choose a reason for hiding this comment

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

I think (?, ?, ?) notation might help make this clearer?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

good idea. Does the following work for you?

        Parameters        ----------        C : 2D (I, J) or 3D (v, I, J) array-like            The color-mapped values.  Color-mapping is controlled by *cmap*,            *norm*, *vmin*, and *vmax*. 3D arrays are supported only if the            cmap supports v channels.        X, Y : array-like, optional            The coordinates of the corners of quadrilaterals of a pcolormesh::                (X[i+1, j], Y[i+1, j])       (X[i+1, j+1], Y[i+1, j+1])                                      ●╶───╴●                                      │     │                                      ●╶───╴●                    (X[i, j], Y[i, j])       (X[i, j+1], Y[i, j+1])            Note that the column index corresponds to the x-coordinate, and            the row index corresponds to y. For details, see the            :ref:`Notes <axes-pcolormesh-grid-orientation>` section below.            If ``shading='flat'`` the dimensions of *X* and *Y* should be one            greater than those of *C*, and the quadrilateral is colored due            to the value at ``C[i, j]``.  If *X*, *Y* and *C* have equal            dimensions, a warning will be raised and the last row and column            of *C* will be ignored.

I'm using uppercase I and J inC : 2D (I, J) or 3D (v, I, J) array-like as these refer to the length, while the lowercase versions are used further down for the corresponding indexes.

X, Y : array-like, optional
The coordinates of the corners of quadrilaterals of a pcolormesh::
Expand DownExpand Up@@ -6555,11 +6562,11 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
See :doc:`/gallery/images_contours_and_fields/pcolormesh_grids`
for more description.

%(cmap_doc)s
%(multi_cmap_doc)s

%(norm_doc)s
%(multi_norm_doc)s

%(vmin_vmax_doc)s
%(multi_vmin_vmax_doc)s

%(colorizer_doc)s

Expand DownExpand Up@@ -6634,8 +6641,17 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
if shading is None:
shading = mpl.rcParams['pcolor.shading']
shading = shading.lower()
X, Y, C, shading = self._pcolorargs('pcolor', *args, shading=shading,
kwargs=kwargs)

if colorizer is None:
cmap = mcolorizer._ensure_cmap(cmap, accept_multivariate=True)
C = mcolorizer._ensure_multivariate_data(args[-1], cmap.n_variates)
else:
C = mcolorizer._ensure_multivariate_data(args[-1],
colorizer.cmap.n_variates)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
ifcolorizerisNone:
cmap=mcolorizer._ensure_cmap(cmap,accept_multivariate=True)
C=mcolorizer._ensure_multivariate_data(args[-1],cmap.n_variates)
else:
C=mcolorizer._ensure_multivariate_data(args[-1],
colorizer.cmap.n_variates)
ifcolorizerisNone:
cmap=mcolorizer._ensure_cmap(cmap,accept_multivariate=True)
colorizer=
C=mcolorizer._ensure_multivariate_data(args[-1],
colorizer.cmap.n_variates)

is there a reason to not instantiate the colorizer here?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I believe it was designed this way to ensure error handling was done in the same way as previously, but I was able to rework it to a cleaner state by doing

mcolorizer.ColorizingArtist._check_exclusionary_keywords(colorizer,                                                          vmin=vmin, vmax=vmax,                                                          norm=norm, cmap=cmap)

before the creation of the QuadMesh rather than

collection._check_exclusionary_keywords(colorizer, vmin=vmin, vmax=vmax)

after.


X, Y, C, shading = self._pcolorargs('pcolor', *args[:-1], C,
shading=shading, kwargs=kwargs)

linewidths = (0.25,)
if 'linewidth' in kwargs:
kwargs['linewidths'] = kwargs.pop('linewidth')
Expand DownExpand Up@@ -6710,6 +6726,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
- (M, N) or M*N: a mesh with scalar data. The values are mapped to
colors using normalization and a colormap. See parameters *norm*,
*cmap*, *vmin*, *vmax*.
- (v, M, N): if coupled with a cmap that supports v scalars
- (M, N, 3): an image with RGB values (0-1 float or 0-255 int).
- (M, N, 4): an image with RGBA values (0-1 float or 0-255 int),
i.e. including transparency.
Expand DownExpand Up@@ -6746,11 +6763,11 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
expanded as needed into the appropriate 2D arrays, making a
rectangular grid.

%(cmap_doc)s
%(multi_cmap_doc)s

%(norm_doc)s
%(multi_norm_doc)s

%(vmin_vmax_doc)s
%(multi_vmin_vmax_doc)s

%(colorizer_doc)s

Expand DownExpand Up@@ -6873,7 +6890,15 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
shading = mpl._val_or_rc(shading, 'pcolor.shading').lower()
kwargs.setdefault('edgecolors', 'none')

X, Y, C, shading = self._pcolorargs('pcolormesh', *args,
if colorizer is None:
cmap = mcolorizer._ensure_cmap(cmap, accept_multivariate=True)
C = mcolorizer._ensure_multivariate_data(args[-1], cmap.n_variates)
else:
C = mcolorizer._ensure_multivariate_data(args[-1],
colorizer.cmap.n_variates)

Comment on lines 6897 to 6899
Copy link
Member

Choose a reason for hiding this comment

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

potentially turn into a helper function if it repeats?

Copy link
ContributorAuthor

@trygvradtrygvradNov 30, 2025
edited
Loading

Choose a reason for hiding this comment

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

The following lines are repeated for pcolor and pcolormesh:

mcolorizer.ColorizingArtist._check_exclusionary_keywords(colorizer,vmin=vmin,vmax=vmax,norm=norm,cmap=cmap)ifcolorizerisNone:colorizer=mcolorizer.Colorizer(cmap=cmap,norm=norm)C=mcolorizer._ensure_multivariate_data(args[-1],colorizer.cmap.n_variates)

We could make this a helper function :)
If so, should it be part of the Axes class? and do you have a suggestion for a name?

EDIT: this is after modifying it in accordance with one of your comments above :)


X, Y, C, shading = self._pcolorargs('pcolormesh', *args[:-1], C,
shading=shading, kwargs=kwargs)
coords = np.stack([X, Y], axis=-1)

Expand Down
32 changes: 19 additions & 13 deletionslib/matplotlib/axes/_axes.pyi
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,13 @@ from matplotlib.collections import (
QuadMesh,
)
from matplotlib.colorizer import Colorizer
from matplotlib.colors import Colormap, Normalize
from matplotlib.colors import (
Colormap,
BivarColormap,
MultivarColormap,
Norm,
Normalize,
)
from matplotlib.container import (
BarContainer, PieContainer, ErrorbarContainer, StemContainer)
from matplotlib.contour import ContourSet, QuadContourSet
Expand DownExpand Up@@ -500,14 +506,14 @@ class Axes(_AxesBase):
def imshow(
self,
X: ArrayLike | PIL.Image.Image,
cmap: str | Colormap | None = ...,
norm: str |Normalize | None = ...,
cmap: str | Colormap |BivarColormap | MultivarColormap |None = ...,
norm: str |Norm | None = ...,
*,
aspect: Literal["equal", "auto"] | float | None = ...,
interpolation: str | None = ...,
alpha: float | ArrayLike | None = ...,
vmin: float | None = ...,
vmax: float | None = ...,
vmin: float |tuple[float] |None = ...,
vmax: float |tuple[float] |None = ...,
colorizer: Colorizer | None = ...,
origin: Literal["upper", "lower"] | None = ...,
extent: tuple[float, float, float, float] | None = ...,
Expand All@@ -524,10 +530,10 @@ class Axes(_AxesBase):
*args: ArrayLike,
shading: Literal["flat", "nearest", "auto"] | None = ...,
alpha: float | None = ...,
norm: str |Normalize | None = ...,
cmap: str | Colormap | None = ...,
vmin: float | None = ...,
vmax: float | None = ...,
norm: str |Norm | None = ...,
cmap: str | Colormap |BivarColormap | MultivarColormap |None = ...,
vmin: float |tuple[float] |None = ...,
vmax: float |tuple[float] |None = ...,
colorizer: Colorizer | None = ...,
data=...,
**kwargs
Expand All@@ -536,10 +542,10 @@ class Axes(_AxesBase):
self,
*args: ArrayLike,
alpha: float | None = ...,
norm: str |Normalize | None = ...,
cmap: str | Colormap | None = ...,
vmin: float | None = ...,
vmax: float | None = ...,
norm: str |Norm | None = ...,
cmap: str | Colormap |BivarColormap | MultivarColormap |None = ...,
vmin: float |tuple[float] |None = ...,
vmax: float |tuple[float] |None = ...,
colorizer: Colorizer | None = ...,
shading: Literal["flat", "nearest", "gouraud", "auto"] | None = ...,
antialiased: bool = ...,
Expand Down
4 changes: 3 additions & 1 deletionlib/matplotlib/collections.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2352,6 +2352,8 @@ def set_array(self, A):
h,w=height,width
ok_shapes= [(h,w,3), (h,w,4), (h,w), (h*w,)]
ifAisnotNone:
ifhasattr(self,'norm'):
A=mcolorizer._ensure_multivariate_data(A,self.norm.n_components)
shape=np.shape(A)
ifshapenotinok_shapes:
raiseValueError(
Expand DownExpand Up@@ -2609,7 +2611,7 @@ def _get_unmasked_polys(self):
mask= (mask[0:-1,0:-1]|mask[1:,1:]|mask[0:-1,1:]|mask[1:,0:-1])
arr=self.get_array()
ifarrisnotNone:
arr=np.ma.getmaskarray(arr)
arr=self._getmaskarray(arr)
ifarr.ndim==3:
# RGB(A) case
mask|=np.any(arr,axis=-1)
Expand Down
11 changes: 8 additions & 3 deletionslib/matplotlib/collections.pyi
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,12 @@ from numpy.typing import ArrayLike, NDArray
from . import colorizer, transforms
from .backend_bases import MouseEvent
from .artist import Artist
from .colors import Normalize, Colormap
from .colors import (
Colormap,
BivarColormap,
MultivarColormap,
Norm,
)
from .lines import Line2D
from .path import Path
from .patches import Patch
Expand All@@ -29,8 +34,8 @@ class Collection(colorizer.ColorizingArtist):
antialiaseds: bool | Sequence[bool] | None = ...,
offsets: tuple[float, float] | Sequence[tuple[float, float]] | None = ...,
offset_transform: transforms.Transform | None = ...,
norm:Normalize | None = ...,
cmap: Colormap | None = ...,
norm:Norm | None = ...,
cmap: Colormap |BivarColormap | MultivarColormap |None = ...,
colorizer: colorizer.Colorizer | None = ...,
pickradius: float = ...,
hatch: str | None = ...,
Expand Down
12 changes: 12 additions & 0 deletionslib/matplotlib/colorizer.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -600,6 +600,18 @@ def get_array(self):
"""
return self._A

def _getmaskarray(self, A):
"""
Similar to np.ma.getmaskarray but also handles the case where
the data has multiple fields.

The return array always has the same shape as the input, and dtype bool
"""
mask = np.ma.getmaskarray(A)
if isinstance(self.norm, colors.MultiNorm):
mask = np.any(mask.view('bool').reshape((*A.shape, -1)), axis=-1)
return mask

def changed(self):
"""
Call this whenever the mappable is changed to notify all the
Expand Down
28 changes: 14 additions & 14 deletionslib/matplotlib/colorizer.pyi
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,13 +9,13 @@ class Colorizer:
callbacks: cbook.CallbackRegistry
def __init__(
self,
cmap: str | colors.Colormap | None = ...,
cmap: str | colors.Colormap |colors.BivarColormap | colors.MultivarColormap |None = ...,
norm: str | colors.Norm | None = ...,
) -> None: ...
@property
def norm(self) -> colors.Norm: ...
@norm.setter
def norm(self, norm: colors.Norm | str | None) -> None: ...
def norm(self, norm: colors.Norm | str |tuple[str] |None) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

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

tuple[str, ...]

def to_rgba(
self,
x: np.ndarray,
Expand All@@ -26,28 +26,28 @@ class Colorizer:
def autoscale(self, A: ArrayLike) -> None: ...
def autoscale_None(self, A: ArrayLike) -> None: ...
@property
def cmap(self) -> colors.Colormap: ...
def cmap(self) -> colors.Colormap | colors.BivarColormap | colors.MultivarColormap: ...
@cmap.setter
def cmap(self, cmap: colors.Colormap | str | None) -> None: ...
def cmap(self, cmap: colors.Colormap |colors.BivarColormap | colors.MultivarColormap |str | None) -> None: ...
def get_clim(self) -> tuple[float, float]: ...
def set_clim(self, vmin: float | tuple[float, float] | None = ..., vmax: float | None = ...) -> None: ...
def changed(self) -> None: ...
@property
def vmin(self) -> float | None: ...
def vmin(self) -> float |tuple[float] |None: ...
@vmin.setter
def vmin(self, value: float | None) -> None: ...
def vmin(self, value: float |tuple[float] |None) -> None: ...
@property
def vmax(self) -> float | None: ...
def vmax(self) -> float |tuple[float] |None: ...
@vmax.setter
def vmax(self, value: float | None) -> None: ...
def vmax(self, value: float |tuple[float] |None) -> None: ...
@property
def clip(self) -> bool: ...
def clip(self) -> bool | tuple[bool, ...]: ...
@clip.setter
def clip(self, value: bool) -> None: ...
def clip(self, value:ArrayLike |bool) -> None: ...


class _ColorizerInterface:
cmap: colors.Colormap
cmap: colors.Colormap | colors.BivarColormap | colors.MultivarColormap
colorbar: colorbar.Colorbar | None
callbacks: cbook.CallbackRegistry
def to_rgba(
Expand All@@ -60,8 +60,8 @@ class _ColorizerInterface:
def get_clim(self) -> tuple[float, float]: ...
def set_clim(self, vmin: float | tuple[float, float] | None = ..., vmax: float | None = ...) -> None: ...
def get_alpha(self) -> float | None: ...
def get_cmap(self) -> colors.Colormap: ...
def set_cmap(self, cmap: str | colors.Colormap) -> None: ...
def get_cmap(self) -> colors.Colormap | colors.BivarColormap | colors.MultivarColormap: ...
def set_cmap(self, cmap: str | colors.Colormap | colors.BivarColormap | colors.MultivarColormap) -> None: ...
@property
def norm(self) -> colors.Norm: ...
@norm.setter
Expand All@@ -75,7 +75,7 @@ class _ScalarMappable(_ColorizerInterface):
def __init__(
self,
norm: colors.Norm | None = ...,
cmap: str | colors.Colormap | None = ...,
cmap: str | colors.Colormap |colors.BivarColormap | colors.MultivarColormap |None = ...,
*,
colorizer: Colorizer | None = ...,
**kwargs
Expand Down
8 changes: 4 additions & 4 deletionslib/matplotlib/colors.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1431,9 +1431,9 @@ def __init__(self, colormaps, combination_mode, name='multivariate colormap'):
Describe how colormaps are combined in sRGB space

- If 'sRGB_add' -> Mixing produces brighter colors
`sRGB = sum(colors)`
``sRGB = sum(colors)``
- If 'sRGB_sub' -> Mixing produces darker colors
`sRGB = 1 - sum(1 - colors)`
``sRGB = 1 - sum(1 - colors)``
name : str, optional
The name of the colormap family.
"""
Expand DownExpand Up@@ -1609,11 +1609,11 @@ def with_extremes(self, *, bad=None, under=None, over=None):
If Matplotlib color, the bad value is set accordingly in the copy

under tuple of :mpltype:`color`, default: None
If tuple, the`under` value of each component is set with the values
If tuple, the'under' value of each component is set with the values
from the tuple.

over tuple of :mpltype:`color`, default: None
If tuple, the`over` value of each component is set with the values
If tuple, the'over' value of each component is set with the values
from the tuple.

Returns
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp