Skip to content

Commit 0fd9687

Browse files
committed
Refactor the code to work with period time time delta indices
1 parent d81df40 commit 0fd9687

File tree

8 files changed

+125
-36
lines changed

8 files changed

+125
-36
lines changed

doc/source/whatsnew/v0.20.0.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ Other Enhancements
521521
- The ``display.show_dimensions`` option can now also be used to specify
522522
whether the length of a ``Series`` should be shown in its repr (:issue:`7117`).
523523
- ``parallel_coordinates()`` has gained a ``sort_labels`` keyword arg that sorts class labels and the colours assigned to them (:issue:`15908`)
524+
- ``Index.map`` can now accept series and dictionary input object (:issue:`12756`).
524525

525526

526527
.. _ISO 8601 duration: https://en.wikipedia.org/wiki/ISO_8601#Durations

pandas/core/indexes/base.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2667,6 +2667,25 @@ def get_indexer_for(self, target, **kwargs):
26672667
indexer, _ = self.get_indexer_non_unique(target, **kwargs)
26682668
return indexer
26692669

2670+
def get_values_from_dict(self, input_dict):
2671+
"""Return the values of the input dictionary in the order the keys are
2672+
in the index. np.nan is returned for index values not in the
2673+
dictionary.
2674+
2675+
Parameters
2676+
----------
2677+
input_dict : dict
2678+
The dictionary from which to extract the values
2679+
2680+
Returns
2681+
-------
2682+
Union[np.array, list]
2683+
2684+
"""
2685+
2686+
return lib.fast_multiget(input_dict, self.values,
2687+
default=np.nan)
2688+
26702689
def _maybe_promote(self, other):
26712690
# A hack, but it works
26722691
from pandas.core.indexes.datetimes import DatetimeIndex
@@ -2710,8 +2729,8 @@ def map(self, mapper):
27102729
27112730
Parameters
27122731
----------
2713-
mapper : function, dict, or Series
2714-
Function to be applied.
2732+
mapper : Union[function, dict, Series]
2733+
Function to be applied or input correspondence object.
27152734
27162735
Returns
27172736
-------
@@ -2724,12 +2743,15 @@ def map(self, mapper):
27242743
from .multi import MultiIndex
27252744

27262745
if isinstance(mapper, ABCSeries):
2727-
indexer = mapper.index.get_indexer(self._values)
2746+
indexer = mapper.index.get_indexer(self.values)
27282747
mapped_values = algos.take_1d(mapper.values, indexer)
2748+
elif isinstance(mapper, dict):
2749+
idx = Index(mapper.keys())
2750+
data = idx.get_values_from_dict(mapper)
2751+
indexer = idx.get_indexer(self.values)
2752+
mapped_values = algos.take_1d(data, indexer)
27292753
else:
2730-
if isinstance(mapper, dict):
2731-
mapper = mapper.get
2732-
mapped_values = self._arrmap(self._values, mapper)
2754+
mapped_values = self._arrmap(self.values, mapper)
27332755

27342756
attributes = self._get_attributes_dict()
27352757
if mapped_values.size and isinstance(mapped_values[0], tuple):

pandas/core/indexes/datetimes.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,30 @@ def get_value_maybe_box(self, series, key):
13981398
key, tz=self.tz)
13991399
return _maybe_box(self, values, series, key)
14001400

1401+
def get_values_from_dict(self, input_dict):
1402+
"""Return the values of the input dictionary in the order the keys are
1403+
in the index. np.nan is returned for index values not in the
1404+
dictionary.
1405+
1406+
Parameters
1407+
----------
1408+
input_dict : dict
1409+
The dictionary from which to extract the values
1410+
1411+
Returns
1412+
-------
1413+
Union[np.array, list]
1414+
1415+
"""
1416+
if len(input_dict):
1417+
# coerce back to datetime objects for lookup
1418+
input_dict = com._dict_compat(input_dict)
1419+
return lib.fast_multiget(input_dict,
1420+
self.asobject.values,
1421+
default=np.nan)
1422+
else:
1423+
return np.nan
1424+
14011425
def get_loc(self, key, method=None, tolerance=None):
14021426
"""
14031427
Get integer location for requested label

pandas/core/indexes/period.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,25 @@ def _get_unique_index(self, dropna=False):
794794
res = res.dropna()
795795
return res
796796

797+
def get_values_from_dict(self, input_dict):
798+
"""Return the values of the input dictionary in the order the keys are
799+
in the index. np.nan is returned for index values not in the
800+
dictionary.
801+
802+
Parameters
803+
----------
804+
input_dict : dict
805+
The dictionary from which to extract the values
806+
807+
Returns
808+
-------
809+
Union[np.array, list]
810+
811+
"""
812+
813+
return np.array([input_dict.get(i, np.nan) for i in self.values]
814+
if input_dict else [np.nan])
815+
797816
def get_loc(self, key, method=None, tolerance=None):
798817
"""
799818
Get integer location for requested label

pandas/core/indexes/timedeltas.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,26 @@ def get_value_maybe_box(self, series, key):
672672
values = self._engine.get_value(_values_from_object(series), key)
673673
return _maybe_box(self, values, series, key)
674674

