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 merge2 commits intomatplotlib:main
base:main
Choose a base branch
Loading
fromtrygvrad:multivar_imshow
Open

Conversation

@trygvrad
Copy link
Contributor

@trygvradtrygvrad commentedSep 24, 2025
edited
Loading

Exposes the functionality ofMultiNorm,BivarColormap andMultivarColormap to the top level plotting functionsax.imshow(),ax.pcolor() andax.pcolormesh(). This coloses#30526, seeBivariate and Multivariate Colormapping
As a side-effect of the pcolor/pcolormesh implementation,Collection also gets the new functionality.

In short, this PR allows you to plot multivariate data more easily, but it does not:

  • Create equivalents to fig.colorbar() for BivarColormap and MultivarColormap to work with ColorizingArtist
  • Select bivariate and multivariate colormaps to include in matplotlib
  • Examples demonstrating the new functionality

These will come in later PRs. SeeBivariate and Multivariate Colormapping

Examples demonstrating new functionality:

importnumpyasnpimportmatplotlibasmplimportmatplotlib.pyplotaspltcmap=mpl.bivar_colormaps['BiPeak']x_0=np.arange(25,dtype='float32').reshape(5,5)%5x_1=np.arange(25,dtype='float32').reshape(5,5).T%5x_0,x_1=x_0+0.3*x_1,x_0*-0.3+x_1,fig,axes=plt.subplots(1,3,figsize=(6,2))axes[0].imshow(x_0,cmap=cmap[0])axes[1].imshow(x_1,cmap=cmap[1])axes[2].imshow((x_0,x_1),cmap=cmap)axes[0].set_title('data 0')axes[1].set_title('data 1')axes[2].set_title('data 0 and 1')
image
fig,axes=plt.subplots(1,6,figsize=(10,2.3))axes[0].imshow((x_0,x_1),cmap='BiPeak',interpolation='nearest')axes[1].matshow((x_0,x_1),cmap='BiPeak')axes[2].pcolor((x_0,x_1),cmap='BiPeak')axes[3].pcolormesh((x_0,x_1),cmap='BiPeak')x=np.arange(5)y=np.arange(5)X,Y=np.meshgrid(x,y)axes[4].pcolormesh(X,Y, (x_0,x_1),cmap='BiPeak')patches= [mpl.patches.Wedge((.3,.7),.1,0,360),# Full circlempl.patches.Wedge((.7,.8),.2,0,360,width=0.05),# Full ringmpl.patches.Wedge((.8,.3),.2,0,45),# Full sectormpl.patches.Wedge((.8,.3),.2,22.5,90,width=0.10),# Ring sector]colors_0=np.arange(len(patches))//2colors_1=np.arange(len(patches))%2p=mpl.collections.PatchCollection(patches,cmap='BiPeak',alpha=0.5)p.set_array((colors_0,colors_1))axes[5].add_collection(p)axes[0].set_title('imshow')axes[1].set_title('matshow')axes[2].set_title('pcolor')axes[3].set_title('pcolormesh (C)')axes[4].set_title('pcolormesh (X, Y, C)')axes[5].set_title('PatchCollection')fig.tight_layout()
image

@QuLogicQuLogic added this to thev3.12.0 milestoneSep 25, 2025
@trygvradtrygvradforce-pushed themultivar_imshow branch 2 times, most recently from1efde4a to35746f5CompareNovember 2, 2025 22:12
@github-actionsgithub-actionsbot added the Documentation: APIfiles in lib/ and doc/api labelNov 2, 2025
@trygvrad
Copy link
ContributorAuthor

I fixed the circleci doc error for this.
It would be great if someone could take a look :)
@QuLogic@story645@ksunden@timhoffm

Comment on lines 697 to 712
iflen(x.dtype.descr)==1:
# Arrays with dtype 'object' get returned here.
# For example the 'c' kwarg of scatter, which supports multiple types.
# `plt.scatter([3, 4], [2, 5], c=[(1, 0, 0), 'y'])`
returnx
else:
# In case of a dtype with multiple fields
# for example image data using a MultiNorm
try:
mask=np.empty(x.shape,dtype=np.dtype('bool, '*len(x.dtype.descr)))
fordd,dminzip(x.dtype.descr,mask.dtype.descr):
mask[dm[0]]=~np.isfinite(x[dd[0]])
xm=np.ma.array(x,mask=mask,copy=False)
exceptTypeError:
returnx
returnxm
Copy link
Member

Choose a reason for hiding this comment

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

Does this get recycled in other places and if so should it be factored into it;s own private function?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

I don't think this is used anywhere else

Comment on lines 6161 to 6162
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

Comment on lines 6425 to 6429
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.

Comment on lines 6555 to 6560
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)
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.

Comment on lines 6803 to 6809
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)

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 :)

fig,axes=plt.subplots(2,3)

