From 93a2a4b30f54655d971eefce494d773aff3f2f85 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 1 Oct 2020 16:13:30 -0700 Subject: [PATCH 1/2] DEPR: automatic alignment on frame.__cmp__(series) --- doc/source/whatsnew/v1.2.0.rst | 1 + pandas/core/ops/__init__.py | 13 +++++++++++++ pandas/tests/arithmetic/test_datetime64.py | 17 +++++++++++++---- pandas/tests/frame/test_arithmetic.py | 8 ++++++-- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 016e8d90e7d21..e86a8cb13d7e5 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -265,6 +265,7 @@ Deprecations - Deprecated indexing :class:`DataFrame` rows with datetime-like strings ``df[string]``, use ``df.loc[string]`` instead (:issue:`36179`) - Deprecated casting an object-dtype index of ``datetime`` objects to :class:`DatetimeIndex` in the :class:`Series` constructor (:issue:`23598`) - Deprecated :meth:`Index.is_all_dates` (:issue:`27744`) +- Deprecated automatic alignment on comparison operations between :class:`DataFrame` and :class:`Series`, do ``frame, ser = frame.align(ser, axis=1, copy=False)`` before e.g. ``frame == ser`` (:issue:`28759`) .. --------------------------------------------------------------------------- diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index 2dc97a3583dfb..b81ebc9cc8724 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -5,6 +5,7 @@ """ import operator from typing import TYPE_CHECKING, Optional, Set, Type +import warnings import numpy as np @@ -513,6 +514,18 @@ def to_series(right): elif isinstance(right, ABCSeries): # axis=1 is default for DataFrame-with-Series op axis = left._get_axis_number(axis) if axis is not None else 1 + + if not flex: + if not left.axes[axis].equals(right.index): + warnings.warn( + "Automatic reindexing on DataFrame vs Series comparisons " + "is deprecated and will be raise ValueError in a future version. " + "Do `left, right = left.align(right, axis=1, copy=False)` " + "before e.g. `left == right`", + FutureWarning, + stacklevel=3, + ) + left, right = left.align( right, join="outer", axis=axis, level=level, copy=False ) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 0dd389ed516c7..c79fe4b8b22f3 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -308,11 +308,16 @@ def test_dt64arr_timestamp_equality(self, box_with_array): expected = tm.box_expected([False, True], xbox) tm.assert_equal(result, expected) - result = ser != ser[0] + warn = FutureWarning if box_with_array is pd.DataFrame else None + with tm.assert_produces_warning(warn): + # alignment for frame vs series comparisons deprecated + result = ser != ser[0] expected = tm.box_expected([False, True], xbox) tm.assert_equal(result, expected) - result = ser != ser[1] + with tm.assert_produces_warning(warn): + # alignment for frame vs series comparisons deprecated + result = ser != ser[1] expected = tm.box_expected([True, True], xbox) tm.assert_equal(result, expected) @@ -320,11 +325,15 @@ def test_dt64arr_timestamp_equality(self, box_with_array): expected = tm.box_expected([True, False], xbox) tm.assert_equal(result, expected) - result = ser == ser[0] + with tm.assert_produces_warning(warn): + # alignment for frame vs series comparisons deprecated + result = ser == ser[0] expected = tm.box_expected([True, False], xbox) tm.assert_equal(result, expected) - result = ser == ser[1] + with tm.assert_produces_warning(warn): + # alignment for frame vs series comparisons deprecated + result = ser == ser[1] expected = tm.box_expected([False, False], xbox) tm.assert_equal(result, expected) diff --git a/pandas/tests/frame/test_arithmetic.py b/pandas/tests/frame/test_arithmetic.py index b3aa5e403e795..d9ef19e174700 100644 --- a/pandas/tests/frame/test_arithmetic.py +++ b/pandas/tests/frame/test_arithmetic.py @@ -795,13 +795,17 @@ def test_frame_with_zero_len_series_corner_cases(): expected = pd.DataFrame(df.values * np.nan, columns=df.columns) tm.assert_frame_equal(result, expected) - result = df == ser + with tm.assert_produces_warning(FutureWarning): + # Automatic alignment for comparisons deprecated + result = df == ser expected = pd.DataFrame(False, index=df.index, columns=df.columns) tm.assert_frame_equal(result, expected) # non-float case should not raise on comparison df2 = pd.DataFrame(df.values.view("M8[ns]"), columns=df.columns) - result = df2 == ser + with tm.assert_produces_warning(FutureWarning): + # Automatic alignment for comparisons deprecated + result = df2 == ser expected = pd.DataFrame(False, index=df.index, columns=df.columns) tm.assert_frame_equal(result, expected) From 7c2bc5a383a178c8502c3956adeae2d848c94c77 Mon Sep 17 00:00:00 2001 From: Brock Date: Thu, 1 Oct 2020 19:27:40 -0700 Subject: [PATCH 2/2] typo fixup --- pandas/core/ops/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index b81ebc9cc8724..49b7aff5af40a 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -519,7 +519,7 @@ def to_series(right): if not left.axes[axis].equals(right.index): warnings.warn( "Automatic reindexing on DataFrame vs Series comparisons " - "is deprecated and will be raise ValueError in a future version. " + "is deprecated and will raise ValueError in a future version. " "Do `left, right = left.align(right, axis=1, copy=False)` " "before e.g. `left == right`", FutureWarning,