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

Commit3e4b469

Browse files
committed
Fix for mouseover image data with a MultiNorm
If an image is shown, and the user hovers over the image, the value at that coordinate should be displayed. The number formating is handled by colorizer.Colorizer._format_cursor_data_override(). This is now updated to also handle the case where a MultiNorm is used (i.e. a bivariat or multivariate colormap), in which case multiple values are displayed.
1 parent9c26a1e commit3e4b469

File tree

3 files changed

+100
-22
lines changed

3 files changed

+100
-22
lines changed

‎lib/matplotlib/colorizer.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -451,39 +451,63 @@ def colorbar(self):
451451
defcolorbar(self,colorbar):
452452
self._colorizer.colorbar=colorbar
453453

454-
def_format_cursor_data_override(self,data):
455-
# This function overwrites Artist.format_cursor_data(). We cannot
456-
# implement cm.ScalarMappable.format_cursor_data() directly, because
457-
# most cm.ScalarMappable subclasses inherit from Artist first and from
458-
# cm.ScalarMappable second, so Artist.format_cursor_data would always
459-
# have precedence over cm.ScalarMappable.format_cursor_data.
460-
461-
# Note if cm.ScalarMappable is depreciated, this functionality should be
462-
# implemented as format_cursor_data() on ColorizingArtist.
463-
n=self.cmap.N
464-
ifnp.ma.getmask(data):
465-
return"[]"
466-
normed=self.norm(data)
454+
@staticmethod
455+
def_sig_digits_from_norm(norm,data,n):
456+
# Determines the number of significant digits
457+
# to use for a number given a norm, and n, where n is the
458+
# number of colors in the colormap.
459+
normed=norm(data)
467460
ifnp.isfinite(normed):
468-
ifisinstance(self.norm,colors.BoundaryNorm):
461+
ifisinstance(norm,colors.BoundaryNorm):
469462
# not an invertible normalization mapping
470-
cur_idx=np.argmin(np.abs(self.norm.boundaries-data))
463+
cur_idx=np.argmin(np.abs(norm.boundaries-data))
471464
neigh_idx=max(0,cur_idx-1)
472465
# use max diff to prevent delta == 0
473466
delta=np.diff(
474-
self.norm.boundaries[neigh_idx:cur_idx+2]
467+
norm.boundaries[neigh_idx:cur_idx+2]
475468
).max()
476-
elifself.norm.vmin==self.norm.vmax:
469+
elifnorm.vmin==norm.vmax:
477470
# singular norms, use delta of 10% of only value
478-
delta=np.abs(self.norm.vmin*.1)
471+
delta=np.abs(norm.vmin*.1)
479472
else:
480473
# Midpoints of neighboring color intervals.
481-
neighbors=self.norm.inverse(
474+
neighbors=norm.inverse(
482475
(int(normed*n)+np.array([0,1]))/n)
483476
delta=abs(neighbors-data).max()
484477
g_sig_digits=cbook._g_sig_digits(data,delta)
485478
else:
486479
g_sig_digits=3# Consistent with default below.
480+
returng_sig_digits
481+
482+
def_format_cursor_data_override(self,data):
483+
# This function overwrites Artist.format_cursor_data(). We cannot
484+
# implement cm.ScalarMappable.format_cursor_data() directly, because
485+
# most cm.ScalarMappable subclasses inherit from Artist first and from
486+
# cm.ScalarMappable second, so Artist.format_cursor_data would always
487+
# have precedence over cm.ScalarMappable.format_cursor_data.
488+
489+
# Note if cm.ScalarMappable is depreciated, this functionality should be
490+
# implemented as format_cursor_data() on ColorizingArtist.
491+
ifnp.ma.getmask(data)ordataisNone:
492+
return"[]"
493+
iflen(data.dtype.descr)>1:
494+
# We have multivariate data encoded as a data type with multiple fields
495+
# NOTE: If any of the fields are masked, "[]" would be returned via
496+
# the if statement above.
497+
s_sig_digits_list= []
498+
ifisinstance(self.cmap,colors.BivarColormap):
499+
n_s= (self.cmap.N,self.cmap.M)
500+
else:
501+
n_s= [part.Nforpartinself.cmap]
502+
os= [f"{d:-#.{self._sig_digits_from_norm(no,d,n)}g}"
503+
forno,d,ninzip(self.norm.norms,data,n_s)]
504+
returnf"[{', '.join(os)}]"
505+
506+
# scalar data
507+
n=self.cmap.N
508+
g_sig_digits=self._sig_digits_from_norm(self.norm,
509+
data,
510+
n)
487511
returnf"[{data:-#.{g_sig_digits}g}]"
488512

489513

‎lib/matplotlib/tests/test_image.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
importmatplotlibasmpl
1616
frommatplotlibimport (
17-
colors,imageasmimage,patches,pyplotasplt,style,rcParams)
17+
cbook,colors,imageasmimage,patches,pyplotasplt,style,rcParams)
1818
frommatplotlib.imageimport (AxesImage,BboxImage,FigureImage,
1919
NonUniformImage,PcolorImage)
2020
frommatplotlib.testing.decoratorsimportcheck_figures_equal,image_comparison
@@ -1130,8 +1130,14 @@ def test_image_cursor_formatting():
11301130
data=np.ma.masked_array([0],mask=[False])
11311131
assertim.format_cursor_data(data)=='[0]'
11321132

