From c66c0fea6ab002d007403b788c8c42b78260d745 Mon Sep 17 00:00:00 2001 From: Derek Sharp Date: Thu, 2 Jun 2022 00:02:00 -0400 Subject: [PATCH 1/2] ENH: Move SettingWithCopyWarning to error/__init__.py per GH27656 --- doc/source/reference/testing.rst | 1 + doc/source/whatsnew/v1.5.0.rst | 2 +- pandas/core/common.py | 4 --- pandas/core/generic.py | 3 ++- pandas/errors/__init__.py | 20 ++++++++++++++ pandas/tests/copy_view/test_indexing.py | 27 ++++++++++--------- .../indexing/test_chaining_and_caching.py | 12 +++++---- pandas/tests/test_errors.py | 1 + 8 files changed, 46 insertions(+), 24 deletions(-) diff --git a/doc/source/reference/testing.rst b/doc/source/reference/testing.rst index e80aa0ace6bc2..0e10b8792eaba 100644 --- a/doc/source/reference/testing.rst +++ b/doc/source/reference/testing.rst @@ -42,6 +42,7 @@ Exceptions and warnings errors.ParserWarning errors.PerformanceWarning errors.SettingWithCopyError + errors.SettingWithCopyWarning errors.SpecificationError errors.UnsortedIndexError errors.UnsupportedFunctionCall diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index 61848cb127029..f1ea5ffe58ef4 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -151,7 +151,7 @@ Other enhancements - A :class:`errors.PerformanceWarning` is now thrown when using ``string[pyarrow]`` dtype with methods that don't dispatch to ``pyarrow.compute`` methods (:issue:`42613`) - Added ``numeric_only`` argument to :meth:`Resampler.sum`, :meth:`Resampler.prod`, :meth:`Resampler.min`, :meth:`Resampler.max`, :meth:`Resampler.first`, and :meth:`Resampler.last` (:issue:`46442`) - ``times`` argument in :class:`.ExponentialMovingWindow` now accepts ``np.timedelta64`` (:issue:`47003`) -- :class:`DataError`, :class:`SpecificationError`, and :class:`SettingWithCopyError` are now exposed in ``pandas.errors`` (:issue:`27656`) +- :class:`DataError`, :class:`SpecificationError`, :class:`SettingWithCopyError`, and :class:`SettingWithCopyWarning` are now exposed in ``pandas.errors`` (:issue:`27656`) .. --------------------------------------------------------------------------- .. _whatsnew_150.notable_bug_fixes: diff --git a/pandas/core/common.py b/pandas/core/common.py index 05eb101dabb98..7225b26a910dd 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -58,10 +58,6 @@ from pandas import Index -class SettingWithCopyWarning(Warning): - pass - - def flatten(line): """ Flatten an arbitrarily nested sequence. diff --git a/pandas/core/generic.py b/pandas/core/generic.py index d5b55a89f28df..7330696e431be 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -70,6 +70,7 @@ AbstractMethodError, InvalidIndexError, SettingWithCopyError, + SettingWithCopyWarning, ) from pandas.util._decorators import ( deprecate_kwarg, @@ -3952,7 +3953,7 @@ def _check_setitem_copy(self, t="setting", force=False): if value == "raise": raise SettingWithCopyError(t) elif value == "warn": - warnings.warn(t, com.SettingWithCopyWarning, stacklevel=find_stack_level()) + warnings.warn(t, SettingWithCopyWarning, stacklevel=find_stack_level()) def __delitem__(self, key) -> None: """ diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index b2a1309d49be3..73e31e54e4259 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -288,3 +288,23 @@ class SettingWithCopyError(ValueError): >>> df.loc[0:3]['A'] = 'a' # doctest: +SKIP ... # SettingWithCopyError: A value is trying to be set on a copy of a... """ + + +class SettingWithCopyWarning(Warning): + """ + Warning is raised when trying to set on a copied slice from a dataframe and + the mode.chained_assignment is set to 'warn.' 'Warn' is the default option. + This can happen unintentionally when chained indexing. + + For more information, see 'Evaluation order matters' on + https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html + + For more information, see 'Indexing view versus copy' on + https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html + + Examples + -------- + >>> df = pd.DataFrame({'A': [1, 1, 1, 2, 2]}, columns=['A']) + >>> df.loc[0:3]['A'] = 'a' # doctest: +SKIP + ... # SettingWithCopyWarning: A value is trying to be set on a copy of a... + """ diff --git a/pandas/tests/copy_view/test_indexing.py b/pandas/tests/copy_view/test_indexing.py index 16cd72cc1cb06..6eb7237c4f41c 100644 --- a/pandas/tests/copy_view/test_indexing.py +++ b/pandas/tests/copy_view/test_indexing.py @@ -1,13 +1,14 @@ import numpy as np import pytest +from pandas.errors import SettingWithCopyWarning + import pandas as pd from pandas import ( DataFrame, Series, ) import pandas._testing as tm -import pandas.core.common as com from pandas.tests.copy_view.util import get_array # ----------------------------------------------------------------------------- @@ -31,7 +32,7 @@ def test_subset_column_selection(using_copy_on_write): assert not np.shares_memory(get_array(subset, "a"), get_array(df, "a")) # INFO this no longer raise warning since pandas 1.4 # with pd.option_context("chained_assignment", "warn"): - # with tm.assert_produces_warning(com.SettingWithCopyWarning): + # with tm.assert_produces_warning(SettingWithCopyWarning): subset.iloc[0, 0] = 0 assert not np.shares_memory(get_array(subset, "a"), get_array(df, "a")) @@ -80,7 +81,7 @@ def test_subset_row_slice(using_copy_on_write): else: # INFO this no longer raise warning since pandas 1.4 # with pd.option_context("chained_assignment", "warn"): - # with tm.assert_produces_warning(com.SettingWithCopyWarning): + # with tm.assert_produces_warning(SettingWithCopyWarning): subset.iloc[0, 0] = 0 subset._mgr._verify_integrity() @@ -119,7 +120,7 @@ def test_subset_column_slice(using_copy_on_write, using_array_manager, dtype): else: # we only get a warning in case of a single block - warn = com.SettingWithCopyWarning if single_block else None + warn = SettingWithCopyWarning if single_block else None with pd.option_context("chained_assignment", "warn"): with tm.assert_produces_warning(warn): subset.iloc[0, 0] = 0 @@ -252,7 +253,7 @@ def test_subset_set_with_row_indexer(indexer_si, indexer, using_copy_on_write): indexer_si(subset)[indexer] = 0 else: # INFO iloc no longer raises warning since pandas 1.4 - warn = com.SettingWithCopyWarning if indexer_si is tm.setitem else None + warn = SettingWithCopyWarning if indexer_si is tm.setitem else None with pd.option_context("chained_assignment", "warn"): with tm.assert_produces_warning(warn): indexer_si(subset)[indexer] = 0 @@ -282,7 +283,7 @@ def test_subset_set_with_mask(using_copy_on_write): subset[mask] = 0 else: with pd.option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): subset[mask] = 0 expected = DataFrame( @@ -309,7 +310,7 @@ def test_subset_set_column(using_copy_on_write): subset["a"] = np.array([10, 11], dtype="int64") else: with pd.option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): subset["a"] = np.array([10, 11], dtype="int64") subset._mgr._verify_integrity() @@ -340,7 +341,7 @@ def test_subset_set_column_with_loc(using_copy_on_write, using_array_manager, dt # warnings and only assert the SettingWithCopyWarning raise_on_extra_warnings = False if using_array_manager else True with tm.assert_produces_warning( - com.SettingWithCopyWarning, + SettingWithCopyWarning, raise_on_extra_warnings=raise_on_extra_warnings, ): subset.loc[:, "a"] = np.array([10, 11], dtype="int64") @@ -377,7 +378,7 @@ def test_subset_set_column_with_loc2(using_copy_on_write, using_array_manager): # warnings and only assert the SettingWithCopyWarning raise_on_extra_warnings = False if using_array_manager else True with tm.assert_produces_warning( - com.SettingWithCopyWarning, + SettingWithCopyWarning, raise_on_extra_warnings=raise_on_extra_warnings, ): subset.loc[:, "a"] = 0 @@ -410,7 +411,7 @@ def test_subset_set_columns(using_copy_on_write, dtype): subset[["a", "c"]] = 0 else: with pd.option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): subset[["a", "c"]] = 0 subset._mgr._verify_integrity() @@ -443,7 +444,7 @@ def test_subset_set_with_column_indexer( # The (i)loc[:, col] inplace deprecation gets triggered here, ignore those # warnings and only assert the SettingWithCopyWarning with tm.assert_produces_warning( - com.SettingWithCopyWarning, raise_on_extra_warnings=False + SettingWithCopyWarning, raise_on_extra_warnings=False ): subset.loc[:, indexer] = 0 @@ -580,7 +581,7 @@ def test_column_as_series(using_copy_on_write, using_array_manager): s[0] = 0 else: with pd.option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): s[0] = 0 expected = Series([0, 2, 3], name="a") @@ -607,7 +608,7 @@ def test_column_as_series_set_with_upcast(using_copy_on_write, using_array_manag s[0] = "foo" else: with pd.option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): s[0] = "foo" expected = Series(["foo", 2, 3], dtype=object, name="a") diff --git a/pandas/tests/indexing/test_chaining_and_caching.py b/pandas/tests/indexing/test_chaining_and_caching.py index c6b36b851a838..47f929d87bd6f 100644 --- a/pandas/tests/indexing/test_chaining_and_caching.py +++ b/pandas/tests/indexing/test_chaining_and_caching.py @@ -3,7 +3,10 @@ import numpy as np import pytest -from pandas.errors import SettingWithCopyError +from pandas.errors import ( + SettingWithCopyError, + SettingWithCopyWarning, +) import pandas.util._test_decorators as td import pandas as pd @@ -15,7 +18,6 @@ option_context, ) import pandas._testing as tm -import pandas.core.common as com msg = "A value is trying to be set on a copy of a slice from a DataFrame" @@ -415,7 +417,7 @@ def test_setting_with_copy_bug_no_warning(self): def test_detect_chained_assignment_warnings_errors(self): df = DataFrame({"A": ["aaa", "bbb", "ccc"], "B": [1, 2, 3]}) with option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): df.loc[0]["A"] = 111 with option_context("chained_assignment", "raise"): @@ -427,7 +429,7 @@ def test_detect_chained_assignment_warnings_filter_and_dupe_cols(self): with option_context("chained_assignment", "warn"): df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, -9]], columns=["a", "a", "c"]) - with tm.assert_produces_warning(com.SettingWithCopyWarning): + with tm.assert_produces_warning(SettingWithCopyWarning): df.c.loc[df.c > 0] = None expected = DataFrame( @@ -441,7 +443,7 @@ def test_detect_chained_assignment_warning_stacklevel(self, rhs): df = DataFrame(np.arange(25).reshape(5, 5)) chained = df.loc[:3] with option_context("chained_assignment", "warn"): - with tm.assert_produces_warning(com.SettingWithCopyWarning) as t: + with tm.assert_produces_warning(SettingWithCopyWarning) as t: chained[2] = rhs assert t[0].filename == __file__ diff --git a/pandas/tests/test_errors.py b/pandas/tests/test_errors.py index cc6421c42c996..4cc40abb1b021 100644 --- a/pandas/tests/test_errors.py +++ b/pandas/tests/test_errors.py @@ -22,6 +22,7 @@ "DataError", "SpecificationError", "SettingWithCopyError", + "SettingWithCopyWarning", ], ) def test_exception_importable(exc): From be8316b0b1fc8db43eb4e22463b86b4ac5ebabf9 Mon Sep 17 00:00:00 2001 From: Derek Sharp Date: Fri, 3 Jun 2022 14:42:41 -0400 Subject: [PATCH 2/2] ENH: change links to sphinx ref --- pandas/errors/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index 73e31e54e4259..8a5840d0e5b3c 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -275,11 +275,11 @@ class SettingWithCopyError(ValueError): the mode.chained_assignment is set to 'raise.' This can happen unintentionally when chained indexing. - For more information, see 'Evaluation order matters' on - https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html + For more information on eveluation order, + see :ref:`the user guide`. - For more information, see 'Indexing view versus copy' on - https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html + For more information on view vs. copy, + see :ref:`the user guide`. Examples -------- @@ -296,11 +296,11 @@ class SettingWithCopyWarning(Warning): the mode.chained_assignment is set to 'warn.' 'Warn' is the default option. This can happen unintentionally when chained indexing. - For more information, see 'Evaluation order matters' on - https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html + For more information on eveluation order, + see :ref:`the user guide`. - For more information, see 'Indexing view versus copy' on - https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html + For more information on view vs. copy, + see :ref:`the user guide`. Examples --------