Skip to content

Commit c7d9eb2

Browse files
committed
Refactor the code to work with period time time delta indices
1 parent e65876f commit c7d9eb2

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
@@ -519,6 +519,7 @@ Other Enhancements
519519
- ``parallel_coordinates()`` has gained a ``sort_labels`` keyword argument that sorts class labels and the colors assigned to them (:issue:`15908`)
520520
- Options added to allow one to turn on/off using ``bottleneck`` and ``numexpr``, see :ref:`here <basics.accelerate>` (:issue:`16157`)
521521
- ``DataFrame.style.bar()`` now accepts two more options to further customize the bar chart. Bar alignment is set with ``align='left'|'mid'|'zero'``, the default is "left", which is backward compatible; You can now pass a list of ``color=[color_negative, color_positive]``. (:issue:`14757`)
522+
- ``Index.map`` can now accept series and dictionary input object (:issue:`12756`).
522523

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

pandas/core/indexes/base.py

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

2823+
def get_values_from_dict(self, input_dict):
2824+
"""Return the values of the input dictionary in the order the keys are
2825+
in the index. np.nan is returned for index values not in the
2826+
dictionary.
2827+
2828+
Parameters
2829+
----------
2830+
input_dict : dict
2831+
The dictionary from which to extract the values
2832+
2833+
Returns
2834+
-------
2835+
Union[np.array, list]
2836+
2837+
"""
2838+
2839+
return lib.fast_multiget(input_dict, self.values,
2840+
default=np.nan)
2841+
28232842
def _maybe_promote(self, other):
28242843
# A hack, but it works
28252844
from pandas.core.indexes.datetimes import DatetimeIndex
@@ -2863,8 +2882,8 @@ def map(self, mapper):
28632882
28642883
Parameters
28652884
----------
2866-
mapper : function, dict, or Series
2867-
Function to be applied.
2885+
mapper : Union[function, dict, Series]
2886+
Function to be applied or input correspondence object.
28682887
28692888
Returns
28702889
-------
@@ -2877,12 +2896,15 @@ def map(self, mapper):
28772896
from .multi import MultiIndex
28782897

28792898
if isinstance(mapper, ABCSeries):
2880-
indexer = mapper.index.get_indexer(self._values)
2899+
indexer = mapper.index.get_indexer(self.values)
28812900
mapped_values = algos.take_1d(mapper.values, indexer)
2901+
elif isinstance(mapper, dict):
2902+
idx = Index(mapper.keys())
2903+
data = idx.get_values_from_dict(mapper)
2904+
indexer = idx.get_indexer(self.values)
2905+
mapped_values = algos.take_1d(data, indexer)
28822906
else:
2883-
if isinstance(mapper, dict):
2884-
mapper = mapper.get
2885-
mapped_values = self._arrmap(self._values, mapper)
2907+
mapped_values = self._arrmap(self.values, mapper)
28862908

28872909
attributes = self._get_attributes_dict()
28882910
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
@@ -1409,6 +1409,30 @@ def get_value_maybe_box(self, series, key):
14091409
key, tz=self.tz)
14101410
return _maybe_box(self, values, series, key)
14111411

1412+
def get_values_from_dict(self, input_dict):
1413+
"""Return the values of the input dictionary in the order the keys are
1414+
in the index. np.nan is returned for index values not in the
1415+
dictionary.
1416+
1417+
Parameters
1418+
----------
1419+
input_dict : dict
1420+
The dictionary from which to extract the values
1421+
1422+
Returns
1423+
-------
1424+
Union[np.array, list]
1425+
1426+
"""
1427+
if len(input_dict):
1428+
# coerce back to datetime objects for lookup
1429+
input_dict = com._dict_compat(input_dict)
1430+
return lib.fast_multiget(input_dict,
1431+
self.asobject.values,
1432+
default=np.nan)
1433+
else:
1434+
return np.nan
1435+
14121436
def get_loc(self, key, method=None, tolerance=None):
14131437
"""
14141438
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
@@ -800,6 +800,25 @@ def _get_unique_index(self, dropna=False):
800800
res = res.dropna()
801801
return res
802802

803+
def get_values_from_dict(self, input_dict):
804+
"""Return the values of the input dictionary in the order the keys are
805+
in the index. np.nan is returned for index values not in the
806+
dictionary.
807+
808+
Parameters
809+
----------
810+
input_dict : dict
811+
The dictionary from which to extract the values
812+
813+
Returns
814+
-------
815+
Union[np.array, list]
816+
817+
"""
818+
819+
return np.array([input_dict.get(i, np.nan) for i in self.values]
820+
if input_dict else [np.nan])
821+
803822
def get_loc(self, key, method=None, tolerance=None):
804823
"""
805824
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
@@ -680,6 +680,26 @@ def get_value_maybe_box(self, series, key):
680680
values = self._engine.get_value(_values_from_object(series), key)
681681
return _maybe_box(self, values, series, key)
682682