# interpolation='nearest' to reduce size of baseline image
axes[0,0].imshow(x_1,interpolation='nearest',alpha=0.5)
Copy link
Member

Choose a reason for hiding this comment

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

are the other interpolations tested?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

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

Nope!,
I'm changing one of tests so that it is :)

Comment on lines 273 to 275
raiseValueError("'data' is the only valid interpolation_stage "
"when using multiple color channels, not "
f"{interpolation_stage}")
Copy link
Member

Choose a reason for hiding this comment

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

flip this around to start with the error then reason then solution

trygvrad reacted with thumbs up emoji
Comment on lines 477 to 479
A_resampled= [_resample(self,a.astype(_get_scaled_dtype(a)),
out_shape,t)
forainarrs]
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
A_resampled= [_resample(self,a.astype(_get_scaled_dtype(a)),
out_shape,t)
forainarrs]
A_resampled= [_resample(self,
a.astype(_get_scaled_dtype(a)),out_shape,t)
forainarrs]

just that the line break here is weird

trygvrad reacted with thumbs up emoji
Comment on lines 490 to 491
mask= (np.where(self._getmaskarray(A),np.float32(np.nan),
np.float32(1))
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
mask= (np.where(self._getmaskarray(A),np.float32(np.nan),
np.float32(1))
mask= (np.where(self._getmaskarray(A),np.float32(np.nan),np.float32(1))

trygvrad reacted with thumbs up emoji
returnfig


def_get_scaled_dtype(A):
Copy link
Member

Choose a reason for hiding this comment

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

make it an inline function inside _make_image

trygvrad reacted with thumbs up emoji
Copy link
Member

@ksundenksunden left a comment

Choose a reason for hiding this comment

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

General thoughts on return types:

Doing things likefloat | tuple[float, ...] as is done for several things here (vmin/vmax, clip, etc) is potentially problematic.

Humans may easily work with that, but type checkers will likely yell that they didn't check for all possible outcomes

None is a bit of a special case in being more acceptable (easier to check, etc)

Consider moving these in new code toalways return a tuple (even if single element) This keeps the branching needed to a minimum and is nottoo cumbersome to work for in the single variable case.

Obviously, existing APIs need to maintain back-compat, so this is limited to new code.

Consider whether conceptually an empty tuple is what is truly meant by theNone case, but if it is not, retainNone

Comment on lines +505 to +506
vmin:float|tuple[float]|None= ...,
vmax:float|tuple[float]|None= ...,
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
vmin:float|tuple[float]|None= ...,
vmax:float|tuple[float]|None= ...,
vmin:float|tuple[float, ...]|None= ...,
vmax:float|tuple[float, ...]|None= ...,

Comment on lines +525 to +526
vmin:float|tuple[float]|None= ...,
vmax:float|tuple[float]|None= ...,
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
vmin:float|tuple[float]|None= ...,
vmax:float|tuple[float]|None= ...,
vmin:float|tuple[float, ...]|None= ...,
vmax:float|tuple[float, ...]|None= ...,

Comment on lines +537 to +538
vmin:float|tuple[float]|None= ...,
vmax:float|tuple[float]|None= ...,
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
vmin:float|tuple[float]|None= ...,
vmax:float|tuple[float]|None= ...,
vmin:float|tuple[float, ...]|None= ...,
vmax:float|tuple[float, ...]|None= ...,

defnorm(self)->colors.Norm: ...
@norm.setter
defnorm(self,norm:colors.Norm|str|None)->None: ...
defnorm(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, ...]

Comment on lines +3747 to +3748
vmin:float|tuple[float]|None=None,
vmax:float|tuple[float]|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.

Suggested change
vmin:float|tuple[float]|None=None,
vmax:float|tuple[float]|None=None,
vmin:float|tuple[float, ...]|None=None,
vmax:float|tuple[float, ...]|None=None,

Comment on lines +3862 to +3863
vmin:float|tuple[float]|None=None,
vmax:float|tuple[float]|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.

Suggested change
vmin:float|tuple[float]|None=None,
vmax:float|tuple[float]|None=None,
vmin:float|tuple[float, ...]|None=None,
vmax:float|tuple[float, ...]|None=None,

Comment on lines +3891 to +3892
vmin:float|tuple[float]|None=None,
vmax:float|tuple[float]|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.

Suggested change
vmin:float|tuple[float]|None=None,
vmax:float|tuple[float]|None=None,
vmin:float|tuple[float, ...]|None=None,
vmax:float|tuple[float, ...]|None=None,

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@story645story645story645 left review comments

@ksundenksundenksunden requested changes

Requested changes must be addressed to merge this pull request.

Assignees

No one assigned

Projects

Milestone

v3.12.0

Development

Successfully merging this pull request may close these issues.

4 participants

@trygvrad@story645@ksunden@QuLogic

[8]ページ先頭

©2009-2025 Movatter.jp