@@ -880,14 +880,10 @@ def __call__ (self, a, *args, **kwargs):
880880 except TypeError :
881881 pass
882882 # Transform to
883- if isinstance (a , MaskedArray ):
884- subtype = type (a )
885- else :
886- subtype = MaskedArray
887- result = result .view (subtype )
888- result ._mask = m
889- result ._update_from (a )
890- return result
883+ masked_result = result .view (get_masked_subclass (a ))
884+ masked_result ._mask = m
885+ masked_result ._update_from (result )
886+ return masked_result
891887 #
892888 def __str__ (self ):
893889 return "Masked version of %s. [Invalid values are masked]" % str (self .f )
@@ -928,8 +924,12 @@ def __init__ (self, mbfunc, fillx=0, filly=0):
928924 def __call__ (self , a , b , * args , ** kwargs ):
929925 "Execute the call behavior."
930926 # Get the data, as ndarray
931- (da , db ) = (getdata (a , subok = False ), getdata (b , subok = False ))
932- # Get the mask
927+ (da , db ) = (getdata (a ), getdata (b ))
928+ # Get the result
929+ with np .errstate ():
930+ np .seterr (divide = 'ignore' , invalid = 'ignore' )
931+ result = self .f (da , db , * args , ** kwargs )
932+ # Get the mask for the result
933933 (ma , mb ) = (getmask (a ), getmask (b ))
934934 if ma is nomask :
935935 if mb is nomask :
@@ -940,66 +940,64 @@ def __call__ (self, a, b, *args, **kwargs):
940940 m = umath .logical_or (ma , getmaskarray (b ))
941941 else :
942942 m = umath .logical_or (ma , mb )
943- # Get the result
944- with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
945- result = self .f (da , db , * args , ** kwargs )
946- # check it worked
947- if result is NotImplemented :
948- return NotImplemented
949943 # Case 1. : scalar
950944 if not result .ndim :
951945 if m :
952946 return masked
953947 return result
954948 # Case 2. : array
955949 # Revert result to da where masked
956- if m is not nomask :
957- np .copyto (result , da , casting = 'unsafe' , where = m )
950+ if m is not nomask and m .any ():
951+ # any errors, just abort; impossible to guarantee masked values
952+ try :
953+ np .copyto (result , 0 , casting = 'unsafe' , where = m )
954+ # avoid using "*" since this may be overlaid
955+ masked_da = umath .multiply (m , da )
956+ # only add back if it can be cast safely
957+ if np .can_cast (masked_da .dtype , result .dtype , casting = 'safe' ):
958+ result += masked_da
959+ except :
960+ pass
958961 # Transforms to a (subclass of) MaskedArray
959- result = result .view (get_masked_subclass (a , b ))
960- result ._mask = m
961- # Update the optional info from the inputs
962- if isinstance (b , MaskedArray ):
963- if isinstance (a , MaskedArray ):
964- result ._update_from (a )
965- else :
966- result ._update_from (b )
967- elif isinstance (a , MaskedArray ):
968- result ._update_from (a )
969- return result
970-
962+ masked_result = result .view (get_masked_subclass (a , b ))
963+ masked_result ._mask = m
964+ masked_result ._update_from (result )
965+ return masked_result
971966
972967 def reduce (self , target , axis = 0 , dtype = None ):
973968 """Reduce `target` along the given `axis`."""
974- if isinstance (target , MaskedArray ):
975- tclass = type (target )
976- else :
977- tclass = MaskedArray
969+ tclass = get_masked_subclass (target )
978970 m = getmask (target )
979971 t = filled (target , self .filly )
980972 if t .shape == ():
981973 t = t .reshape (1 )
982974 if m is not nomask :
983975 m = make_mask (m , copy = 1 )
984976 m .shape = (1 ,)
977+
985978 if m is nomask :
986- return self .f .reduce (t , axis ).view (tclass )
987- t = t .view (tclass )
988- t ._mask = m
989- tr = self .f .reduce (getdata (t ), axis , dtype = dtype or t .dtype )
990- mr = umath .logical_and .reduce (m , axis )
991- tr = tr .view (tclass )
992- if mr .ndim > 0 :
993- tr ._mask = mr
994- return tr
995- elif mr :
996- return masked
997- return tr
979+ tr = self .f .reduce (t , axis )
980+ mr = nomask
981+ else :
982+ tr = self .f .reduce (t , axis , dtype = dtype or t .dtype )
983+ mr = umath .logical_and .reduce (m , axis )
998984
999- def outer (self , a , b ):
985+ if not tr .shape :
986+ if mr :
987+ return masked
988+ else :
989+ return tr
990+ masked_tr = tr .view (tclass )
991+ masked_tr ._mask = mr
992+ masked_tr ._update_from (tr )
993+ return masked_tr
994+
995+ def outer (self , a , b ):
1000996 """Return the function applied to the outer product of a and b.
1001997
1002998 """
999+ (da , db ) = (getdata (a ), getdata (b ))
1000+ d = self .f .outer (da , db )
10031001 ma = getmask (a )
10041002 mb = getmask (b )
10051003 if ma is nomask and mb is nomask :
@@ -1010,31 +1008,28 @@ def outer (self, a, b):
10101008 m = umath .logical_or .outer (ma , mb )
10111009 if (not m .ndim ) and m :
10121010 return masked
1013- (da , db ) = (getdata (a ), getdata (b ))
1014- d = self .f .outer (da , db )
1015- # check it worked
1016- if d is NotImplemented :
1017- return NotImplemented
10181011 if m is not nomask :
10191012 np .copyto (d , da , where = m )
1020- if d .shape :
1021- d = d .view (get_masked_subclass (a , b ))
1022- d ._mask = m
1023- return d
1013+ if not d .shape :
1014+ return d
1015+ masked_d = d .view (get_masked_subclass (a , b ))
1016+ masked_d ._mask = m
1017+ masked_d ._update_from (d )
1018+ return masked_d
10241019
1025- def accumulate (self , target , axis = 0 ):
1020+ def accumulate (self , target , axis = 0 ):
10261021 """Accumulate `target` along `axis` after filling with y fill
10271022 value.
10281023
10291024 """
1030- if isinstance (target , MaskedArray ):
1031- tclass = type (target )
1032- else :
1033- tclass = MaskedArray
1025+ tclass = get_masked_subclass (target )
10341026 t = filled (target , self .filly )
1035- return self .f .accumulate (t , axis ).view (tclass )
1027+ result = self .f .accumulate (t , axis )
1028+ masked_result = result .view (tclass )
1029+ masked_result ._update_from (result )
1030+ return masked_result
10361031
1037- def __str__ (self ):
1032+ def __str__ (self ):
10381033 return "Masked version of " + str (self .f )
10391034
10401035
@@ -1074,19 +1069,15 @@ def __init__ (self, dbfunc, domain, fillx=0, filly=0):
10741069
10751070 def __call__ (self , a , b , * args , ** kwargs ):
10761071 "Execute the call behavior."
1077- # Get the data and the mask
1078- (da , db ) = (getdata (a , subok = False ), getdata (b , subok = False ))
1079- (ma , mb ) = (getmask (a ), getmask (b ))
1072+ # Get the data
1073+ (da , db ) = (getdata (a ), getdata (b ))
10801074 # Get the result
10811075 with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
10821076 result = self .f (da , db , * args , ** kwargs )
1083- # check it worked
1084- if result is NotImplemented :
1085- return NotImplemented
1086- # Get the mask as a combination of ma, mb and invalid
1077+ # Get the mask as a combination of the source masks and invalid
10871078 m = ~ umath .isfinite (result )
1088- m |= ma
1089- m |= mb
1079+ m |= getmask ( a )
1080+ m |= getmask ( b )
10901081 # Apply the domain
10911082 domain = ufunc_domain .get (self .f , None )
10921083 if domain is not None :
@@ -1097,18 +1088,23 @@ def __call__(self, a, b, *args, **kwargs):
10971088 return masked
10981089 else :
10991090 return result
1100- # When the mask is True, put back da
1101- np .copyto (result , da , casting = 'unsafe' , where = m )
1102- result = result .view (get_masked_subclass (a , b ))
1103- result ._mask = m
1104- if isinstance (b , MaskedArray ):
1105- if isinstance (a , MaskedArray ):
1106- result ._update_from (a )
1107- else :
1108- result ._update_from (b )
1109- elif isinstance (a , MaskedArray ):
1110- result ._update_from (a )
1111- return result
1091+ # When the mask is True, put back da if possible
1092+ # any errors, just abort; impossible to guarantee masked values
1093+ try :
1094+ np .copyto (result , 0 , casting = 'unsafe' , where = m )
1095+ # avoid using "*" since this may be overlaid
1096+ masked_da = umath .multiply (m , da )
1097+ # only add back if it can be cast safely
1098+ if np .can_cast (masked_da .dtype , result .dtype , casting = 'safe' ):
1099+ result += masked_da
1100+ except :
1101+ pass
1102+
1103+ # Transforms to a (subclass of) MaskedArray
1104+ masked_result = result .view (get_masked_subclass (a , b ))
1105+ masked_result ._mask = m
1106+ masked_result ._update_from (result )
1107+ return masked_result
11121108
11131109 def __str__ (self ):
11141110 return "Masked version of " + str (self .f )
@@ -1361,7 +1357,7 @@ def getmaskarray(arr):
13611357 """
13621358 mask = getmask (arr )
13631359 if mask is nomask :
1364- mask = make_mask_none (np .shape (arr ), getdata (arr ). dtype )
1360+ mask = make_mask_none (np .shape (arr ), getattr (arr , ' dtype' , None ) )
13651361 return mask
13661362
13671363def is_mask (m ):
@@ -3756,34 +3752,38 @@ def __ne__(self, other):
37563752 return check
37573753 #
37583754 def __add__ (self , other ):
3759- "Add other to self , and return a new masked array."
3755+ "Add self to other , and return a new masked array."
37603756 if self ._delegate_binop (other ):
37613757 return NotImplemented
37623758 return add (self , other )
37633759 #
37643760 def __radd__ (self , other ):
37653761 "Add other to self, and return a new masked array."
3766- return add (self , other )
3762+ # In analogy with __rsub__ and __rdiv__, use original order:
3763+ # we get here from `other + self`.
3764+ return add (other , self )
37673765 #
37683766 def __sub__ (self , other ):
3769- "Subtract other to self, and return a new masked array."
3767+ "Subtract other from self, and return a new masked array."
37703768 if self ._delegate_binop (other ):
37713769 return NotImplemented
37723770 return subtract (self , other )
37733771 #
37743772 def __rsub__ (self , other ):
3775- "Subtract other to self , and return a new masked array."
3773+ "Subtract self from other , and return a new masked array."
37763774 return subtract (other , self )
37773775 #
37783776 def __mul__ (self , other ):
3779- "Multiply other by self , and return a new masked array."
3777+ "Multiply self by other , and return a new masked array."
37803778 if self ._delegate_binop (other ):
37813779 return NotImplemented
37823780 return multiply (self , other )
37833781 #
37843782 def __rmul__ (self , other ):
37853783 "Multiply other by self, and return a new masked array."
3786- return multiply (self , other )
3784+ # In analogy with __rsub__ and __rdiv__, use original order:
3785+ # we get here from `other * self`.
3786+ return multiply (other , self )
37873787 #
37883788 def __div__ (self , other ):
37893789 "Divide other into self, and return a new masked array."
@@ -3798,7 +3798,7 @@ def __truediv__(self, other):
37983798 return true_divide (self , other )
37993799 #
38003800 def __rtruediv__ (self , other ):
3801- "Divide other into self , and return a new masked array."
3801+ "Divide self into other , and return a new masked array."
38023802 return true_divide (other , self )
38033803 #
38043804 def __floordiv__ (self , other ):
@@ -3808,7 +3808,7 @@ def __floordiv__(self, other):
38083808 return floor_divide (self , other )
38093809 #
38103810 def __rfloordiv__ (self , other ):
3811- "Divide other into self , and return a new masked array."
3811+ "Divide self into other , and return a new masked array."
38123812 return floor_divide (other , self )
38133813 #
38143814 def __pow__ (self , other ):
@@ -3818,7 +3818,7 @@ def __pow__(self, other):
38183818 return power (self , other )
38193819 #
38203820 def __rpow__ (self , other ):
3821- "Raise self to the power other , masking the potential NaNs/Infs"
3821+ "Raise other to the power self , masking the potential NaNs/Infs"
38223822 return power (other , self )
38233823 #............................................
38243824 def __iadd__ (self , other ):
0 commit comments