From ada7cda9e035d39a4531fed4436e9e1d92cfa284 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 01:47:47 -0600 Subject: [PATCH 01/19] Added FrozenList subtraction --- pandas/indexes/frozen.py | 7 ++++++- pandas/tests/indexes/test_frozen.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index e043ba64bbad7..efcf23e7ec0db 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -28,8 +28,13 @@ def __add__(self, other): if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) - + __iadd__ = __add__ + + def __sub__(self, other): + other = set(other) + temp = [x for x in self if x not in other] + return self.__class__(temp) # Python 2 compat def __getslice__(self, i, j): diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index a82409fbf9513..ac2b94b94d9e0 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -22,7 +22,17 @@ def test_add(self): result = (1, 2, 3) + self.container expected = FrozenList([1, 2, 3] + self.lst) self.check_result(result, expected) - + + def test_sub(self): + result = self.container - [2] + expected = FrozenList([1, 3, 4, 5]) + self.check_result(result, expected) + + def test_sub_dupe(self): + result = FrozenList([1, 2, 3, 2]) - [2] + expected = FrozenList([1, 3]) + self.check_result(result, expected) + def test_inplace(self): q = r = self.container q += [5] From f6381a8823e631de399d8be8d9838392381a4de3 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 14:51:49 -0600 Subject: [PATCH 02/19] Removed whitespace --- pandas/indexes/frozen.py | 4 ++-- pandas/tests/indexes/test_frozen.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index efcf23e7ec0db..d422ef31d490b 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -28,9 +28,9 @@ def __add__(self, other): if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) - + __iadd__ = __add__ - + def __sub__(self, other): other = set(other) temp = [x for x in self if x not in other] diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index ac2b94b94d9e0..5c928b91de2b6 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -22,17 +22,17 @@ def test_add(self): result = (1, 2, 3) + self.container expected = FrozenList([1, 2, 3] + self.lst) self.check_result(result, expected) - + def test_sub(self): result = self.container - [2] expected = FrozenList([1, 3, 4, 5]) - self.check_result(result, expected) - + self.check_result(result, expected) + def test_sub_dupe(self): result = FrozenList([1, 2, 3, 2]) - [2] expected = FrozenList([1, 3]) - self.check_result(result, expected) - + self.check_result(result, expected) + def test_inplace(self): q = r = self.container q += [5] From cd73faaa43708332c6896951667a01866534392f Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 14:57:07 -0600 Subject: [PATCH 03/19] Updated whatsnew to reflect changes --- doc/source/whatsnew/v0.20.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index c94429b469641..cf61f1975f8e9 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -26,6 +26,8 @@ Check the :ref:`API Changes ` and :ref:`deprecations New features ~~~~~~~~~~~~ +- Added subtraction from FrozenLists (:issue:`15475`) + - Integration with the ``feather-format``, including a new top-level ``pd.read_feather()`` and ``DataFrame.to_feather()`` method, see :ref:`here `. - ``.str.replace`` now accepts a callable, as replacement, which is passed to ``re.sub`` (:issue:`15055`) From 2fad2f7cc78a0850fe1d68b4075a3b2bbc8c53e8 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 16:49:23 -0600 Subject: [PATCH 04/19] Added __isub__ and groupby example to docs --- doc/source/groupby.rst | 8 ++++++++ doc/source/whatsnew/v0.20.0.txt | 2 +- pandas/indexes/frozen.py | 2 ++ pandas/tests/indexes/test_frozen.py | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index 8484ccd69a983..4870ce38a846a 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -126,6 +126,14 @@ We could naturally group by either the ``A`` or ``B`` columns or both: grouped = df.groupby('A') grouped = df.groupby(['A', 'B']) +If we also have a MultiIndex on columns ``A`` and ``B``, we can group by all +but the specified columns + +.. ipython:: python + + df.set_index(['A', 'B']) + grouped = df.groupby(level=df.index.names - ['B']) + These will split the DataFrame on its index (rows). We could also split by the columns: diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index cf61f1975f8e9..ffdcc74986f0b 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -26,7 +26,7 @@ Check the :ref:`API Changes ` and :ref:`deprecations New features ~~~~~~~~~~~~ -- Added subtraction from FrozenLists (:issue:`15475`) +- Added difference from FrozenLists (:issue:`15475`) - Integration with the ``feather-format``, including a new top-level ``pd.read_feather()`` and ``DataFrame.to_feather()`` method, see :ref:`here `. - ``.str.replace`` now accepts a callable, as replacement, which is passed to ``re.sub`` (:issue:`15055`) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index d422ef31d490b..c3959643c1d36 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -36,6 +36,8 @@ def __sub__(self, other): temp = [x for x in self if x not in other] return self.__class__(temp) + __isub__ = __sub__ + # Python 2 compat def __getslice__(self, i, j): return self.__class__(super(FrozenList, self).__getslice__(i, j)) diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 5c928b91de2b6..7de730debddc7 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -40,6 +40,13 @@ def test_inplace(self): # other shouldn't be mutated self.check_result(r, self.lst) + def test_inplace_sub(self): + q = r = self.container + q -= [5] + self.check_result(q, self.container - [5]) + # other shouldn't be mutated + self.check_result(r, self.lst) + class TestFrozenNDArray(CheckImmutable, CheckStringMixin, tm.TestCase): mutable_methods = ('put', 'itemset', 'fill') From fdcfbbb3b425ba51e4ebc82b24723f615f6eaa69 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 17:21:36 -0600 Subject: [PATCH 05/19] Added versionadded tag in docs and renamed test_inplace to test_inplace_add for consistency --- doc/source/groupby.rst | 2 ++ pandas/tests/indexes/test_frozen.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index 4870ce38a846a..cf73129810798 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -126,6 +126,8 @@ We could naturally group by either the ``A`` or ``B`` columns or both: grouped = df.groupby('A') grouped = df.groupby(['A', 'B']) +.. versionadded:: 0.20 + If we also have a MultiIndex on columns ``A`` and ``B``, we can group by all but the specified columns diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 7de730debddc7..881ff79e082d8 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -33,7 +33,7 @@ def test_sub_dupe(self): expected = FrozenList([1, 3]) self.check_result(result, expected) - def test_inplace(self): + def test_inplace_add(self): q = r = self.container q += [5] self.check_result(q, self.lst + [5]) From 2e4384911c02969150a770743d8648441bfc8c9c Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 20:47:26 -0600 Subject: [PATCH 06/19] Changed __sub__ to difference --- doc/source/groupby.rst | 2 +- pandas/indexes/frozen.py | 12 +++++------- pandas/tests/indexes/test_frozen.py | 25 +++++++++---------------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index cf73129810798..a5176fd9cb8b2 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -134,7 +134,7 @@ but the specified columns .. ipython:: python df.set_index(['A', 'B']) - grouped = df.groupby(level=df.index.names - ['B']) + grouped = df.groupby(level=df.index.names.difference(['B']) These will split the DataFrame on its index (rows). We could also split by the columns: diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index c3959643c1d36..fa3b26a8318d1 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -31,13 +31,6 @@ def __add__(self, other): __iadd__ = __add__ - def __sub__(self, other): - other = set(other) - temp = [x for x in self if x not in other] - return self.__class__(temp) - - __isub__ = __sub__ - # Python 2 compat def __getslice__(self, i, j): return self.__class__(super(FrozenList, self).__getslice__(i, j)) @@ -87,6 +80,11 @@ def __repr__(self): __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled pop = append = extend = remove = sort = insert = _disabled + def difference(self, other): + other = set(other) + temp = [x for x in self if x not in other] + return self.__class__(temp) + class FrozenNDArray(PandasObject, np.ndarray): diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 881ff79e082d8..e1c5f5fb3b81a 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -23,16 +23,6 @@ def test_add(self): expected = FrozenList([1, 2, 3] + self.lst) self.check_result(result, expected) - def test_sub(self): - result = self.container - [2] - expected = FrozenList([1, 3, 4, 5]) - self.check_result(result, expected) - - def test_sub_dupe(self): - result = FrozenList([1, 2, 3, 2]) - [2] - expected = FrozenList([1, 3]) - self.check_result(result, expected) - def test_inplace_add(self): q = r = self.container q += [5] @@ -40,12 +30,15 @@ def test_inplace_add(self): # other shouldn't be mutated self.check_result(r, self.lst) - def test_inplace_sub(self): - q = r = self.container - q -= [5] - self.check_result(q, self.container - [5]) - # other shouldn't be mutated - self.check_result(r, self.lst) + def test_difference(self): + result = self.container.difference([2]) + expected = FrozenList([1, 3, 4, 5]) + self.check_result(result, expected) + + def test_difference_dupe(self): + result = FrozenList([1, 2, 3, 2]).difference([2]) + expected = FrozenList([1, 3]) + self.check_result(result, expected) class TestFrozenNDArray(CheckImmutable, CheckStringMixin, tm.TestCase): From cb950895e952ac87ff22dc0473773b6bfe3aa098 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 21:35:12 -0600 Subject: [PATCH 07/19] Depricated __add__ in favor of union --- doc/source/whatsnew/v0.20.0.txt | 2 +- pandas/indexes/frozen.py | 8 ++++++++ pandas/tests/indexes/test_frozen.py | 15 ++------------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index ffdcc74986f0b..2e49b186b919e 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -486,7 +486,7 @@ Deprecations - ``DataFrame.astype()`` has deprecated the ``raise_on_error`` parameter in favor of ``errors`` (:issue:`14878`) - ``Series.sortlevel`` and ``DataFrame.sortlevel`` have been deprecated in favor of ``Series.sort_index`` and ``DataFrame.sort_index`` (:issue:`15099`) - importing ``concat`` from ``pandas.tools.merge`` has been deprecated in favor of imports from the ``pandas`` namespace. This should only affect explict imports (:issue:`15358`) - +- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15506`) .. _whatsnew_0200.prior_deprecations: Removal of prior version deprecations/changes diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index fa3b26a8318d1..9da3291d99389 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -13,6 +13,8 @@ from pandas.types.cast import _coerce_indexer_dtype from pandas.formats.printing import pprint_thing +import warnings + class FrozenList(PandasObject, list): @@ -25,6 +27,7 @@ class FrozenList(PandasObject, list): # typechecks def __add__(self, other): + warnings.warn("__add__ is deprecated, use union(...)", FutureWarning) if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) @@ -80,6 +83,11 @@ def __repr__(self): __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled pop = append = extend = remove = sort = insert = _disabled + def union(self, other): + if isinstance(other, tuple): + other = list(other) + return self.__class__(super(FrozenList, self).__add__(other)) + def difference(self, other): other = set(other) temp = [x for x in self if x not in other] diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index e1c5f5fb3b81a..04887d18bda6e 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -14,22 +14,11 @@ def setUp(self): self.container = FrozenList(self.lst) self.klass = FrozenList - def test_add(self): - result = self.container + (1, 2, 3) + def test_union(self): + result = self.container.union((1, 2, 3)) expected = FrozenList(self.lst + [1, 2, 3]) self.check_result(result, expected) - result = (1, 2, 3) + self.container - expected = FrozenList([1, 2, 3] + self.lst) - self.check_result(result, expected) - - def test_inplace_add(self): - q = r = self.container - q += [5] - self.check_result(q, self.lst + [5]) - # other shouldn't be mutated - self.check_result(r, self.lst) - def test_difference(self): result = self.container.difference([2]) expected = FrozenList([1, 3, 4, 5]) From 2ab85cbc6cdf6ab5c29d5727b9ea3f7c7dd3739c Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 21:39:17 -0600 Subject: [PATCH 08/19] Fixed issue number --- doc/source/whatsnew/v0.20.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 2e49b186b919e..49fd4419c9a5d 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -486,7 +486,7 @@ Deprecations - ``DataFrame.astype()`` has deprecated the ``raise_on_error`` parameter in favor of ``errors`` (:issue:`14878`) - ``Series.sortlevel`` and ``DataFrame.sortlevel`` have been deprecated in favor of ``Series.sort_index`` and ``DataFrame.sort_index`` (:issue:`15099`) - importing ``concat`` from ``pandas.tools.merge`` has been deprecated in favor of imports from the ``pandas`` namespace. This should only affect explict imports (:issue:`15358`) -- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15506`) +- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15475`) .. _whatsnew_0200.prior_deprecations: Removal of prior version deprecations/changes From 6a2b48dc0a23b6a88072d0f3f5f2785fdd19f201 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Tue, 28 Feb 2017 19:22:58 -0600 Subject: [PATCH 09/19] Added docstrings, depricated __iadd__, changed __add__ to use self.union() --- doc/source/groupby.rst | 4 ++-- doc/source/whatsnew/v0.20.0.txt | 2 +- pandas/indexes/frozen.py | 11 +++++++---- pandas/tests/indexes/test_frozen.py | 3 +++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index a5176fd9cb8b2..539352db1e623 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -133,8 +133,8 @@ but the specified columns .. ipython:: python - df.set_index(['A', 'B']) - grouped = df.groupby(level=df.index.names.difference(['B']) + df2 = df.set_index(['A', 'B']) + grouped = df2.groupby(level=df2.index.names.difference(['B']) These will split the DataFrame on its index (rows). We could also split by the columns: diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 49fd4419c9a5d..2c6ade8e64e6e 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -26,7 +26,7 @@ Check the :ref:`API Changes ` and :ref:`deprecations New features ~~~~~~~~~~~~ -- Added difference from FrozenLists (:issue:`15475`) +- Added ``.difference()`` method for FrozenLists (:issue:`15475`) - Integration with the ``feather-format``, including a new top-level ``pd.read_feather()`` and ``DataFrame.to_feather()`` method, see :ref:`here `. - ``.str.replace`` now accepts a callable, as replacement, which is passed to ``re.sub`` (:issue:`15055`) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index 9da3291d99389..d8e86dba5b627 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -28,11 +28,11 @@ class FrozenList(PandasObject, list): def __add__(self, other): warnings.warn("__add__ is deprecated, use union(...)", FutureWarning) - if isinstance(other, tuple): - other = list(other) - return self.__class__(super(FrozenList, self).__add__(other)) + return self.union(other) - __iadd__ = __add__ + def __iadd__(self, other): + warnings.warn("__iadd__ is deprecated, use union(...)", FutureWarning) + return self.union(other) # Python 2 compat def __getslice__(self, i, j): @@ -84,11 +84,14 @@ def __repr__(self): pop = append = extend = remove = sort = insert = _disabled def union(self, other): + """Returns a FrozenList with other concatenated to the end of self""" if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) def difference(self, other): + """Returns a FrozenList with the same elements as self, but with elements + that are also in other removed.""" other = set(other) temp = [x for x in self if x not in other] return self.__class__(temp) diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 04887d18bda6e..0248f28c57ba7 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -14,6 +14,9 @@ def setUp(self): self.container = FrozenList(self.lst) self.klass = FrozenList + def test_add(self): + self.assert_produces_warning(FutureWarning) + def test_union(self): result = self.container.union((1, 2, 3)) expected = FrozenList(self.lst + [1, 2, 3]) From 73564abca1c83910c16a47b764e7913427a275fb Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 01:47:47 -0600 Subject: [PATCH 10/19] Added FrozenList subtraction --- pandas/indexes/frozen.py | 7 ++++++- pandas/tests/indexes/test_frozen.py | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index e043ba64bbad7..efcf23e7ec0db 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -28,8 +28,13 @@ def __add__(self, other): if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) - + __iadd__ = __add__ + + def __sub__(self, other): + other = set(other) + temp = [x for x in self if x not in other] + return self.__class__(temp) # Python 2 compat def __getslice__(self, i, j): diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index a82409fbf9513..ac2b94b94d9e0 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -22,7 +22,17 @@ def test_add(self): result = (1, 2, 3) + self.container expected = FrozenList([1, 2, 3] + self.lst) self.check_result(result, expected) - + + def test_sub(self): + result = self.container - [2] + expected = FrozenList([1, 3, 4, 5]) + self.check_result(result, expected) + + def test_sub_dupe(self): + result = FrozenList([1, 2, 3, 2]) - [2] + expected = FrozenList([1, 3]) + self.check_result(result, expected) + def test_inplace(self): q = r = self.container q += [5] From 0fc7e1927e049e558c087977e7fa12b7a3b97ace Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 14:51:49 -0600 Subject: [PATCH 11/19] Removed whitespace --- pandas/indexes/frozen.py | 4 ++-- pandas/tests/indexes/test_frozen.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index efcf23e7ec0db..d422ef31d490b 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -28,9 +28,9 @@ def __add__(self, other): if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) - + __iadd__ = __add__ - + def __sub__(self, other): other = set(other) temp = [x for x in self if x not in other] diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index ac2b94b94d9e0..5c928b91de2b6 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -22,17 +22,17 @@ def test_add(self): result = (1, 2, 3) + self.container expected = FrozenList([1, 2, 3] + self.lst) self.check_result(result, expected) - + def test_sub(self): result = self.container - [2] expected = FrozenList([1, 3, 4, 5]) - self.check_result(result, expected) - + self.check_result(result, expected) + def test_sub_dupe(self): result = FrozenList([1, 2, 3, 2]) - [2] expected = FrozenList([1, 3]) - self.check_result(result, expected) - + self.check_result(result, expected) + def test_inplace(self): q = r = self.container q += [5] From 79dd958a1980d950bf8ebb13e3b4888bc394cef4 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 14:57:07 -0600 Subject: [PATCH 12/19] Updated whatsnew to reflect changes --- doc/source/whatsnew/v0.20.0.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 54df7514a882d..3d224582e5eae 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -26,6 +26,8 @@ Check the :ref:`API Changes ` and :ref:`deprecations New features ~~~~~~~~~~~~ +- Added subtraction from FrozenLists (:issue:`15475`) + - Integration with the ``feather-format``, including a new top-level ``pd.read_feather()`` and ``DataFrame.to_feather()`` method, see :ref:`here `. - ``.str.replace`` now accepts a callable, as replacement, which is passed to ``re.sub`` (:issue:`15055`) From 0ea8d217c4de79ba47bd40b179999764d03166ab Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 16:49:23 -0600 Subject: [PATCH 13/19] Added __isub__ and groupby example to docs --- doc/source/groupby.rst | 8 ++++++++ doc/source/whatsnew/v0.20.0.txt | 2 +- pandas/indexes/frozen.py | 2 ++ pandas/tests/indexes/test_frozen.py | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index 8484ccd69a983..4870ce38a846a 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -126,6 +126,14 @@ We could naturally group by either the ``A`` or ``B`` columns or both: grouped = df.groupby('A') grouped = df.groupby(['A', 'B']) +If we also have a MultiIndex on columns ``A`` and ``B``, we can group by all +but the specified columns + +.. ipython:: python + + df.set_index(['A', 'B']) + grouped = df.groupby(level=df.index.names - ['B']) + These will split the DataFrame on its index (rows). We could also split by the columns: diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 3d224582e5eae..fb3a70608b5bf 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -26,7 +26,7 @@ Check the :ref:`API Changes ` and :ref:`deprecations New features ~~~~~~~~~~~~ -- Added subtraction from FrozenLists (:issue:`15475`) +- Added difference from FrozenLists (:issue:`15475`) - Integration with the ``feather-format``, including a new top-level ``pd.read_feather()`` and ``DataFrame.to_feather()`` method, see :ref:`here `. - ``.str.replace`` now accepts a callable, as replacement, which is passed to ``re.sub`` (:issue:`15055`) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index d422ef31d490b..c3959643c1d36 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -36,6 +36,8 @@ def __sub__(self, other): temp = [x for x in self if x not in other] return self.__class__(temp) + __isub__ = __sub__ + # Python 2 compat def __getslice__(self, i, j): return self.__class__(super(FrozenList, self).__getslice__(i, j)) diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 5c928b91de2b6..7de730debddc7 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -40,6 +40,13 @@ def test_inplace(self): # other shouldn't be mutated self.check_result(r, self.lst) + def test_inplace_sub(self): + q = r = self.container + q -= [5] + self.check_result(q, self.container - [5]) + # other shouldn't be mutated + self.check_result(r, self.lst) + class TestFrozenNDArray(CheckImmutable, CheckStringMixin, tm.TestCase): mutable_methods = ('put', 'itemset', 'fill') From cd7de26487a216537d37b51ebd96b2bb407d5357 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 17:21:36 -0600 Subject: [PATCH 14/19] Added versionadded tag in docs and renamed test_inplace to test_inplace_add for consistency --- doc/source/groupby.rst | 2 ++ pandas/tests/indexes/test_frozen.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index 4870ce38a846a..cf73129810798 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -126,6 +126,8 @@ We could naturally group by either the ``A`` or ``B`` columns or both: grouped = df.groupby('A') grouped = df.groupby(['A', 'B']) +.. versionadded:: 0.20 + If we also have a MultiIndex on columns ``A`` and ``B``, we can group by all but the specified columns diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 7de730debddc7..881ff79e082d8 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -33,7 +33,7 @@ def test_sub_dupe(self): expected = FrozenList([1, 3]) self.check_result(result, expected) - def test_inplace(self): + def test_inplace_add(self): q = r = self.container q += [5] self.check_result(q, self.lst + [5]) From ccd75c7978cf0dce7e10224973663f36d0a9270b Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 20:47:26 -0600 Subject: [PATCH 15/19] Changed __sub__ to difference --- doc/source/groupby.rst | 2 +- pandas/indexes/frozen.py | 12 +++++------- pandas/tests/indexes/test_frozen.py | 25 +++++++++---------------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index cf73129810798..a5176fd9cb8b2 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -134,7 +134,7 @@ but the specified columns .. ipython:: python df.set_index(['A', 'B']) - grouped = df.groupby(level=df.index.names - ['B']) + grouped = df.groupby(level=df.index.names.difference(['B']) These will split the DataFrame on its index (rows). We could also split by the columns: diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index c3959643c1d36..fa3b26a8318d1 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -31,13 +31,6 @@ def __add__(self, other): __iadd__ = __add__ - def __sub__(self, other): - other = set(other) - temp = [x for x in self if x not in other] - return self.__class__(temp) - - __isub__ = __sub__ - # Python 2 compat def __getslice__(self, i, j): return self.__class__(super(FrozenList, self).__getslice__(i, j)) @@ -87,6 +80,11 @@ def __repr__(self): __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled pop = append = extend = remove = sort = insert = _disabled + def difference(self, other): + other = set(other) + temp = [x for x in self if x not in other] + return self.__class__(temp) + class FrozenNDArray(PandasObject, np.ndarray): diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 881ff79e082d8..e1c5f5fb3b81a 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -23,16 +23,6 @@ def test_add(self): expected = FrozenList([1, 2, 3] + self.lst) self.check_result(result, expected) - def test_sub(self): - result = self.container - [2] - expected = FrozenList([1, 3, 4, 5]) - self.check_result(result, expected) - - def test_sub_dupe(self): - result = FrozenList([1, 2, 3, 2]) - [2] - expected = FrozenList([1, 3]) - self.check_result(result, expected) - def test_inplace_add(self): q = r = self.container q += [5] @@ -40,12 +30,15 @@ def test_inplace_add(self): # other shouldn't be mutated self.check_result(r, self.lst) - def test_inplace_sub(self): - q = r = self.container - q -= [5] - self.check_result(q, self.container - [5]) - # other shouldn't be mutated - self.check_result(r, self.lst) + def test_difference(self): + result = self.container.difference([2]) + expected = FrozenList([1, 3, 4, 5]) + self.check_result(result, expected) + + def test_difference_dupe(self): + result = FrozenList([1, 2, 3, 2]).difference([2]) + expected = FrozenList([1, 3]) + self.check_result(result, expected) class TestFrozenNDArray(CheckImmutable, CheckStringMixin, tm.TestCase): From 3d6cee58c67e8a4584260119e3420d71c43f6b6b Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 21:35:12 -0600 Subject: [PATCH 16/19] Depricated __add__ in favor of union --- doc/source/whatsnew/v0.20.0.txt | 1 + pandas/indexes/frozen.py | 8 ++++++++ pandas/tests/indexes/test_frozen.py | 15 ++------------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index fb3a70608b5bf..de5f7e0a588b6 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -536,6 +536,7 @@ Deprecations - ``Series.sortlevel`` and ``DataFrame.sortlevel`` have been deprecated in favor of ``Series.sort_index`` and ``DataFrame.sort_index`` (:issue:`15099`) - importing ``concat`` from ``pandas.tools.merge`` has been deprecated in favor of imports from the ``pandas`` namespace. This should only affect explict imports (:issue:`15358`) - ``Series/DataFrame/Panel.consolidate()`` been deprecated as a public method. (:issue:`15483`) +- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15506`) .. _whatsnew_0200.prior_deprecations: diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index fa3b26a8318d1..9da3291d99389 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -13,6 +13,8 @@ from pandas.types.cast import _coerce_indexer_dtype from pandas.formats.printing import pprint_thing +import warnings + class FrozenList(PandasObject, list): @@ -25,6 +27,7 @@ class FrozenList(PandasObject, list): # typechecks def __add__(self, other): + warnings.warn("__add__ is deprecated, use union(...)", FutureWarning) if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) @@ -80,6 +83,11 @@ def __repr__(self): __setitem__ = __setslice__ = __delitem__ = __delslice__ = _disabled pop = append = extend = remove = sort = insert = _disabled + def union(self, other): + if isinstance(other, tuple): + other = list(other) + return self.__class__(super(FrozenList, self).__add__(other)) + def difference(self, other): other = set(other) temp = [x for x in self if x not in other] diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index e1c5f5fb3b81a..04887d18bda6e 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -14,22 +14,11 @@ def setUp(self): self.container = FrozenList(self.lst) self.klass = FrozenList - def test_add(self): - result = self.container + (1, 2, 3) + def test_union(self): + result = self.container.union((1, 2, 3)) expected = FrozenList(self.lst + [1, 2, 3]) self.check_result(result, expected) - result = (1, 2, 3) + self.container - expected = FrozenList([1, 2, 3] + self.lst) - self.check_result(result, expected) - - def test_inplace_add(self): - q = r = self.container - q += [5] - self.check_result(q, self.lst + [5]) - # other shouldn't be mutated - self.check_result(r, self.lst) - def test_difference(self): result = self.container.difference([2]) expected = FrozenList([1, 3, 4, 5]) From 66b3b91a6ebfb8b20b77de050dbf3fd16966bb70 Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Sat, 25 Feb 2017 21:39:17 -0600 Subject: [PATCH 17/19] Fixed issue number --- doc/source/whatsnew/v0.20.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index de5f7e0a588b6..c98feeb6b13a8 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -536,7 +536,7 @@ Deprecations - ``Series.sortlevel`` and ``DataFrame.sortlevel`` have been deprecated in favor of ``Series.sort_index`` and ``DataFrame.sort_index`` (:issue:`15099`) - importing ``concat`` from ``pandas.tools.merge`` has been deprecated in favor of imports from the ``pandas`` namespace. This should only affect explict imports (:issue:`15358`) - ``Series/DataFrame/Panel.consolidate()`` been deprecated as a public method. (:issue:`15483`) -- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15506`) +- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15475`) .. _whatsnew_0200.prior_deprecations: From 6f6c1409ac715badaeb1bec74a2ed283cd7b9d1c Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Tue, 28 Feb 2017 19:22:58 -0600 Subject: [PATCH 18/19] Added docstrings, depricated __iadd__, changed __add__ to use self.union() --- doc/source/groupby.rst | 4 ++-- doc/source/whatsnew/v0.20.0.txt | 2 +- pandas/indexes/frozen.py | 11 +++++++---- pandas/tests/indexes/test_frozen.py | 3 +++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/doc/source/groupby.rst b/doc/source/groupby.rst index a5176fd9cb8b2..539352db1e623 100644 --- a/doc/source/groupby.rst +++ b/doc/source/groupby.rst @@ -133,8 +133,8 @@ but the specified columns .. ipython:: python - df.set_index(['A', 'B']) - grouped = df.groupby(level=df.index.names.difference(['B']) + df2 = df.set_index(['A', 'B']) + grouped = df2.groupby(level=df2.index.names.difference(['B']) These will split the DataFrame on its index (rows). We could also split by the columns: diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index c98feeb6b13a8..eec85f647b2c6 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -26,7 +26,7 @@ Check the :ref:`API Changes ` and :ref:`deprecations New features ~~~~~~~~~~~~ -- Added difference from FrozenLists (:issue:`15475`) +- Added ``.difference()`` method for FrozenLists (:issue:`15475`) - Integration with the ``feather-format``, including a new top-level ``pd.read_feather()`` and ``DataFrame.to_feather()`` method, see :ref:`here `. - ``.str.replace`` now accepts a callable, as replacement, which is passed to ``re.sub`` (:issue:`15055`) diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index 9da3291d99389..d8e86dba5b627 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -28,11 +28,11 @@ class FrozenList(PandasObject, list): def __add__(self, other): warnings.warn("__add__ is deprecated, use union(...)", FutureWarning) - if isinstance(other, tuple): - other = list(other) - return self.__class__(super(FrozenList, self).__add__(other)) + return self.union(other) - __iadd__ = __add__ + def __iadd__(self, other): + warnings.warn("__iadd__ is deprecated, use union(...)", FutureWarning) + return self.union(other) # Python 2 compat def __getslice__(self, i, j): @@ -84,11 +84,14 @@ def __repr__(self): pop = append = extend = remove = sort = insert = _disabled def union(self, other): + """Returns a FrozenList with other concatenated to the end of self""" if isinstance(other, tuple): other = list(other) return self.__class__(super(FrozenList, self).__add__(other)) def difference(self, other): + """Returns a FrozenList with the same elements as self, but with elements + that are also in other removed.""" other = set(other) temp = [x for x in self if x not in other] return self.__class__(temp) diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 04887d18bda6e..0248f28c57ba7 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -14,6 +14,9 @@ def setUp(self): self.container = FrozenList(self.lst) self.klass = FrozenList + def test_add(self): + self.assert_produces_warning(FutureWarning) + def test_union(self): result = self.container.union((1, 2, 3)) expected = FrozenList(self.lst + [1, 2, 3]) From 428a1b33f5a48bce94dccebb47949193610a749d Mon Sep 17 00:00:00 2001 From: Ben Thayer Date: Wed, 1 Mar 2017 16:23:41 -0600 Subject: [PATCH 19/19] Added __iadd__ test, fixed whatsnew --- doc/source/whatsnew/v0.20.0.txt | 3 ++- pandas/indexes/frozen.py | 4 +++- pandas/tests/indexes/test_frozen.py | 7 +++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index eec85f647b2c6..361bbe584a704 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -536,7 +536,8 @@ Deprecations - ``Series.sortlevel`` and ``DataFrame.sortlevel`` have been deprecated in favor of ``Series.sort_index`` and ``DataFrame.sort_index`` (:issue:`15099`) - importing ``concat`` from ``pandas.tools.merge`` has been deprecated in favor of imports from the ``pandas`` namespace. This should only affect explict imports (:issue:`15358`) - ``Series/DataFrame/Panel.consolidate()`` been deprecated as a public method. (:issue:`15483`) -- ``FrozenList.__add__`` has been depricated in favor of ``FrozenList.union`` (:issue: `15475`) +- ``FrozenList.__add__`` has been depricated. (:issue: `15475`) +- ``FrozenList.__iadd__`` has been depricated. (:issue: `15475`) .. _whatsnew_0200.prior_deprecations: diff --git a/pandas/indexes/frozen.py b/pandas/indexes/frozen.py index d8e86dba5b627..47e2557333ec7 100644 --- a/pandas/indexes/frozen.py +++ b/pandas/indexes/frozen.py @@ -32,7 +32,9 @@ def __add__(self, other): def __iadd__(self, other): warnings.warn("__iadd__ is deprecated, use union(...)", FutureWarning) - return self.union(other) + if isinstance(other, tuple): + other = list(other) + return super(FrozenList, self).__iadd__(other) # Python 2 compat def __getslice__(self, i, j): diff --git a/pandas/tests/indexes/test_frozen.py b/pandas/tests/indexes/test_frozen.py index 0248f28c57ba7..b583dc7e42d62 100644 --- a/pandas/tests/indexes/test_frozen.py +++ b/pandas/tests/indexes/test_frozen.py @@ -17,6 +17,13 @@ def setUp(self): def test_add(self): self.assert_produces_warning(FutureWarning) + def test_iadd(self): + q = q1 = [1, 2, 3, 4, 5] + q = FrozenList(q) + q += [1, 2, 3] + expected = FrozenList(q1 + [1, 2, 3]) + self.check_result(q, expected) + def test_union(self): result = self.container.union((1, 2, 3)) expected = FrozenList(self.lst + [1, 2, 3])