Skip to content

Commit 0ecc2c7

Browse files
authored
BUG: all-NaT TDI division with object dtype preserve td64 (#44237)
1 parent 5d9912c commit 0ecc2c7

File tree

3 files changed

+35
-16
lines changed

3 files changed

+35
-16
lines changed

doc/source/whatsnew/v1.4.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ Datetimelike
482482

483483
Timedelta
484484
^^^^^^^^^
485-
-
485+
- Bug in division of all-``NaT`` :class:`TimeDeltaIndex`, :class:`Series` or :class:`DataFrame` column with object-dtype arraylike of numbers failing to infer the result as timedelta64-dtype (:issue:`39750`)
486486
-
487487

488488
Timezones

pandas/core/arrays/timedeltas.py

+20-6
Original file line numberDiff line numberDiff line change
@@ -573,12 +573,17 @@ def __truediv__(self, other):
573573

574574
# We need to do dtype inference in order to keep DataFrame ops
575575
# behavior consistent with Series behavior
576-
inferred = lib.infer_dtype(result)
576+
inferred = lib.infer_dtype(result, skipna=False)
577577
if inferred == "timedelta":
578578
flat = result.ravel()
579579
result = type(self)._from_sequence(flat).reshape(result.shape)
580580
elif inferred == "floating":
581581
result = result.astype(float)
582+
elif inferred == "datetime":
583+
# GH#39750 this occurs when result is all-NaT, in which case
584+
# we want to interpret these NaTs as td64.
585+
# We construct an all-td64NaT result.
586+
result = self * np.nan
582587

583588
return result
584589

@@ -679,13 +684,22 @@ def __floordiv__(self, other):
679684
elif is_object_dtype(other.dtype):
680685
# error: Incompatible types in assignment (expression has type
681686
# "List[Any]", variable has type "ndarray")
682-
result = [ # type: ignore[assignment]
683-
self[n] // other[n] for n in range(len(self))
684-
]
685-
result = np.array(result)
686-
if lib.infer_dtype(result, skipna=False) == "timedelta":
687+
srav = self.ravel()
688+
orav = other.ravel()
689+
res_list = [srav[n] // orav[n] for n in range(len(srav))]
690+
result_flat = np.asarray(res_list)
691+
inferred = lib.infer_dtype(result_flat, skipna=False)
692+
693+
result = result_flat.reshape(self.shape)
694+
695+
if inferred == "timedelta":
687696
result, _ = sequence_to_td64ns(result)
688697
return type(self)(result)
698+
if inferred == "datetime":
699+
# GH#39750 occurs when result is all-NaT, which in this
700+
# case should be interpreted as td64nat. This can only
701+
# occur when self is all-td64nat
702+
return self * np.nan
689703
return result
690704

691705
elif is_integer_dtype(other.dtype) or is_float_dtype(other.dtype):

pandas/tests/arithmetic/test_timedelta64.py

+14-9
Original file line numberDiff line numberDiff line change
@@ -2022,7 +2022,7 @@ def test_td64arr_rmul_numeric_array(
20222022
ids=lambda x: type(x).__name__,
20232023
)
20242024
def test_td64arr_div_numeric_array(
2025-
self, box_with_array, vector, any_real_numpy_dtype, using_array_manager
2025+
self, box_with_array, vector, any_real_numpy_dtype
20262026
):
20272027
# GH#4521
20282028
# divide/multiply by integers
@@ -2062,14 +2062,6 @@ def test_td64arr_div_numeric_array(
20622062
expected = tm.box_expected(expected, xbox)
20632063
assert tm.get_dtype(expected) == "m8[ns]"
20642064

2065-
if using_array_manager and box_with_array is DataFrame:
2066-
# TODO the behaviour is buggy here (third column with all-NaT
2067-
# as result doesn't get preserved as timedelta64 dtype).
2068-
# Reported at https://github.com/pandas-dev/pandas/issues/39750
2069-
# Changing the expected instead of xfailing to continue to test
2070-
# the correct behaviour for the other columns
2071-
expected[2] = Series([NaT, NaT], dtype=object)
2072-
20732065
tm.assert_equal(result, expected)
20742066

20752067
with pytest.raises(TypeError, match=pattern):
@@ -2136,6 +2128,19 @@ def test_float_series_rdiv_td64arr(self, box_with_array, names):
21362128
else:
21372129
tm.assert_equal(result, expected)
21382130

2131+
def test_td64arr_all_nat_div_object_dtype_numeric(self, box_with_array):
2132+
# GH#39750 make sure we infer the result as td64
2133+
tdi = TimedeltaIndex([NaT, NaT])
2134+
2135+
left = tm.box_expected(tdi, box_with_array)
2136+
right = np.array([2, 2.0], dtype=object)
2137+
2138+
result = left / right
2139+
tm.assert_equal(result, left)
2140+
2141+
result = left // right
2142+
tm.assert_equal(result, left)
2143+
21392144

21402145
class TestTimedelta64ArrayLikeArithmetic:
21412146
# Arithmetic tests for timedelta64[ns] vectors fully parametrized over

0 commit comments

Comments
 (0)