diff --git a/doc/source/user_guide/timeseries.rst b/doc/source/user_guide/timeseries.rst index 3fdab0fd26643..c4e69eba9bf61 100644 --- a/doc/source/user_guide/timeseries.rst +++ b/doc/source/user_guide/timeseries.rst @@ -574,6 +574,7 @@ partial string selection is a form of label slicing, the endpoints **will be** i would include matching times on an included date: .. ipython:: python + :okwarning: dft = pd.DataFrame(np.random.randn(100000, 1), columns=['A'], index=pd.date_range('20130101', periods=100000, freq='T')) @@ -677,6 +678,7 @@ If index resolution is second, then the minute-accurate timestamp gives a If the timestamp string is treated as a slice, it can be used to index ``DataFrame`` with ``[]`` as well. .. ipython:: python + :okwarning: dft_minute = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]}, index=series_minute.index) @@ -689,7 +691,11 @@ If the timestamp string is treated as a slice, it can be used to index ``DataFra To *always* have unambiguous selection, whether the row is treated as a slice or a single selection, use ``.loc``. + As of version 1.1.0, row-based indexing is deprecated. Users should always use ``.loc`` + for label-based row indexing. + .. ipython:: python + :okwarning: dft_minute.loc['2011-12-31 23:59'] @@ -1968,6 +1974,7 @@ You can pass in dates and strings to ``Series`` and ``DataFrame`` with ``PeriodI Passing a string representing a lower frequency than ``PeriodIndex`` returns partial sliced data. .. ipython:: python + :okwarning: ps['2011'] @@ -1982,6 +1989,7 @@ Passing a string representing a lower frequency than ``PeriodIndex`` returns par As with ``DatetimeIndex``, the endpoints will be included in the result. The example below slices data starting from 10:00 to 11:59. .. ipython:: python + :okwarning: dfp['2013-01-01 10H':'2013-01-01 11H'] diff --git a/doc/source/whatsnew/v0.11.0.rst b/doc/source/whatsnew/v0.11.0.rst index 148ee349b049c..fc84fae11078d 100644 --- a/doc/source/whatsnew/v0.11.0.rst +++ b/doc/source/whatsnew/v0.11.0.rst @@ -367,6 +367,7 @@ Enhancements - You can now select with a string from a DataFrame with a datelike index, in a similar way to a Series (:issue:`3070`) .. ipython:: python + :okwarning: idx = pd.date_range("2001-10-1", periods=5, freq='M') ts = pd.Series(np.random.rand(len(idx)), index=idx) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index f3a0cf3841b5b..4545bad54a908 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2776,6 +2776,13 @@ def __getitem__(self, key): if indexer is not None: # either we have a slice or we have a string that can be converted # to a slice for partial-string date indexing + if not isinstance(key, slice): + warnings.warn( + "row-based slicing on a DataFrame is deprecated and will " + "raise in a future version. Use `df.loc[key]` instead.", + FutureWarning, + stacklevel=2, + ) return self._slice(indexer, axis=0) # Do we have a (boolean) DataFrame? @@ -2926,6 +2933,13 @@ def __setitem__(self, key, value): if indexer is not None: # either we have a slice or we have a string that can be converted # to a slice for partial-string date indexing + if not isinstance(key, slice): + warnings.warn( + "row-based slicing on a DataFrame is deprecated and will " + "raise in a future version. Use `df.loc[key]` instead.", + FutureWarning, + stacklevel=2, + ) return self._setitem_slice(indexer, value) if isinstance(key, DataFrame) or getattr(key, "ndim", None) == 2: diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 6a679708206fc..fb93ef22461b9 100755 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1,4 +1,4 @@ -from typing import Hashable, List, Tuple, Union +from typing import TYPE_CHECKING, Hashable, List, Tuple, Union import numpy as np @@ -29,6 +29,10 @@ from pandas.core.indexes.api import Index from pandas.core.indexes.base import InvalidIndexError +if TYPE_CHECKING: + from pandas import DataFrame + + # "null slice" _NS = slice(None, None) @@ -2167,7 +2171,7 @@ def _tuplify(ndim: int, loc: Hashable) -> Tuple[Union[Hashable, slice], ...]: return tuple(_tup) -def convert_to_index_sliceable(obj, key): +def convert_to_index_sliceable(obj: "DataFrame", key): """ If we are index sliceable, then return my slicer, otherwise return None. """ diff --git a/pandas/tests/indexes/datetimes/test_partial_slicing.py b/pandas/tests/indexes/datetimes/test_partial_slicing.py index 946d658e90132..9a294e5db4c69 100644 --- a/pandas/tests/indexes/datetimes/test_partial_slicing.py +++ b/pandas/tests/indexes/datetimes/test_partial_slicing.py @@ -309,7 +309,8 @@ def test_partial_slicing_dataframe(self): tm.assert_series_equal(result, expected) # Frame should return slice as well - result = df[ts_string] + with tm.assert_produces_warning(FutureWarning): + result = df[ts_string] expected = df[theslice] tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/series/indexing/test_datetime.py b/pandas/tests/series/indexing/test_datetime.py index 77085ef547690..7e3829f3f9e8a 100644 --- a/pandas/tests/series/indexing/test_datetime.py +++ b/pandas/tests/series/indexing/test_datetime.py @@ -641,7 +641,9 @@ def test_indexing(): expected.name = "A" df = DataFrame(dict(A=ts)) - result = df["2001"]["A"] + with tm.assert_produces_warning(FutureWarning): + ser = df["2001"] + result = ser["A"] tm.assert_series_equal(expected, result) # setting @@ -651,7 +653,9 @@ def test_indexing(): df.loc["2001", "A"] = 1 - result = df["2001"]["A"] + with tm.assert_produces_warning(FutureWarning): + ser = df["2001"] + result = ser["A"] tm.assert_series_equal(expected, result) # GH3546 (not including times on the last day)