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

Commit05c1b76

Browse files
phobsonOceanWolf
authored andcommitted
ENH: Add OffsetNorm and tests
Borrows heavily from@Tillsen's solution found onStackOverflow here:http://goo.gl/RPXMYBUsed with his permission dicussesd on Github here:https://github.com/matplotlib/matplotlib/pull/3858`
1 parentb9bc7d1 commit05c1b76

File tree

2 files changed

+306
-2
lines changed

2 files changed

+306
-2
lines changed

‎lib/matplotlib/colors.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ def rgb2hex(rgb):
225225
a='#%02x%02x%02x'%tuple([int(np.round(val*255))forvalinrgb[:3]])
226226
returna
227227

228+
228229
hexColorPattern=re.compile("\A#[a-fA-F0-9]{6}\Z")
229230

230231

@@ -963,6 +964,127 @@ def scaled(self):
963964
return (self.vminisnotNoneandself.vmaxisnotNone)
964965

965966

967+
classOffsetNorm(Normalize):
968+
"""
969+
A subclass of matplotlib.colors.Normalize.
970+
971+
Normalizes data into the ``[0.0, 1.0]`` interval.
972+
"""
973+
def__init__(self,vmin=None,vcenter=None,vmax=None,clip=False):
974+
"""Normalize data with an offset midpoint
975+
976+
Useful when mapping data unequally centered around a conceptual
977+
center, e.g., data that range from -2 to 4, with 0 as the midpoint.
978+
979+
Parameters
980+
----------
981+
vmin : optional float
982+
The data value that defines ``0.0`` in the normalized data.
983+
Defaults to the min value of the dataset.
984+
985+
vcenter : optional float
986+
The data value that defines ``0.5`` in the normalized data.
987+
Defaults to halfway between *vmin* and *vmax*.
988+
989+
vmax : option float
990+
The data value that defines ``1.0`` in the normalized data.
991+
Defaults to the the max value of the dataset.
992+
993+
clip : optional bool (default is False)
994+
If *clip* is True, values beyond *vmin* and *vmax* will be set
995+
to ``0.0`` or ``1.0``, respectively. Otherwise, values outside
996+
the ``[0.0, 1.0]`` will be returned.
997+
998+
Examples
999+
--------
1000+
>>> import matplotlib.colors as mcolors
1001+
>>> offset = mcolors.OffsetNorm(vmin=-2., vcenter=0., vmax=4.)
1002+
>>> data = [-2., -1., 0., 1., 2., 3., 4.]
1003+
>>> offset(data)
1004+
array([0., 0.25, 0.5, 0.625, 0.75, 0.875, 1.0])
1005+
1006+
"""
1007+
1008+
self.vmin=vmin
1009+
self.vcenter=vcenter
1010+
self.vmax=vmax
1011+
self.clip=clip
1012+
1013+
def__call__(self,value,clip=None):
1014+
ifclipisNone:
1015+
clip=self.clip
1016+
1017+
result,is_scalar=self.process_value(value)
1018+
1019+
self.autoscale_None(result)
1020+
vmin,vcenter,vmax=self.vmin,self.vcenter,self.vmax
1021+
ifvmin==vmax==vcenter:
1022+
result.fill(0)
1023+
elifnotvmin<=vcenter<=vmax:
1024+
raiseValueError("minvalue must be less than or equal to "
1025+
"centervalue which must be less than or "
1026+
"equal to maxvalue")
1027+
else:
1028+
vmin=float(vmin)
1029+
vcenter=float(vcenter)
1030+
vmax=float(vmax)
1031+
ifclip:
1032+
mask=ma.getmask(result)
1033+
result=ma.array(np.clip(result.filled(vmax),vmin,vmax),
1034+
mask=mask)
1035+
1036+
# ma division is very slow; we can take a shortcut
1037+
resdat=result.data
1038+
1039+
#First scale to -1 to 1 range, than to from 0 to 1.
1040+
resdat-=vcenter
1041+
resdat[resdat>0]/=abs(vmax-vcenter)
1042+
resdat[resdat<0]/=abs(vmin-vcenter)
1043+
1044+
resdat/=2.
1045+
resdat+=0.5
1046+
result=np.ma.array(resdat,mask=result.mask,copy=False)
1047+
1048+
ifis_scalar:
1049+
result=result[0]
1050+
1051+
returnresult
1052+
1053+
definverse(self,value):
1054+
ifnotself.scaled():
1055+
raiseValueError("Not invertible until scaled")
1056+
1057+
vmin,vcenter,vmax=self.vmin,self.vcenter,self.vmax
1058+
vmin=float(self.vmin)
1059+
vcenter=float(self.vcenter)
1060+
vmax=float(self.vmax)
1061+
1062+
ifcbook.iterable(value):
1063+
val=ma.asarray(value)
1064+
val=2* (val-0.5)
1065+
val[val>0]*=abs(vmax-vcenter)
1066+
val[val<0]*=abs(vmin-vcenter)
1067+
val+=vcenter
1068+
returnval
1069+
else:
1070+
val=2* (val-0.5)
1071+
ifval<0:
1072+
returnval*abs(vmin-vcenter)+vcenter
1073+
else:
1074+
returnval*abs(vmax-vcenter)+vcenter
1075+
1076+
defautoscale_None(self,A):
1077+
' autoscale only None-valued vmin or vmax'
1078+
ifself.vminisNoneandnp.size(A)>0:
1079+
self.vmin=ma.min(A)
1080+
1081+
ifself.vmaxisNoneandnp.size(A)>0:
1082+
self.vmax=ma.max(A)
1083+
1084+
ifself.vcenterisNone:
1085+
self.vcenter= (self.vmax+self.vmin)*0.5
1086+
1087+
9661088
classLogNorm(Normalize):
9671089
"""
9681090
Normalize a given value to the 0-1 range on a log scale

‎lib/matplotlib/tests/test_colors.py

Lines changed: 184 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
importitertools
66
fromdistutils.versionimportLooseVersionasV
77

8-
fromnose.toolsimportassert_raises,assert_equal,assert_true
8+
fromnose.toolsimport (assert_raises,assert_equal,assert_true,assert_false
9+
raises)
910

1011
importnumpyasnp
1112
fromnumpy.testing.utilsimportassert_array_equal,assert_array_almost_equal
@@ -163,6 +164,182 @@ def test_Normalize():
163164
_mask_tester(norm,vals)
164165

165166

167+
class_base_NormMixin(object):
168+
deftest_call(self):
169+
normed_vals=self.norm(self.vals)
170+
assert_array_almost_equal(normed_vals,self.expected)
171+
172+
deftest_inverse(self):
173+
_inverse_tester(self.norm,self.vals)
174+
175+
deftest_scalar(self):
176+
_scalar_tester(self.norm,self.vals)
177+
178+
deftest_mask(self):
179+
_mask_tester(self.norm,self.vals)
180+
181+
deftest_autoscale(self):
182+
norm=self.normclass()
183+
norm.autoscale([10,20,30,40])
184+
assert_equal(norm.vmin,10.)
185+
assert_equal(norm.vmax,40.)
186+
187+
deftest_autoscale_None_vmin(self):
188+
norm=self.normclass(vmin=0,vmax=None)
189+
norm.autoscale_None([1,2,3,4,5])
190+
assert_equal(norm.vmin,0.)
191+
assert_equal(norm.vmax,5.)
192+
193+
deftest_autoscale_None_vmax(self):
194+
norm=self.normclass(vmin=None,vmax=10)
195+
norm.autoscale_None([1,2,3,4,5])
196+
assert_equal(norm.vmin,1.)
197+
assert_equal(norm.vmax,10.)
198+
199+
deftest_scale(self):
200+
norm=self.normclass()
201+
assert_false(norm.scaled())
202+
203+
norm([1,2,3,4])
204+
assert_true(norm.scaled())
205+
206+
deftest_process_value_scalar(self):
207+
res,is_scalar=mcolors.Normalize.process_value(5)
208+
assert_true(is_scalar)
209+
assert_array_equal(res,np.array([5.]))
210+
211+
deftest_process_value_list(self):
212+
res,is_scalar=mcolors.Normalize.process_value([5,10])
213+
assert_false(is_scalar)
214+
assert_array_equal(res,np.array([5.,10.]))
215+
216+
deftest_process_value_tuple(self):
217+
res,is_scalar=mcolors.Normalize.process_value((5,10))
218+
assert_false(is_scalar)
219+
assert_array_equal(res,np.array([5.,10.]))
220+
221+
deftest_process_value_array(self):
222+
res,is_scalar=mcolors.Normalize.process_value(np.array([5,10]))
223+
assert_false(is_scalar)
224+
assert_array_equal(res,np.array([5.,10.]))
225+
226+
227+
classtest_OffsetNorm_Even(_base_NormMixin):
228+
defsetup(self):
229+
self.normclass=mcolors.OffsetNorm
230+
self.norm=self.normclass(vmin=-1,vcenter=0,vmax=4)
231+
self.vals=np.array([-1.0,-0.5,0.0,1.0,2.0,3.0,4.0])
232+
self.expected=np.array([0.0,0.25,0.5,0.625,0.75,0.875,1.0])
233+
234+
235+
classtest_OffsetNorm_Odd(_base_NormMixin):
236+
defsetup(self):
237+
self.normclass=mcolors.OffsetNorm
238+
self.norm=self.normclass(vmin=-2,vcenter=0,vmax=5)
239+
self.vals=np.array([-2.0,-1.0,0.0,1.0,2.0,3.0,4.0,5.0])
240+
self.expected=np.array([0.0,0.25,0.5,0.6,0.7,0.8,0.9,1.0])
241+
242+
243+
classtest_OffsetNorm_AllNegative(_base_NormMixin):
244+
defsetup(self):
245+
self.normclass=mcolors.OffsetNorm
246+
self.norm=self.normclass(vmin=-10,vcenter=-8,vmax=-2)
247+
self.vals=np.array([-10.,-9.,-8.,-6.,-4.,-2.])
248+
self.expected=np.array([0.0,0.25,0.5,0.666667,0.833333,1.0])
249+
250+
251+
classtest_OffsetNorm_AllPositive(_base_NormMixin):
252+
defsetup(self):
253+
self.normclass=mcolors.OffsetNorm
254+
self.norm=self.normclass(vmin=0,vcenter=3,vmax=9)
255+
self.vals=np.array([0.,1.5,3.,4.5,6.0,7.5,9.])
256+
self.expected=np.array([0.0,0.25,0.5,0.625,0.75,0.875,1.0])
257+
258+
259+
classtest_OffsetNorm_NoVs(_base_NormMixin):
260+
defsetup(self):
261+
self.normclass=mcolors.OffsetNorm
262+
self.norm=self.normclass(vmin=None,vcenter=None,vmax=None)
263+
self.vals=np.array([-2.0,-1.0,0.0,1.0,2.0,3.0,4.0])
264+
self.expected=np.array([0.,0.16666667,0.33333333,
265+
0.5,0.66666667,0.83333333,1.0])
266+
self.expected_vmin=-2
267+
self.expected_vcenter=1
268+
self.expected_vmax=4
269+
270+
deftest_vmin(self):
271+
assert_true(self.norm.vminisNone)
272+
self.norm(self.vals)
273+
assert_equal(self.norm.vmin,self.expected_vmin)
274+
275+
deftest_vcenter(self):
276+
assert_true(self.norm.vcenterisNone)
277+
self.norm(self.vals)
278+
assert_equal(self.norm.vcenter,self.expected_vcenter)
279+
280+
deftest_vmax(self):
281+
assert_true(self.norm.vmaxisNone)
282+
self.norm(self.vals)
283+
assert_equal(self.norm.vmax,self.expected_vmax)
284+
285+
286+
classtest_OffsetNorm_VminEqualsVcenter(_base_NormMixin):
287+
defsetup(self):
288+
self.normclass=mcolors.OffsetNorm
289+
self.norm=self.normclass(vmin=-2,vcenter=-2,vmax=2)
290+
self.vals=np.array([-2.0,-1.0,0.0,1.0,2.0])
291+
self.expected=np.array([0.5,0.625,0.75,0.875,1.0])
292+
293+
294+
classtest_OffsetNorm_VmaxEqualsVcenter(_base_NormMixin):
295+
defsetup(self):
296+
self.normclass=mcolors.OffsetNorm
297+
self.norm=self.normclass(vmin=-2,vcenter=2,vmax=2)
298+
self.vals=np.array([-2.0,-1.0,0.0,1.0,2.0])
299+
self.expected=np.array([0.0,0.125,0.25,0.375,0.5])
300+
301+
302+
classtest_OffsetNorm_VsAllEqual(_base_NormMixin):
303+
defsetup(self):
304+
self.v=10
305+
self.normclass=mcolors.OffsetNorm
306+
self.norm=self.normclass(vmin=self.v,vcenter=self.v,vmax=self.v)
307+
self.vals=np.array([-2.0,-1.0,0.0,1.0,2.0])
308+
self.expected=np.array([0.0,0.0,0.0,0.0,0.0])
309+
self.expected_inv=self.expected+self.v
310+
311+
deftest_inverse(self):
312+
assert_array_almost_equal(
313+
self.norm.inverse(self.norm(self.vals)),
314+
self.expected_inv
315+
)
316+
317+
318+
classtest_OffsetNorm_Errors(object):
319+
defsetup(self):
320+
self.vals=np.arange(50)
321+
322+
@raises(ValueError)
323+
deftest_VminGTVcenter(self):
324+
norm=mcolors.OffsetNorm(vmin=10,vcenter=0,vmax=20)
325+
norm(self.vals)
326+
327+
@raises(ValueError)
328+
deftest_VminGTVmax(self):
329+
norm=mcolors.OffsetNorm(vmin=10,vcenter=0,vmax=5)
330+
norm(self.vals)
331+
332+
@raises(ValueError)
333+
deftest_VcenterGTVmax(self):
334+
norm=mcolors.OffsetNorm(vmin=10,vcenter=25,vmax=20)
335+
norm(self.vals)
336+
337+
@raises(ValueError)
338+
deftest_premature_scaling(self):
339+
norm=mcolors.OffsetNorm()
340+
norm.inverse(np.array([0.1,0.5,0.9]))
341+
342+
166343
deftest_SymLogNorm():
167344
"""
168345
Test SymLogNorm behavior
@@ -281,7 +458,12 @@ def test_cmap_and_norm_from_levels_and_colors2():
281458
'Wih extend={0!r} and data '
282459
'value={1!r}'.format(extend,d_val))
283460

284-
assert_raises(ValueError,mcolors.from_levels_and_colors,levels,colors)
461+
assert_raises(
462+
ValueError,
463+
mcolors.from_levels_and_colors,
464+
levels,
465+
colors
466+
)
285467

286468

287469
deftest_rgb_hsv_round_trip():

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp