13
13
from pandas .util .decorators import Appender
14
14
import pandas .core .common as com
15
15
import pandas .computation .expressions as expressions
16
+ from pandas .lib import isscalar
17
+ from pandas .tslib import iNaT
16
18
from pandas .core .common import (bind_method , is_list_like , notnull , isnull ,
17
- _values_from_object , _maybe_match_name )
19
+ _values_from_object , _maybe_match_name ,
20
+ needs_i8_conversion , is_integer_dtype )
18
21
19
22
# -----------------------------------------------------------------------------
20
23
# Functions that add arithmetic methods to objects, given arithmetic factory
@@ -257,7 +260,7 @@ class _TimeOp(object):
257
260
Generally, you should use classmethod ``maybe_convert_for_time_op`` as an
258
261
entry point.
259
262
"""
260
- fill_value = tslib . iNaT
263
+ fill_value = iNaT
261
264
wrap_results = staticmethod (lambda x : x )
262
265
dtype = None
263
266
@@ -346,7 +349,7 @@ def _convert_to_array(self, values, name=None, other=None):
346
349
if (other is not None and other .dtype == 'timedelta64[ns]' and
347
350
all (isnull (v ) for v in values )):
348
351
values = np .empty (values .shape , dtype = other .dtype )
349
- values [:] = tslib . iNaT
352
+ values [:] = iNaT
350
353
351
354
# a datelike
352
355
elif isinstance (values , pd .DatetimeIndex ):
@@ -381,7 +384,7 @@ def _convert_to_array(self, values, name=None, other=None):
381
384
# all nan, so ok, use the other dtype (e.g. timedelta or datetime)
382
385
if isnull (values ).all ():
383
386
values = np .empty (values .shape , dtype = other .dtype )
384
- values [:] = tslib . iNaT
387
+ values [:] = iNaT
385
388
else :
386
389
raise TypeError (
387
390
'incompatible type [{0}] for a datetime/timedelta '
@@ -549,26 +552,59 @@ def na_op(x, y):
549
552
elif com .is_categorical_dtype (y ) and not lib .isscalar (y ):
550
553
return op (y ,x )
551
554
552
- if x .dtype == np . object_ :
555
+ if com . is_object_dtype ( x .dtype ) :
553
556
if isinstance (y , list ):
554
557
y = lib .list_to_object_array (y )
555
558
556
559
if isinstance (y , (np .ndarray , pd .Series )):
557
- if y .dtype != np . object_ :
560
+ if not com . is_object_dtype ( y .dtype ) :
558
561
result = lib .vec_compare (x , y .astype (np .object_ ), op )
559
562
else :
560
563
result = lib .vec_compare (x , y , op )
561
564
else :
562
565
result = lib .scalar_compare (x , y , op )
563
566
else :
564
567
568
+ # numpy does not like comparisons vs None
569
+ if lib .isscalar (y ) and isnull (y ):
570
+ y = np .nan
571
+
572
+ # we want to compare like types
573
+ # we only want to convert to integer like if
574
+ # we are not NotImplemented, otherwise
575
+ # we would allow datetime64 (but viewed as i8) against
576
+ # integer comparisons
577
+ if needs_i8_conversion (x ) and (not isscalar (y ) and is_integer_dtype (y )):
578
+ raise TypeError ("invalid type comparison" )
579
+ elif (not isscalar (y ) and needs_i8_conversion (y )) and is_integer_dtype (x ):
580
+ raise TypeError ("invalid type comparison" )
581
+
582
+ # we have a datetime/timedelta and may need to convert
583
+ mask = None
584
+ if needs_i8_conversion (x ) or (not isscalar (y ) and needs_i8_conversion (y )):
585
+
586
+ if isscalar (y ):
587
+ y = _index .convert_scalar (x ,_values_from_object (y ))
588
+ else :
589
+ y = y .view ('i8' )
590
+
591
+ if name == '__ne__' :
592
+ mask = notnull (x )
593
+ else :
594
+ mask = isnull (x )
595
+
596
+ x = x .view ('i8' )
597
+
565
598
try :
566
599
result = getattr (x , name )(y )
567
600
if result is NotImplemented :
568
601
raise TypeError ("invalid type comparison" )
569
- except ( AttributeError ) :
602
+ except AttributeError :
570
603
result = op (x , y )
571
604
605
+ if mask is not None and mask .any ():
606
+ result [mask ] = False
607
+
572
608
return result
573
609
574
610
def wrapper (self , other , axis = None ):
@@ -596,23 +632,18 @@ def wrapper(self, other, axis=None):
596
632
raise TypeError (msg .format (op = op ,typ = self .dtype ))
597
633
598
634
599
- mask = isnull (self )
600
-
601
635
if com .is_categorical_dtype (self ):
602
636
# cats are a special case as get_values() would return an ndarray, which would then
603
637
# not take categories ordering into account
604
638
# we can go directly to op, as the na_op would just test again and dispatch to it.
605
639
res = op (self .values , other )
606
640
else :
607
641
values = self .get_values ()
608
- other = _index .convert_scalar (values ,_values_from_object (other ))
609
-
610
- if issubclass (values .dtype .type , (np .datetime64 , np .timedelta64 )):
611
- values = values .view ('i8' )
642
+ if is_list_like (other ):
643
+ other = np .asarray (other )
612
644
613
- # scalars
614
645
res = na_op (values , other )
615
- if np .isscalar (res ):
646
+ if lib .isscalar (res ):
616
647
raise TypeError ('Could not compare %s type with Series'
617
648
% type (other ))
618
649
@@ -621,11 +652,6 @@ def wrapper(self, other, axis=None):
621
652
622
653
res = pd .Series (res , index = self .index , name = self .name ,
623
654
dtype = 'bool' )
624
-
625
- # mask out the invalids
626
- if mask .any ():
627
- res [mask ] = masker
628
-
629
655
return res
630
656
return wrapper
631
657
@@ -643,8 +669,7 @@ def na_op(x, y):
643
669
y = lib .list_to_object_array (y )
644
670
645
671
if isinstance (y , (np .ndarray , pd .Series )):
646
- if (x .dtype == np .bool_ and
647
- y .dtype == np .bool_ ): # pragma: no cover
672
+ if (com .is_bool_dtype (x .dtype ) and com .is_bool_dtype (y .dtype )):
648
673
result = op (x , y ) # when would this be hit?
649
674
else :
650
675
x = com ._ensure_object (x )
@@ -1046,7 +1071,7 @@ def na_op(x, y):
1046
1071
1047
1072
# work only for scalars
1048
1073
def f (self , other ):
1049
- if not np .isscalar (other ):
1074
+ if not lib .isscalar (other ):
1050
1075
raise ValueError ('Simple arithmetic with %s can only be '
1051
1076
'done with scalar values' %
1052
1077
self ._constructor .__name__ )
0 commit comments