Skip to content

Commit 8435c1d

Browse files
committed
make map return an index if it operates on an index, multi index, or categorical index; map on a categorical will either return a categorical or an index (rather than a numpy array)
1 parent e3d943d commit 8435c1d

File tree

7 files changed

+76
-31
lines changed

7 files changed

+76
-31
lines changed

doc/source/whatsnew/v0.19.1.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,29 @@ Performance Improvements
2626
- Improved performance in ``DataFrame.asof(where)`` when ``where`` is a scalar (:issue:`14461)
2727

2828

29+
.. _whatsnew_0191.api:
2930

31+
API changes
32+
~~~~~~~~~~~
33+
34+
- ``map`` on an ``Index`` now returns an ``Index``, not an array (:issue:`12766`)
35+
.. ipython:: python
36+
37+
idx = Index([1, 2])
38+
idx
39+
40+
Previous Behavior:
41+
42+
.. code-block:: ipython
43+
44+
In [3]: idx.map(lambda x: x * 2)
45+
Out[3]: array([2, 4])
46+
47+
New Behavior:
48+
49+
.. ipython:: python
50+
51+
idx.map(lambda x: x * 2)
3052

3153
.. _whatsnew_0191.bug_fixes:
3254

pandas/core/categorical.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ def map(self, mapper):
943943
944944
Returns
945945
-------
946-
applied : Categorical or np.ndarray.
946+
applied : Categorical or Index.
947947
"""
948948
new_categories = self.categories.map(mapper)
949949
try:

pandas/indexes/base.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2427,7 +2427,7 @@ def groupby(self, values):
24272427

24282428
def map(self, mapper):
24292429
"""
2430-
Apply mapper function to its values.
2430+
Apply mapper function to an index.
24312431
24322432
Parameters
24332433
----------
@@ -2436,9 +2436,12 @@ def map(self, mapper):
24362436
24372437
Returns
24382438
-------
2439-
applied : array
2439+
applied : Index
2440+
The output of the mapping function applied to the index.
24402441
"""
2441-
return self._arrmap(self.values, mapper)
2442+
attributes = self._get_attributes_dict()
2443+
attributes['copy'] = False
2444+
return Index(self._arrmap(self.values, mapper), **attributes)
24422445

24432446
def isin(self, values, level=None):
24442447
"""

pandas/indexes/category.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,15 +524,15 @@ def map(self, mapper):
524524
----------
525525
mapper : callable
526526
Function to be applied. When all categories are mapped
527-
to different categories, the result will be Categorical which has
528-
the same order property as the original. Otherwise, the result will
529-
be np.ndarray.
527+
to different categories, the result will be a CategoricalIndex
528+
which has the same order property as the original. Otherwise,
529+
the result will be a Index.
530530
531531
Returns
532532
-------
533-
applied : Categorical or np.ndarray.
533+
applied : CategoricalIndex or Index
534534
"""
535-
return self.values.map(mapper)
535+
return self._shallow_copy_with_infer(self.values.map(mapper))
536536

537537
def delete(self, loc):
538538
"""

pandas/tests/indexes/test_base.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,26 @@ def test_sub(self):
766766
self.assertRaises(TypeError, lambda: idx - idx.tolist())
767767
self.assertRaises(TypeError, lambda: idx.tolist() - idx)
768768

769+
def test_map_identity_mapping(self):
770+
for name, cur_index in self.indices.items():
771+
if name == 'tuples':
772+
expected = Index(cur_index.values, tupleize_cols=False)
773+
self.assert_index_equal(expected, cur_index.map(lambda x: x))
774+
else:
775+
self.assert_index_equal(cur_index, cur_index.map(lambda x: x))
776+
777+
def test_map_that_returns_tuples_creates_index_not_multi_index(self):
778+
boolean_index = tm.makeIntIndex(3).map(lambda x: (x, x == 1))
779+
expected = Index([(0, False), (1, True), (2, False)],
780+
tupleize_cols=False)
781+
self.assert_index_equal(boolean_index, expected)
782+
783+
def test_map_that_reduces_multi_index_to_single_index_returns_index(self):
784+
first_level = ['foo', 'bar', 'baz']
785+
multi_index = MultiIndex.from_tuples(lzip(first_level, [1, 2, 3]))
786+
reduced_index = multi_index.map(lambda x: x[0])
787+
self.assert_index_equal(reduced_index, Index(first_level))
788+
769789
def test_append_multiple(self):
770790
index = Index(['a', 'b', 'c', 'd', 'e', 'f'])
771791

@@ -1192,16 +1212,16 @@ def check_slice(in_slice, expected):
11921212
self.assert_index_equal(result, expected)
11931213

11941214
for in_slice, expected in [
1195-
(SLC[::-1], 'yxdcb'), (SLC['b':'y':-1], ''),
1196-
(SLC['b'::-1], 'b'), (SLC[:'b':-1], 'yxdcb'),
1197-
(SLC[:'y':-1], 'y'), (SLC['y'::-1], 'yxdcb'),
1198-
(SLC['y'::-4], 'yb'),
1199-
# absent labels
1200-
(SLC[:'a':-1], 'yxdcb'), (SLC[:'a':-2], 'ydb'),
1201-
(SLC['z'::-1], 'yxdcb'), (SLC['z'::-3], 'yc'),
1202-
(SLC['m'::-1], 'dcb'), (SLC[:'m':-1], 'yx'),
1203-
(SLC['a':'a':-1], ''), (SLC['z':'z':-1], ''),
1204-
(SLC['m':'m':-1], '')
1215+
(SLC[::-1], 'yxdcb'), (SLC['b':'y':-1], ''),
1216+
(SLC['b'::-1], 'b'), (SLC[:'b':-1], 'yxdcb'),
1217+
(SLC[:'y':-1], 'y'), (SLC['y'::-1], 'yxdcb'),
1218+
(SLC['y'::-4], 'yb'),
1219+
# absent labels
1220+
(SLC[:'a':-1], 'yxdcb'), (SLC[:'a':-2], 'ydb'),
1221+
(SLC['z'::-1], 'yxdcb'), (SLC['z'::-3], 'yc'),
1222+
(SLC['m'::-1], 'dcb'), (SLC[:'m':-1], 'yx'),
1223+
(SLC['a':'a':-1], ''), (SLC['z':'z':-1], ''),
1224+
(SLC['m':'m':-1], '')
12051225
]:
12061226
check_slice(in_slice, expected)
12071227

pandas/tests/indexes/test_category.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -207,19 +207,19 @@ def test_map(self):
207207
ci = pd.CategoricalIndex(list('ABABC'), categories=list('CBA'),
208208
ordered=True)
209209
result = ci.map(lambda x: x.lower())
210-
exp = pd.Categorical(list('ababc'), categories=list('cba'),
211-
ordered=True)
212-
tm.assert_categorical_equal(result, exp)
210+
exp = pd.CategoricalIndex(list('ababc'), categories=list('cba'),
211+
ordered=True)
212+
tm.assert_index_equal(result, exp)
213213

214214
ci = pd.CategoricalIndex(list('ABABC'), categories=list('BAC'),
215215
ordered=False, name='XXX')
216216
result = ci.map(lambda x: x.lower())
217-
exp = pd.Categorical(list('ababc'), categories=list('bac'),
218-
ordered=False)
219-
tm.assert_categorical_equal(result, exp)
217+
exp = pd.CategoricalIndex(list('ababc'), categories=list('bac'),
218+
ordered=False, name='XXX')
219+
tm.assert_index_equal(result, exp)
220220

221-
tm.assert_numpy_array_equal(ci.map(lambda x: 1),
222-
np.array([1] * 5, dtype=np.int64))
221+
tm.assert_index_equal(ci.map(lambda x: 1),
222+
Index(np.array([1] * 5, dtype=np.int64), name='XXX'))
223223

224224
# change categories dtype
225225
ci = pd.CategoricalIndex(list('ABABC'), categories=list('BAC'),
@@ -228,9 +228,9 @@ def f(x):
228228
return {'A': 10, 'B': 20, 'C': 30}.get(x)
229229

230230
result = ci.map(f)
231-
exp = pd.Categorical([10, 20, 10, 20, 30], categories=[20, 10, 30],
232-
ordered=False)
233-
tm.assert_categorical_equal(result, exp)
231+
exp = pd.CategoricalIndex([10, 20, 10, 20, 30], categories=[20, 10, 30],
232+
ordered=False)
233+
tm.assert_index_equal(result, exp)
234234

235235
def test_where(self):
236236
i = self.create_index()

pandas/tests/test_categorical.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1664,7 +1664,7 @@ def test_map(self):
16641664
tm.assert_categorical_equal(result, exp)
16651665

16661666
result = c.map(lambda x: 1)
1667-
tm.assert_numpy_array_equal(result, np.array([1] * 5, dtype=np.int64))
1667+
tm.assert_index_equal(result, Index(np.array([1] * 5, dtype=np.int64)))
16681668

16691669

16701670
class TestCategoricalAsBlock(tm.TestCase):

0 commit comments

Comments
 (0)