Skip to content

Commit def8b96

Browse files
jbrockmendeljorisvandenbossche
authored andcommitted
REGR: fix TimedeltaIndex sum and datetime subtraction with NaT (#25282, #25317) (#25329)
1 parent 66d486e commit def8b96

File tree

6 files changed

+48
-4
lines changed

6 files changed

+48
-4
lines changed

doc/source/whatsnew/v0.24.2.rst

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Fixed Regressions
2626

2727
- Fixed regression in :meth:`DataFrame.duplicated()`, where empty dataframe was not returning a boolean dtyped Series. (:issue:`25184`)
2828
- Fixed regression in :meth:`Series.min` and :meth:`Series.max` where ``numeric_only=True`` was ignored when the ``Series`` contained ```Categorical`` data (:issue:`25299`)
29+
- Fixed regression in subtraction between :class:`Series` objects with ``datetime64[ns]`` dtype incorrectly raising ``OverflowError`` when the `Series` on the right contains null values (:issue:`25317`)
30+
- Fixed regression in :class:`TimedeltaIndex` where `np.sum(index)` incorrectly returned a zero-dimensional object instead of a scalar (:issue:`25282`)
2931
- Fixed regression in ``IntervalDtype`` construction where passing an incorrect string with 'Interval' as a prefix could result in a ``RecursionError``. (:issue:`25338`)
3032

3133
.. _whatsnew_0242.enhancements:

pandas/core/arrays/datetimes.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -720,11 +720,11 @@ def _sub_datetime_arraylike(self, other):
720720

721721
self_i8 = self.asi8
722722
other_i8 = other.asi8
723+
arr_mask = self._isnan | other._isnan
723724
new_values = checked_add_with_arr(self_i8, -other_i8,
724-
arr_mask=self._isnan)
725+
arr_mask=arr_mask)
725726
if self._hasnans or other._hasnans:
726-
mask = (self._isnan) | (other._isnan)
727-
new_values[mask] = iNaT
727+
new_values[arr_mask] = iNaT
728728
return new_values.view('timedelta64[ns]')
729729

730730
def _add_offset(self, offset):

pandas/core/arrays/timedeltas.py

+5
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def __init__(self, values, dtype=_TD_DTYPE, freq=None, copy=False):
190190
"ndarray, or Series or Index containing one of those."
191191
)
192192
raise ValueError(msg.format(type(values).__name__))
193+
if values.ndim != 1:
194+
raise ValueError("Only 1-dimensional input arrays are supported.")
193195

194196
if values.dtype == 'i8':
195197
# for compat with datetime/timedelta/period shared methods,
@@ -945,6 +947,9 @@ def sequence_to_td64ns(data, copy=False, unit="ns", errors="raise"):
945947
.format(dtype=data.dtype))
946948

947949
data = np.array(data, copy=copy)
950+
if data.ndim != 1:
951+
raise ValueError("Only 1-dimensional input arrays are supported.")
952+
948953
assert data.dtype == 'm8[ns]', data
949954
return data, inferred_freq
950955

pandas/core/indexes/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,8 @@ def __array_wrap__(self, result, context=None):
665665
"""
666666
Gets called after a ufunc.
667667
"""
668-
if is_bool_dtype(result):
668+
result = lib.item_from_zerodim(result)
669+
if is_bool_dtype(result) or lib.is_scalar(result):
669670
return result
670671

671672
attrs = self._get_attributes_dict()

pandas/tests/arithmetic/test_datetime64.py

+14
Original file line numberDiff line numberDiff line change
@@ -1440,6 +1440,20 @@ def test_dt64arr_add_sub_offset_ndarray(self, tz_naive_fixture,
14401440
class TestDatetime64OverflowHandling(object):
14411441
# TODO: box + de-duplicate
14421442

1443+
def test_dt64_overflow_masking(self, box_with_array):
1444+
# GH#25317
1445+
left = Series([Timestamp('1969-12-31')])
1446+
right = Series([NaT])
1447+
1448+
left = tm.box_expected(left, box_with_array)
1449+
right = tm.box_expected(right, box_with_array)
1450+
1451+
expected = TimedeltaIndex([NaT])
1452+
expected = tm.box_expected(expected, box_with_array)
1453+
1454+
result = left - right
1455+
tm.assert_equal(result, expected)
1456+
14431457
def test_dt64_series_arith_overflow(self):
14441458
# GH#12534, fixed by GH#19024
14451459
dt = pd.Timestamp('1700-01-31')

pandas/tests/arrays/test_timedeltas.py

+22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@
99

1010

1111
class TestTimedeltaArrayConstructor(object):
12+
def test_only_1dim_accepted(self):
13+
# GH#25282
14+
arr = np.array([0, 1, 2, 3], dtype='m8[h]').astype('m8[ns]')
15+
16+
with pytest.raises(ValueError, match="Only 1-dimensional"):
17+
# 2-dim
18+
TimedeltaArray(arr.reshape(2, 2))
19+
20+
with pytest.raises(ValueError, match="Only 1-dimensional"):
21+
# 0-dim
22+
TimedeltaArray(arr[[0]].squeeze())
23+
1224
def test_freq_validation(self):
1325
# ensure that the public constructor cannot create an invalid instance
1426
arr = np.array([0, 0, 1], dtype=np.int64) * 3600 * 10**9
@@ -51,6 +63,16 @@ def test_copy(self):
5163

5264

5365
class TestTimedeltaArray(object):
66+
def test_np_sum(self):
67+
# GH#25282
68+
vals = np.arange(5, dtype=np.int64).view('m8[h]').astype('m8[ns]')
69+
arr = TimedeltaArray(vals)
70+
result = np.sum(arr)
71+
assert result == vals.sum()
72+
73+
result = np.sum(pd.TimedeltaIndex(arr))
74+
assert result == vals.sum()
75+
5476
def test_from_sequence_dtype(self):
5577
msg = "dtype .*object.* cannot be converted to timedelta64"
5678
with pytest.raises(ValueError, match=msg):

0 commit comments

Comments
 (0)