@@ -341,10 +341,8 @@ def get_op(cls, left, right, name, na_op):
341
341
normal numpy path.
342
342
"""
343
343
is_timedelta_lhs = is_timedelta64_dtype (left )
344
- is_datetime_lhs = (is_datetime64_dtype (left ) or
345
- is_datetime64tz_dtype (left ))
346
344
347
- if not ( is_datetime_lhs or is_timedelta_lhs ) :
345
+ if not is_timedelta_lhs :
348
346
return _Op (left , right , name , na_op )
349
347
else :
350
348
return _TimeOp (left , right , name , na_op )
@@ -364,14 +362,8 @@ def __init__(self, left, right, name, na_op):
364
362
rvalues = self ._convert_to_array (right , name = name , other = lvalues )
365
363
366
364
# left
367
- self .is_offset_lhs = is_offsetlike (left )
368
365
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
375
367
376
368
# right
377
369
self .is_offset_rhs = is_offsetlike (right )
@@ -387,34 +379,6 @@ def __init__(self, left, right, name, na_op):
387
379
self .lvalues , self .rvalues = self ._convert_for_datetime (lvalues ,
388
380
rvalues )
389
381
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
-
418
382
def _validate_timedelta (self , name ):
419
383
# assumes self.is_timedelta_lhs
420
384
@@ -440,44 +404,8 @@ def _validate_timedelta(self, name):
440
404
'of a series/ndarray of type datetime64[ns] '
441
405
'or a timedelta' )
442
406
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
-
466
407
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 )
481
409
482
410
def _check_timedelta_with_numeric (self , name ):
483
411
if name not in ('__div__' , '__truediv__' , '__mul__' , '__rmul__' ):
@@ -498,7 +426,7 @@ def _convert_to_array(self, values, name=None, other=None):
498
426
# if this is a Series that contains relevant dtype info, then use this
499
427
# instead of the inferred type; this avoids coercing Series([NaT],
500
428
# dtype='datetime64[ns]') to Series([NaT], dtype='timedelta64[ns]')
501
- elif (isinstance (values , pd .Series ) and
429
+ elif (isinstance (values , ( pd .Series , ABCDatetimeIndex ) ) and
502
430
(is_timedelta64_dtype (values ) or is_datetime64_dtype (values ))):
503
431
supplied_dtype = values .dtype
504
432
@@ -513,13 +441,11 @@ def _convert_to_array(self, values, name=None, other=None):
513
441
values = np .empty (values .shape , dtype = 'timedelta64[ns]' )
514
442
values [:] = iNaT
515
443
516
- # a datelike
517
444
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
523
449
values = pd .DatetimeIndex (values )
524
450
# datetime array with tz
525
451
elif is_datetimetz (values ):
@@ -571,17 +497,10 @@ def _convert_for_datetime(self, lvalues, rvalues):
571
497
mask = isna (lvalues ) | isna (rvalues )
572
498
573
499
# datetimes require views
574
- if self .is_datetime_lhs or self . is_datetime_rhs :
500
+ if self .is_datetime_rhs :
575
501
576
502
# 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 :
585
504
self .dtype = rvalues .dtype
586
505
else :
587
506
self .dtype = 'datetime64[ns]'
@@ -601,15 +520,11 @@ def _offset(lvalues, rvalues):
601
520
self .na_op = lambda x , y : getattr (x , self .name )(y )
602
521
return lvalues , rvalues
603
522
604
- if self .is_offset_lhs :
605
- lvalues , rvalues = _offset (lvalues , rvalues )
606
- elif self .is_offset_rhs :
523
+ if self .is_offset_rhs :
607
524
rvalues , lvalues = _offset (rvalues , lvalues )
608
525
else :
609
526
610
527
# with tz, convert to UTC
611
- if self .is_datetime64tz_lhs :
612
- lvalues = lvalues .tz_convert ('UTC' ).tz_localize (None )
613
528
if self .is_datetime64tz_rhs :
614
529
rvalues = rvalues .tz_convert ('UTC' ).tz_localize (None )
615
530
@@ -622,8 +537,6 @@ def _offset(lvalues, rvalues):
622
537
self .dtype = 'timedelta64[ns]'
623
538
624
539
# convert Tick DateOffset to underlying delta
625
- if self .is_offset_lhs :
626
- lvalues = to_timedelta (lvalues , box = False )
627
540
if self .is_offset_rhs :
628
541
rvalues = to_timedelta (rvalues , box = False )
629
542
@@ -634,7 +547,7 @@ def _offset(lvalues, rvalues):
634
547
# time delta division -> unit less
635
548
# integer gets converted to timedelta in np < 1.6
636
549
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
638
551
self .name in ('__div__' , '__rdiv__' ,
639
552
'__truediv__' , '__rtruediv__' ,
640
553
'__floordiv__' , '__rfloordiv__' )):
@@ -750,10 +663,16 @@ def wrapper(left, right, name=name, na_op=na_op):
750
663
return NotImplemented
751
664
752
665
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 )
753
673
754
674
converted = _Op .get_op (left , right , name , na_op )
755
675
756
- left , right = converted .left , converted .right
757
676
lvalues , rvalues = converted .lvalues , converted .rvalues
758
677
dtype = converted .dtype
759
678
wrap_results = converted .wrap_results
@@ -775,6 +694,7 @@ def wrapper(left, right, name=name, na_op=na_op):
775
694
res_name = left .name
776
695
777
696
result = wrap_results (safe_na_op (lvalues , rvalues ))
697
+ res_name = _get_series_op_result_name (left , right )
778
698
return construct_result (
779
699
left ,
780
700
result ,
@@ -786,6 +706,15 @@ def wrapper(left, right, name=name, na_op=na_op):
786
706
return wrapper
787
707
788
708
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
+
789
718
def _comp_method_OBJECT_ARRAY (op , x , y ):
790
719
if isinstance (y , list ):
791
720
y = construct_1d_object_array_from_listlike (y )
@@ -1388,23 +1317,6 @@ def f(self, other):
1388
1317
1389
1318
def _arith_method_PANEL (op , name , str_rep = None , fill_zeros = None ,
1390
1319
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
1408
1320
1409
1321
# work only for scalars
1410
1322
def f (self , other ):
0 commit comments