5
5
"""
6
6
import datetime
7
7
import operator
8
- from typing import Any , Callable
8
+ from typing import Any , Callable , Tuple
9
9
10
10
import numpy as np
11
11
42
42
ABCSeries ,
43
43
ABCSparseArray ,
44
44
ABCSparseSeries ,
45
- ABCTimedeltaArray ,
46
45
)
47
46
from pandas .core .dtypes .missing import isna , notna
48
47
@@ -134,14 +133,15 @@ def _maybe_match_name(a, b):
134
133
return None
135
134
136
135
137
- def maybe_upcast_for_op (obj ):
136
+ def maybe_upcast_for_op (obj , shape : Tuple [ int , ...] ):
138
137
"""
139
138
Cast non-pandas objects to pandas types to unify behavior of arithmetic
140
139
and comparison operations.
141
140
142
141
Parameters
143
142
----------
144
143
obj: object
144
+ shape : tuple[int]
145
145
146
146
Returns
147
147
-------
@@ -157,13 +157,22 @@ def maybe_upcast_for_op(obj):
157
157
# implementation; otherwise operation against numeric-dtype
158
158
# raises TypeError
159
159
return Timedelta (obj )
160
- elif isinstance (obj , np .timedelta64 ) and not isna (obj ):
160
+ elif isinstance (obj , np .timedelta64 ):
161
+ if isna (obj ):
162
+ # wrapping timedelta64("NaT") in Timedelta returns NaT,
163
+ # which would incorrectly be treated as a datetime-NaT, so
164
+ # we broadcast and wrap in a Series
165
+ right = np .broadcast_to (obj , shape )
166
+
167
+ # Note: we use Series instead of TimedeltaIndex to avoid having
168
+ # to worry about catching NullFrequencyError.
169
+ return pd .Series (right )
170
+
161
171
# In particular non-nanosecond timedelta64 needs to be cast to
162
172
# nanoseconds, or else we get undesired behavior like
163
173
# np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D')
164
- # The isna check is to avoid casting timedelta64("NaT"), which would
165
- # return NaT and incorrectly be treated as a datetime-NaT.
166
174
return Timedelta (obj )
175
+
167
176
elif isinstance (obj , np .ndarray ) and is_timedelta64_dtype (obj ):
168
177
# GH#22390 Unfortunately we need to special-case right-hand
169
178
# timedelta64 dtypes because numpy casts integer dtypes to
@@ -975,7 +984,7 @@ def wrapper(left, right):
975
984
976
985
left , right = _align_method_SERIES (left , right )
977
986
res_name = get_op_result_name (left , right )
978
- right = maybe_upcast_for_op (right )
987
+ right = maybe_upcast_for_op (right , left . shape )
979
988
980
989
if is_categorical_dtype (left ):
981
990
raise TypeError (
@@ -1003,31 +1012,11 @@ def wrapper(left, right):
1003
1012
return construct_result (left , result , index = left .index , name = res_name )
1004
1013
1005
1014
elif is_timedelta64_dtype (right ):
1006
- # We should only get here with non-scalar or timedelta64('NaT')
1007
- # values for right
1008
- # Note: we cannot use dispatch_to_index_op because
1009
- # that may incorrectly raise TypeError when we
1010
- # should get NullFrequencyError
1011
- orig_right = right
1012
- if is_scalar (right ):
1013
- # broadcast and wrap in a TimedeltaIndex
1014
- assert np .isnat (right )
1015
- right = np .broadcast_to (right , left .shape )
1016
- right = pd .TimedeltaIndex (right )
1017
-
1018
- assert isinstance (right , (pd .TimedeltaIndex , ABCTimedeltaArray , ABCSeries ))
1019
- try :
1020
- result = op (left ._values , right )
1021
- except NullFrequencyError :
1022
- if orig_right is not right :
1023
- # i.e. scalar timedelta64('NaT')
1024
- # We get a NullFrequencyError because we broadcast to
1025
- # TimedeltaIndex, but this should be TypeError.
1026
- raise TypeError (
1027
- "incompatible type for a datetime/timedelta "
1028
- "operation [{name}]" .format (name = op .__name__ )
1029
- )
1030
- raise
1015
+ # We should only get here with non-scalar values for right
1016
+ # upcast by maybe_upcast_for_op
1017
+ assert not isinstance (right , (np .timedelta64 , np .ndarray ))
1018
+
1019
+ result = op (left ._values , right )
1031
1020
1032
1021
# We do not pass dtype to ensure that the Series constructor
1033
1022
# does inference in the case where `result` has object-dtype.
0 commit comments