diff --git a/doc/source/whatsnew/v0.22.0.txt b/doc/source/whatsnew/v0.22.0.txt index 8afdd1b2e22b3..44d8844795d3c 100644 --- a/doc/source/whatsnew/v0.22.0.txt +++ b/doc/source/whatsnew/v0.22.0.txt @@ -46,6 +46,8 @@ Other API Changes - :class:`Timestamp` will no longer silently ignore invalid ``freq`` arguments (:issue:`5168`) - :class:`CacheableOffset` and :class:`WeekDay` are no longer available in the ``pandas.tseries.offsets`` module (:issue:`17830`) - `tseries.frequencies.get_freq_group()` and `tseries.frequencies.DAYS` are removed from the public API (:issue:`18034`) +- :func:`Series.truncate` and :func:`DataFrame.truncate` will raise a ``ValueError`` if the index is not sorted instead of an unhelpful ``KeyError`` (:issue:`17935`) + .. _whatsnew_0220.deprecations: diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 9af4b889ac5a0..5f0630feba653 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -6338,6 +6338,11 @@ def truncate(self, before=None, after=None, axis=None, copy=True): axis = self._get_axis_number(axis) ax = self._get_axis(axis) + # GH 17935 + # Check that index is sorted + if not ax.is_monotonic_increasing and not ax.is_monotonic_decreasing: + raise ValueError("truncate requires a sorted index") + # if we have a date index, convert to dates, otherwise # treat like a slice if ax.is_all_dates: diff --git a/pandas/tests/frame/test_timeseries.py b/pandas/tests/frame/test_timeseries.py index d3f58434a91ae..d6d5ccc6487c4 100644 --- a/pandas/tests/frame/test_timeseries.py +++ b/pandas/tests/frame/test_timeseries.py @@ -377,6 +377,33 @@ def test_truncate_copy(self): truncated.values[:] = 5. assert not (self.tsframe.values[5:11] == 5).any() + def test_truncate_nonsortedindex(self): + # GH 17935 + + df = pd.DataFrame({'A': ['a', 'b', 'c', 'd', 'e']}, + index=[5, 3, 2, 9, 0]) + with tm.assert_raises_regex(ValueError, + 'truncate requires a sorted index'): + df.truncate(before=3, after=9) + + rng = pd.date_range('2011-01-01', '2012-01-01', freq='W') + ts = pd.DataFrame({'A': np.random.randn(len(rng)), + 'B': np.random.randn(len(rng))}, + index=rng) + with tm.assert_raises_regex(ValueError, + 'truncate requires a sorted index'): + ts.sort_values('A', ascending=False).truncate(before='2011-11', + after='2011-12') + + df = pd.DataFrame({3: np.random.randn(5), + 20: np.random.randn(5), + 2: np.random.randn(5), + 0: np.random.randn(5)}, + columns=[3, 20, 2, 0]) + with tm.assert_raises_regex(ValueError, + 'truncate requires a sorted index'): + df.truncate(before=2, after=20, axis=1) + def test_asfreq(self): offset_monthly = self.tsframe.asfreq(offsets.BMonthEnd()) rule_monthly = self.tsframe.asfreq('BM') diff --git a/pandas/tests/series/test_period.py b/pandas/tests/series/test_period.py index b4ff25d2630b8..9d5ef5e51ff20 100644 --- a/pandas/tests/series/test_period.py +++ b/pandas/tests/series/test_period.py @@ -272,10 +272,9 @@ def test_truncate(self): pd.Period('2017-09-03') ]) series2 = pd.Series([1, 2, 3], index=idx2) - result2 = series2.truncate(after='2017-09-02') + result2 = series2.sort_index().truncate(after='2017-09-02') expected_idx2 = pd.PeriodIndex([ - pd.Period('2017-09-03'), pd.Period('2017-09-02') ]) - tm.assert_series_equal(result2, pd.Series([1, 2], index=expected_idx2)) + tm.assert_series_equal(result2, pd.Series([2], index=expected_idx2)) diff --git a/pandas/tests/series/test_timeseries.py b/pandas/tests/series/test_timeseries.py index 6018260708335..e782293d98ead 100644 --- a/pandas/tests/series/test_timeseries.py +++ b/pandas/tests/series/test_timeseries.py @@ -236,6 +236,22 @@ def test_truncate(self): before=self.ts.index[-1] + offset, after=self.ts.index[0] - offset) + def test_truncate_nonsortedindex(self): + # GH 17935 + + s = pd.Series(['a', 'b', 'c', 'd', 'e'], + index=[5, 3, 2, 9, 0]) + with tm.assert_raises_regex(ValueError, + 'truncate requires a sorted index'): + s.truncate(before=3, after=9) + + rng = pd.date_range('2011-01-01', '2012-01-01', freq='W') + ts = pd.Series(np.random.randn(len(rng)), index=rng) + with tm.assert_raises_regex(ValueError, + 'truncate requires a sorted index'): + ts.sort_values(ascending=False).truncate(before='2011-11', + after='2011-12') + def test_asfreq(self): ts = Series([0., 1., 2.], index=[datetime(2009, 10, 30), datetime( 2009, 11, 30), datetime(2009, 12, 31)])