675+
def get_values_from_dict(self, input_dict):
676+
"""Return the values of the input dictionary in the order the keys are
677+
in the index. np.nan is returned for index values not in the
678+
dictionary.
679+
680+
Parameters
681+
----------
682+
input_dict : dict
683+
The dictionary from which to extract the values
684+
685+
Returns
686+
-------
687+
Union[np.array, list]
688+
689+
"""
690+
691+
return np.array([input_dict.get(i, np.nan)
692+
for i in self.asobject.values]
693+
if input_dict else [np.nan])
694+
675695
def get_loc(self, key, method=None, tolerance=None):
676696
"""
677697
Get integer location for requested label

pandas/core/series.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -184,23 +184,9 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
184184
index = Index(data)
185185
else:
186186
index = Index(_try_sort(data))
187+
187188
try:
188-
if isinstance(index, DatetimeIndex):
189-
if len(data):
190-
# coerce back to datetime objects for lookup
191-
data = _dict_compat(data)
192-
data = lib.fast_multiget(data,
193-
index.asobject.values,
194-
default=np.nan)
195-
else:
196-
data = np.nan
197-
# GH #12169
198-
elif isinstance(index, (PeriodIndex, TimedeltaIndex)):
199-
data = ([data.get(i, nan) for i in index]
200-
if data else np.nan)
201-
else:
202-
data = lib.fast_multiget(data, index.values,
203-
default=np.nan)
189+
data = index.get_values_from_dict(data)
204190
except TypeError:
205191
data = ([data.get(i, nan) for i in index]
206192
if data else np.nan)

pandas/tests/indexes/test_base.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -822,10 +822,10 @@ def test_map_tseries_indices_return_index(self):
822822
exp = Index(range(24), name='hourly')
823823
tm.assert_index_equal(exp, date_index.map(lambda x: x.hour))
824824

825-
def test_map_with_series_all_indices(self):
825+
def test_map_with_dict_and_series(self):
826826
expected = Index(['foo', 'bar', 'baz'])
827827
mapper = Series(expected.values, index=[0, 1, 2])
828-
self.assert_index_equal(tm.makeIntIndex(3).map(mapper), expected)
828+
tm.assert_index_equal(tm.makeIntIndex(3).map(mapper), expected)
829829

830830
# GH 12766
831831
# special = []
@@ -835,41 +835,58 @@ def test_map_with_series_all_indices(self):
835835
orig_values = ['a', 'B', 1, 'a']
836836
new_values = ['one', 2, 3.0, 'one']
837837
cur_index = CategoricalIndex(orig_values, name='XXX')
838+
expected = CategoricalIndex(new_values,
839+
name='XXX', categories=[3.0, 2, 'one'])
840+
838841
mapper = pd.Series(new_values[:-1], index=orig_values[:-1])
839-
expected = CategoricalIndex(new_values, name='XXX')
840842
output = cur_index.map(mapper)
841-
self.assert_numpy_array_equal(expected.values.get_values(), output.values.get_values())
842-
self.assert_equal(expected.name, output.name)
843+
# Order of categories in output can be different
844+
tm.assert_index_equal(expected, output)
845+
846+
mapper = {o: n for o, n in
847+
zip(orig_values[:-1], new_values[:-1])}
848+
output = cur_index.map(mapper)
849+
# Order of categories in output can be different
850+
tm.assert_index_equal(expected, output)
843851

844852
for name in list(set(self.indices.keys()) - set(special)):
845853
cur_index = self.indices[name]
846854
expected = Index(np.arange(len(cur_index), 0, -1))
847-
mapper = pd.Series(expected.values, index=cur_index)
848-
print(name)
849-
output = cur_index.map(mapper)
850-
self.assert_index_equal(expected, cur_index.map(mapper))
855+
mapper = pd.Series(expected, index=cur_index)
856+
tm.assert_index_equal(expected, cur_index.map(mapper))
857+
858+
mapper = {o: n for o, n in
859+
zip(cur_index, expected)}
860+
if mapper:
861+
tm.assert_index_equal(expected, cur_index.map(mapper))
862+
else:
863+
# The expected index type is Int64Index
864+
# but the output defaults to Float64
865+
tm.assert_index_equal(Float64Index([]),
866+
cur_index.map(mapper))
851867

852868
def test_map_with_categorical_series(self):
853869
# GH 12756
854870
a = Index([1, 2, 3, 4])
855-
b = Series(["even", "odd", "even", "odd"], dtype="category")
871+
b = Series(["even", "odd", "even", "odd"],
872+
dtype="category")
856873
c = Series(["even", "odd", "even", "odd"])
857874

858875
exp = CategoricalIndex(["odd", "even", "odd", np.nan])
859-
self.assert_index_equal(a.map(b), exp)
876+
tm.assert_index_equal(a.map(b), exp)
860877
exp = Index(["odd", "even", "odd", np.nan])
861-
self.assert_index_equal(a.map(c), exp)
878+
tm.assert_index_equal(a.map(c), exp)
862879

863880
def test_map_with_non_function_missing_values(self):
864881
# GH 12756
865882
expected = Index([2., np.nan, 'foo'])
866883
input = Index([2, 1, 0])
867884

868885
mapper = Series(['foo', 2., 'baz'], index=[0, 2, -1])
869-
self.assert_index_equal(expected, input.map(mapper))
886+
tm.assert_index_equal(expected, input.map(mapper))
870887

871888
mapper = {0: 'foo', 2: 2.0, -1: 'baz'}
872-
self.assert_index_equal(expected, input.map(mapper))
889+
tm.assert_index_equal(expected, input.map(mapper))
873890

874891
def test_append_multiple(self):
875892
index = Index(['a', 'b', 'c', 'd', 'e', 'f'])

pandas/tseries/tests/test_period.py

Whitespace-only changes.

0 commit comments

Comments
 (0)