diff --git a/doc/source/whatsnew/v0.18.1.txt b/doc/source/whatsnew/v0.18.1.txt index 3e45b2ca37229..857b0cdc3aec6 100644 --- a/doc/source/whatsnew/v0.18.1.txt +++ b/doc/source/whatsnew/v0.18.1.txt @@ -237,3 +237,4 @@ Bug Fixes - Bug in ``.describe()`` resets categorical columns information (:issue:`11558`) - Bug where ``loffset`` argument was not applied when calling ``resample().count()`` on a timeseries (:issue:`12725`) - ``pd.read_excel()`` now accepts path objects (e.g. ``pathlib.Path``, ``py.path.local``) for the file path, in line with other ``read_*`` functions (:issue:`12655`) +- Bug in ``DataFrame`` inconsistent behavior ``last_valid_index()``, ``first_valid_index`` (:issue:`12800`) \ No newline at end of file diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 99fa722aebb7b..96a2b87a1bdb7 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -3766,12 +3766,18 @@ def first_valid_index(self): """ Return label for first non-NA/null value """ + if len(self) == 0: + return None + return self.index[self.count(1) > 0][0] def last_valid_index(self): """ Return label for last non-NA/null value """ + if len(self) == 0: + return None + return self.index[self.count(1) > 0][-1] # ---------------------------------------------------------------------- diff --git a/pandas/tests/frame/test_timeseries.py b/pandas/tests/frame/test_timeseries.py index 115e942dceb0f..f20f52fb9c07d 100644 --- a/pandas/tests/frame/test_timeseries.py +++ b/pandas/tests/frame/test_timeseries.py @@ -336,3 +336,9 @@ def test_first_last_valid(self): index = frame.last_valid_index() self.assertEqual(index, frame.index[-6]) + + # GH #12800 + def test_empty_first_last(self): + empty_frame = DataFrame() + self.assertIsNone(empty_frame.last_valid_index()) + self.assertIsNone(empty_frame.first_valid_index()) \ No newline at end of file diff --git a/pandas/tests/series/test_timeseries.py b/pandas/tests/series/test_timeseries.py index 00b5f01483e29..dec30f97d2e2a 100644 --- a/pandas/tests/series/test_timeseries.py +++ b/pandas/tests/series/test_timeseries.py @@ -19,7 +19,6 @@ class TestSeriesTimeSeries(TestData, tm.TestCase): - _multiprocess_can_split_ = True def test_shift(self): @@ -222,6 +221,7 @@ def test_asof(self): def test_getitem_setitem_datetimeindex(self): from pandas import date_range + N = 50 # testing with timezone, GH #2785 rng = date_range('1/1/1990', periods=N, freq='H', tz='US/Eastern') @@ -304,6 +304,7 @@ def test_getitem_setitem_datetime_tz_pytz(self): from pytz import timezone as tz from pandas import date_range + N = 50 # testing with timezone, GH #2785 rng = date_range('1/1/1990', periods=N, freq='H', tz='US/Eastern') @@ -343,6 +344,7 @@ def test_getitem_setitem_datetime_tz_dateutil(self): x) # handle special case for utc in dateutil from pandas import date_range + N = 50 # testing with timezone, GH #2785 rng = date_range('1/1/1990', periods=N, freq='H', tz='US/Eastern') @@ -372,6 +374,7 @@ def test_getitem_setitem_datetime_tz_dateutil(self): def test_getitem_setitem_periodindex(self): from pandas import period_range + N = 50 rng = period_range('1/1/1990', periods=N, freq='H') ts = Series(np.random.randn(N), index=rng) @@ -460,6 +463,7 @@ def test_asof_periodindex(self): def test_asof_more(self): from pandas import date_range + s = Series([nan, nan, 1, 2, nan, nan, 3, 4, 5], index=date_range('1/1/2000', periods=9)) @@ -617,3 +621,9 @@ def test_timeseries_coercion(self): self.assertTrue(ser.is_time_series) self.assertTrue(ser.index.is_all_dates) self.assertIsInstance(ser.index, DatetimeIndex) + + # GH #12800 + def test_empty_first_last(self): + empty_frame = Series() + self.assertIsNone(empty_frame.last_valid_index()) + self.assertIsNone(empty_frame.first_valid_index()) \ No newline at end of file