1133-
data=np.nan
1134-
assertim.format_cursor_data(data)=='[nan]'
1133+
# This used to test
1134+
# > data = np.nan
1135+
# > assert im.format_cursor_data(data) == '[nan]'
1136+
# However, a value of nan will be masked by `cbook.safe_masked_invalid(data)`
1137+
# called by `image._ImageBase._normalize_image_array(data)`
1138+
# The test is therefore changed to:
1139+
data=cbook.safe_masked_invalid(np.nan)
1140+
assertim.format_cursor_data(data)=='[]'
11351141

11361142

11371143
@check_figures_equal()

‎lib/matplotlib/tests/test_multivariate_axes.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,54 @@ def test_cmap_error():
629629
mpl.collections.PatchCollection([],cmap='not_a_cmap')
630630

631631

632+
deftest_artist_format_cursor_data_multivar():
633+
634+
X=np.zeros((4,3))
635+
X[0,0]=0.9
636+
X[0,1]=0.99
637+
X[0,2]=0.999
638+
X[1,0]=-1
639+
X[1,1]=0
640+
X[1,2]=1
641+
X[2,0]=0.09
642+
X[2,1]=0.009
643+
X[2,2]=0.0009
644+
X[3,0]=np.nan
645+
646+
Y=np.arange(np.prod(X.shape)).reshape(X.shape)
647+
648+
labels_list= [
649+
"[0.9, 0.00]",
650+
"[1., 1.00]",
651+
"[1., 2.00]",
652+
"[-1.0, 3.00]",
653+
"[0.0, 4.00]",
654+
"[1.0, 5.00]",
655+
"[0.09, 6.00]",
656+
"[0.009, 7.00]",
657+
"[0.0009, 8.00]",
658+
"[]",
659+
]
660+
661+
pos= [[0,0], [1,0], [2,0],
662+
[0,1], [1,1], [2,1],
663+
[0,2], [1,2], [2,2],
664+
[3,0]]
665+
666+
frommatplotlib.backend_basesimportMouseEvent
667+
668+
forcmapin ['BiOrangeBlue','2VarAddA']:
669+
fig,ax=plt.subplots()
670+
norm=mpl.colors.BoundaryNorm(np.linspace(-1,1,20),256)
671+
data= (X,Y)
672+
im=ax.imshow(data,cmap=cmap,norm=(norm,None))
673+
674+
forv,textinzip(pos,labels_list):
675+
xdisp,ydisp=ax.transData.transform(v)
676+
event=MouseEvent('motion_notify_event',fig.canvas,xdisp,ydisp)
677+
assertim.format_cursor_data(im.get_cursor_data(event))==text
678+
679+
632680
deftest_multivariate_safe_masked_invalid():
633681
dt=np.dtype('float32, float32').newbyteorder('>')
634682
x=np.zeros(2,dtype=dt)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp