Skip to content

Commit 360e727

Browse files
JustinZhengBCWillAyd
authored andcommitted
BUG GH23282 calling min on series of NaT returns NaT (#23289)
1 parent a26005a commit 360e727

File tree

3 files changed

+42
-15
lines changed

3 files changed

+42
-15
lines changed

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ Datetimelike
10301030
- Bug in :func:`to_datetime` with an :class:`Index` argument that would drop the ``name`` from the result (:issue:`21697`)
10311031
- Bug in :class:`PeriodIndex` where adding or subtracting a :class:`timedelta` or :class:`Tick` object produced incorrect results (:issue:`22988`)
10321032
- Bug in :func:`date_range` when decrementing a start date to a past end date by a negative frequency (:issue:`23270`)
1033+
- Bug in :meth:`Series.min` which would return ``NaN`` instead of ``NaT`` when called on a series of ``NaT`` (:issue:`23282`)
10331034
- Bug in :func:`DataFrame.combine` with datetimelike values raising a TypeError (:issue:`23079`)
10341035
- Bug in :func:`date_range` with frequency of ``Day`` or higher where dates sufficiently far in the future could wrap around to the past instead of raising ``OutOfBoundsDatetime`` (:issue:`14187`)
10351036
- Bug in :class:`PeriodIndex` with attribute ``freq.n`` greater than 1 where adding a :class:`DateOffset` object would return incorrect results (:issue:`23215`)

pandas/core/nanops.py

+23-15
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ def _get_values(values, skipna, fill_value=None, fill_value_typ=None,
244244
elif is_float_dtype(dtype):
245245
dtype_max = np.float64
246246

247-
return values, mask, dtype, dtype_max
247+
return values, mask, dtype, dtype_max, fill_value
248248

249249

250250
def _isfinite(values):
@@ -266,16 +266,21 @@ def _view_if_needed(values):
266266
return values
267267

268268

269-
def _wrap_results(result, dtype):
269+
def _wrap_results(result, dtype, fill_value=None):
270270
""" wrap our results if needed """
271271

272272
if is_datetime64_dtype(dtype):
273273
if not isinstance(result, np.ndarray):
274+
assert not isna(fill_value), "Expected non-null fill_value"
275+
if result == fill_value:
276+
result = np.nan
274277
result = tslibs.Timestamp(result)
275278
else:
276279
result = result.view(dtype)
277280
elif is_timedelta64_dtype(dtype):
278281
if not isinstance(result, np.ndarray):
282+
if result == fill_value:
283+
result = np.nan
279284

280285
# raise if we have a timedelta64[ns] which is too large
281286
if np.fabs(result) > _int64_max:
@@ -346,8 +351,8 @@ def nanany(values, axis=None, skipna=True, mask=None):
346351
>>> nanops.nanany(s)
347352
False
348353
"""
349-
values, mask, dtype, _ = _get_values(values, skipna, False, copy=skipna,
350-
mask=mask)
354+
values, mask, dtype, _, _ = _get_values(values, skipna, False, copy=skipna,
355+
mask=mask)
351356
return values.any(axis)
352357

353358

@@ -379,8 +384,8 @@ def nanall(values, axis=None, skipna=True, mask=None):
379384
>>> nanops.nanall(s)
380385
False
381386
"""
382-
values, mask, dtype, _ = _get_values(values, skipna, True, copy=skipna,
383-
mask=mask)
387+
values, mask, dtype, _, _ = _get_values(values, skipna, True, copy=skipna,
388+
mask=mask)
384389
return values.all(axis)
385390

386391

@@ -409,7 +414,8 @@ def nansum(values, axis=None, skipna=True, min_count=0, mask=None):
409414
>>> nanops.nansum(s)
410415
3.0
411416
"""
412-
values, mask, dtype, dtype_max = _get_values(values, skipna, 0, mask=mask)
417+
values, mask, dtype, dtype_max, _ = _get_values(values,
418+
skipna, 0, mask=mask)
413419
dtype_sum = dtype_max
414420
if is_float_dtype(dtype):
415421
dtype_sum = dtype
@@ -448,7 +454,8 @@ def nanmean(values, axis=None, skipna=True, mask=None):
448454
>>> nanops.nanmean(s)
449455
1.5
450456
"""
451-
values, mask, dtype, dtype_max = _get_values(values, skipna, 0, mask=mask)
457+
values, mask, dtype, dtype_max, _ = _get_values(
458+
values, skipna, 0, mask=mask)
452459
dtype_sum = dtype_max
453460
dtype_count = np.float64
454461
if is_integer_dtype(dtype) or is_timedelta64_dtype(dtype):
@@ -501,7 +508,7 @@ def get_median(x):
501508
return np.nan
502509
return np.nanmedian(x[mask])
503510

504-
values, mask, dtype, dtype_max = _get_values(values, skipna, mask=mask)
511+
values, mask, dtype, dtype_max, _ = _get_values(values, skipna, mask=mask)
505512
if not is_float_dtype(values):
506513
values = values.astype('f8')
507514
values[mask] = np.nan
@@ -705,7 +712,8 @@ def nansem(values, axis=None, skipna=True, ddof=1, mask=None):
705712
def _nanminmax(meth, fill_value_typ):
706713
@bottleneck_switch()
707714
def reduction(values, axis=None, skipna=True, mask=None):
708-
values, mask, dtype, dtype_max = _get_values(
715+
716+
values, mask, dtype, dtype_max, fill_value = _get_values(
709717
values, skipna, fill_value_typ=fill_value_typ, mask=mask)
710718

711719
if ((axis is not None and values.shape[axis] == 0) or
@@ -719,7 +727,7 @@ def reduction(values, axis=None, skipna=True, mask=None):
719727
else:
720728
result = getattr(values, meth)(axis)
721729

722-
result = _wrap_results(result, dtype)
730+
result = _wrap_results(result, dtype, fill_value)
723731
return _maybe_null_out(result, axis, mask)
724732

725733
reduction.__name__ = 'nan' + meth
@@ -753,8 +761,8 @@ def nanargmax(values, axis=None, skipna=True, mask=None):
753761
>>> nanops.nanargmax(s)
754762
4
755763
"""
756-
values, mask, dtype, _ = _get_values(values, skipna, fill_value_typ='-inf',
757-
mask=mask)
764+
values, mask, dtype, _, _ = _get_values(
765+
values, skipna, fill_value_typ='-inf', mask=mask)
758766
result = values.argmax(axis)
759767
result = _maybe_arg_null_out(result, axis, mask, skipna)
760768
return result
@@ -783,8 +791,8 @@ def nanargmin(values, axis=None, skipna=True, mask=None):
783791
>>> nanops.nanargmin(s)
784792
0
785793
"""
786-
values, mask, dtype, _ = _get_values(values, skipna, fill_value_typ='+inf',
787-
mask=mask)
794+
values, mask, dtype, _, _ = _get_values(
795+
values, skipna, fill_value_typ='+inf', mask=mask)
788796
result = values.argmin(axis)
789797
result = _maybe_arg_null_out(result, axis, mask, skipna)
790798
return result

pandas/tests/series/test_datetime_values.py

+18
Original file line numberDiff line numberDiff line change
@@ -508,3 +508,21 @@ def test_dt_timetz_accessor(self, tz_naive_fixture):
508508
time(22, 14, tzinfo=tz)])
509509
result = s.dt.timetz
510510
tm.assert_series_equal(result, expected)
511+
512+
@pytest.mark.parametrize('nat', [
513+
pd.Series([pd.NaT, pd.NaT]),
514+
pd.Series([pd.NaT, pd.Timedelta('nat')]),
515+
pd.Series([pd.Timedelta('nat'), pd.Timedelta('nat')])])
516+
def test_minmax_nat_series(self, nat):
517+
# GH 23282
518+
assert nat.min() is pd.NaT
519+
assert nat.max() is pd.NaT
520+
521+
@pytest.mark.parametrize('nat', [
522+
# GH 23282
523+
pd.DataFrame([pd.NaT, pd.NaT]),
524+
pd.DataFrame([pd.NaT, pd.Timedelta('nat')]),
525+
pd.DataFrame([pd.Timedelta('nat'), pd.Timedelta('nat')])])
526+
def test_minmax_nat_dataframe(self, nat):
527+
assert nat.min()[0] is pd.NaT
528+
assert nat.max()[0] is pd.NaT

0 commit comments

Comments
 (0)