Skip to content

Commit b14a24f

Browse files
jbrockmendelAlexKirko
authored andcommitted
BUG/Compat: dt64/td64 cummin, cummax on np 1.18 (pandas-dev#30460)
1 parent d245e99 commit b14a24f

File tree

2 files changed

+46
-28
lines changed

2 files changed

+46
-28
lines changed

pandas/core/generic.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -11131,11 +11131,35 @@ def cum_func(self, axis=None, skipna=True, *args, **kwargs):
1113111131
axis = self._get_axis_number(axis)
1113211132

1113311133
y = com.values_from_object(self).copy()
11134+
d = self._construct_axes_dict()
11135+
d["copy"] = False
11136+
11137+
if issubclass(y.dtype.type, (np.datetime64, np.timedelta64)):
11138+
# numpy 1.18 started sorting NaTs at the end instead of beginning,
11139+
# so we need to work around to maintain backwards-consistency.
11140+
orig_dtype = y.dtype
11141+
if accum_func == np.minimum.accumulate:
11142+
# Note: the accum_func comparison fails as an "is" comparison
11143+
# Note that "y" is always a copy, so we can safely modify it
11144+
mask = isna(self)
11145+
y = y.view("i8")
11146+
y[mask] = np.iinfo(np.int64).max
11147+
11148+
result = accum_func(y.view("i8"), axis).view(orig_dtype)
11149+
if skipna:
11150+
mask = isna(self)
11151+
np.putmask(result, mask, iNaT)
11152+
elif accum_func == np.minimum.accumulate:
11153+
# Restore NaTs that we masked previously
11154+
nz = (~np.asarray(mask)).nonzero()[0]
11155+
if len(nz):
11156+
# everything up to the first non-na entry stays NaT
11157+
result[: nz[0]] = iNaT
11158+
11159+
if self.ndim == 1:
11160+
# restore dt64tz dtype
11161+
d["dtype"] = self.dtype
1113411162

11135-
if skipna and issubclass(y.dtype.type, (np.datetime64, np.timedelta64)):
11136-
result = accum_func(y, axis)
11137-
mask = isna(self)
11138-
np.putmask(result, mask, iNaT)
1113911163
elif skipna and not issubclass(y.dtype.type, (np.integer, np.bool_)):
1114011164
mask = isna(self)
1114111165
np.putmask(y, mask, mask_a)
@@ -11144,8 +11168,6 @@ def cum_func(self, axis=None, skipna=True, *args, **kwargs):
1114411168
else:
1114511169
result = accum_func(y, axis)
1114611170

11147-
d = self._construct_axes_dict()
11148-
d["copy"] = False
1114911171
return self._constructor(result, **d).__finalize__(self)
1115011172

1115111173
return set_function_name(cum_func, name, cls)

pandas/tests/series/test_cumulative.py

+18-22
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
import numpy as np
1111
import pytest
1212

13-
from pandas.compat.numpy import _np_version_under1p18
14-
1513
import pandas as pd
1614
import pandas.util.testing as tm
1715

@@ -63,53 +61,54 @@ def test_cummax(self, datetime_series):
6361

6462
tm.assert_series_equal(result, expected)
6563

66-
@pytest.mark.xfail(
67-
not _np_version_under1p18, reason="numpy 1.18 changed min/max behavior for NaT"
68-
)
69-
def test_cummin_datetime64(self):
64+
@pytest.mark.parametrize("tz", [None, "US/Pacific"])
65+
def test_cummin_datetime64(self, tz):
7066
s = pd.Series(
71-
pd.to_datetime(["NaT", "2000-1-2", "NaT", "2000-1-1", "NaT", "2000-1-3"])
67+
pd.to_datetime(
68+
["NaT", "2000-1-2", "NaT", "2000-1-1", "NaT", "2000-1-3"]
69+
).tz_localize(tz)
7270
)
7371

7472
expected = pd.Series(
75-
pd.to_datetime(["NaT", "2000-1-2", "NaT", "2000-1-1", "NaT", "2000-1-1"])
73+
pd.to_datetime(
74+
["NaT", "2000-1-2", "NaT", "2000-1-1", "NaT", "2000-1-1"]
75+
).tz_localize(tz)
7676
)
7777
result = s.cummin(skipna=True)
7878
tm.assert_series_equal(expected, result)
7979

8080
expected = pd.Series(
8181
pd.to_datetime(
8282
["NaT", "2000-1-2", "2000-1-2", "2000-1-1", "2000-1-1", "2000-1-1"]
83-
)
83+
).tz_localize(tz)
8484
)
8585
result = s.cummin(skipna=False)
8686
tm.assert_series_equal(expected, result)
8787

88-
@pytest.mark.xfail(
89-
not _np_version_under1p18, reason="numpy 1.18 changed min/max behavior for NaT"
90-
)
91-
def test_cummax_datetime64(self):
88+
@pytest.mark.parametrize("tz", [None, "US/Pacific"])
89+
def test_cummax_datetime64(self, tz):
9290
s = pd.Series(
93-
pd.to_datetime(["NaT", "2000-1-2", "NaT", "2000-1-1", "NaT", "2000-1-3"])
91+
pd.to_datetime(
92+
["NaT", "2000-1-2", "NaT", "2000-1-1", "NaT", "2000-1-3"]
93+
).tz_localize(tz)
9494
)
9595

9696
expected = pd.Series(
97-
pd.to_datetime(["NaT", "2000-1-2", "NaT", "2000-1-2", "NaT", "2000-1-3"])
97+
pd.to_datetime(
98+
["NaT", "2000-1-2", "NaT", "2000-1-2", "NaT", "2000-1-3"]
99+
).tz_localize(tz)
98100
)
99101
result = s.cummax(skipna=True)
100102
tm.assert_series_equal(expected, result)
101103

102104
expected = pd.Series(
103105
pd.to_datetime(
104106
["NaT", "2000-1-2", "2000-1-2", "2000-1-2", "2000-1-2", "2000-1-3"]
105-
)
107+
).tz_localize(tz)
106108
)
107109
result = s.cummax(skipna=False)
108110
tm.assert_series_equal(expected, result)
109111

110-
@pytest.mark.xfail(
111-
not _np_version_under1p18, reason="numpy 1.18 changed min/max behavior for NaT"
112-
)
113112
def test_cummin_timedelta64(self):
114113
s = pd.Series(pd.to_timedelta(["NaT", "2 min", "NaT", "1 min", "NaT", "3 min"]))
115114

@@ -125,9 +124,6 @@ def test_cummin_timedelta64(self):
125124
result = s.cummin(skipna=False)
126125
tm.assert_series_equal(expected, result)
127126

128-
@pytest.mark.xfail(
129-
not _np_version_under1p18, reason="numpy 1.18 changed min/max behavior for NaT"
130-
)
131127
def test_cummax_timedelta64(self):
132128
s = pd.Series(pd.to_timedelta(["NaT", "2 min", "NaT", "1 min", "NaT", "3 min"]))
133129

0 commit comments

Comments
 (0)