From dbcc655b46d4992f0f0e272268c90478fd392362 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Wed, 31 Aug 2016 12:29:42 +0200 Subject: [PATCH] API/DEPR: Remove +/- as setops for Index (GH8227) --- doc/source/whatsnew/v0.19.0.txt | 37 ++++++++++++++++++++++++++++++ pandas/indexes/base.py | 25 ++++++-------------- pandas/indexes/category.py | 2 +- pandas/indexes/multi.py | 1 + pandas/tests/indexes/test_base.py | 24 +++++++++++-------- pandas/tests/indexes/test_multi.py | 17 ++++++++------ 6 files changed, 70 insertions(+), 36 deletions(-) diff --git a/doc/source/whatsnew/v0.19.0.txt b/doc/source/whatsnew/v0.19.0.txt index a422e667e32a7..fd9446cc45c08 100644 --- a/doc/source/whatsnew/v0.19.0.txt +++ b/doc/source/whatsnew/v0.19.0.txt @@ -919,6 +919,43 @@ of ``int64`` (:issue:`13988`) pi = pd.PeriodIndex(['2011-01', '2011-02'], freq='M') pi.values + +.. _whatsnew_0190.api.setops: + +Index ``+`` / ``-`` no longer used for set operations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Addition and subtraction of the base Index type (not the numeric subclasses) +previously performed set operations (set union and difference). This +behaviour was already deprecated since 0.15.0 (in favor using the specific +``.union()`` and ``.difference()`` methods), and is now disabled. When +possible, ``+`` and ``-`` are now used for element-wise operations, for +example for concatenating strings (:issue:`8227`, :issue:`14127`). + +Previous Behavior: + +.. code-block:: ipython + + In [1]: pd.Index(['a', 'b']) + pd.Index(['a', 'c']) + FutureWarning: using '+' to provide set union with Indexes is deprecated, use '|' or .union() + Out[1]: Index(['a', 'b', 'c'], dtype='object') + +The same operation will now perform element-wise addition: + +.. ipython:: python + + pd.Index(['a', 'b']) + pd.Index(['a', 'c']) + +Note that numeric Index objects already performed element-wise operations. +For example, the behaviour of adding two integer Indexes: + +.. ipython:: python + + pd.Index([1, 2, 3]) + pd.Index([2, 3, 4]) + +is unchanged. The base ``Index`` is now made consistent with this behaviour. + + .. _whatsnew_0190.api.difference: ``Index.difference`` and ``.symmetric_difference`` changes diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py index dac0e650cb923..d4ca18a6713b5 100644 --- a/pandas/indexes/base.py +++ b/pandas/indexes/base.py @@ -1739,28 +1739,16 @@ def argsort(self, *args, **kwargs): return result.argsort(*args, **kwargs) def __add__(self, other): - if is_list_like(other): - warnings.warn("using '+' to provide set union with Indexes is " - "deprecated, use '|' or .union()", FutureWarning, - stacklevel=2) - if isinstance(other, Index): - return self.union(other) return Index(np.array(self) + other) def __radd__(self, other): - if is_list_like(other): - warnings.warn("using '+' to provide set union with Indexes is " - "deprecated, use '|' or .union()", FutureWarning, - stacklevel=2) return Index(other + np.array(self)) __iadd__ = __add__ def __sub__(self, other): - warnings.warn("using '-' to provide set differences with Indexes is " - "deprecated, use .difference()", FutureWarning, - stacklevel=2) - return self.difference(other) + raise TypeError("cannot perform __sub__ with this index type: " + "{typ}".format(typ=type(self))) def __and__(self, other): return self.intersection(other) @@ -1990,7 +1978,8 @@ def symmetric_difference(self, other, result_name=None): ----- ``symmetric_difference`` contains elements that appear in either ``idx1`` or ``idx2`` but not both. Equivalent to the Index created by - ``(idx1 - idx2) + (idx2 - idx1)`` with duplicates dropped. + ``idx1.difference(idx2) | idx2.difference(idx1)`` with duplicates + dropped. Examples -------- @@ -3333,8 +3322,8 @@ def _evaluate_compare(self, other): cls.__ge__ = _make_compare(operator.ge) @classmethod - def _add_numericlike_set_methods_disabled(cls): - """ add in the numeric set-like methods to disable """ + def _add_numeric_methods_add_sub_disabled(cls): + """ add in the numeric add/sub methods to disable """ def _make_invalid_op(name): def invalid_op(self, other=None): @@ -3349,7 +3338,7 @@ def invalid_op(self, other=None): @classmethod def _add_numeric_methods_disabled(cls): - """ add in numeric methods to disable """ + """ add in numeric methods to disable other than add/sub """ def _make_invalid_op(name): def invalid_op(self, other=None): diff --git a/pandas/indexes/category.py b/pandas/indexes/category.py index d4fc746c652ca..c1f5d47e1e04f 100644 --- a/pandas/indexes/category.py +++ b/pandas/indexes/category.py @@ -649,7 +649,7 @@ def _add_accessors(cls): typ='method', overwrite=True) -CategoricalIndex._add_numericlike_set_methods_disabled() +CategoricalIndex._add_numeric_methods_add_sub_disabled() CategoricalIndex._add_numeric_methods_disabled() CategoricalIndex._add_logical_methods_disabled() CategoricalIndex._add_comparison_methods() diff --git a/pandas/indexes/multi.py b/pandas/indexes/multi.py index f42410fcdf098..09c755b2c9792 100644 --- a/pandas/indexes/multi.py +++ b/pandas/indexes/multi.py @@ -2219,6 +2219,7 @@ def isin(self, values, level=None): MultiIndex._add_numeric_methods_disabled() +MultiIndex._add_numeric_methods_add_sub_disabled() MultiIndex._add_logical_methods_disabled() diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 0ef7e6bf3be97..7f68318d4d7d3 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -730,16 +730,6 @@ def test_union(self): expected = Index(list('ab'), name='A') tm.assert_index_equal(union, expected) - def test_add(self): - - # - API change GH 8226 - with tm.assert_produces_warning(): - self.strIndex + self.strIndex - with tm.assert_produces_warning(): - self.strIndex + self.strIndex.tolist() - with tm.assert_produces_warning(): - self.strIndex.tolist() + self.strIndex - with tm.assert_produces_warning(RuntimeWarning): firstCat = self.strIndex.union(self.dateIndex) secondCat = self.strIndex.union(self.strIndex) @@ -755,6 +745,13 @@ def test_add(self): tm.assert_contains_all(self.strIndex, secondCat) tm.assert_contains_all(self.dateIndex, firstCat) + def test_add(self): + idx = self.strIndex + expected = Index(self.strIndex.values * 2) + self.assert_index_equal(idx + idx, expected) + self.assert_index_equal(idx + idx.tolist(), expected) + self.assert_index_equal(idx.tolist() + idx, expected) + # test add and radd idx = Index(list('abc')) expected = Index(['a1', 'b1', 'c1']) @@ -762,6 +759,13 @@ def test_add(self): expected = Index(['1a', '1b', '1c']) self.assert_index_equal('1' + idx, expected) + def test_sub(self): + idx = self.strIndex + self.assertRaises(TypeError, lambda: idx - 'a') + self.assertRaises(TypeError, lambda: idx - idx) + self.assertRaises(TypeError, lambda: idx - idx.tolist()) + self.assertRaises(TypeError, lambda: idx.tolist() - idx) + def test_append_multiple(self): index = Index(['a', 'b', 'c', 'd', 'e', 'f']) diff --git a/pandas/tests/indexes/test_multi.py b/pandas/tests/indexes/test_multi.py index 25de6c5091853..5248f0775d22f 100644 --- a/pandas/tests/indexes/test_multi.py +++ b/pandas/tests/indexes/test_multi.py @@ -1408,21 +1408,24 @@ def test_intersection(self): # result = self.index & tuples # self.assertTrue(result.equals(tuples)) - def test_difference(self): + def test_sub(self): first = self.index - result = first.difference(self.index[-3:]) - # - API change GH 8226 - with tm.assert_produces_warning(): + # - now raises (previously was set op difference) + with tm.assertRaises(TypeError): first - self.index[-3:] - with tm.assert_produces_warning(): + with tm.assertRaises(TypeError): self.index[-3:] - first - with tm.assert_produces_warning(): + with tm.assertRaises(TypeError): self.index[-3:] - first.tolist() + with tm.assertRaises(TypeError): + first.tolist() - self.index[-3:] - self.assertRaises(TypeError, lambda: first.tolist() - self.index[-3:]) + def test_difference(self): + first = self.index + result = first.difference(self.index[-3:]) expected = MultiIndex.from_tuples(sorted(self.index[:-3].values), sortorder=0, names=self.index.names)