From 110d63f01a207c42d40c7f0b038f33fd27489467 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 17 May 2021 18:33:46 +0100 Subject: [PATCH 01/22] added deprecate_nonkeyword_arguments to function where --- pandas/core/generic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index a09cc0a6324c0..69cac5cde1098 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -61,6 +61,7 @@ InvalidIndexError, ) from pandas.util._decorators import ( + deprecate_nonkeyword_arguments, doc, rewrite_axis_style_signature, ) @@ -9077,6 +9078,7 @@ def _where( name="where", name_other="mask", ) + @deprecate_nonkeyword_arguments(version="2.0", allowed_args=["self", "labels"]) def where( self, cond, From 66927330f8b93e65312cf310cb28d72875e18de6 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Tue, 18 May 2021 21:31:16 +0100 Subject: [PATCH 02/22] updated generic.py where function --- pandas/core/generic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 69cac5cde1098..cf56703a87c3e 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9078,7 +9078,9 @@ def _where( name="where", name_other="mask", ) - @deprecate_nonkeyword_arguments(version="2.0", allowed_args=["self", "labels"]) + @deprecate_nonkeyword_arguments( + version="2.0", allowed_args=["self", "cond", "other"] + ) def where( self, cond, From 861e2bbf16f10ebc02548860933d2649245c32b4 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Tue, 18 May 2021 21:41:58 +0100 Subject: [PATCH 03/22] added line 651 in whatsnew v1.3.0.rst --- doc/source/whatsnew/v1.3.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 9298bc6a61bae..ca590d58fc71e 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -648,7 +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 non-keyword arguments to ``where`` in :class:`NDFrame` (:issue:`41523`) .. --------------------------------------------------------------------------- From b4050f4fbbdddb9750590bdcdaecd77adb69f948 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Tue, 18 May 2021 22:37:23 +0100 Subject: [PATCH 04/22] removed cond from test_where --- pandas/tests/generic/test_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/generic/test_generic.py b/pandas/tests/generic/test_generic.py index 43784f87cf2fb..91008d474bd2c 100644 --- a/pandas/tests/generic/test_generic.py +++ b/pandas/tests/generic/test_generic.py @@ -339,7 +339,7 @@ def test_copy_and_deepcopy(self, shape, func): assert obj_copy is not obj self._compare(obj_copy, obj) - def test_where(self, cond): + def test_where(self): s = Series(range(5)) with tm.assert_produces_warning(FutureWarning): s.where(s > 1, 10) From 518d204bdedc8035170d8b704aa978830709f980 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Tue, 18 May 2021 22:45:04 +0100 Subject: [PATCH 05/22] changed test to be s.where(s > 1, 10, False) to triger warning --- pandas/tests/generic/test_generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/generic/test_generic.py b/pandas/tests/generic/test_generic.py index 91008d474bd2c..ec2a134b07746 100644 --- a/pandas/tests/generic/test_generic.py +++ b/pandas/tests/generic/test_generic.py @@ -342,7 +342,7 @@ def test_copy_and_deepcopy(self, shape, func): def test_where(self): s = Series(range(5)) with tm.assert_produces_warning(FutureWarning): - s.where(s > 1, 10) + s.where(s > 1, 10, False) class TestNDFrame: From a5ca29bb5c20f5ba8d0f2f157e5480c61e290644 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Thu, 20 May 2021 08:15:01 +0100 Subject: [PATCH 06/22] changed version to None --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 7c0b8ad4676e5..bf443958b8d66 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9084,7 +9084,7 @@ def _where( name_other="mask", ) @deprecate_nonkeyword_arguments( - version="2.0", allowed_args=["self", "cond", "other"] + version=None, allowed_args=["self", "cond", "other"] ) def where( self, From 3d58b4ba5793d4064d9505cb1ae91b8542a23942 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Thu, 20 May 2021 19:02:50 +0100 Subject: [PATCH 07/22] deleted test_where from test_generic.py --- pandas/tests/generic/test_generic.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pandas/tests/generic/test_generic.py b/pandas/tests/generic/test_generic.py index d646fcabd7254..254a0b8dfd34e 100644 --- a/pandas/tests/generic/test_generic.py +++ b/pandas/tests/generic/test_generic.py @@ -339,11 +339,6 @@ def test_copy_and_deepcopy(self, shape, func): assert obj_copy is not obj self._compare(obj_copy, obj) - def test_where(self): - s = Series(range(5)) - with tm.assert_produces_warning(FutureWarning): - s.where(s > 1, 10, False) - class TestNDFrame: # tests that don't fit elsewhere From 90b1fde53ecc3a88eefb9864ea2034f7592b782a Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Thu, 20 May 2021 19:21:24 +0100 Subject: [PATCH 08/22] added test_where to frame folder --- pandas/tests/frame/indexing/test_where.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index 7244624e563e3..6b867453309c8 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -745,3 +745,11 @@ def test_where_bool_comparison(): } ) tm.assert_frame_equal(result, expected) + + +def test_where(self): + + s = Series(range(5)) + + with tm.assert_produces_warning(FutureWarning): + s.where(s > 1, 10, False) From 72a99839064e65d795d916462af75cd210b88445 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Thu, 20 May 2021 19:27:42 +0100 Subject: [PATCH 09/22] added test_where to frame folder --- pandas/tests/frame/indexing/test_where.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index 6b867453309c8..6e55f10a935bb 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -747,7 +747,7 @@ def test_where_bool_comparison(): tm.assert_frame_equal(result, expected) -def test_where(self): +def test_where(): s = Series(range(5)) From 65bba0b599faac90ab063a1b93b8532cf2639893 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Thu, 20 May 2021 19:38:36 +0100 Subject: [PATCH 10/22] added test_where to series folder --- pandas/tests/series/indexing/test_where.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pandas/tests/series/indexing/test_where.py b/pandas/tests/series/indexing/test_where.py index b13fd18405839..dd3d7824271a7 100644 --- a/pandas/tests/series/indexing/test_where.py +++ b/pandas/tests/series/indexing/test_where.py @@ -140,6 +140,9 @@ def test_where(): rs = s2.where(cond[:3], -s2) tm.assert_series_equal(rs, expected) + with tm.assert_produces_warning(FutureWarning): + s.where(s > 1, 10, False) + def test_where_error(): s = Series(np.random.randn(5)) From 0258119f11e71d388104ebb84d5232b1d1e9f19c Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 22:25:59 +0100 Subject: [PATCH 11/22] edited the whatsnew, mentioned other than cond and other --- doc/source/whatsnew/v1.3.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 258e391b9220c..88ab36c942268 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -681,6 +681,7 @@ Deprecations - 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`) - Deprecated construction of :class:`Series` or :class:`DataFrame` with ``DatetimeTZDtype`` data and ``datetime64[ns]`` dtype. Use ``Series(data).dt.tz_localize(None)`` instead (:issue:`41555`,:issue:`33401`) +- Deprecated non-keyword arguments to ``where`` other than ``"cond"`` and ``"other"`` in :class:`NDFrame` (:issue:`41523`) .. _whatsnew_130.deprecations.nuisance_columns: From 40df58bb93932e6648e7af6b9a2afeed6d081d3b Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 22:29:11 +0100 Subject: [PATCH 12/22] changed stacklevel to 3 for futurewarning --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 83b7fe3fe48df..d4abd903ee0de 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9258,7 +9258,7 @@ def mask( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=2, + stacklevel=3, ) # see gh-21891 From 3ea8e927763568727a0924d44ec70139b8a6ddf5 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 22:42:14 +0100 Subject: [PATCH 13/22] changed frame/index/test_where_non_keyword --- pandas/tests/frame/indexing/test_where.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index 32a499f6e9168..0152fa3229b5a 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -757,3 +757,13 @@ def test_where_none_nan_coerce(): ) result = expected.where(expected.notnull(), None) tm.assert_frame_equal(result, expected) + + +def test_where_non_keyword(): + + s = DataFrame(range(5)) + + with tm.assert_produces_warning(FutureWarning): + result = s.where(s > 1, 10, False) + expected = DataFrame([10, 10, 2, 3, 4]) + tm.assert_frame_equal(expected, result) From 79ffbf7ed53239764e1b921ada96080d8d6f8c21 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 23:10:12 +0100 Subject: [PATCH 14/22] added where function in frame.py --- pandas/core/frame.py | 148 +++++++++++++++++++++++++++++++++++++++++ pandas/core/generic.py | 2 +- 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 7b564d55a342c..b38ad1821355b 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -10688,6 +10688,154 @@ def interpolate( **kwargs, ) + @deprecate_nonkeyword_arguments( + version=None, allowed_args=["self", "cond", "other"] + ) + def where( + self, + cond, + other=np.nan, + inplace=False, + axis=None, + level=None, + errors="raise", + try_cast=lib.no_default, + ): + """ + Replace values where the condition is {cond_rev}. + + Parameters + ---------- + cond : bool {klass}, array-like, or callable + Where `cond` is {cond}, keep the original value. Where + {cond_rev}, replace with corresponding value from `other`. + If `cond` is callable, it is computed on the {klass} and + should return boolean {klass} or array. The callable must + not change input {klass} (though pandas doesn't check it). + other : scalar, {klass}, or callable + Entries where `cond` is {cond_rev} are replaced with + corresponding value from `other`. + If other is callable, it is computed on the {klass} and + should return scalar or {klass}. The callable must not + change input {klass} (though pandas doesn't check it). + inplace : bool, default False + Whether to perform the operation in place on the data. + axis : int, default None + Alignment axis if needed. + level : int, default None + Alignment level if needed. + errors : str, {{'raise', 'ignore'}}, default 'raise' + Note that currently this parameter won't affect + the results and will always coerce to a suitable dtype. + + - 'raise' : allow exceptions to be raised. + - 'ignore' : suppress exceptions. On error return original object. + + try_cast : bool, default None + Try to cast the result back to the input type (if possible). + + .. deprecated:: 1.3.0 + Manually cast back if necessary. + + Returns + ------- + Same type as caller or None if ``inplace=True``. + + See Also + -------- + :func:`DataFrame.{name_other}` : Return an object of same shape as + self. + + Notes + ----- + The {name} method is an application of the if-then idiom. For each + element in the calling DataFrame, if ``cond`` is ``{cond}`` the + element is used; otherwise the corresponding element from the DataFrame + ``other`` is used. + + The signature for :func:`DataFrame.where` differs from + :func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to + ``np.where(m, df1, df2)``. + + For further details and examples see the ``{name}`` documentation in + :ref:`indexing `. + + Examples + -------- + >>> s = pd.Series(range(5)) + >>> s.where(s > 0) + 0 NaN + 1 1.0 + 2 2.0 + 3 3.0 + 4 4.0 + dtype: float64 + >>> s.mask(s > 0) + 0 0.0 + 1 NaN + 2 NaN + 3 NaN + 4 NaN + dtype: float64 + + >>> s.where(s > 1, 10) + 0 10 + 1 10 + 2 2 + 3 3 + 4 4 + dtype: int64 + >>> s.mask(s > 1, 10) + 0 0 + 1 1 + 2 10 + 3 10 + 4 10 + dtype: int64 + + >>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B']) + >>> df + A B + 0 0 1 + 1 2 3 + 2 4 5 + 3 6 7 + 4 8 9 + >>> m = df % 3 == 0 + >>> df.where(m, -df) + A B + 0 0 -1 + 1 -2 3 + 2 -4 -5 + 3 6 -7 + 4 -8 9 + >>> df.where(m, -df) == np.where(m, df, -df) + A B + 0 True True + 1 True True + 2 True True + 3 True True + 4 True True + >>> df.where(m, -df) == df.mask(~m, -df) + A B + 0 True True + 1 True True + 2 True True + 3 True True + 4 True True + """ + other = com.apply_if_callable(other, self) + + if try_cast is not lib.no_default: + warnings.warn( + "try_cast keyword is deprecated and will be removed in a " + "future version", + FutureWarning, + stacklevel=3, + ) + + return self._where(cond, other, inplace, axis, level, errors=errors) + DataFrame._add_numeric_operations() diff --git a/pandas/core/generic.py b/pandas/core/generic.py index d4abd903ee0de..cabc95a8b849c 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9225,7 +9225,7 @@ def where( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=2, + stacklevel=3, ) return self._where(cond, other, inplace, axis, level, errors=errors) From cfdb43accb62935fa4cf2edd6cd515bad0566909 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 23:12:12 +0100 Subject: [PATCH 15/22] added where function in series.py --- pandas/core/series.py | 148 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/pandas/core/series.py b/pandas/core/series.py index 4eba0db7e98ec..2fb047286066e 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -5310,6 +5310,154 @@ def interpolate( **kwargs, ) + @deprecate_nonkeyword_arguments( + version=None, allowed_args=["self", "cond", "other"] + ) + def where( + self, + cond, + other=np.nan, + inplace=False, + axis=None, + level=None, + errors="raise", + try_cast=lib.no_default, + ): + """ + Replace values where the condition is {cond_rev}. + + Parameters + ---------- + cond : bool {klass}, array-like, or callable + Where `cond` is {cond}, keep the original value. Where + {cond_rev}, replace with corresponding value from `other`. + If `cond` is callable, it is computed on the {klass} and + should return boolean {klass} or array. The callable must + not change input {klass} (though pandas doesn't check it). + other : scalar, {klass}, or callable + Entries where `cond` is {cond_rev} are replaced with + corresponding value from `other`. + If other is callable, it is computed on the {klass} and + should return scalar or {klass}. The callable must not + change input {klass} (though pandas doesn't check it). + inplace : bool, default False + Whether to perform the operation in place on the data. + axis : int, default None + Alignment axis if needed. + level : int, default None + Alignment level if needed. + errors : str, {{'raise', 'ignore'}}, default 'raise' + Note that currently this parameter won't affect + the results and will always coerce to a suitable dtype. + + - 'raise' : allow exceptions to be raised. + - 'ignore' : suppress exceptions. On error return original object. + + try_cast : bool, default None + Try to cast the result back to the input type (if possible). + + .. deprecated:: 1.3.0 + Manually cast back if necessary. + + Returns + ------- + Same type as caller or None if ``inplace=True``. + + See Also + -------- + :func:`DataFrame.{name_other}` : Return an object of same shape as + self. + + Notes + ----- + The {name} method is an application of the if-then idiom. For each + element in the calling DataFrame, if ``cond`` is ``{cond}`` the + element is used; otherwise the corresponding element from the DataFrame + ``other`` is used. + + The signature for :func:`DataFrame.where` differs from + :func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to + ``np.where(m, df1, df2)``. + + For further details and examples see the ``{name}`` documentation in + :ref:`indexing `. + + Examples + -------- + >>> s = pd.Series(range(5)) + >>> s.where(s > 0) + 0 NaN + 1 1.0 + 2 2.0 + 3 3.0 + 4 4.0 + dtype: float64 + >>> s.mask(s > 0) + 0 0.0 + 1 NaN + 2 NaN + 3 NaN + 4 NaN + dtype: float64 + + >>> s.where(s > 1, 10) + 0 10 + 1 10 + 2 2 + 3 3 + 4 4 + dtype: int64 + >>> s.mask(s > 1, 10) + 0 0 + 1 1 + 2 10 + 3 10 + 4 10 + dtype: int64 + + >>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B']) + >>> df + A B + 0 0 1 + 1 2 3 + 2 4 5 + 3 6 7 + 4 8 9 + >>> m = df % 3 == 0 + >>> df.where(m, -df) + A B + 0 0 -1 + 1 -2 3 + 2 -4 -5 + 3 6 -7 + 4 -8 9 + >>> df.where(m, -df) == np.where(m, df, -df) + A B + 0 True True + 1 True True + 2 True True + 3 True True + 4 True True + >>> df.where(m, -df) == df.mask(~m, -df) + A B + 0 True True + 1 True True + 2 True True + 3 True True + 4 True True + """ + other = com.apply_if_callable(other, self) + + if try_cast is not lib.no_default: + warnings.warn( + "try_cast keyword is deprecated and will be removed in a " + "future version", + FutureWarning, + stacklevel=3, + ) + + return self._where(cond, other, inplace, axis, level, errors=errors) + # ---------------------------------------------------------------------- # Add index _AXIS_ORDERS = ["index"] From fe7d729a63d61ce30508ae2705150d16147c98f6 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 23:21:56 +0100 Subject: [PATCH 16/22] changed allowed_args in frame --- pandas/core/frame.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index b38ad1821355b..1993838e82e72 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -10688,9 +10688,7 @@ def interpolate( **kwargs, ) - @deprecate_nonkeyword_arguments( - version=None, allowed_args=["self", "cond", "other"] - ) + @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "cond"]) def where( self, cond, @@ -10831,7 +10829,7 @@ def where( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=3, + stacklevel=2, ) return self._where(cond, other, inplace, axis, level, errors=errors) From c40cb38164a4c5a7a6eb3bcdf2697be7264eb6d6 Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 23:23:47 +0100 Subject: [PATCH 17/22] changed allowed_args in series --- pandas/core/series.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 2fb047286066e..1b5cf86a13844 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -5310,9 +5310,7 @@ def interpolate( **kwargs, ) - @deprecate_nonkeyword_arguments( - version=None, allowed_args=["self", "cond", "other"] - ) + @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "cond"]) def where( self, cond, @@ -5453,7 +5451,7 @@ def where( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=3, + stacklevel=2, ) return self._where(cond, other, inplace, axis, level, errors=errors) From 8e875ac8426016fd6d7a8dc2226c163cc11bb02d Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 23:24:51 +0100 Subject: [PATCH 18/22] changed allowed_args in generic --- pandas/core/generic.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index cabc95a8b849c..54d415037bd5c 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9082,9 +9082,7 @@ def _where( name="where", name_other="mask", ) - @deprecate_nonkeyword_arguments( - version=None, allowed_args=["self", "cond", "other"] - ) + @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "cond"]) def where( self, cond, @@ -9225,7 +9223,7 @@ def where( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=3, + stacklevel=2, ) return self._where(cond, other, inplace, axis, level, errors=errors) From 9c102fff0149c86da98209adf9d5176e3cbf144c Mon Sep 17 00:00:00 2001 From: Jie Zheng Date: Mon, 24 May 2021 23:31:12 +0100 Subject: [PATCH 19/22] adding github issue number --- pandas/tests/frame/indexing/test_where.py | 2 +- pandas/tests/series/indexing/test_where.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index 0152fa3229b5a..f43d9673318ef 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -760,7 +760,7 @@ def test_where_none_nan_coerce(): def test_where_non_keyword(): - + # GH 41523 s = DataFrame(range(5)) with tm.assert_produces_warning(FutureWarning): diff --git a/pandas/tests/series/indexing/test_where.py b/pandas/tests/series/indexing/test_where.py index dd3d7824271a7..2ba7c0865db5b 100644 --- a/pandas/tests/series/indexing/test_where.py +++ b/pandas/tests/series/indexing/test_where.py @@ -144,6 +144,16 @@ def test_where(): s.where(s > 1, 10, False) +def test_where_non_keyword(): + # GH 41523 + s = Series(range(5)) + + with tm.assert_produces_warning(FutureWarning): + result = s.where(s > 1, 10, False) + expected = Series([10, 10, 2, 3, 4]) + tm.assert_series_equal(expected, result) + + def test_where_error(): s = Series(np.random.randn(5)) cond = s > 0 From 74c03937f1dc90dd4fdcd84b68c6b9474f7e860e Mon Sep 17 00:00:00 2001 From: MarcoGorelli Date: Tue, 25 May 2021 19:03:00 +0100 Subject: [PATCH 20/22] fixup --- doc/source/whatsnew/v1.3.0.rst | 2 +- pandas/core/frame.py | 139 +-------------------- pandas/core/generic.py | 5 +- pandas/core/series.py | 139 +-------------------- pandas/tests/frame/indexing/test_where.py | 7 +- pandas/tests/series/indexing/test_where.py | 10 +- 6 files changed, 22 insertions(+), 280 deletions(-) diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 88ab36c942268..ce20006a8d3db 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -681,7 +681,7 @@ Deprecations - 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`) - Deprecated construction of :class:`Series` or :class:`DataFrame` with ``DatetimeTZDtype`` data and ``datetime64[ns]`` dtype. Use ``Series(data).dt.tz_localize(None)`` instead (:issue:`41555`,:issue:`33401`) -- Deprecated non-keyword arguments to ``where`` other than ``"cond"`` and ``"other"`` in :class:`NDFrame` (:issue:`41523`) +- Deprecated passing arguments as positional in :meth:`DataFrame.where` and :meth:`Series.where` (other than ``"cond"`` and ``"other"``) (:issue:`41485`) .. _whatsnew_130.deprecations.nuisance_columns: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 1993838e82e72..a24585b68a9ea 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -10688,7 +10688,9 @@ def interpolate( **kwargs, ) - @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "cond"]) + @deprecate_nonkeyword_arguments( + version=None, allowed_args=["self", "cond", "other"] + ) def where( self, cond, @@ -10699,140 +10701,7 @@ def where( errors="raise", try_cast=lib.no_default, ): - """ - Replace values where the condition is {cond_rev}. - - Parameters - ---------- - cond : bool {klass}, array-like, or callable - Where `cond` is {cond}, keep the original value. Where - {cond_rev}, replace with corresponding value from `other`. - If `cond` is callable, it is computed on the {klass} and - should return boolean {klass} or array. The callable must - not change input {klass} (though pandas doesn't check it). - other : scalar, {klass}, or callable - Entries where `cond` is {cond_rev} are replaced with - corresponding value from `other`. - If other is callable, it is computed on the {klass} and - should return scalar or {klass}. The callable must not - change input {klass} (though pandas doesn't check it). - inplace : bool, default False - Whether to perform the operation in place on the data. - axis : int, default None - Alignment axis if needed. - level : int, default None - Alignment level if needed. - errors : str, {{'raise', 'ignore'}}, default 'raise' - Note that currently this parameter won't affect - the results and will always coerce to a suitable dtype. - - - 'raise' : allow exceptions to be raised. - - 'ignore' : suppress exceptions. On error return original object. - - try_cast : bool, default None - Try to cast the result back to the input type (if possible). - - .. deprecated:: 1.3.0 - Manually cast back if necessary. - - Returns - ------- - Same type as caller or None if ``inplace=True``. - - See Also - -------- - :func:`DataFrame.{name_other}` : Return an object of same shape as - self. - - Notes - ----- - The {name} method is an application of the if-then idiom. For each - element in the calling DataFrame, if ``cond`` is ``{cond}`` the - element is used; otherwise the corresponding element from the DataFrame - ``other`` is used. - - The signature for :func:`DataFrame.where` differs from - :func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to - ``np.where(m, df1, df2)``. - - For further details and examples see the ``{name}`` documentation in - :ref:`indexing `. - - Examples - -------- - >>> s = pd.Series(range(5)) - >>> s.where(s > 0) - 0 NaN - 1 1.0 - 2 2.0 - 3 3.0 - 4 4.0 - dtype: float64 - >>> s.mask(s > 0) - 0 0.0 - 1 NaN - 2 NaN - 3 NaN - 4 NaN - dtype: float64 - - >>> s.where(s > 1, 10) - 0 10 - 1 10 - 2 2 - 3 3 - 4 4 - dtype: int64 - >>> s.mask(s > 1, 10) - 0 0 - 1 1 - 2 10 - 3 10 - 4 10 - dtype: int64 - - >>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B']) - >>> df - A B - 0 0 1 - 1 2 3 - 2 4 5 - 3 6 7 - 4 8 9 - >>> m = df % 3 == 0 - >>> df.where(m, -df) - A B - 0 0 -1 - 1 -2 3 - 2 -4 -5 - 3 6 -7 - 4 -8 9 - >>> df.where(m, -df) == np.where(m, df, -df) - A B - 0 True True - 1 True True - 2 True True - 3 True True - 4 True True - >>> df.where(m, -df) == df.mask(~m, -df) - A B - 0 True True - 1 True True - 2 True True - 3 True True - 4 True True - """ - other = com.apply_if_callable(other, self) - - if try_cast is not lib.no_default: - warnings.warn( - "try_cast keyword is deprecated and will be removed in a " - "future version", - FutureWarning, - stacklevel=2, - ) - - return self._where(cond, other, inplace, axis, level, errors=errors) + return super().where(cond, other, inplace, axis, level, errors, try_cast) DataFrame._add_numeric_operations() diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 54d415037bd5c..03f5f22d8318f 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -61,7 +61,6 @@ InvalidIndexError, ) from pandas.util._decorators import ( - deprecate_nonkeyword_arguments, doc, rewrite_axis_style_signature, ) @@ -9074,7 +9073,6 @@ def _where( result = self._constructor(new_data) return result.__finalize__(self) - @final @doc( klass=_shared_doc_kwargs["klass"], cond="True", @@ -9082,7 +9080,6 @@ def _where( name="where", name_other="mask", ) - @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "cond"]) def where( self, cond, @@ -9223,7 +9220,7 @@ def where( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=2, + stacklevel=4, ) return self._where(cond, other, inplace, axis, level, errors=errors) diff --git a/pandas/core/series.py b/pandas/core/series.py index 1b5cf86a13844..2da98e5dfd020 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -5310,7 +5310,9 @@ def interpolate( **kwargs, ) - @deprecate_nonkeyword_arguments(version=None, allowed_args=["self", "cond"]) + @deprecate_nonkeyword_arguments( + version=None, allowed_args=["self", "cond", "other"] + ) def where( self, cond, @@ -5321,140 +5323,7 @@ def where( errors="raise", try_cast=lib.no_default, ): - """ - Replace values where the condition is {cond_rev}. - - Parameters - ---------- - cond : bool {klass}, array-like, or callable - Where `cond` is {cond}, keep the original value. Where - {cond_rev}, replace with corresponding value from `other`. - If `cond` is callable, it is computed on the {klass} and - should return boolean {klass} or array. The callable must - not change input {klass} (though pandas doesn't check it). - other : scalar, {klass}, or callable - Entries where `cond` is {cond_rev} are replaced with - corresponding value from `other`. - If other is callable, it is computed on the {klass} and - should return scalar or {klass}. The callable must not - change input {klass} (though pandas doesn't check it). - inplace : bool, default False - Whether to perform the operation in place on the data. - axis : int, default None - Alignment axis if needed. - level : int, default None - Alignment level if needed. - errors : str, {{'raise', 'ignore'}}, default 'raise' - Note that currently this parameter won't affect - the results and will always coerce to a suitable dtype. - - - 'raise' : allow exceptions to be raised. - - 'ignore' : suppress exceptions. On error return original object. - - try_cast : bool, default None - Try to cast the result back to the input type (if possible). - - .. deprecated:: 1.3.0 - Manually cast back if necessary. - - Returns - ------- - Same type as caller or None if ``inplace=True``. - - See Also - -------- - :func:`DataFrame.{name_other}` : Return an object of same shape as - self. - - Notes - ----- - The {name} method is an application of the if-then idiom. For each - element in the calling DataFrame, if ``cond`` is ``{cond}`` the - element is used; otherwise the corresponding element from the DataFrame - ``other`` is used. - - The signature for :func:`DataFrame.where` differs from - :func:`numpy.where`. Roughly ``df1.where(m, df2)`` is equivalent to - ``np.where(m, df1, df2)``. - - For further details and examples see the ``{name}`` documentation in - :ref:`indexing `. - - Examples - -------- - >>> s = pd.Series(range(5)) - >>> s.where(s > 0) - 0 NaN - 1 1.0 - 2 2.0 - 3 3.0 - 4 4.0 - dtype: float64 - >>> s.mask(s > 0) - 0 0.0 - 1 NaN - 2 NaN - 3 NaN - 4 NaN - dtype: float64 - - >>> s.where(s > 1, 10) - 0 10 - 1 10 - 2 2 - 3 3 - 4 4 - dtype: int64 - >>> s.mask(s > 1, 10) - 0 0 - 1 1 - 2 10 - 3 10 - 4 10 - dtype: int64 - - >>> df = pd.DataFrame(np.arange(10).reshape(-1, 2), columns=['A', 'B']) - >>> df - A B - 0 0 1 - 1 2 3 - 2 4 5 - 3 6 7 - 4 8 9 - >>> m = df % 3 == 0 - >>> df.where(m, -df) - A B - 0 0 -1 - 1 -2 3 - 2 -4 -5 - 3 6 -7 - 4 -8 9 - >>> df.where(m, -df) == np.where(m, df, -df) - A B - 0 True True - 1 True True - 2 True True - 3 True True - 4 True True - >>> df.where(m, -df) == df.mask(~m, -df) - A B - 0 True True - 1 True True - 2 True True - 3 True True - 4 True True - """ - other = com.apply_if_callable(other, self) - - if try_cast is not lib.no_default: - warnings.warn( - "try_cast keyword is deprecated and will be removed in a " - "future version", - FutureWarning, - stacklevel=2, - ) - - return self._where(cond, other, inplace, axis, level, errors=errors) + return super().where(cond, other, inplace, axis, level, errors, try_cast) # ---------------------------------------------------------------------- # Add index diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index f43d9673318ef..63c3803b1f0d3 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -763,7 +763,12 @@ def test_where_non_keyword(): # GH 41523 s = DataFrame(range(5)) - with tm.assert_produces_warning(FutureWarning): + msg = ( + "In a future version of pandas all arguments of " + "DataFrame.where except for the arguments 'cond' " + "and 'other' will be keyword-only" + ) + with tm.assert_produces_warning(FutureWarning, match=msg): result = s.where(s > 1, 10, False) expected = DataFrame([10, 10, 2, 3, 4]) tm.assert_frame_equal(expected, result) diff --git a/pandas/tests/series/indexing/test_where.py b/pandas/tests/series/indexing/test_where.py index 2ba7c0865db5b..d52a7341e413e 100644 --- a/pandas/tests/series/indexing/test_where.py +++ b/pandas/tests/series/indexing/test_where.py @@ -140,15 +140,17 @@ def test_where(): rs = s2.where(cond[:3], -s2) tm.assert_series_equal(rs, expected) - with tm.assert_produces_warning(FutureWarning): - s.where(s > 1, 10, False) - def test_where_non_keyword(): # GH 41523 s = Series(range(5)) - with tm.assert_produces_warning(FutureWarning): + msg = ( + "In a future version of pandas all arguments of " + "Series.where except for the arguments 'cond' " + "and 'other' will be keyword-only" + ) + with tm.assert_produces_warning(FutureWarning, match=msg): result = s.where(s > 1, 10, False) expected = Series([10, 10, 2, 3, 4]) tm.assert_series_equal(expected, result) From 17e892ab7e5345efdc902e89985177ee1c68811c Mon Sep 17 00:00:00 2001 From: MarcoGorelli Date: Tue, 25 May 2021 19:08:26 +0100 Subject: [PATCH 21/22] revert change to mask --- pandas/core/generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 03f5f22d8318f..ffebb30cda8db 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9253,7 +9253,7 @@ def mask( "try_cast keyword is deprecated and will be removed in a " "future version", FutureWarning, - stacklevel=3, + stacklevel=2, ) # see gh-21891 From 5aad6bca43629f05a749b47d159fc948f0100975 Mon Sep 17 00:00:00 2001 From: MarcoGorelli Date: Tue, 25 May 2021 19:11:03 +0100 Subject: [PATCH 22/22] fixup gh issue number --- pandas/tests/frame/indexing/test_where.py | 5 ++--- pandas/tests/series/indexing/test_where.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pandas/tests/frame/indexing/test_where.py b/pandas/tests/frame/indexing/test_where.py index 63c3803b1f0d3..0405d150c0c04 100644 --- a/pandas/tests/frame/indexing/test_where.py +++ b/pandas/tests/frame/indexing/test_where.py @@ -759,10 +759,9 @@ def test_where_none_nan_coerce(): tm.assert_frame_equal(result, expected) -def test_where_non_keyword(): - # GH 41523 +def test_where_non_keyword_deprecation(): + # GH 41485 s = DataFrame(range(5)) - msg = ( "In a future version of pandas all arguments of " "DataFrame.where except for the arguments 'cond' " diff --git a/pandas/tests/series/indexing/test_where.py b/pandas/tests/series/indexing/test_where.py index d52a7341e413e..0c6b9bd924759 100644 --- a/pandas/tests/series/indexing/test_where.py +++ b/pandas/tests/series/indexing/test_where.py @@ -141,10 +141,9 @@ def test_where(): tm.assert_series_equal(rs, expected) -def test_where_non_keyword(): - # GH 41523 +def test_where_non_keyword_deprecation(): + # GH 41485 s = Series(range(5)) - msg = ( "In a future version of pandas all arguments of " "Series.where except for the arguments 'cond' "