diff --git a/doc/source/v0.14.1.txt b/doc/source/v0.14.1.txt index f4a179c60c76c..97e6ee51511bc 100644 --- a/doc/source/v0.14.1.txt +++ b/doc/source/v0.14.1.txt @@ -89,7 +89,7 @@ Enhancements - Add ``NotImplementedError`` for simultaneous use of ``chunksize`` and ``nrows`` for read_csv() (:issue:`6774`). - +- ``PeriodIndex`` is represented as the same format as ``DatetimeIndex`` (:issue:`7601`) diff --git a/pandas/core/base.py b/pandas/core/base.py index aff2713ee85f5..b06b0856d5909 100644 --- a/pandas/core/base.py +++ b/pandas/core/base.py @@ -432,4 +432,36 @@ def max(self, axis=None): max_stamp = masked.asi8.max() return self._box_func(max_stamp) + @property + def _formatter_func(self): + """ + Format function to convert value to representation + """ + return str + + def _format_footer(self): + tagline = 'Length: %d, Freq: %s, Timezone: %s' + return tagline % (len(self), self.freqstr, self.tz) + + def __unicode__(self): + formatter = self._formatter_func + summary = str(self.__class__) + '\n' + + n = len(self) + if n == 0: + pass + elif n == 1: + first = formatter(self[0]) + summary += '[%s]\n' % first + elif n == 2: + first = formatter(self[0]) + last = formatter(self[-1]) + summary += '[%s, %s]\n' % (first, last) + else: + first = formatter(self[0]) + last = formatter(self[-1]) + summary += '[%s, ..., %s]\n' % (first, last) + + summary += self._format_footer() + return summary diff --git a/pandas/tests/test_base.py b/pandas/tests/test_base.py index f41e745013f08..c2fb7017ee4d6 100644 --- a/pandas/tests/test_base.py +++ b/pandas/tests/test_base.py @@ -572,6 +572,40 @@ def test_minmax(self): obj = DatetimeIndex([pd.NaT, pd.NaT, pd.NaT]) self.assertTrue(pd.isnull(getattr(obj, op)())) + def test_representation(self): + idx1 = DatetimeIndex([], freq='D') + idx2 = DatetimeIndex(['2011-01-01'], freq='D') + idx3 = DatetimeIndex(['2011-01-01', '2011-01-02'], freq='D') + idx4 = DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], freq='D') + idx5 = DatetimeIndex(['2011-01-01 09:00', '2011-01-01 10:00', '2011-01-01 11:00'], + freq='H', tz='Asia/Tokyo') + idx6 = DatetimeIndex(['2011-01-01 09:00', '2011-01-01 10:00', pd.NaT], + tz='US/Eastern') + + exp1 = """ +Length: 0, Freq: D, Timezone: None""" + exp2 = """ +[2011-01-01] +Length: 1, Freq: D, Timezone: None""" + exp3 = """ +[2011-01-01, 2011-01-02] +Length: 2, Freq: D, Timezone: None""" + exp4 = """ +[2011-01-01, ..., 2011-01-03] +Length: 3, Freq: D, Timezone: None""" + exp5 = """ +[2011-01-01 09:00:00+09:00, ..., 2011-01-01 11:00:00+09:00] +Length: 3, Freq: H, Timezone: Asia/Tokyo""" + exp6 = """ +[2011-01-01 09:00:00-05:00, ..., NaT] +Length: 3, Freq: None, Timezone: US/Eastern""" + + for idx, expected in zip([idx1, idx2, idx3, idx4, idx5, idx6], + [exp1, exp2, exp3, exp4, exp5, exp6]): + for func in ['__repr__', '__unicode__', '__str__']: + result = getattr(idx, func)() + self.assertEqual(result, expected) + class TestPeriodIndexOps(Ops): _allowed = '_allow_period_index_ops' @@ -650,6 +684,52 @@ def test_minmax(self): self.assertEqual(result.ordinal, tslib.iNaT) self.assertEqual(result.freq, 'M') + def test_representation(self): + # GH 7601 + idx1 = PeriodIndex([], freq='D') + idx2 = PeriodIndex(['2011-01-01'], freq='D') + idx3 = PeriodIndex(['2011-01-01', '2011-01-02'], freq='D') + idx4 = PeriodIndex(['2011-01-01', '2011-01-02', '2011-01-03'], freq='D') + idx5 = PeriodIndex(['2011', '2012', '2013'], freq='A') + idx6 = PeriodIndex(['2011-01-01 09:00', '2012-02-01 10:00', 'NaT'], freq='H') + + idx7 = pd.period_range('2013Q1', periods=1, freq="Q") + idx8 = pd.period_range('2013Q1', periods=2, freq="Q") + idx9 = pd.period_range('2013Q1', periods=3, freq="Q") + + exp1 = """ +Length: 0, Freq: D""" + exp2 = """ +[2011-01-01] +Length: 1, Freq: D""" + exp3 = """ +[2011-01-01, 2011-01-02] +Length: 2, Freq: D""" + exp4 = """ +[2011-01-01, ..., 2011-01-03] +Length: 3, Freq: D""" + exp5 = """ +[2011, ..., 2013] +Length: 3, Freq: A-DEC""" + exp6 = """ +[2011-01-01 09:00, ..., NaT] +Length: 3, Freq: H""" + exp7 = """ +[2013Q1] +Length: 1, Freq: Q-DEC""" + exp8 = """ +[2013Q1, 2013Q2] +Length: 2, Freq: Q-DEC""" + exp9 = """ +[2013Q1, ..., 2013Q3] +Length: 3, Freq: Q-DEC""" + + for idx, expected in zip([idx1, idx2, idx3, idx4, idx5, idx6, idx7, idx8, idx9], + [exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8, exp9]): + for func in ['__repr__', '__unicode__', '__str__']: + result = getattr(idx, func)() + self.assertEqual(result, expected) + if __name__ == '__main__': import nose diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 9761f22e4520d..9473b10876600 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -565,31 +565,11 @@ def _is_dates_only(self): from pandas.core.format import _is_dates_only return _is_dates_only(self.values) - def __unicode__(self): + @property + def _formatter_func(self): from pandas.core.format import _get_format_datetime64 - formatter = _get_format_datetime64(is_dates_only=self._is_dates_only) - - values = self.values - - freq = self.freqstr - summary = str(self.__class__) - if len(self) == 1: - first = formatter(values[0], tz=self.tz) - summary += '\n[%s]' % first - elif len(self) == 2: - first = formatter(values[0], tz=self.tz) - last = formatter(values[-1], tz=self.tz) - summary += '\n[%s, %s]' % (first, last) - elif len(self) > 2: - first = formatter(values[0], tz=self.tz) - last = formatter(values[-1], tz=self.tz) - summary += '\n[%s, ..., %s]' % (first, last) - - tagline = '\nLength: %d, Freq: %s, Timezone: %s' - summary += tagline % (len(self), freq, self.tz) - - return summary + return lambda x: formatter(x, tz=self.tz) def __reduce__(self): """Necessary for making this object picklable""" diff --git a/pandas/tseries/period.py b/pandas/tseries/period.py index 5ded7161130bc..77e9677f0b723 100644 --- a/pandas/tseries/period.py +++ b/pandas/tseries/period.py @@ -1129,37 +1129,9 @@ def __array_finalize__(self, obj): self.name = getattr(obj, 'name', None) self._reset_identity() - def __repr__(self): - output = com.pprint_thing(self.__class__) + '\n' - output += 'freq: %s\n' % self.freq - n = len(self) - if n == 1: - output += '[%s]\n' % (self[0]) - elif n == 2: - output += '[%s, %s]\n' % (self[0], self[-1]) - elif n: - output += '[%s, ..., %s]\n' % (self[0], self[-1]) - output += 'length: %d' % n - return output - - def __unicode__(self): - output = self.__class__.__name__ - output += u('(') - prefix = '' if compat.PY3 else 'u' - mapper = "{0}'{{0}}'".format(prefix) - output += '[{0}]'.format(', '.join(map(mapper.format, self))) - output += ", freq='{0}'".format(self.freq) - output += ')' - return output - - def __bytes__(self): - encoding = com.get_option('display.encoding') - return self.__unicode__().encode(encoding, 'replace') - - def __str__(self): - if compat.PY3: - return self.__unicode__() - return self.__bytes__() + def _format_footer(self): + tagline = 'Length: %d, Freq: %s' + return tagline % (len(self), self.freqstr) def take(self, indices, axis=None): """ diff --git a/pandas/tseries/tests/test_period.py b/pandas/tseries/tests/test_period.py index 84c0c40de369a..c4bac4b9b14f0 100644 --- a/pandas/tseries/tests/test_period.py +++ b/pandas/tseries/tests/test_period.py @@ -1798,65 +1798,39 @@ def test_asfreq_nat(self): expected = PeriodIndex(['2011Q1', '2011Q1', 'NaT', '2011Q2'], freq='Q') self.assertTrue(result.equals(expected)) - def test_ts_repr(self): - index = PeriodIndex(freq='A', start='1/1/2001', end='12/31/2010') - ts = Series(np.random.randn(len(index)), index=index) - repr(ts) # ?? - - val = period_range('2013Q1', periods=1, freq="Q") - expected = "\nfreq: Q-DEC\n[2013Q1]\nlength: 1" - assert_equal(repr(val), expected) - - val = period_range('2013Q1', periods=2, freq="Q") - expected = "\nfreq: Q-DEC\n[2013Q1, 2013Q2]\nlength: 2" - assert_equal(repr(val), expected) - - val = period_range('2013Q1', periods=3, freq="Q") - expected = "\nfreq: Q-DEC\n[2013Q1, ..., 2013Q3]\nlength: 3" - assert_equal(repr(val), expected) - - def test_period_index_unicode(self): + def test_period_index_length(self): pi = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009') assert_equal(len(pi), 9) - assert_equal(pi, eval(compat.text_type(pi))) pi = PeriodIndex(freq='Q', start='1/1/2001', end='12/1/2009') assert_equal(len(pi), 4 * 9) - assert_equal(pi, eval(compat.text_type(pi))) pi = PeriodIndex(freq='M', start='1/1/2001', end='12/1/2009') assert_equal(len(pi), 12 * 9) - assert_equal(pi, eval(compat.text_type(pi))) start = Period('02-Apr-2005', 'B') i1 = PeriodIndex(start=start, periods=20) assert_equal(len(i1), 20) assert_equal(i1.freq, start.freq) assert_equal(i1[0], start) - assert_equal(i1, eval(compat.text_type(i1))) end_intv = Period('2006-12-31', 'W') i1 = PeriodIndex(end=end_intv, periods=10) assert_equal(len(i1), 10) assert_equal(i1.freq, end_intv.freq) assert_equal(i1[-1], end_intv) - assert_equal(i1, eval(compat.text_type(i1))) end_intv = Period('2006-12-31', '1w') i2 = PeriodIndex(end=end_intv, periods=10) assert_equal(len(i1), len(i2)) self.assertTrue((i1 == i2).all()) assert_equal(i1.freq, i2.freq) - assert_equal(i1, eval(compat.text_type(i1))) - assert_equal(i2, eval(compat.text_type(i2))) end_intv = Period('2006-12-31', ('w', 1)) i2 = PeriodIndex(end=end_intv, periods=10) assert_equal(len(i1), len(i2)) self.assertTrue((i1 == i2).all()) assert_equal(i1.freq, i2.freq) - assert_equal(i1, eval(compat.text_type(i1))) - assert_equal(i2, eval(compat.text_type(i2))) try: PeriodIndex(start=start, end=end_intv) @@ -1866,7 +1840,6 @@ def test_period_index_unicode(self): end_intv = Period('2005-05-01', 'B') i1 = PeriodIndex(start=start, end=end_intv) - assert_equal(i1, eval(compat.text_type(i1))) try: PeriodIndex(start=start) @@ -1879,12 +1852,10 @@ def test_period_index_unicode(self): i2 = PeriodIndex([end_intv, Period('2005-05-05', 'B')]) assert_equal(len(i2), 2) assert_equal(i2[0], end_intv) - assert_equal(i2, eval(compat.text_type(i2))) i2 = PeriodIndex(np.array([end_intv, Period('2005-05-05', 'B')])) assert_equal(len(i2), 2) assert_equal(i2[0], end_intv) - assert_equal(i2, eval(compat.text_type(i2))) # Mixed freq should fail vals = [end_intv, Period('2006-12-31', 'w')]