@@ -509,20 +509,22 @@ def __call__(self, X, alpha=None, bytes=False):
509
509
xa = np .array ([X ])
510
510
else :
511
511
vtype = 'array'
512
- # force a copy here -- the ma.array and filled functions
513
- # do force a cop of the data by default - JDH
514
- xma = ma .array (X , copy = True )
515
- xa = xma .filled (0 )
516
- mask_bad = ma .getmask (xma )
512
+ xma = ma .array (X , copy = False )
513
+ mask_bad = xma .mask
514
+ xa = xma .data .copy () # Copy here to avoid side effects.
515
+ del xma
516
+ # masked values are substituted below; no need to fill them here
517
+
517
518
if xa .dtype .char in np .typecodes ['Float' ]:
518
519
np .putmask (xa , xa == 1.0 , 0.9999999 ) #Treat 1.0 as slightly less than 1.
519
520
# The following clip is fast, and prevents possible
520
521
# conversion of large positive values to negative integers.
521
522
523
+ xa *= self .N
522
524
if NP_CLIP_OUT :
523
- np .clip (xa * self . N , - 1 , self .N , out = xa )
525
+ np .clip (xa , - 1 , self .N , out = xa )
524
526
else :
525
- xa = np .clip (xa * self . N , - 1 , self .N )
527
+ xa = np .clip (xa , - 1 , self .N )
526
528
527
529
# ensure that all 'under' values will still have negative
528
530
# value after casting to int
@@ -532,8 +534,11 @@ def __call__(self, X, alpha=None, bytes=False):
532
534
# otherwise the under-range values get converted to over-range.
533
535
np .putmask (xa , xa > self .N - 1 , self ._i_over )
534
536
np .putmask (xa , xa < 0 , self ._i_under )
535
- if mask_bad is not None and mask_bad .shape == xa .shape :
536
- np .putmask (xa , mask_bad , self ._i_bad )
537
+ if mask_bad is not None :
538
+ if mask_bad .shape == xa .shape :
539
+ np .putmask (xa , mask_bad , self ._i_bad )
540
+ elif mask_bad :
541
+ xa .fill (self ._i_bad )
537
542
if bytes :
538
543
lut = (self ._lut * 255 ).astype (np .uint8 )
539
544
else :
@@ -797,32 +802,60 @@ def __init__(self, vmin=None, vmax=None, clip=False):
797
802
self .vmax = vmax
798
803
self .clip = clip
799
804
805
+ @staticmethod
806
+ def process_value (value ):
807
+ """
808
+ Homogenize the input *value* for easy and efficient normalization.
809
+
810
+ *value* can be a scalar or sequence.
811
+
812
+ Returns *result*, *is_scalar*, where *result* is a
813
+ masked array matching *value*. Float dtypes are preserved;
814
+ integer types with two bytes or smaller are converted to
815
+ np.float32, and larger types are converted to np.float.
816
+ Preserving float32 when possible, and using in-place operations,
817
+ can greatly improve speed for large arrays.
818
+
819
+ Experimental; we may want to add an option to force the
820
+ use of float32.
821
+ """
822
+ if cbook .iterable (value ):
823
+ is_scalar = False
824
+ result = ma .asarray (value )
825
+ if result .dtype .kind == 'f' :
826
+ if isinstance (value , np .ndarray ):
827
+ result = result .copy ()
828
+ elif result .dtype .itemsize > 2 :
829
+ result = result .astype (np .float )
830
+ else :
831
+ result = result .astype (np .float32 )
832
+ else :
833
+ is_scalar = True
834
+ result = ma .array ([value ]).astype (np .float )
835
+ return result , is_scalar
836
+
800
837
def __call__ (self , value , clip = None ):
801
838
if clip is None :
802
839
clip = self .clip
803
840
804
- if cbook .iterable (value ):
805
- vtype = 'array'
806
- val = ma .asarray (value ).astype (np .float )
807
- else :
808
- vtype = 'scalar'
809
- val = ma .array ([value ]).astype (np .float )
841
+ result , is_scalar = self .process_value (value )
810
842
811
- self .autoscale_None (val )
843
+ self .autoscale_None (result )
812
844
vmin , vmax = self .vmin , self .vmax
813
845
if vmin > vmax :
814
846
raise ValueError ("minvalue must be less than or equal to maxvalue" )
815
- elif vmin == vmax :
816
- result = 0.0 * val
847
+ elif vmin == vmax :
848
+ result . fill ( 0 ) # Or should it be all masked? Or 0.5?
817
849
else :
818
850
vmin = float (vmin )
819
851
vmax = float (vmax )
820
852
if clip :
821
- mask = ma .getmask (val )
822
- val = ma .array (np .clip (val .filled (vmax ), vmin , vmax ),
823
- mask = mask )
824
- result = (val - vmin ) / (vmax - vmin )
825
- if vtype == 'scalar' :
853
+ mask = ma .getmask (result )
854
+ result = ma .array (np .clip (result .filled (vmax ), vmin , vmax ),
855
+ mask = mask )
856
+ result -= vmin
857
+ result /= vmax - vmin
858
+ if is_scalar :
826
859
result = result [0 ]
827
860
return result
828
861
@@ -847,8 +880,10 @@ def autoscale(self, A):
847
880
848
881
def autoscale_None (self , A ):
849
882
' autoscale only None-valued vmin or vmax'
850
- if self .vmin is None : self .vmin = ma .min (A )
851
- if self .vmax is None : self .vmax = ma .max (A )
883
+ if self .vmin is None :
884
+ self .vmin = ma .min (A )
885
+ if self .vmax is None :
886
+ self .vmax = ma .max (A )
852
887
853
888
def scaled (self ):
854
889
'return true if vmin and vmax set'
@@ -862,30 +897,29 @@ def __call__(self, value, clip=None):
862
897
if clip is None :
863
898
clip = self .clip
864
899
865
- if cbook .iterable (value ):
866
- vtype = 'array'
867
- val = ma .asarray (value ).astype (np .float )
868
- else :
869
- vtype = 'scalar'
870
- val = ma .array ([value ]).astype (np .float )
900
+ result , is_scalar = self .process_value (value )
871
901
872
- val = ma .masked_less_equal (val , 0 , copy = False )
902
+ result = ma .masked_less_equal (result , 0 , copy = False )
873
903
874
- self .autoscale_None (val )
904
+ self .autoscale_None (result )
875
905
vmin , vmax = self .vmin , self .vmax
876
906
if vmin > vmax :
877
907
raise ValueError ("minvalue must be less than or equal to maxvalue" )
878
908
elif vmin <= 0 :
879
909
raise ValueError ("values must all be positive" )
880
910
elif vmin == vmax :
881
- result = 0.0 * val
911
+ result . fill ( 0 )
882
912
else :
883
913
if clip :
884
- mask = ma .getmask (val )
885
- val = ma .array (np .clip (val .filled (vmax ), vmin , vmax ),
914
+ mask = ma .getmask (result )
915
+ val = ma .array (np .clip (result .filled (vmax ), vmin , vmax ),
886
916
mask = mask )
887
- result = (ma .log (val )- np .log (vmin ))/ (np .log (vmax )- np .log (vmin ))
888
- if vtype == 'scalar' :
917
+ #result = (ma.log(result)-np.log(vmin))/(np.log(vmax)-np.log(vmin))
918
+ # in-place equivalent of above can be much faster
919
+ np .ma .log (result , result )
920
+ result -= np .log (vmin )
921
+ result /= (np .log (vmax ) - np .log (vmin ))
922
+ if is_scalar :
889
923
result = result [0 ]
890
924
return result
891
925
0 commit comments