diff --git a/doc/source/release.rst b/doc/source/release.rst index 7c09c2a6f16ac..62d3201eb3a58 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -48,6 +48,8 @@ pandas 0.13 with a different block ordering (:issue:`4096`) - The ``by`` argument now works correctly with the ``layout`` argument (:issue:`4102`, :issue:`4014`) in ``*.hist`` plotting methods + - Fixed bug in ``PeriodIndex.map`` where using ``str`` would return the str + representation of the index (:issue:`4136`) pandas 0.12 =========== diff --git a/doc/source/v0.13.0.txt b/doc/source/v0.13.0.txt index 7f63c545c5664..a8b61f338d044 100644 --- a/doc/source/v0.13.0.txt +++ b/doc/source/v0.13.0.txt @@ -24,6 +24,9 @@ Bug Fixes - The ``by`` argument now works correctly with the ``layout`` argument (:issue:`4102`, :issue:`4014`) in ``*.hist`` plotting methods + - Fixed bug in ``PeriodIndex.map`` where using ``str`` would return the str + representation of the index (:issue:`4136`) + See the :ref:`full release notes ` or issue tracker on GitHub for a complete list. diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index 2db32b14e2eb3..4fec590dddd14 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -469,7 +469,6 @@ def dt64arr_to_periodarr(data, freq, tz): # --- Period index sketch - def _period_index_cmp(opname): """ Wrap comparison operations to convert datetime-like to datetime64 @@ -493,6 +492,7 @@ def wrapper(self, other): return result return wrapper + class PeriodIndex(Int64Index): """ Immutable ndarray holding ordinal values indicating regular periods in @@ -791,10 +791,12 @@ def to_datetime(self, dayfirst=False): # Especially important for group-by functionality def map(self, f): try: - return f(self) - except: - values = self._get_object_array() - return _algos.arrmap_object(values, f) + result = f(self) + if not isinstance(result, np.ndarray): + raise TypeError + return result + except Exception: + return _algos.arrmap_object(self.asobject, f) def _get_object_array(self): freq = self.freq @@ -1169,6 +1171,7 @@ def __setstate__(self, state): else: # pragma: no cover np.ndarray.__setstate__(self, state) + def _get_ordinal_range(start, end, periods, freq): if com._count_not_none(start, end, periods) < 2: raise ValueError('Must specify 2 of start, end, periods') diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py index 01c984ec2b07d..9fd5e6bf5f3e9 100644 --- a/pandas/tseries/tests/test_period.py +++ b/pandas/tseries/tests/test_period.py @@ -27,6 +27,7 @@ from pandas import Series, TimeSeries, DataFrame from pandas.util.testing import assert_series_equal, assert_almost_equal import pandas.util.testing as tm +from pandas.util import py3compat from numpy.testing import assert_array_equal @@ -1990,7 +1991,30 @@ def test_map(self): result = index.map(lambda x: x.ordinal) exp = [x.ordinal for x in index] - self.assert_(np.array_equal(result, exp)) + assert_array_equal(result, exp) + + def test_map_with_string_constructor(self): + raw = [2005, 2007, 2009] + index = PeriodIndex(raw, freq='A') + types = str, + if not py3compat.PY3: + types += unicode, + + for t in types: + expected = np.array(map(t, raw), dtype=object) + res = index.map(t) + + # should return an array + self.assert_(isinstance(res, np.ndarray)) + + # preserve element types + self.assert_(all(isinstance(resi, t) for resi in res)) + + # dtype should be object + self.assertEqual(res.dtype, np.dtype('object').type) + + # lastly, values should compare equal + assert_array_equal(res, expected) def test_convert_array_of_periods(self): rng = period_range('1/1/2000', periods=20, freq='D')