@@ -341,10 +341,8 @@ def get_op(cls, left, right, name, na_op):
341341 normal numpy path.
342342 """
343343 is_timedelta_lhs = is_timedelta64_dtype (left )
344- is_datetime_lhs = (is_datetime64_dtype (left ) or
345- is_datetime64tz_dtype (left ))
346344
347- if not ( is_datetime_lhs or is_timedelta_lhs ) :
345+ if not is_timedelta_lhs :
348346 return _Op (left , right , name , na_op )
349347 else :
350348 return _TimeOp (left , right , name , na_op )
@@ -364,14 +362,8 @@ def __init__(self, left, right, name, na_op):
364362 rvalues = self ._convert_to_array (right , name = name , other = lvalues )
365363
366364 # left
367- self .is_offset_lhs = is_offsetlike (left )
368365 self .is_timedelta_lhs = is_timedelta64_dtype (lvalues )
369- self .is_datetime64_lhs = is_datetime64_dtype (lvalues )
370- self .is_datetime64tz_lhs = is_datetime64tz_dtype (lvalues )
371- self .is_datetime_lhs = (self .is_datetime64_lhs or
372- self .is_datetime64tz_lhs )
373- self .is_integer_lhs = left .dtype .kind in ['i' , 'u' ]
374- self .is_floating_lhs = left .dtype .kind == 'f'
366+ assert self .is_timedelta_lhs
375367
376368 # right
377369 self .is_offset_rhs = is_offsetlike (right )
@@ -387,34 +379,6 @@ def __init__(self, left, right, name, na_op):
387379 self .lvalues , self .rvalues = self ._convert_for_datetime (lvalues ,
388380 rvalues )
389381
390- def _validate_datetime (self , lvalues , rvalues , name ):
391- # assumes self.is_datetime_lhs
392-
393- if (self .is_timedelta_rhs or self .is_offset_rhs ):
394- # datetime and timedelta/DateOffset
395- if name not in ('__add__' , '__radd__' , '__sub__' ):
396- raise TypeError ("can only operate on a datetime with a rhs of "
397- "a timedelta/DateOffset for addition and "
398- "subtraction, but the operator [{name}] was "
399- "passed" .format (name = name ))
400-
401- elif self .is_datetime_rhs :
402- # 2 datetimes
403- if name not in ('__sub__' , '__rsub__' ):
404- raise TypeError ("can only operate on a datetimes for"
405- " subtraction, but the operator [{name}] was"
406- " passed" .format (name = name ))
407-
408- # if tz's must be equal (same or None)
409- if getattr (lvalues , 'tz' , None ) != getattr (rvalues , 'tz' , None ):
410- raise ValueError ("Incompatible tz's on datetime subtraction "
411- "ops" )
412-
413- else :
414- raise TypeError ('cannot operate on a series without a rhs '
415- 'of a series/ndarray of type datetime64[ns] '
416- 'or a timedelta' )
417-
418382 def _validate_timedelta (self , name ):
419383 # assumes self.is_timedelta_lhs
420384
@@ -440,44 +404,8 @@ def _validate_timedelta(self, name):
440404 'of a series/ndarray of type datetime64[ns] '
441405 'or a timedelta' )
442406
443- def _validate_offset (self , name ):
444- # assumes self.is_offset_lhs
445-
446- if self .is_timedelta_rhs :
447- # 2 timedeltas
448- if name not in ('__div__' , '__rdiv__' , '__truediv__' ,
449- '__rtruediv__' , '__add__' , '__radd__' , '__sub__' ,
450- '__rsub__' ):
451- raise TypeError ("can only operate on a timedeltas for addition"
452- ", subtraction, and division, but the operator"
453- " [{name}] was passed" .format (name = name ))
454-
455- elif self .is_datetime_rhs :
456- if name not in ('__add__' , '__radd__' ):
457- raise TypeError ("can only operate on a timedelta/DateOffset "
458- "and a datetime for addition, but the operator"
459- " [{name}] was passed" .format (name = name ))
460-
461- else :
462- raise TypeError ('cannot operate on a series without a rhs '
463- 'of a series/ndarray of type datetime64[ns] '
464- 'or a timedelta' )
465-
466407 def _validate (self , lvalues , rvalues , name ):
467- if self .is_datetime_lhs :
468- return self ._validate_datetime (lvalues , rvalues , name )
469- elif self .is_timedelta_lhs :
470- return self ._validate_timedelta (name )
471- elif self .is_offset_lhs :
472- return self ._validate_offset (name )
473-
474- if ((self .is_integer_lhs or self .is_floating_lhs ) and
475- self .is_timedelta_rhs ):
476- self ._check_timedelta_with_numeric (name )
477- else :
478- raise TypeError ('cannot operate on a series without a rhs '
479- 'of a series/ndarray of type datetime64[ns] '
480- 'or a timedelta' )
408+ return self ._validate_timedelta (name )
481409
482410 def _check_timedelta_with_numeric (self , name ):
483411 if name not in ('__div__' , '__truediv__' , '__mul__' , '__rmul__' ):
@@ -498,7 +426,7 @@ def _convert_to_array(self, values, name=None, other=None):
498426 # if this is a Series that contains relevant dtype info, then use this
499427 # instead of the inferred type; this avoids coercing Series([NaT],
500428 # dtype='datetime64[ns]') to Series([NaT], dtype='timedelta64[ns]')
501- elif (isinstance (values , pd .Series ) and
429+ elif (isinstance (values , ( pd .Series , ABCDatetimeIndex ) ) and
502430 (is_timedelta64_dtype (values ) or is_datetime64_dtype (values ))):
503431 supplied_dtype = values .dtype
504432
@@ -513,13 +441,11 @@ def _convert_to_array(self, values, name=None, other=None):
513441 values = np .empty (values .shape , dtype = 'timedelta64[ns]' )
514442 values [:] = iNaT
515443
516- # a datelike
517444 elif isinstance (values , ABCDatetimeIndex ):
518- # TODO: why are we casting to_series in the first place?
519- values = values .to_series (keep_tz = True )
520- # datetime with tz
521- elif (isinstance (ovalues , datetime .datetime ) and
522- hasattr (ovalues , 'tzinfo' )):
445+ # a datelike
446+ pass
447+ elif isinstance (ovalues , datetime .datetime ):
448+ # datetime scalar
523449 values = pd .DatetimeIndex (values )
524450 # datetime array with tz
525451 elif is_datetimetz (values ):
@@ -571,17 +497,10 @@ def _convert_for_datetime(self, lvalues, rvalues):
571497 mask = isna (lvalues ) | isna (rvalues )
572498
573499 # datetimes require views
574- if self .is_datetime_lhs or self . is_datetime_rhs :
500+ if self .is_datetime_rhs :
575501
576502 # datetime subtraction means timedelta
577- if self .is_datetime_lhs and self .is_datetime_rhs :
578- if self .name in ('__sub__' , '__rsub__' ):
579- self .dtype = 'timedelta64[ns]'
580- else :
581- self .dtype = 'datetime64[ns]'
582- elif self .is_datetime64tz_lhs :
583- self .dtype = lvalues .dtype
584- elif self .is_datetime64tz_rhs :
503+ if self .is_datetime64tz_rhs :
585504 self .dtype = rvalues .dtype
586505 else :
587506 self .dtype = 'datetime64[ns]'
@@ -601,15 +520,11 @@ def _offset(lvalues, rvalues):
601520 self .na_op = lambda x , y : getattr (x , self .name )(y )
602521 return lvalues , rvalues
603522
604- if self .is_offset_lhs :
605- lvalues , rvalues = _offset (lvalues , rvalues )
606- elif self .is_offset_rhs :
523+ if self .is_offset_rhs :
607524 rvalues , lvalues = _offset (rvalues , lvalues )
608525 else :
609526
610527 # with tz, convert to UTC
611- if self .is_datetime64tz_lhs :
612- lvalues = lvalues .tz_convert ('UTC' ).tz_localize (None )
613528 if self .is_datetime64tz_rhs :
614529 rvalues = rvalues .tz_convert ('UTC' ).tz_localize (None )
615530
@@ -622,8 +537,6 @@ def _offset(lvalues, rvalues):
622537 self .dtype = 'timedelta64[ns]'
623538
624539 # convert Tick DateOffset to underlying delta
625- if self .is_offset_lhs :
626- lvalues = to_timedelta (lvalues , box = False )
627540 if self .is_offset_rhs :
628541 rvalues = to_timedelta (rvalues , box = False )
629542
@@ -634,7 +547,7 @@ def _offset(lvalues, rvalues):
634547 # time delta division -> unit less
635548 # integer gets converted to timedelta in np < 1.6
636549 if ((self .is_timedelta_lhs and self .is_timedelta_rhs ) and
637- not self .is_integer_rhs and not self . is_integer_lhs and
550+ not self .is_integer_rhs and
638551 self .name in ('__div__' , '__rdiv__' ,
639552 '__truediv__' , '__rtruediv__' ,
640553 '__floordiv__' , '__rfloordiv__' )):
@@ -750,10 +663,16 @@ def wrapper(left, right, name=name, na_op=na_op):
750663 return NotImplemented
751664
752665 left , right = _align_method_SERIES (left , right )
666+ if is_datetime64_dtype (left ) or is_datetime64tz_dtype (left ):
667+ result = op (pd .DatetimeIndex (left ), right )
668+ res_name = _get_series_op_result_name (left , right )
669+ result .name = res_name # needs to be overriden if None
670+ return construct_result (left , result ,
671+ index = left .index , name = res_name ,
672+ dtype = result .dtype )
753673
754674 converted = _Op .get_op (left , right , name , na_op )
755675
756- left , right = converted .left , converted .right
757676 lvalues , rvalues = converted .lvalues , converted .rvalues
758677 dtype = converted .dtype
759678 wrap_results = converted .wrap_results
@@ -775,6 +694,7 @@ def wrapper(left, right, name=name, na_op=na_op):
775694 res_name = left .name
776695
777696 result = wrap_results (safe_na_op (lvalues , rvalues ))
697+ res_name = _get_series_op_result_name (left , right )
778698 return construct_result (
779699 left ,
780700 result ,
@@ -786,6 +706,15 @@ def wrapper(left, right, name=name, na_op=na_op):
786706 return wrapper
787707
788708
709+ def _get_series_op_result_name (left , right ):
710+ # `left` is always a pd.Series
711+ if isinstance (right , (ABCSeries , pd .Index )):
712+ name = _maybe_match_name (left , right )
713+ else :
714+ name = left .name
715+ return name
716+
717+
789718def _comp_method_OBJECT_ARRAY (op , x , y ):
790719 if isinstance (y , list ):
791720 y = construct_1d_object_array_from_listlike (y )
@@ -1388,23 +1317,6 @@ def f(self, other):
13881317
13891318def _arith_method_PANEL (op , name , str_rep = None , fill_zeros = None ,
13901319 default_axis = None , ** eval_kwargs ):
1391- # copied from Series na_op above, but without unnecessary branch for
1392- # non-scalar
1393- def na_op (x , y ):
1394- import pandas .core .computation .expressions as expressions
1395-
1396- try :
1397- result = expressions .evaluate (op , str_rep , x , y , ** eval_kwargs )
1398- except TypeError :
1399-
1400- # TODO: might need to find_common_type here?
1401- result = np .empty (len (x ), dtype = x .dtype )
1402- mask = notna (x )
1403- result [mask ] = op (x [mask ], y )
1404- result , changed = maybe_upcast_putmask (result , ~ mask , np .nan )
1405-
1406- result = missing .fill_zeros (result , x , y , name , fill_zeros )
1407- return result
14081320
14091321 # work only for scalars
14101322 def f (self , other ):
0 commit comments