39
39
ABCSeries ,
40
40
ABCDataFrame ,
41
41
ABCIndex ,
42
- ABCPeriodIndex ,
43
- ABCSparseSeries )
42
+ ABCSparseSeries , ABCSparseArray )
44
43
45
44
46
45
def _gen_eval_kwargs (name ):
@@ -445,17 +444,22 @@ def names(x):
445
444
return new_methods
446
445
447
446
448
- def add_methods (cls , new_methods , force ):
447
+ def add_methods (cls , new_methods ):
449
448
for name , method in new_methods .items ():
449
+ # For most methods, if we find that the class already has a method
450
+ # of the same name, it is OK to over-write it. The exception is
451
+ # inplace methods (__iadd__, __isub__, ...) for SparseArray, which
452
+ # retain the np.ndarray versions.
453
+ force = not (issubclass (cls , ABCSparseArray ) and
454
+ name .startswith ('__i' ))
450
455
if force or name not in cls .__dict__ :
451
456
bind_method (cls , name , method )
452
457
453
458
454
459
# ----------------------------------------------------------------------
455
460
# Arithmetic
456
461
def add_special_arithmetic_methods (cls , arith_method = None ,
457
- comp_method = None , bool_method = None ,
458
- force = False ):
462
+ comp_method = None , bool_method = None ):
459
463
"""
460
464
Adds the full suite of special arithmetic methods (``__add__``,
461
465
``__sub__``, etc.) to the class.
@@ -469,9 +473,6 @@ def add_special_arithmetic_methods(cls, arith_method=None,
469
473
factory for rich comparison - signature: f(op, name, str_rep)
470
474
bool_method : function (optional)
471
475
factory for boolean methods - signature: f(op, name, str_rep)
472
- force : bool, default False
473
- if False, checks whether function is defined **on ``cls.__dict__``**
474
- before defining if True, always defines functions on class base
475
476
"""
476
477
new_methods = _create_methods (cls , arith_method , comp_method , bool_method ,
477
478
special = True )
@@ -512,12 +513,11 @@ def f(self, other):
512
513
__ior__ = _wrap_inplace_method (new_methods ["__or__" ]),
513
514
__ixor__ = _wrap_inplace_method (new_methods ["__xor__" ])))
514
515
515
- add_methods (cls , new_methods = new_methods , force = force )
516
+ add_methods (cls , new_methods = new_methods )
516
517
517
518
518
519
def add_flex_arithmetic_methods (cls , flex_arith_method ,
519
- flex_comp_method = None , flex_bool_method = None ,
520
- force = False ):
520
+ flex_comp_method = None , flex_bool_method = None ):
521
521
"""
522
522
Adds the full suite of flex arithmetic methods (``pow``, ``mul``, ``add``)
523
523
to the class.
@@ -529,9 +529,6 @@ def add_flex_arithmetic_methods(cls, flex_arith_method,
529
529
f(op, name, str_rep)
530
530
flex_comp_method : function, optional,
531
531
factory for rich comparison - signature: f(op, name, str_rep)
532
- force : bool, default False
533
- if False, checks whether function is defined **on ``cls.__dict__``**
534
- before defining if True, always defines functions on class base
535
532
"""
536
533
new_methods = _create_methods (cls , flex_arith_method ,
537
534
flex_comp_method , flex_bool_method ,
@@ -544,7 +541,7 @@ def add_flex_arithmetic_methods(cls, flex_arith_method,
544
541
if k in new_methods :
545
542
new_methods .pop (k )
546
543
547
- add_methods (cls , new_methods = new_methods , force = force )
544
+ add_methods (cls , new_methods = new_methods )
548
545
549
546
550
547
# -----------------------------------------------------------------------------
@@ -614,14 +611,11 @@ def na_op(x, y):
614
611
result = np .empty (x .size , dtype = dtype )
615
612
mask = notna (x ) & notna (y )
616
613
result [mask ] = op (x [mask ], com ._values_from_object (y [mask ]))
617
- elif isinstance (x , np .ndarray ):
614
+ else :
615
+ assert isinstance (x , np .ndarray )
618
616
result = np .empty (len (x ), dtype = x .dtype )
619
617
mask = notna (x )
620
618
result [mask ] = op (x [mask ], y )
621
- else :
622
- raise TypeError ("{typ} cannot perform the operation "
623
- "{op}" .format (typ = type (x ).__name__ ,
624
- op = str_rep ))
625
619
626
620
result , changed = maybe_upcast_putmask (result , ~ mask , np .nan )
627
621
@@ -658,6 +652,10 @@ def wrapper(left, right, name=name, na_op=na_op):
658
652
index = left .index , name = res_name ,
659
653
dtype = result .dtype )
660
654
655
+ elif is_categorical_dtype (left ):
656
+ raise TypeError ("{typ} cannot perform the operation "
657
+ "{op}" .format (typ = type (left ).__name__ , op = str_rep ))
658
+
661
659
lvalues = left .values
662
660
rvalues = right
663
661
if isinstance (rvalues , ABCSeries ):
@@ -745,24 +743,19 @@ def na_op(x, y):
745
743
elif is_categorical_dtype (y ) and not is_scalar (y ):
746
744
return op (y , x )
747
745
748
- if is_object_dtype (x .dtype ):
746
+ elif is_object_dtype (x .dtype ):
749
747
result = _comp_method_OBJECT_ARRAY (op , x , y )
748
+
749
+ elif is_datetimelike_v_numeric (x , y ):
750
+ raise TypeError ("invalid type comparison" )
751
+
750
752
else :
751
753
752
754
# we want to compare like types
753
755
# we only want to convert to integer like if
754
756
# we are not NotImplemented, otherwise
755
757
# we would allow datetime64 (but viewed as i8) against
756
758
# integer comparisons
757
- if is_datetimelike_v_numeric (x , y ):
758
- raise TypeError ("invalid type comparison" )
759
-
760
- # numpy does not like comparisons vs None
761
- if is_scalar (y ) and isna (y ):
762
- if name == '__ne__' :
763
- return np .ones (len (x ), dtype = bool )
764
- else :
765
- return np .zeros (len (x ), dtype = bool )
766
759
767
760
# we have a datetime/timedelta and may need to convert
768
761
mask = None
@@ -795,39 +788,44 @@ def wrapper(self, other, axis=None):
795
788
if axis is not None :
796
789
self ._get_axis_number (axis )
797
790
798
- if isinstance (other , ABCSeries ):
791
+ if isinstance (other , ABCDataFrame ): # pragma: no cover
792
+ # Defer to DataFrame implementation; fail early
793
+ return NotImplemented
794
+
795
+ elif isinstance (other , ABCSeries ):
799
796
name = com ._maybe_match_name (self , other )
800
797
if not self ._indexed_same (other ):
801
798
msg = 'Can only compare identically-labeled Series objects'
802
799
raise ValueError (msg )
803
- return self ._constructor (na_op (self .values , other .values ),
804
- index = self .index , name = name )
805
- elif isinstance (other , ABCDataFrame ): # pragma: no cover
806
- return NotImplemented
800
+ res_values = na_op (self .values , other .values )
801
+ return self ._constructor (res_values , index = self .index , name = name )
802
+
807
803
elif isinstance (other , (np .ndarray , pd .Index )):
808
804
# do not check length of zerodim array
809
805
# as it will broadcast
810
806
if (not is_scalar (lib .item_from_zerodim (other )) and
811
807
len (self ) != len (other )):
812
808
raise ValueError ('Lengths must match to compare' )
813
809
814
- if isinstance (other , ABCPeriodIndex ):
815
- # temp workaround until fixing GH 13637
816
- # tested in test_nat_comparisons
817
- # (pandas.tests.series.test_operators.TestSeriesOperators)
818
- return self ._constructor (na_op (self .values ,
819
- other .astype (object ).values ),
820
- index = self .index )
821
-
822
- return self ._constructor (na_op (self .values , np .asarray (other )),
810
+ res_values = na_op (self .values , np .asarray (other ))
811
+ return self ._constructor (res_values ,
823
812
index = self .index ).__finalize__ (self )
824
813
825
- elif isinstance (other , pd .Categorical ):
826
- if not is_categorical_dtype (self ):
827
- msg = ("Cannot compare a Categorical for op {op} with Series "
828
- "of dtype {typ}.\n If you want to compare values, use "
829
- "'series <op> np.asarray(other)'." )
830
- raise TypeError (msg .format (op = op , typ = self .dtype ))
814
+ elif (isinstance (other , pd .Categorical ) and
815
+ not is_categorical_dtype (self )):
816
+ raise TypeError ("Cannot compare a Categorical for op {op} with "
817
+ "Series of dtype {typ}.\n If you want to compare "
818
+ "values, use 'series <op> np.asarray(other)'."
819
+ .format (op = op , typ = self .dtype ))
820
+
821
+ elif is_scalar (other ) and isna (other ):
822
+ # numpy does not like comparisons vs None
823
+ if op is operator .ne :
824
+ res_values = np .ones (len (self ), dtype = bool )
825
+ else :
826
+ res_values = np .zeros (len (self ), dtype = bool )
827
+ return self ._constructor (res_values , index = self .index ,
828
+ name = self .name , dtype = 'bool' )
831
829
832
830
if is_categorical_dtype (self ):
833
831
# cats are a special case as get_values() would return an ndarray,
@@ -877,11 +875,10 @@ def na_op(x, y):
877
875
y = _ensure_object (y )
878
876
result = lib .vec_binop (x , y , op )
879
877
else :
878
+ # let null fall thru
879
+ if not isna (y ):
880
+ y = bool (y )
880
881
try :
881
-
882
- # let null fall thru
883
- if not isna (y ):
884
- y = bool (y )
885
882
result = lib .scalar_binop (x , y , op )
886
883
except :
887
884
msg = ("cannot compare a dtyped [{dtype}] array "
@@ -899,26 +896,31 @@ def wrapper(self, other):
899
896
900
897
self , other = _align_method_SERIES (self , other , align_asobject = True )
901
898
902
- if isinstance (other , ABCSeries ):
899
+ if isinstance (other , ABCDataFrame ):
900
+ # Defer to DataFrame implementation; fail early
901
+ return NotImplemented
902
+
903
+ elif isinstance (other , ABCSeries ):
903
904
name = com ._maybe_match_name (self , other )
904
905
is_other_int_dtype = is_integer_dtype (other .dtype )
905
906
other = fill_int (other ) if is_other_int_dtype else fill_bool (other )
906
907
907
908
filler = (fill_int if is_self_int_dtype and is_other_int_dtype
908
909
else fill_bool )
909
- return filler (self ._constructor (na_op (self .values , other .values ),
910
- index = self .index , name = name ))
911
910
912
- elif isinstance (other , ABCDataFrame ):
913
- return NotImplemented
911
+ res_values = na_op (self .values , other .values )
912
+ unfilled = self ._constructor (res_values ,
913
+ index = self .index , name = name )
914
+ return filler (unfilled )
914
915
915
916
else :
916
917
# scalars, list, tuple, np.array
917
918
filler = (fill_int if is_self_int_dtype and
918
919
is_integer_dtype (np .asarray (other )) else fill_bool )
919
- return filler (self ._constructor (
920
- na_op (self .values , other ),
921
- index = self .index )).__finalize__ (self )
920
+
921
+ res_values = na_op (self .values , other )
922
+ unfilled = self ._constructor (res_values , index = self .index )
923
+ return filler (unfilled ).__finalize__ (self )
922
924
923
925
return wrapper
924
926
@@ -1023,21 +1025,23 @@ def na_op(x, y):
1023
1025
mask = notna (xrav ) & notna (yrav )
1024
1026
xrav = xrav [mask ]
1025
1027
1026
- # we may need to manually
1027
- # broadcast a 1 element array
1028
1028
if yrav .shape != mask .shape :
1029
- yrav = np .empty (mask .shape , dtype = yrav .dtype )
1030
- yrav .fill (yrav .item ())
1029
+ # FIXME: GH#5284, GH#5035, GH#19448
1030
+ # Without specifically raising here we get mismatched
1031
+ # errors in Py3 (TypeError) vs Py2 (ValueError)
1032
+ raise ValueError ('Cannot broadcast operands together.' )
1031
1033
1032
1034
yrav = yrav [mask ]
1033
- if np . prod ( xrav .shape ) and np . prod ( yrav . shape ) :
1035
+ if xrav .size :
1034
1036
with np .errstate (all = 'ignore' ):
1035
1037
result [mask ] = op (xrav , yrav )
1036
- elif hasattr (x , 'size' ):
1038
+
1039
+ elif isinstance (x , np .ndarray ):
1040
+ # mask is only meaningful for x
1037
1041
result = np .empty (x .size , dtype = x .dtype )
1038
1042
mask = notna (xrav )
1039
1043
xrav = xrav [mask ]
1040
- if np . prod ( xrav .shape ) :
1044
+ if xrav .size :
1041
1045
with np .errstate (all = 'ignore' ):
1042
1046
result [mask ] = op (xrav , y )
1043
1047
else :
0 commit comments