Skip to content

Commit 1e8ed10

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

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
@@ -514,6 +514,7 @@ Other Enhancements
514514
- ``parallel_coordinates()`` has gained a ``sort_labels`` keyword argument that sorts class labels and the colors assigned to them (:issue:`15908`)
515515
- Options added to allow one to turn on/off using ``bottleneck`` and ``numexpr``, see :ref:`here <basics.accelerate>` (:issue:`16157`)
516516
- ``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`)
517+
- ``Index.map`` can now accept series and dictionary input object (:issue:`12756`).
517518

518519

519520
.. _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
@@ -2668,6 +2668,25 @@ def get_indexer_for(self, target, **kwargs):
26682668
indexer, _ = self.get_indexer_non_unique(target, **kwargs)
26692669
return indexer
26702670

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

27272746
if isinstance(mapper, ABCSeries):
2728-
indexer = mapper.index.get_indexer(self._values)
2747+
indexer = mapper.index.get_indexer(self.values)
27292748
mapped_values = algos.take_1d(mapper.values, indexer)
2749+
elif isinstance(mapper, dict):
2750+
idx = Index(mapper.keys())
2751+
data = idx.get_values_from_dict(mapper)
2752+
indexer = idx.get_indexer(self.values)
2753+
mapped_values = algos.take_1d(data, indexer)
27302754
else:
2731-
if isinstance(mapper, dict):
2732-
mapper = mapper.get
2733-
mapped_values = self._arrmap(self._values, mapper)
2755+
mapped_values = self._arrmap(self.values, mapper)
27342756

27352757
attributes = self._get_attributes_dict()
27362758
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
@@ -187,23 +187,9 @@ def __init__(self, data=None, index=None, dtype=None, name=None,
187187
index = Index(data)
188188
else:
189189
index = Index(_try_sort(data))
190+
190191
try:
191-
if isinstance(index, DatetimeIndex):
192-
if len(data):
193-
# coerce back to datetime objects for lookup
194-
data = _dict_compat(data)
195-
data = lib.fast_multiget(data,
196-
index.asobject.values,
197-
default=np.nan)
198-
else:
199-
data = np.nan
200-
# GH #12169
201-
elif isinstance(index, (PeriodIndex, TimedeltaIndex)):
202-
data = ([data.get(i, nan) for i in index]
203-
if data else np.nan)
204-
else:
205-
data = lib.fast_multiget(data, index.values,
206-
default=np.nan)
192+
data = index.get_values_from_dict(data)
207193
except TypeError:
208194
data = ([data.get(i, nan) for i in index]
209195
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
@@ -821,10 +821,10 @@ def test_map_tseries_indices_return_index(self):
821821
exp = Index(range(24), name='hourly')
822822
tm.assert_index_equal(exp, date_index.map(lambda x: x.hour))
823823

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

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

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

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

857874
exp = CategoricalIndex(["odd", "even", "odd", np.nan])
858-
self.assert_index_equal(a.map(b), exp)
875+
tm.assert_index_equal(a.map(b), exp)
859876
exp = Index(["odd", "even", "odd", np.nan])
860-
self.assert_index_equal(a.map(c), exp)
877+
tm.assert_index_equal(a.map(c), exp)
861878

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

867884
mapper = Series(['foo', 2., 'baz'], index=[0, 2, -1])
868-
self.assert_index_equal(expected, input.map(mapper))
885+
tm.assert_index_equal(expected, input.map(mapper))
869886

870887
mapper = {0: 'foo', 2: 2.0, -1: 'baz'}
871-
self.assert_index_equal(expected, input.map(mapper))
888+
tm.assert_index_equal(expected, input.map(mapper))
872889

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

pandas/tseries/tests/test_period.py

Whitespace-only changes.

0 commit comments

Comments
 (0)