From f6e9ac109d5f7122f9d59dbab30c2e9a2d521751 Mon Sep 17 00:00:00 2001 From: jmholzer Date: Tue, 18 May 2021 20:25:32 +0000 Subject: [PATCH 1/6] ENH: Deprecate non-keyword arguments for Index.set_names. --- doc/source/whatsnew/v1.3.0.rst | 1 + pandas/core/indexes/base.py | 2 ++ pandas/tests/indexes/multi/test_get_set.py | 13 +++++++++++++ pandas/tests/indexes/test_base.py | 13 +++++++++++++ 4 files changed, 29 insertions(+) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 9298bc6a61bae..9eeb23a3928a1 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -648,6 +648,7 @@ Deprecations - Deprecated setting :attr:`Categorical._codes`, create a new :class:`Categorical` with the desired codes instead (:issue:`40606`) - Deprecated behavior of :meth:`DatetimeIndex.union` with mixed timezones; in a future version both will be cast to UTC instead of object dtype (:issue:`39328`) - Deprecated using ``usecols`` with out of bounds indices for ``read_csv`` with ``engine="c"`` (:issue:`25623`) +- Deprecated passing arguments as positional in :meth:`Index.set_names` (except for ``names``) (:issue:`41485`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 9f0a80ba0f5c7..e059c4096f7e6 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -54,6 +54,7 @@ from pandas.util._decorators import ( Appender, cache_readonly, + deprecate_nonkeyword_arguments, doc, ) @@ -1526,6 +1527,7 @@ def _set_names(self, values, level=None) -> None: names = property(fset=_set_names, fget=_get_names) + @deprecate_nonkeyword_arguments(version="2.0", allowed_args=["self", "names"]) @final def set_names(self, names, level=None, inplace: bool = False): """ diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 0c561395788ad..dc2004322b1d2 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -345,6 +345,19 @@ def test_set_names_with_nlevel_1(inplace): tm.assert_index_equal(result, expected) +def test_multi_set_names_pos_args_deprecation(): + # GH#41485 + idx = MultiIndex.from_product([["python", "cobra"], [2018, 2019]]) + + msg = ( + "Starting with pandas version 2.0 all arguments of Index.set_names " + "except for the argument 'names' will be keyword-only" + ) + + with tm.assert_produces_warning(FutureWarning, match=msg): + idx.set_names(["kind", "year"], None) + + @pytest.mark.parametrize("ordered", [True, False]) def test_set_levels_categorical(ordered): # GH13854 diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 47657fff56ceb..0537b2898a7f6 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -1738,3 +1738,16 @@ def test_construct_from_memoryview(klass, extra_kwargs): result = klass(memoryview(np.arange(2000, 2005)), **extra_kwargs) expected = klass(range(2000, 2005), **extra_kwargs) tm.assert_index_equal(result, expected) + + +def test_index_set_names_pos_args_deprecation(): + # GH#41485 + idx = Index([1, 2, 3, 4]) + + msg = ( + "Starting with pandas version 2.0 all arguments of Index.set_names " + "except for the argument 'names' will be keyword-only" + ) + + with tm.assert_produces_warning(FutureWarning, match=msg): + idx.set_names("quarter", None) From 17f5ff0415a194a3a66cc55f82be3409cba75528 Mon Sep 17 00:00:00 2001 From: jmholzer Date: Wed, 19 May 2021 20:14:09 +0000 Subject: [PATCH 2/6] ENH: Deprecate non-keyword arguments for Index.set_names. --- pandas/core/indexes/base.py | 2 +- pandas/tests/indexes/multi/test_get_set.py | 12 ++++++++++-- pandas/tests/indexes/test_base.py | 8 ++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index e059c4096f7e6..04598de9ce24e 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1527,7 +1527,7 @@ def _set_names(self, values, level=None) -> None: names = property(fset=_set_names, fget=_get_names) - @deprecate_nonkeyword_arguments(version="2.0", allowed_args=["self", "names"]) + @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "names"]) @final def set_names(self, names, level=None, inplace: bool = False): """ diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index dc2004322b1d2..0f23cbca61761 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -350,12 +350,20 @@ def test_multi_set_names_pos_args_deprecation(): idx = MultiIndex.from_product([["python", "cobra"], [2018, 2019]]) msg = ( - "Starting with pandas version 2.0 all arguments of Index.set_names " + "In a future version of pandas all arguments of Index.set_names " "except for the argument 'names' will be keyword-only" ) with tm.assert_produces_warning(FutureWarning, match=msg): - idx.set_names(["kind", "year"], None) + result = idx.set_names(["kind", "year"], None) + + expected = MultiIndex( + levels=[["python", "cobra"], [2018, 2019]], + codes=[[0, 0, 1, 1], [0, 1, 0, 1]], + names=["kind", "year"], + ) + + tm.assert_index_equal(result, expected) @pytest.mark.parametrize("ordered", [True, False]) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 0537b2898a7f6..a322228765d14 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -1745,9 +1745,13 @@ def test_index_set_names_pos_args_deprecation(): idx = Index([1, 2, 3, 4]) msg = ( - "Starting with pandas version 2.0 all arguments of Index.set_names " + "In a future version of pandas all arguments of Index.set_names " "except for the argument 'names' will be keyword-only" ) with tm.assert_produces_warning(FutureWarning, match=msg): - idx.set_names("quarter", None) + result = idx.set_names("quarter", None) + + expected = Index([1, 2, 3, 4], name="quarter") + + tm.assert_index_equal(result, expected) From fc525c80f94af4021ae18d8dc8135ca8cb760043 Mon Sep 17 00:00:00 2001 From: jmholzer Date: Tue, 25 May 2021 17:18:17 +0000 Subject: [PATCH 3/6] ENH: Deprecate non-keyword arguments for Index.set_names. --- pandas/core/indexes/base.py | 1 - pandas/core/indexes/multi.py | 11 ++++++++--- pandas/tests/indexes/multi/test_get_set.py | 6 +----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 04598de9ce24e..0d33baed1d379 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1528,7 +1528,6 @@ def _set_names(self, values, level=None) -> None: names = property(fset=_set_names, fget=_get_names) @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "names"]) - @final def set_names(self, names, level=None, inplace: bool = False): """ Set Index or MultiIndex name. diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 1a3719233a1da..35025de53552a 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -41,6 +41,7 @@ from pandas.util._decorators import ( Appender, cache_readonly, + deprecate_nonkeyword_arguments, doc, ) @@ -295,7 +296,6 @@ class MultiIndex(Index): _levels = FrozenList() _codes = FrozenList() _comparables = ["names"] - rename = Index.set_names sortorder: int | None @@ -3780,7 +3780,7 @@ def delete(self, loc) -> MultiIndex: verify_integrity=False, ) - @doc(Index.isin) + # @doc(Index.isin) def isin(self, values, level=None) -> np.ndarray: if level is None: values = MultiIndex.from_tuples(values, names=self.names)._values @@ -3788,11 +3788,16 @@ def isin(self, values, level=None) -> np.ndarray: else: num = self._get_level_number(level) levs = self.get_level_values(num) - if levs.size == 0: return np.zeros(len(levs), dtype=np.bool_) return levs.isin(values) + @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "names"]) + def set_names(self, names, level=None, inplace: bool = False) -> MultiIndex | None: + return super().set_names(names=names, level=level, inplace=inplace) + + rename = set_names + # --------------------------------------------------------------- # Arithmetic/Numeric Methods - Disabled diff --git a/pandas/tests/indexes/multi/test_get_set.py b/pandas/tests/indexes/multi/test_get_set.py index 0f23cbca61761..ca247ed23de9c 100644 --- a/pandas/tests/indexes/multi/test_get_set.py +++ b/pandas/tests/indexes/multi/test_get_set.py @@ -348,21 +348,17 @@ def test_set_names_with_nlevel_1(inplace): def test_multi_set_names_pos_args_deprecation(): # GH#41485 idx = MultiIndex.from_product([["python", "cobra"], [2018, 2019]]) - msg = ( - "In a future version of pandas all arguments of Index.set_names " + "In a future version of pandas all arguments of MultiIndex.set_names " "except for the argument 'names' will be keyword-only" ) - with tm.assert_produces_warning(FutureWarning, match=msg): result = idx.set_names(["kind", "year"], None) - expected = MultiIndex( levels=[["python", "cobra"], [2018, 2019]], codes=[[0, 0, 1, 1], [0, 1, 0, 1]], names=["kind", "year"], ) - tm.assert_index_equal(result, expected) From aa2e644b288b884ec94a71b9b52287c4d936dd74 Mon Sep 17 00:00:00 2001 From: jmholzer Date: Tue, 25 May 2021 17:22:58 +0000 Subject: [PATCH 4/6] ENH: Deprecate non-keyword arguments for Index.set_names. --- doc/source/whatsnew/v1.3.0.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index c729b389836d4..2b25156056d31 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -674,9 +674,7 @@ Deprecations - Deprecated setting :attr:`Categorical._codes`, create a new :class:`Categorical` with the desired codes instead (:issue:`40606`) - Deprecated behavior of :meth:`DatetimeIndex.union` with mixed timezones; in a future version both will be cast to UTC instead of object dtype (:issue:`39328`) - Deprecated using ``usecols`` with out of bounds indices for ``read_csv`` with ``engine="c"`` (:issue:`25623`) -<<<<<<< HEAD -- Deprecated passing arguments as positional in :meth:`Index.set_names` (except for ``names``) (:issue:`41485`) -======= +- Deprecated passing arguments as positional in :meth:`Index.set_names` and :meth:`MultiIndex.set_names` (except for ``names``) (:issue:`41485`) - Deprecated special treatment of lists with first element a Categorical in the :class:`DataFrame` constructor; pass as ``pd.DataFrame({col: categorical, ...})`` instead (:issue:`38845`) - Deprecated passing arguments as positional (except for ``"method"``) in :meth:`DataFrame.interpolate` and :meth:`Series.interpolate` (:issue:`41485`) - Deprecated passing arguments (apart from ``value``) as positional in :meth:`DataFrame.fillna` and :meth:`Series.fillna` (:issue:`41485`) @@ -722,7 +720,6 @@ For example: Out[5]: A 24 dtype: int64 ->>>>>>> origin .. --------------------------------------------------------------------------- From 2d39763c8684be9d7aab5282cbd3e52f9fcb3746 Mon Sep 17 00:00:00 2001 From: jmholzer Date: Tue, 25 May 2021 17:27:47 +0000 Subject: [PATCH 5/6] Fix commented decorator. --- pandas/core/indexes/multi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 35025de53552a..3c070889c1854 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -3780,7 +3780,7 @@ def delete(self, loc) -> MultiIndex: verify_integrity=False, ) - # @doc(Index.isin) + @doc(Index.isin) def isin(self, values, level=None) -> np.ndarray: if level is None: values = MultiIndex.from_tuples(values, names=self.names)._values From dc528b03af798580141a58ee9b9a456e18bce5d2 Mon Sep 17 00:00:00 2001 From: Marco Gorelli Date: Wed, 26 May 2021 21:14:31 +0100 Subject: [PATCH 6/6] fixup --- pandas/core/indexes/multi.py | 5 ++++- pandas/tests/indexes/test_base.py | 4 ---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 65b7eb746fad1..1362679ae0064 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -3584,7 +3584,9 @@ def _get_reconciled_name_object(self, other) -> MultiIndex: """ names = self._maybe_match_names(other) if self.names != names: - return self.rename(names) + # Incompatible return value type (got "Optional[MultiIndex]", expected + # "MultiIndex") + return self.rename(names) # type: ignore[return-value] return self def _maybe_match_names(self, other): @@ -3778,6 +3780,7 @@ def isin(self, values, level=None) -> np.ndarray: else: num = self._get_level_number(level) levs = self.get_level_values(num) + if levs.size == 0: return np.zeros(len(levs), dtype=np.bool_) return levs.isin(values) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index e573747d48b36..f75e4af888643 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -1743,17 +1743,13 @@ def test_construct_from_memoryview(klass, extra_kwargs): def test_index_set_names_pos_args_deprecation(): # GH#41485 idx = Index([1, 2, 3, 4]) - msg = ( "In a future version of pandas all arguments of Index.set_names " "except for the argument 'names' will be keyword-only" ) - with tm.assert_produces_warning(FutureWarning, match=msg): result = idx.set_names("quarter", None) - expected = Index([1, 2, 3, 4], name="quarter") - tm.assert_index_equal(result, expected)