From 8bbe838b3394cf18a37a6f81d9bcf9892e3251c8 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 02:21:54 +0200 Subject: [PATCH 01/19] BUG - Allow a mapper of type collections.abc.Mapping when using Series.map --- pandas/core/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index 064a51bf0ce74..870e039c6cdd4 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -2,6 +2,7 @@ Base and utility classes for pandas objects. """ import builtins +from collections import OrderedDict, abc import textwrap from typing import Dict, FrozenSet, List, Optional @@ -1107,7 +1108,7 @@ def _map_values(self, mapper, na_action=None): # we can fastpath dict/Series to an efficient map # as we know that we are not going to have to yield # python types - if isinstance(mapper, dict): + if isinstance(mapper, abc.Mapping): if hasattr(mapper, "__missing__"): # If a dictionary subclass defines a default value method, # convert mapper to a lookup function (GH #15999). From a5be9259eb0ea8a4cb34048b194878841c37d9b9 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 03:04:51 +0200 Subject: [PATCH 02/19] BUG - Fixed Series.map to work with no-missing ABC Mapping --- pandas/core/series.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index aa5af9bb893fa..6e8e392a67044 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4,7 +4,8 @@ from io import StringIO from shutil import get_terminal_size from textwrap import dedent -from typing import IO, Any, Callable, Hashable, List, Optional +from typing import Any, Callable, Hashable, List, Optional +from collections import abc import warnings import numpy as np @@ -250,7 +251,7 @@ def __init__( else: data = data.reindex(index, copy=copy) data = data._data - elif isinstance(data, dict): + elif isinstance(data, abc.Mapping): data, index = self._init_dict(data, index, dtype) dtype = None copy = False From e9d772aa5bde8975d6bca31f594f49858fdda813 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 03:05:33 +0200 Subject: [PATCH 03/19] TST - Added a test for Series.map with dict-like (abc.Mapping) mapper --- pandas/tests/series/test_apply.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 30b8b5c7c8545..634664665b2fd 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -1,4 +1,4 @@ -from collections import Counter, defaultdict +from collections import Counter, OrderedDict, defaultdict, abc from itertools import chain import numpy as np @@ -627,6 +627,26 @@ class DictWithoutMissing(dict): expected = Series([np.nan, np.nan, "three"]) tm.assert_series_equal(result, expected) + def test_map_abc_mapping(self): + class NonDictMapping(abc.Mapping): + def __init__(self): + self._data = {3: "three"} + + def __getitem__(self, key): + return self._data.__getitem__(key) + + def __iter__(self): + return self._data.__iter__() + + def __len__(self): + return self._data.__len__() + + s = Series([1, 2, 3]) + not_a_dictionary = NonDictMapping() + result = s.map(not_a_dictionary) + expected = Series([np.nan, np.nan, "three"]) + tm.assert_series_equal(result, expected) + def test_map_box(self): vals = [pd.Timestamp("2011-01-01"), pd.Timestamp("2011-01-02")] s = pd.Series(vals) From 1aeb197ce8725fcc5d611748475d9ddb3769f49e Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 09:56:25 +0200 Subject: [PATCH 04/19] BUG - In Series.map, only look for __missing__ if the mapper is a dict subclass. TST - Add a test for the aforementioned bug --- pandas/core/base.py | 2 +- pandas/tests/series/test_apply.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index 870e039c6cdd4..bcd045fdb8d4c 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -1109,7 +1109,7 @@ def _map_values(self, mapper, na_action=None): # as we know that we are not going to have to yield # python types if isinstance(mapper, abc.Mapping): - if hasattr(mapper, "__missing__"): + if isinstance(mapper, dict) and hasattr(mapper, "__missing__"): # If a dictionary subclass defines a default value method, # convert mapper to a lookup function (GH #15999). dict_with_default = mapper diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 634664665b2fd..0fd809cbee625 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -647,6 +647,31 @@ def __len__(self): expected = Series([np.nan, np.nan, "three"]) tm.assert_series_equal(result, expected) + def test_map_abc_mapping_with_missing(self): + class NonDictMappingWithMissing(abc.Mapping): + def __init__(self): + self._data = {3: "three"} + + def __getitem__(self, key): + return self._data.__getitem__(key) + + def __iter__(self): + return self._data.__iter__() + + def __len__(self): + return self._data.__len__() + + def __missing__(self, key): + return "missing" + + s = Series([1, 2, 3]) + not_a_dictionary = NonDictMappingWithMissing() + result = s.map(not_a_dictionary) + # __missing__ is a dict concept, not a Mapping concept, + # so it should not change the result! + expected = Series([np.nan, np.nan, "three"]) + tm.assert_series_equal(result, expected) + def test_map_box(self): vals = [pd.Timestamp("2011-01-01"), pd.Timestamp("2011-01-02")] s = pd.Series(vals) From e866095f0a4a0971bc2a12f6534e0ac759c902b3 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 10:11:59 +0200 Subject: [PATCH 05/19] CLN - Add a newline for readability --- pandas/core/series.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/core/series.py b/pandas/core/series.py index 6e8e392a67044..90439c130eeef 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -183,6 +183,7 @@ class Series(base.IndexOpsMixin, generic.NDFrame): def __init__( self, data=None, index=None, dtype=None, name=None, copy=False, fastpath=False ): + # we are called internally, so short-circuit if fastpath: From 3c00d57def27a181e174122f12df5d2364586579 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 10:22:45 +0200 Subject: [PATCH 06/19] CLN - Import sorting fix --- pandas/core/series.py | 2 +- pandas/tests/series/test_apply.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 90439c130eeef..d83fe0300c5cb 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1,11 +1,11 @@ """ Data structure for 1-dimensional cross-sectional and time series data """ +from collections import abc from io import StringIO from shutil import get_terminal_size from textwrap import dedent from typing import Any, Callable, Hashable, List, Optional -from collections import abc import warnings import numpy as np diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 0fd809cbee625..9067480339070 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -1,4 +1,4 @@ -from collections import Counter, OrderedDict, defaultdict, abc +from collections import Counter, OrderedDict, abc, defaultdict from itertools import chain import numpy as np From 64df51303d981b8478a95c964ed16e9cad9543f4 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 14:41:28 +0200 Subject: [PATCH 07/19] DOC - Added collections.abc.Mapping to arg doc of Series.map DOC - Moved whatsnew of the aforementioned change from BUG to ENH --- doc/example.feather | Bin 0 -> 1120 bytes doc/source/whatsnew/v1.0.0.rst | 1 + pandas/core/series.py | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 doc/example.feather diff --git a/doc/example.feather b/doc/example.feather new file mode 100644 index 0000000000000000000000000000000000000000..38dbf087a1590606ae40aaab16fd2d592b771b0e GIT binary patch literal 1120 zcmZuxyGlbr5S^R5Nz{a}NW>HtK_sM7>|!IaNGdxCSXd;{_yCQdR#tw3l~`I>OA5ci zQbZ6$tgQTs#B+9c!YinJCM9>)^273K{XQhooJB2<^&GtHH$(bNO86J_M!J zyGzt-fVTu_y`vB(l0%(5PA9RI8omTJPF`^*F6IUYC9RG~2~6L$#r{j48ZpBzkgR*? zeK=XyxVoIL>-SgCJxtfJc!iX{qHf5i{ID};{XtI7TL*ORLEuwa*BRfN-m;sO{Zgj$ ztzwsCRRGO(Kw^-p-}PKGd}}=UlwnHzzIVB^>*KSHVAFV==PAS|XXz*6tt_YQ$5f~C oX+L~3>;6heD7rV}T*>^s5KCq8YfagILHpBwHUBLPZTxTf15K=C=l}o! literal 0 HcmV?d00001 diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index efea1fc1f525f..6a299bc415f49 100755 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -223,6 +223,7 @@ Other enhancements - :meth:`DataFrame.to_markdown` and :meth:`Series.to_markdown` added (:issue:`11052`) - :meth:`DataFrame.drop_duplicates` has gained ``ignore_index`` keyword to reset index (:issue:`30114`) - Added new writer for exporting Stata dta files in version 118, ``StataWriter118``. This format supports exporting strings containing Unicode characters (:issue:`23573`) +- :meth:`Series.map` now accepts ``collections.abc.Mapping`` subclasses as a mapper (:issue:`29733`) Build Changes ^^^^^^^^^^^^^ diff --git a/pandas/core/series.py b/pandas/core/series.py index d83fe0300c5cb..20e9af0d63b2a 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3515,7 +3515,7 @@ def map(self, arg, na_action=None): Parameters ---------- - arg : function, dict, or Series + arg : function, dict, colletions.abc.Mapping subclass or Series Mapping correspondence. na_action : {None, 'ignore'}, default None If 'ignore', propagate NaN values, without passing them to the From d823d5da034e87dca9fcb06fd570f8797f8e670f Mon Sep 17 00:00:00 2001 From: ohad83 Date: Fri, 22 Nov 2019 15:18:10 +0200 Subject: [PATCH 08/19] DOC - Removed superfluous 'dict' from Series.map argument description DOC - Added issue number for tests of Series.map with abc.Mapping argument --- pandas/core/series.py | 2 +- pandas/tests/series/test_apply.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 20e9af0d63b2a..a476a4eccc3d7 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3515,7 +3515,7 @@ def map(self, arg, na_action=None): Parameters ---------- - arg : function, dict, colletions.abc.Mapping subclass or Series + arg : function, colletions.abc.Mapping subclass or Series Mapping correspondence. na_action : {None, 'ignore'}, default None If 'ignore', propagate NaN values, without passing them to the diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 9067480339070..6d710486aee42 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -628,6 +628,8 @@ class DictWithoutMissing(dict): tm.assert_series_equal(result, expected) def test_map_abc_mapping(self): + # https://github.com/pandas-dev/pandas/issues/29733 + # Check collections.abc.Mapping support as mapper for Series.map class NonDictMapping(abc.Mapping): def __init__(self): self._data = {3: "three"} @@ -648,6 +650,8 @@ def __len__(self): tm.assert_series_equal(result, expected) def test_map_abc_mapping_with_missing(self): + # https://github.com/pandas-dev/pandas/issues/29733 + # Check collections.abc.Mapping support as mapper for Series.map class NonDictMappingWithMissing(abc.Mapping): def __init__(self): self._data = {3: "three"} From fa839601018a6cddb4fbc8849cff100549b21a57 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Sat, 23 Nov 2019 11:40:12 +0200 Subject: [PATCH 09/19] TST - Moved NonDictMapping test util class to pandas/util/testing.py TST - Added a test of constructing a series from NonDictMapping --- pandas/tests/series/test_apply.py | 33 +++--------------------- pandas/tests/series/test_constructors.py | 8 ++++++ pandas/util/testing.py | 16 +++++++++++- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 6d710486aee42..c65d99a1bcbb4 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -1,4 +1,4 @@ -from collections import Counter, OrderedDict, abc, defaultdict +from collections import Counter, OrderedDict, defaultdict from itertools import chain import numpy as np @@ -630,21 +630,8 @@ class DictWithoutMissing(dict): def test_map_abc_mapping(self): # https://github.com/pandas-dev/pandas/issues/29733 # Check collections.abc.Mapping support as mapper for Series.map - class NonDictMapping(abc.Mapping): - def __init__(self): - self._data = {3: "three"} - - def __getitem__(self, key): - return self._data.__getitem__(key) - - def __iter__(self): - return self._data.__iter__() - - def __len__(self): - return self._data.__len__() - s = Series([1, 2, 3]) - not_a_dictionary = NonDictMapping() + not_a_dictionary = tm.TestNonDictMapping({3: "three"}) result = s.map(not_a_dictionary) expected = Series([np.nan, np.nan, "three"]) tm.assert_series_equal(result, expected) @@ -652,24 +639,12 @@ def __len__(self): def test_map_abc_mapping_with_missing(self): # https://github.com/pandas-dev/pandas/issues/29733 # Check collections.abc.Mapping support as mapper for Series.map - class NonDictMappingWithMissing(abc.Mapping): - def __init__(self): - self._data = {3: "three"} - - def __getitem__(self, key): - return self._data.__getitem__(key) - - def __iter__(self): - return self._data.__iter__() - - def __len__(self): - return self._data.__len__() - + class NonDictMappingWithMissing(tm.TestNonDictMapping): def __missing__(self, key): return "missing" s = Series([1, 2, 3]) - not_a_dictionary = NonDictMappingWithMissing() + not_a_dictionary = NonDictMappingWithMissing({3: "three"}) result = s.map(not_a_dictionary) # __missing__ is a dict concept, not a Mapping concept, # so it should not change the result! diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 20a83ec4cd162..09e52f6b51a57 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -1089,6 +1089,14 @@ def create_data(constructor): tm.assert_series_equal(result_datetime, expected) tm.assert_series_equal(result_Timestamp, expected) + def test_constructor_mapping(self): + # GH 29788 + ndm = tm.TestNonDictMapping({3: "three"}) + result = Series(ndm) + expected = Series(["three"], index=[3]) + + tm.assert_series_equal(result, expected) + def test_constructor_list_of_tuples(self): data = [(1, 1), (2, 2), (2, 3)] s = Series(data) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index c31cddc102afb..92e65c4219382 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1,5 +1,5 @@ import bz2 -from collections import Counter +from collections import Counter, abc from contextlib import contextmanager from datetime import datetime from functools import wraps @@ -2128,6 +2128,20 @@ def __init__(self, *args, **kwargs): dict.__init__(self, *args, **kwargs) +class TestNonDictMapping(abc.Mapping): + def __init__(self, underlying_dict): + self._data = underlying_dict + + def __getitem__(self, key): + return self._data.__getitem__(key) + + def __iter__(self): + return self._data.__iter__() + + def __len__(self): + return self._data.__len__() + + def optional_args(decorator): """allows a decorator to take optional positional and keyword arguments. Assumes that taking a single, callable, positional argument means that From e536b4d8d6e28eb85f0ff54d5865b3b4961488e9 Mon Sep 17 00:00:00 2001 From: ohad83 Date: Mon, 25 Nov 2019 21:56:25 +0200 Subject: [PATCH 10/19] CLN - Better way to import from collections.abc DOC - Typo fix --- pandas/core/base.py | 5 +++-- pandas/core/series.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index bcd045fdb8d4c..ae668a9eddf72 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -2,7 +2,8 @@ Base and utility classes for pandas objects. """ import builtins -from collections import OrderedDict, abc +from collections import OrderedDict +from collections.abc import Mapping import textwrap from typing import Dict, FrozenSet, List, Optional @@ -1108,7 +1109,7 @@ def _map_values(self, mapper, na_action=None): # we can fastpath dict/Series to an efficient map # as we know that we are not going to have to yield # python types - if isinstance(mapper, abc.Mapping): + if isinstance(mapper, Mapping): if isinstance(mapper, dict) and hasattr(mapper, "__missing__"): # If a dictionary subclass defines a default value method, # convert mapper to a lookup function (GH #15999). diff --git a/pandas/core/series.py b/pandas/core/series.py index a476a4eccc3d7..b8077087000d6 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1,7 +1,7 @@ """ Data structure for 1-dimensional cross-sectional and time series data """ -from collections import abc +from collections.abc import Mapping from io import StringIO from shutil import get_terminal_size from textwrap import dedent @@ -252,7 +252,7 @@ def __init__( else: data = data.reindex(index, copy=copy) data = data._data - elif isinstance(data, abc.Mapping): + elif isinstance(data, Mapping): data, index = self._init_dict(data, index, dtype) dtype = None copy = False @@ -3515,7 +3515,7 @@ def map(self, arg, na_action=None): Parameters ---------- - arg : function, colletions.abc.Mapping subclass or Series + arg : function, collections.abc.Mapping subclass or Series Mapping correspondence. na_action : {None, 'ignore'}, default None If 'ignore', propagate NaN values, without passing them to the From 102b82ac470a2fb3cc5b559ba888acb254db6b8e Mon Sep 17 00:00:00 2001 From: ohad83 Date: Thu, 5 Dec 2019 23:03:18 +0200 Subject: [PATCH 11/19] Revert "CLN - Better way to import from collections.abc" This reverts commit 5fe41408d7c422734955e2ff74e2e0bdfa897785. Apparently it's an unwanted pattern checked for by code_checks.sh (from collections.abc import) --- pandas/core/base.py | 5 ++--- pandas/core/series.py | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index ae668a9eddf72..bcd045fdb8d4c 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -2,8 +2,7 @@ Base and utility classes for pandas objects. """ import builtins -from collections import OrderedDict -from collections.abc import Mapping +from collections import OrderedDict, abc import textwrap from typing import Dict, FrozenSet, List, Optional @@ -1109,7 +1108,7 @@ def _map_values(self, mapper, na_action=None): # we can fastpath dict/Series to an efficient map # as we know that we are not going to have to yield # python types - if isinstance(mapper, Mapping): + if isinstance(mapper, abc.Mapping): if isinstance(mapper, dict) and hasattr(mapper, "__missing__"): # If a dictionary subclass defines a default value method, # convert mapper to a lookup function (GH #15999). diff --git a/pandas/core/series.py b/pandas/core/series.py index b8077087000d6..a476a4eccc3d7 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1,7 +1,7 @@ """ Data structure for 1-dimensional cross-sectional and time series data """ -from collections.abc import Mapping +from collections import abc from io import StringIO from shutil import get_terminal_size from textwrap import dedent @@ -252,7 +252,7 @@ def __init__( else: data = data.reindex(index, copy=copy) data = data._data - elif isinstance(data, Mapping): + elif isinstance(data, abc.Mapping): data, index = self._init_dict(data, index, dtype) dtype = None copy = False @@ -3515,7 +3515,7 @@ def map(self, arg, na_action=None): Parameters ---------- - arg : function, collections.abc.Mapping subclass or Series + arg : function, colletions.abc.Mapping subclass or Series Mapping correspondence. na_action : {None, 'ignore'}, default None If 'ignore', propagate NaN values, without passing them to the From 2cb36bb3f95255ed7e2db64a3206458dc537e9bd Mon Sep 17 00:00:00 2001 From: ohad83 Date: Thu, 5 Dec 2019 23:04:58 +0200 Subject: [PATCH 12/19] DOC - Type fix --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index a476a4eccc3d7..8a76c9927a545 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3515,7 +3515,7 @@ def map(self, arg, na_action=None): Parameters ---------- - arg : function, colletions.abc.Mapping subclass or Series + arg : function, collections.abc.Mapping subclass or Series Mapping correspondence. na_action : {None, 'ignore'}, default None If 'ignore', propagate NaN values, without passing them to the From 3074c8cdaacc2aa13508e5f401c64c5a005fee0f Mon Sep 17 00:00:00 2001 From: ohad83 Date: Thu, 5 Dec 2019 23:06:57 +0200 Subject: [PATCH 13/19] CLN - remove example.feather which was mistakenly added --- doc/example.feather | Bin 1120 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/example.feather diff --git a/doc/example.feather b/doc/example.feather deleted file mode 100644 index 38dbf087a1590606ae40aaab16fd2d592b771b0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1120 zcmZuxyGlbr5S^R5Nz{a}NW>HtK_sM7>|!IaNGdxCSXd;{_yCQdR#tw3l~`I>OA5ci zQbZ6$tgQTs#B+9c!YinJCM9>)^273K{XQhooJB2<^&GtHH$(bNO86J_M!J zyGzt-fVTu_y`vB(l0%(5PA9RI8omTJPF`^*F6IUYC9RG~2~6L$#r{j48ZpBzkgR*? zeK=XyxVoIL>-SgCJxtfJc!iX{qHf5i{ID};{XtI7TL*ORLEuwa*BRfN-m;sO{Zgj$ ztzwsCRRGO(Kw^-p-}PKGd}}=UlwnHzzIVB^>*KSHVAFV==PAS|XXz*6tt_YQ$5f~C oX+L~3>;6heD7rV}T*>^s5KCq8YfagILHpBwHUBLPZTxTf15K=C=l}o! From 49637e17dcc5ba3ffbc3a0962c8801f56bd6e3fa Mon Sep 17 00:00:00 2001 From: ohad83 Date: Sun, 8 Dec 2019 20:56:06 +0200 Subject: [PATCH 14/19] CLN: Checking is_dict_like instead of is abc.Mapping --- pandas/core/base.py | 5 +++-- pandas/core/series.py | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index bcd045fdb8d4c..a9bd9f752bcff 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -2,7 +2,7 @@ Base and utility classes for pandas objects. """ import builtins -from collections import OrderedDict, abc +from collections import OrderedDict import textwrap from typing import Dict, FrozenSet, List, Optional @@ -21,6 +21,7 @@ is_datetime64_ns_dtype, is_datetime64tz_dtype, is_extension_array_dtype, + is_dict_like, is_list_like, is_object_dtype, is_scalar, @@ -1108,7 +1109,7 @@ def _map_values(self, mapper, na_action=None): # we can fastpath dict/Series to an efficient map # as we know that we are not going to have to yield # python types - if isinstance(mapper, abc.Mapping): + if is_dict_like(mapper): if isinstance(mapper, dict) and hasattr(mapper, "__missing__"): # If a dictionary subclass defines a default value method, # convert mapper to a lookup function (GH #15999). diff --git a/pandas/core/series.py b/pandas/core/series.py index 8a76c9927a545..19bac2229f8a9 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -1,7 +1,6 @@ """ Data structure for 1-dimensional cross-sectional and time series data """ -from collections import abc from io import StringIO from shutil import get_terminal_size from textwrap import dedent @@ -252,7 +251,7 @@ def __init__( else: data = data.reindex(index, copy=copy) data = data._data - elif isinstance(data, abc.Mapping): + elif is_dict_like(data): data, index = self._init_dict(data, index, dtype) dtype = None copy = False From 08403e9fc9ce4e3462f195bd6250e228a868dc4a Mon Sep 17 00:00:00 2001 From: ohad83 Date: Sun, 8 Dec 2019 21:19:12 +0200 Subject: [PATCH 15/19] CLN: Changed import sorting --- pandas/core/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index a9bd9f752bcff..64274aefa60bd 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -20,8 +20,8 @@ is_categorical_dtype, is_datetime64_ns_dtype, is_datetime64tz_dtype, - is_extension_array_dtype, is_dict_like, + is_extension_array_dtype, is_list_like, is_object_dtype, is_scalar, From 97d245dd63383d03f5c07cb5a2169442a0ff15ec Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Wed, 1 Jan 2020 11:49:46 -0500 Subject: [PATCH 16/19] use fixtures --- pandas/conftest.py | 36 ++++++++++++++++++++++++ pandas/tests/frame/test_constructors.py | 8 +++--- pandas/tests/series/test_api.py | 4 +-- pandas/tests/series/test_apply.py | 8 +++--- pandas/tests/series/test_constructors.py | 4 +-- pandas/util/testing.py | 21 +------------- 6 files changed, 49 insertions(+), 32 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 0a3bf31cf9666..eb7263fe116cc 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -1,3 +1,4 @@ +from collections import abc from datetime import date, time, timedelta, timezone from decimal import Decimal import operator @@ -894,3 +895,38 @@ def index_or_series(request): See GH#29725 """ return request.param + + +@pytest.fixture +def dict_subclass(): + """ + Fixture for a dictionary subclass. + """ + + class TestSubDict(dict): + def __init__(self, *args, **kwargs): + dict.__init__(self, *args, **kwargs) + + return TestSubDict + + +@pytest.fixture +def non_mapping_dict_subclass(): + """ + Fixture for a non-mapping dictionary subclass. + """ + + class TestNonDictMapping(abc.Mapping): + def __init__(self, underlying_dict): + self._data = underlying_dict + + def __getitem__(self, key): + return self._data.__getitem__(key) + + def __iter__(self): + return self._data.__iter__() + + def __len__(self): + return self._data.__len__() + + return TestNonDictMapping diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index ffdf1435f74e0..4e7d8c3054cf2 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -511,17 +511,17 @@ def test_constructor_with_embedded_frames(self): result = df2.loc[1, 0] tm.assert_frame_equal(result, df1 + 10) - def test_constructor_subclass_dict(self, float_frame): + def test_constructor_subclass_dict(self, float_frame, dict_subclass): # Test for passing dict subclass to constructor data = { - "col1": tm.TestSubDict((x, 10.0 * x) for x in range(10)), - "col2": tm.TestSubDict((x, 20.0 * x) for x in range(10)), + "col1": dict_subclass((x, 10.0 * x) for x in range(10)), + "col2": dict_subclass((x, 20.0 * x) for x in range(10)), } df = DataFrame(data) refdf = DataFrame({col: dict(val.items()) for col, val in data.items()}) tm.assert_frame_equal(refdf, df) - data = tm.TestSubDict(data.items()) + data = dict_subclass(data.items()) df = DataFrame(data) tm.assert_frame_equal(refdf, df) diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index a187a1362297c..89a60d371770a 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -126,8 +126,8 @@ def test_constructor_dict(self): expected = Series([1, 2, np.nan, 0], index=["b", "c", "d", "a"]) tm.assert_series_equal(result, expected) - def test_constructor_subclass_dict(self): - data = tm.TestSubDict((x, 10.0 * x) for x in range(10)) + def test_constructor_subclass_dict(self, dict_subclass): + data = dict_subclass((x, 10.0 * x) for x in range(10)) series = Series(data) expected = Series(dict(data.items())) tm.assert_series_equal(series, expected) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index c65d99a1bcbb4..4b4d128d4a523 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -627,19 +627,19 @@ class DictWithoutMissing(dict): expected = Series([np.nan, np.nan, "three"]) tm.assert_series_equal(result, expected) - def test_map_abc_mapping(self): + def test_map_abc_mapping(self, non_mapping_dict_subclass): # https://github.com/pandas-dev/pandas/issues/29733 # Check collections.abc.Mapping support as mapper for Series.map s = Series([1, 2, 3]) - not_a_dictionary = tm.TestNonDictMapping({3: "three"}) + not_a_dictionary = non_mapping_dict_subclass({3: "three"}) result = s.map(not_a_dictionary) expected = Series([np.nan, np.nan, "three"]) tm.assert_series_equal(result, expected) - def test_map_abc_mapping_with_missing(self): + def test_map_abc_mapping_with_missing(self, non_mapping_dict_subclass): # https://github.com/pandas-dev/pandas/issues/29733 # Check collections.abc.Mapping support as mapper for Series.map - class NonDictMappingWithMissing(tm.TestNonDictMapping): + class NonDictMappingWithMissing(non_mapping_dict_subclass): def __missing__(self, key): return "missing" diff --git a/pandas/tests/series/test_constructors.py b/pandas/tests/series/test_constructors.py index 09e52f6b51a57..1c3f1404215d3 100644 --- a/pandas/tests/series/test_constructors.py +++ b/pandas/tests/series/test_constructors.py @@ -1089,9 +1089,9 @@ def create_data(constructor): tm.assert_series_equal(result_datetime, expected) tm.assert_series_equal(result_Timestamp, expected) - def test_constructor_mapping(self): + def test_constructor_mapping(self, non_mapping_dict_subclass): # GH 29788 - ndm = tm.TestNonDictMapping({3: "three"}) + ndm = non_mapping_dict_subclass({3: "three"}) result = Series(ndm) expected = Series(["three"], index=[3]) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 92e65c4219382..2e201339d4d77 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1,5 +1,5 @@ import bz2 -from collections import Counter, abc +from collections import Counter from contextlib import contextmanager from datetime import datetime from functools import wraps @@ -2123,25 +2123,6 @@ def makeMissingDataframe(density=0.9, random_state=None): return df -class TestSubDict(dict): - def __init__(self, *args, **kwargs): - dict.__init__(self, *args, **kwargs) - - -class TestNonDictMapping(abc.Mapping): - def __init__(self, underlying_dict): - self._data = underlying_dict - - def __getitem__(self, key): - return self._data.__getitem__(key) - - def __iter__(self): - return self._data.__iter__() - - def __len__(self): - return self._data.__len__() - - def optional_args(decorator): """allows a decorator to take optional positional and keyword arguments. Assumes that taking a single, callable, positional argument means that From 5dcb9a6a657c13096e108a66391da5e5848b0537 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Wed, 1 Jan 2020 12:14:29 -0500 Subject: [PATCH 17/19] removed uneeded import --- pandas/core/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/base.py b/pandas/core/base.py index 64274aefa60bd..ef7e59c9e19d7 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -2,7 +2,6 @@ Base and utility classes for pandas objects. """ import builtins -from collections import OrderedDict import textwrap from typing import Dict, FrozenSet, List, Optional From 8d3f2799bf9f6d520bdeec633c27d25cda8ee25f Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Wed, 1 Jan 2020 12:15:06 -0500 Subject: [PATCH 18/19] moar uneeded imports --- pandas/tests/series/test_apply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/series/test_apply.py b/pandas/tests/series/test_apply.py index 4b4d128d4a523..37bedc1ab7508 100644 --- a/pandas/tests/series/test_apply.py +++ b/pandas/tests/series/test_apply.py @@ -1,4 +1,4 @@ -from collections import Counter, OrderedDict, defaultdict +from collections import Counter, defaultdict from itertools import chain import numpy as np From f8eebc2b88380b8fb59251ef5a49172655861be2 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Wed, 1 Jan 2020 12:29:12 -0500 Subject: [PATCH 19/19] missing import --- pandas/core/series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 19bac2229f8a9..8936420053607 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -4,7 +4,7 @@ from io import StringIO from shutil import get_terminal_size from textwrap import dedent -from typing import Any, Callable, Hashable, List, Optional +from typing import IO, Any, Callable, Hashable, List, Optional import warnings import numpy as np