diff --git a/doc/source/v0.14.1.txt b/doc/source/v0.14.1.txt index 0aa30e536ef48..ef8de452d1d38 100644 --- a/doc/source/v0.14.1.txt +++ b/doc/source/v0.14.1.txt @@ -65,3 +65,4 @@ There are no experimental changes in 0.14.1 Bug Fixes ~~~~~~~~~ +- Bug in ``Index.min`` and ``max`` doesn't handle ``nan`` and ``NaT`` properly (:issue:`7261`) diff --git a/pandas/core/base.py b/pandas/core/base.py index 0e7bc0fee8a48..6bbcc33c2271b 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -236,11 +236,13 @@ def _wrap_access_object(self, obj): def max(self): """ The maximum value of the object """ - return self.values.max() + import pandas.core.nanops + return pandas.core.nanops.nanmax(self.values) def min(self): """ The minimum value of the object """ - return self.values.min() + import pandas.core.nanops + return pandas.core.nanops.nanmin(self.values) def value_counts(self, normalize=False, sort=True, ascending=False, bins=None): diff --git a/pandas/tests/test_base.py b/pandas/tests/test_base.py index 927e096f8d769..4aaab3b2c52a5 100644 --- a/pandas/tests/test_base.py +++ b/pandas/tests/test_base.py @@ -1,5 +1,5 @@ import re -from datetime import timedelta +from datetime import datetime, timedelta import numpy as np import pandas.compat as compat import pandas as pd @@ -210,6 +210,39 @@ def test_ops(self): expected = expected.astype('M8[ns]').astype('int64') self.assertEqual(result.value, expected) + def test_nanops(self): + # GH 7261 + for op in ['max','min']: + for klass in [Index, Series]: + + obj = klass([np.nan, 2.0]) + self.assertEqual(getattr(obj, op)(), 2.0) + + obj = klass([np.nan]) + self.assertTrue(pd.isnull(getattr(obj, op)())) + + obj = klass([]) + self.assertTrue(pd.isnull(getattr(obj, op)())) + + obj = klass([pd.NaT, datetime(2011, 11, 1)]) + # check DatetimeIndex monotonic path + self.assertEqual(getattr(obj, op)(), datetime(2011, 11, 1)) + + obj = klass([pd.NaT, datetime(2011, 11, 1), pd.NaT]) + # check DatetimeIndex non-monotonic path + self.assertEqual(getattr(obj, op)(), datetime(2011, 11, 1)) + + # explicitly create DatetimeIndex + obj = DatetimeIndex([]) + self.assertTrue(pd.isnull(getattr(obj, op)())) + + obj = DatetimeIndex([pd.NaT]) + self.assertTrue(pd.isnull(getattr(obj, op)())) + + obj = DatetimeIndex([pd.NaT, pd.NaT, pd.NaT]) + self.assertTrue(pd.isnull(getattr(obj, op)())) + + def test_value_counts_unique_nunique(self): for o in self.objs: klass = type(o) diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 0e18509d9bc26..d83022c814d0a 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -1758,20 +1758,28 @@ def min(self, axis=None): """ Overridden ndarray.min to return a Timestamp """ - if self.is_monotonic: - return self[0] + mask = self.asi8 == tslib.iNaT + masked = self[~mask] + if len(masked) == 0: + return tslib.NaT + elif self.is_monotonic: + return masked[0] else: - min_stamp = self.asi8.min() + min_stamp = masked.asi8.min() return Timestamp(min_stamp, tz=self.tz) def max(self, axis=None): """ Overridden ndarray.max to return a Timestamp """ - if self.is_monotonic: - return self[-1] + mask = self.asi8 == tslib.iNaT + masked = self[~mask] + if len(masked) == 0: + return tslib.NaT + elif self.is_monotonic: + return masked[-1] else: - max_stamp = self.asi8.max() + max_stamp = masked.asi8.max() return Timestamp(max_stamp, tz=self.tz) def to_julian_date(self):