From 2a677196169f706f78c60fc14de0c1d76f4b40eb Mon Sep 17 00:00:00 2001 From: Evan Wright Date: Sun, 26 Jun 2016 12:41:55 -0400 Subject: [PATCH 1/3] ENH: Allow dict as the argument to Index.map (GH13517) --- doc/source/whatsnew/v0.18.2.txt | 1 + pandas/indexes/base.py | 7 +++++-- pandas/tests/indexes/test_base.py | 12 +++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt index 40fec4d071f16..5cf2daf050b26 100644 --- a/doc/source/whatsnew/v0.18.2.txt +++ b/doc/source/whatsnew/v0.18.2.txt @@ -207,6 +207,7 @@ Other enhancements - The ``pd.read_csv()`` with ``engine='python'`` has gained support for the ``na_filter`` option (:issue:`13321`) - The ``pd.read_csv()`` with ``engine='python'`` has gained support for the ``memory_map`` option (:issue:`13381`) +- ``Index.map`` now accepts a dictionary as its argument (:issue:`13517`) - ``Index.astype()`` now accepts an optional boolean argument ``copy``, which allows optional copying if the requirements on dtype are satisfied (:issue:`13209`) - ``Index`` now supports the ``.where()`` function for same shape indexing (:issue:`13170`) diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py index 96472698ba9d9..1cce480efb49e 100644 --- a/pandas/indexes/base.py +++ b/pandas/indexes/base.py @@ -2295,14 +2295,17 @@ def map(self, mapper): Parameters ---------- - mapper : callable + mapper : callable or dict Function to be applied. Returns ------- applied : array """ - return self._arrmap(self.values, mapper) + if isinstance(mapper, dict): + return self._arrmap(self.values, lambda key: mapper[key]) + else: + return self._arrmap(self.values, mapper) def isin(self, values, level=None): """ diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index d535eaa238567..b0ff53052afa4 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -20,7 +20,7 @@ Float64Index, Int64Index, CategoricalIndex, DatetimeIndex, TimedeltaIndex, PeriodIndex) -from pandas.util.testing import assert_almost_equal +from pandas.util.testing import assert_almost_equal, assert_numpy_array_equal from pandas.compat.numpy import np_datetime64_compat import pandas.core.config as cf @@ -1462,6 +1462,16 @@ def test_conversion_preserves_name(self): self.assertEqual(i.name, pd.to_datetime(i).name) self.assertEqual(i.name, pd.to_timedelta(i).name) + def test_map_dict(self): + # GH 13517 + i = pd.Index(['a', 'b', 'c', 'd']) + d = {'a': 0, 'b': 2, 'c': 1, 'd': 5} + + actual = i.map(d) + expected = np.array([0, 2, 1, 5]) + + tm.assert_numpy_array_equal(actual, expected) + def test_string_index_repr(self): # py3/py2 repr can differ because of "u" prefix # which also affects to displayed element size From e82dc92ad5651da03907018d72164336a956ce14 Mon Sep 17 00:00:00 2001 From: Evan Wright Date: Sun, 26 Jun 2016 15:48:52 -0400 Subject: [PATCH 2/3] Allow for dict-like mappers that are not dicts, and for missing values --- doc/source/whatsnew/v0.18.2.txt | 2 +- pandas/indexes/base.py | 6 +++--- pandas/tests/indexes/test_base.py | 13 ++++++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v0.18.2.txt b/doc/source/whatsnew/v0.18.2.txt index 5cf2daf050b26..44b6059c8951b 100644 --- a/doc/source/whatsnew/v0.18.2.txt +++ b/doc/source/whatsnew/v0.18.2.txt @@ -207,7 +207,7 @@ Other enhancements - The ``pd.read_csv()`` with ``engine='python'`` has gained support for the ``na_filter`` option (:issue:`13321`) - The ``pd.read_csv()`` with ``engine='python'`` has gained support for the ``memory_map`` option (:issue:`13381`) -- ``Index.map`` now accepts a dictionary as its argument (:issue:`13517`) +- ``Index.map`` now accepts a dict-like object as its argument (:issue:`13517`) - ``Index.astype()`` now accepts an optional boolean argument ``copy``, which allows optional copying if the requirements on dtype are satisfied (:issue:`13209`) - ``Index`` now supports the ``.where()`` function for same shape indexing (:issue:`13170`) diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py index 1cce480efb49e..8c44af742e071 100644 --- a/pandas/indexes/base.py +++ b/pandas/indexes/base.py @@ -2295,15 +2295,15 @@ def map(self, mapper): Parameters ---------- - mapper : callable or dict + mapper : callable or dict-like Function to be applied. Returns ------- applied : array """ - if isinstance(mapper, dict): - return self._arrmap(self.values, lambda key: mapper[key]) + if com.is_dict_like(mapper): + return self._arrmap(self.values, lambda key: mapper[key] if key in mapper.keys() else None) else: return self._arrmap(self.values, mapper) diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index b0ff53052afa4..6c9ce343fd80e 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -1465,11 +1465,22 @@ def test_conversion_preserves_name(self): def test_map_dict(self): # GH 13517 i = pd.Index(['a', 'b', 'c', 'd']) - d = {'a': 0, 'b': 2, 'c': 1, 'd': 5} + # actual dict + d = {'a': 0, 'b': 2, 'c': 1, 'd': 5} actual = i.map(d) expected = np.array([0, 2, 1, 5]) + tm.assert_numpy_array_equal(actual, expected) + + # series + s = pd.Series([0, 2, 1, 5], index=['a', 'b', 'c', 'd']) + actual = i.map(s) + tm.assert_numpy_array_equal(actual, expected) + # missing values + d2 = {'b': 2, 'd': 5} + actual = i.map(d2) + expected = np.array([np.nan, 2, np.nan, 5]) tm.assert_numpy_array_equal(actual, expected) def test_string_index_repr(self): From 0b327096830e54b741f15737e344fb0fdf67aac9 Mon Sep 17 00:00:00 2001 From: Evan Wright Date: Sun, 26 Jun 2016 16:19:37 -0400 Subject: [PATCH 3/3] Split too-long line --- pandas/indexes/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/indexes/base.py b/pandas/indexes/base.py index 8c44af742e071..558e5800036ed 100644 --- a/pandas/indexes/base.py +++ b/pandas/indexes/base.py @@ -2303,7 +2303,8 @@ def map(self, mapper): applied : array """ if com.is_dict_like(mapper): - return self._arrmap(self.values, lambda key: mapper[key] if key in mapper.keys() else None) + map_fn = lambda key: mapper[key] if key in mapper.keys() else None + return self._arrmap(self.values, map_fn) else: return self._arrmap(self.values, mapper)