683+
def get_values_from_dict(self, input_dict):
684+
"""Return the values of the input dictionary in the order the keys are
685+
in the index. np.nan is returned for index values not in the
686+
dictionary.
687+
688+
Parameters
689+
----------
690+
input_dict : dict
691+
The dictionary from which to extract the values
692+
693+
Returns
694+
-------
695+
Union[np.array, list]
696+
697+
"""
698+
699+
return np.array([input_dict.get(i, np.nan)
700+
for i in self.asobject.values]
701+
if input_dict else [np.nan])
702+
683703
def get_loc(self, key, method=None, tolerance=None):
684704
"""
685705
Get integer location for requested label

pandas/core/series.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -202,23 +202,9 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
202202
index = Index(data)
203203
else:
204204
index = Index(_try_sort(data))
205+
205206
try:
206-
if isinstance(index, DatetimeIndex):
207-
if len(data):
208-
# coerce back to datetime objects for lookup
209-
data = _dict_compat(data)
210-
data = lib.fast_multiget(data,
211-
index.asobject.values,
212-
default=np.nan)
213-
else:
214-
data = np.nan
215-
# GH #12169
216-
elif isinstance(index, (PeriodIndex, TimedeltaIndex)):
217-
data = ([data.get(i, np.nan) for i in index]
218-
if data else np.nan)
219-
else:
220-
data = lib.fast_multiget(data, index.values,
221-
default=np.nan)
207+
data = index.get_values_from_dict(data)
222208
except TypeError:
223209
data = ([data.get(i, np.nan) for i in index]
224210
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
@@ -829,10 +829,10 @@ def test_map_tseries_indices_return_index(self):
829829
exp = Index(range(24), name='hourly')
830830
tm.assert_index_equal(exp, date_index.map(lambda x: x.hour))
831831

832-
def test_map_with_series_all_indices(self):
832+
def test_map_with_dict_and_series(self):
833833
expected = Index(['foo', 'bar', 'baz'])
834834
mapper = Series(expected.values, index=[0, 1, 2])
835-
self.assert_index_equal(tm.makeIntIndex(3).map(mapper), expected)
835+
tm.assert_index_equal(tm.makeIntIndex(3).map(mapper), expected)
836836

837837
# GH 12766
838838
# special = []
@@ -842,41 +842,58 @@ def test_map_with_series_all_indices(self):
842842
orig_values = ['a', 'B', 1, 'a']
843843
new_values = ['one', 2, 3.0, 'one']
844844
cur_index = CategoricalIndex(orig_values, name='XXX')
845+
expected = CategoricalIndex(new_values,
846+
name='XXX', categories=[3.0, 2, 'one'])
847+
845848
mapper = pd.Series(new_values[:-1], index=orig_values[:-1])
846-
expected = CategoricalIndex(new_values, name='XXX')
847849
output = cur_index.map(mapper)
848-
self.assert_numpy_array_equal(expected.values.get_values(), output.values.get_values())
849-
self.assert_equal(expected.name, output.name)
850+
# Order of categories in output can be different
851+
tm.assert_index_equal(expected, output)
852+
853+
mapper = {o: n for o, n in
854+
zip(orig_values[:-1], new_values[:-1])}
855+
output = cur_index.map(mapper)
856+
# Order of categories in output can be different
857+
tm.assert_index_equal(expected, output)
850858

851859
for name in list(set(self.indices.keys()) - set(special)):
852860
cur_index = self.indices[name]
853861
expected = Index(np.arange(len(cur_index), 0, -1))
854-
mapper = pd.Series(expected.values, index=cur_index)
855-
print(name)
856-
output = cur_index.map(mapper)
857-
self.assert_index_equal(expected, cur_index.map(mapper))
862+
mapper = pd.Series(expected, index=cur_index)
863+
tm.assert_index_equal(expected, cur_index.map(mapper))
864+
865+
mapper = {o: n for o, n in
866+
zip(cur_index, expected)}
867+
if mapper:
868+
tm.assert_index_equal(expected, cur_index.map(mapper))
869+
else:
870+
# The expected index type is Int64Index
871+
# but the output defaults to Float64
872+
tm.assert_index_equal(Float64Index([]),
873+
cur_index.map(mapper))
858874

859875
def test_map_with_categorical_series(self):
860876
# GH 12756
861877
a = Index([1, 2, 3, 4])
862-
b = Series(["even", "odd", "even", "odd"], dtype="category")
878+
b = Series(["even", "odd", "even", "odd"],
879+
dtype="category")
863880
c = Series(["even", "odd", "even", "odd"])
864881

865882
exp = CategoricalIndex(["odd", "even", "odd", np.nan])
866-
self.assert_index_equal(a.map(b), exp)
883+
tm.assert_index_equal(a.map(b), exp)
867884
exp = Index(["odd", "even", "odd", np.nan])
868-
self.assert_index_equal(a.map(c), exp)
885+
tm.assert_index_equal(a.map(c), exp)
869886

870887
def test_map_with_non_function_missing_values(self):
871888
# GH 12756
872889
expected = Index([2., np.nan, 'foo'])
873890
input = Index([2, 1, 0])
874891

875892
mapper = Series(['foo', 2., 'baz'], index=[0, 2, -1])
876-
self.assert_index_equal(expected, input.map(mapper))
893+
tm.assert_index_equal(expected, input.map(mapper))
877894

878895
mapper = {0: 'foo', 2: 2.0, -1: 'baz'}
879-
self.assert_index_equal(expected, input.map(mapper))
896+
tm.assert_index_equal(expected, input.map(mapper))
880897

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

pandas/tseries/tests/test_period.py

Whitespace-only changes.

0 commit comments

Comments
 (0)