diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 496ffb2768ccc..454098f4ace04 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -474,7 +474,8 @@ Deprecations - :class:`Index` methods ``&``, ``|``, and ``^`` behaving as the set operations :meth:`Index.intersection`, :meth:`Index.union`, and :meth:`Index.symmetric_difference`, respectively, are deprecated and in the future will behave as pointwise boolean operations matching :class:`Series` behavior. Use the named set methods instead (:issue:`36758`) - :meth:`Categorical.is_dtype_equal` and :meth:`CategoricalIndex.is_dtype_equal` are deprecated, will be removed in a future version (:issue:`37545`) - :meth:`Series.slice_shift` and :meth:`DataFrame.slice_shift` are deprecated, use :meth:`Series.shift` or :meth:`DataFrame.shift` instead (:issue:`37601`) -- Partial slicing on unordered :class:`DatetimeIndexes` with keys, which are not in Index is deprecated and will be removed in a future version (:issue:`18531`) +- Partial slicing on unordered :class:`DatetimeIndex` with keys, which are not in Index is deprecated and will be removed in a future version (:issue:`18531`) +- Deprecated :meth:`Index.asi8` for :class:`Index` subclasses other than :class:`DatetimeIndex`, :class:`TimedeltaIndex`, and :class:`PeriodIndex` (:issue:`37877`) - The ``inplace`` parameter of :meth:`Categorical.remove_unused_categories` is deprecated and will be removed in a future version (:issue:`37643`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 1b3e4864843f3..5209d83ade309 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -415,6 +415,11 @@ def asi8(self): ndarray An ndarray with int64 dtype. """ + warnings.warn( + "Index.asi8 is deprecated and will be removed in a future version", + FutureWarning, + stacklevel=2, + ) return None @classmethod @@ -4738,12 +4743,13 @@ def argsort(self, *args, **kwargs) -> np.ndarray: >>> idx[order] Index(['a', 'b', 'c', 'd'], dtype='object') """ - result = self.asi8 - - if result is None: - result = np.array(self) + if needs_i8_conversion(self.dtype): + # TODO: these do not match the underlying EA argsort methods GH#37863 + return self.asi8.argsort(*args, **kwargs) - return result.argsort(*args, **kwargs) + # This works for either ndarray or EA, is overriden + # by RangeIndex, MultIIndex + return self._data.argsort(*args, **kwargs) @final def get_value(self, series: "Series", key): diff --git a/pandas/core/indexes/numeric.py b/pandas/core/indexes/numeric.py index 9eb8a8b719d41..9dacdd6dea9ca 100644 --- a/pandas/core/indexes/numeric.py +++ b/pandas/core/indexes/numeric.py @@ -1,5 +1,6 @@ import operator from typing import Any +import warnings import numpy as np @@ -266,6 +267,11 @@ def inferred_type(self) -> str: @property def asi8(self) -> np.ndarray: # do not cache or you'll create a memory leak + warnings.warn( + "Index.asi8 is deprecated and will be removed in a future version", + FutureWarning, + stacklevel=2, + ) return self._values.view(self._default_dtype) diff --git a/pandas/core/tools/numeric.py b/pandas/core/tools/numeric.py index 32ca83787c4c1..4af32b219d380 100644 --- a/pandas/core/tools/numeric.py +++ b/pandas/core/tools/numeric.py @@ -10,6 +10,7 @@ is_number, is_numeric_dtype, is_scalar, + needs_i8_conversion, ) from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries @@ -123,8 +124,9 @@ def to_numeric(arg, errors="raise", downcast=None): values = arg.values elif isinstance(arg, ABCIndexClass): is_index = True - values = arg.asi8 - if values is None: + if needs_i8_conversion(arg.dtype): + values = arg.asi8 + else: values = arg.values elif isinstance(arg, (list, tuple)): values = np.array(arg, dtype="O") diff --git a/pandas/core/window/rolling.py b/pandas/core/window/rolling.py index e74ae5311125e..51a1e2102c273 100644 --- a/pandas/core/window/rolling.py +++ b/pandas/core/window/rolling.py @@ -337,6 +337,13 @@ def _get_roll_func(self, func_name: str) -> Callable[..., Any]: ) return window_func + @property + def _index_array(self): + # TODO: why do we get here with e.g. MultiIndex? + if needs_i8_conversion(self._on.dtype): + return self._on.asi8 + return None + def _get_window_indexer(self) -> BaseIndexer: """ Return an indexer class that will compute the window start and end bounds @@ -345,7 +352,7 @@ def _get_window_indexer(self) -> BaseIndexer: return self.window if self.is_freq_type: return VariableWindowIndexer( - index_array=self._on.asi8, window_size=self.window + index_array=self._index_array, window_size=self.window ) return FixedWindowIndexer(window_size=self.window) @@ -2140,7 +2147,7 @@ def _get_window_indexer(self) -> GroupbyIndexer: """ rolling_indexer: Type[BaseIndexer] indexer_kwargs: Optional[Dict[str, Any]] = None - index_array = self._on.asi8 + index_array = self._index_array window = self.window if isinstance(self.window, BaseIndexer): rolling_indexer = type(self.window) diff --git a/pandas/tests/indexes/test_common.py b/pandas/tests/indexes/test_common.py index d15b560419f6d..a10be99dff076 100644 --- a/pandas/tests/indexes/test_common.py +++ b/pandas/tests/indexes/test_common.py @@ -13,7 +13,16 @@ from pandas.core.dtypes.common import is_period_dtype, needs_i8_conversion import pandas as pd -from pandas import CategoricalIndex, MultiIndex, RangeIndex +from pandas import ( + CategoricalIndex, + DatetimeIndex, + Int64Index, + MultiIndex, + PeriodIndex, + RangeIndex, + TimedeltaIndex, + UInt64Index, +) import pandas._testing as tm @@ -348,6 +357,18 @@ def test_ravel_deprecation(self, index): with tm.assert_produces_warning(FutureWarning): index.ravel() + def test_asi8_deprecation(self, index): + # GH#37877 + if isinstance( + index, (Int64Index, UInt64Index, DatetimeIndex, TimedeltaIndex, PeriodIndex) + ): + warn = None + else: + warn = FutureWarning + + with tm.assert_produces_warning(warn): + index.asi8 + @pytest.mark.parametrize("na_position", [None, "middle"]) def test_sort_values_invalid_na_position(index_with_missing, na_position):