Skip to content

Commit b747903

Browse files
committed
Merge pull request #7602 from sinhrks/periodrepr
API: DatetimeIndex and PeriodIndex have same representation
2 parents 3b7916f + 2776286 commit b747903

File tree

6 files changed

+120
-85
lines changed

6 files changed

+120
-85
lines changed

doc/source/v0.14.1.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Enhancements
8989
- Add ``NotImplementedError`` for simultaneous use of ``chunksize`` and ``nrows``
9090
for read_csv() (:issue:`6774`).
9191

92-
92+
- ``PeriodIndex`` is represented as the same format as ``DatetimeIndex`` (:issue:`7601`)
9393

9494

9595

pandas/core/base.py

+32
Original file line numberDiff line numberDiff line change
@@ -432,4 +432,36 @@ def max(self, axis=None):
432432
max_stamp = masked.asi8.max()
433433
return self._box_func(max_stamp)
434434

435+
@property
436+
def _formatter_func(self):
437+
"""
438+
Format function to convert value to representation
439+
"""
440+
return str
441+
442+
def _format_footer(self):
443+
tagline = 'Length: %d, Freq: %s, Timezone: %s'
444+
return tagline % (len(self), self.freqstr, self.tz)
445+
446+
def __unicode__(self):
447+
formatter = self._formatter_func
448+
summary = str(self.__class__) + '\n'
449+
450+
n = len(self)
451+
if n == 0:
452+
pass
453+
elif n == 1:
454+
first = formatter(self[0])
455+
summary += '[%s]\n' % first
456+
elif n == 2:
457+
first = formatter(self[0])
458+
last = formatter(self[-1])
459+
summary += '[%s, %s]\n' % (first, last)
460+
else:
461+
first = formatter(self[0])
462+
last = formatter(self[-1])
463+
summary += '[%s, ..., %s]\n' % (first, last)
464+
465+
summary += self._format_footer()
466+
return summary
435467

pandas/tests/test_base.py

+80
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,40 @@ def test_minmax(self):
572572
obj = DatetimeIndex([pd.NaT, pd.NaT, pd.NaT])
573573
self.assertTrue(pd.isnull(getattr(obj, op)()))
574574

575+
def test_representation(self):
576+
idx1 = DatetimeIndex([], freq='D')
577+
idx2 = DatetimeIndex(['2011-01-01'], freq='D')
578+
idx3 = DatetimeIndex(['2011-01-01', '2011-01-02'], freq='D')
579+
idx4 = DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], freq='D')
580+
idx5 = DatetimeIndex(['2011-01-01 09:00', '2011-01-01 10:00', '2011-01-01 11:00'],
581+
freq='H', tz='Asia/Tokyo')
582+
idx6 = DatetimeIndex(['2011-01-01 09:00', '2011-01-01 10:00', pd.NaT],
583+
tz='US/Eastern')
584+
585+
exp1 = """<class 'pandas.tseries.index.DatetimeIndex'>
586+
Length: 0, Freq: D, Timezone: None"""
587+
exp2 = """<class 'pandas.tseries.index.DatetimeIndex'>
588+
[2011-01-01]
589+
Length: 1, Freq: D, Timezone: None"""
590+
exp3 = """<class 'pandas.tseries.index.DatetimeIndex'>
591+
[2011-01-01, 2011-01-02]
592+
Length: 2, Freq: D, Timezone: None"""
593+
exp4 = """<class 'pandas.tseries.index.DatetimeIndex'>
594+
[2011-01-01, ..., 2011-01-03]
595+
Length: 3, Freq: D, Timezone: None"""
596+
exp5 = """<class 'pandas.tseries.index.DatetimeIndex'>
597+
[2011-01-01 09:00:00+09:00, ..., 2011-01-01 11:00:00+09:00]
598+
Length: 3, Freq: H, Timezone: Asia/Tokyo"""
599+
exp6 = """<class 'pandas.tseries.index.DatetimeIndex'>
600+
[2011-01-01 09:00:00-05:00, ..., NaT]
601+
Length: 3, Freq: None, Timezone: US/Eastern"""
602+
603+
for idx, expected in zip([idx1, idx2, idx3, idx4, idx5, idx6],
604+
[exp1, exp2, exp3, exp4, exp5, exp6]):
605+
for func in ['__repr__', '__unicode__', '__str__']:
606+
result = getattr(idx, func)()
607+
self.assertEqual(result, expected)
608+
575609

576610
class TestPeriodIndexOps(Ops):
577611
_allowed = '_allow_period_index_ops'
@@ -650,6 +684,52 @@ def test_minmax(self):
650684
self.assertEqual(result.ordinal, tslib.iNaT)
651685
self.assertEqual(result.freq, 'M')
652686

687+
def test_representation(self):
688+
# GH 7601
689+
idx1 = PeriodIndex([], freq='D')
690+
idx2 = PeriodIndex(['2011-01-01'], freq='D')
691+
idx3 = PeriodIndex(['2011-01-01', '2011-01-02'], freq='D')
692+
idx4 = PeriodIndex(['2011-01-01', '2011-01-02', '2011-01-03'], freq='D')
693+
idx5 = PeriodIndex(['2011', '2012', '2013'], freq='A')
694+
idx6 = PeriodIndex(['2011-01-01 09:00', '2012-02-01 10:00', 'NaT'], freq='H')
695+
696+
idx7 = pd.period_range('2013Q1', periods=1, freq="Q")
697+
idx8 = pd.period_range('2013Q1', periods=2, freq="Q")
698+
idx9 = pd.period_range('2013Q1', periods=3, freq="Q")
699+
700+
exp1 = """<class 'pandas.tseries.period.PeriodIndex'>
701+
Length: 0, Freq: D"""
702+
exp2 = """<class 'pandas.tseries.period.PeriodIndex'>
703+
[2011-01-01]
704+
Length: 1, Freq: D"""
705+
exp3 = """<class 'pandas.tseries.period.PeriodIndex'>
706+
[2011-01-01, 2011-01-02]
707+
Length: 2, Freq: D"""
708+
exp4 = """<class 'pandas.tseries.period.PeriodIndex'>
709+
[2011-01-01, ..., 2011-01-03]
710+
Length: 3, Freq: D"""
711+
exp5 = """<class 'pandas.tseries.period.PeriodIndex'>
712+
[2011, ..., 2013]
713+
Length: 3, Freq: A-DEC"""
714+
exp6 = """<class 'pandas.tseries.period.PeriodIndex'>
715+
[2011-01-01 09:00, ..., NaT]
716+
Length: 3, Freq: H"""
717+
exp7 = """<class 'pandas.tseries.period.PeriodIndex'>
718+
[2013Q1]
719+
Length: 1, Freq: Q-DEC"""
720+
exp8 = """<class 'pandas.tseries.period.PeriodIndex'>
721+
[2013Q1, 2013Q2]
722+
Length: 2, Freq: Q-DEC"""
723+
exp9 = """<class 'pandas.tseries.period.PeriodIndex'>
724+
[2013Q1, ..., 2013Q3]
725+
Length: 3, Freq: Q-DEC"""
726+
727+
for idx, expected in zip([idx1, idx2, idx3, idx4, idx5, idx6, idx7, idx8, idx9],
728+
[exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8, exp9]):
729+
for func in ['__repr__', '__unicode__', '__str__']:
730+
result = getattr(idx, func)()
731+
self.assertEqual(result, expected)
732+
653733

654734
if __name__ == '__main__':
655735
import nose

pandas/tseries/index.py

+3-23
Original file line numberDiff line numberDiff line change
@@ -565,31 +565,11 @@ def _is_dates_only(self):
565565
from pandas.core.format import _is_dates_only
566566
return _is_dates_only(self.values)
567567

568-
def __unicode__(self):
568+
@property
569+
def _formatter_func(self):
569570
from pandas.core.format import _get_format_datetime64
570-
571571
formatter = _get_format_datetime64(is_dates_only=self._is_dates_only)
572-
573-
values = self.values
574-
575-
freq = self.freqstr
576-
summary = str(self.__class__)
577-
if len(self) == 1:
578-
first = formatter(values[0], tz=self.tz)
579-
summary += '\n[%s]' % first
580-
elif len(self) == 2:
581-
first = formatter(values[0], tz=self.tz)
582-
last = formatter(values[-1], tz=self.tz)
583-
summary += '\n[%s, %s]' % (first, last)
584-
elif len(self) > 2:
585-
first = formatter(values[0], tz=self.tz)
586-
last = formatter(values[-1], tz=self.tz)
587-
summary += '\n[%s, ..., %s]' % (first, last)
588-
589-
tagline = '\nLength: %d, Freq: %s, Timezone: %s'
590-
summary += tagline % (len(self), freq, self.tz)
591-
592-
return summary
572+
return lambda x: formatter(x, tz=self.tz)
593573

594574
def __reduce__(self):
595575
"""Necessary for making this object picklable"""

pandas/tseries/period.py

+3-31
Original file line numberDiff line numberDiff line change
@@ -1129,37 +1129,9 @@ def __array_finalize__(self, obj):
11291129
self.name = getattr(obj, 'name', None)
11301130
self._reset_identity()
11311131

1132-
def __repr__(self):
1133-
output = com.pprint_thing(self.__class__) + '\n'
1134-
output += 'freq: %s\n' % self.freq
1135-
n = len(self)
1136-
if n == 1:
1137-
output += '[%s]\n' % (self[0])
1138-
elif n == 2:
1139-
output += '[%s, %s]\n' % (self[0], self[-1])
1140-
elif n:
1141-
output += '[%s, ..., %s]\n' % (self[0], self[-1])
1142-
output += 'length: %d' % n
1143-
return output
1144-
1145-
def __unicode__(self):
1146-
output = self.__class__.__name__
1147-
output += u('(')
1148-
prefix = '' if compat.PY3 else 'u'
1149-
mapper = "{0}'{{0}}'".format(prefix)
1150-
output += '[{0}]'.format(', '.join(map(mapper.format, self)))
1151-
output += ", freq='{0}'".format(self.freq)
1152-
output += ')'
1153-
return output
1154-
1155-
def __bytes__(self):
1156-
encoding = com.get_option('display.encoding')
1157-
return self.__unicode__().encode(encoding, 'replace')
1158-
1159-
def __str__(self):
1160-
if compat.PY3:
1161-
return self.__unicode__()
1162-
return self.__bytes__()
1132+
def _format_footer(self):
1133+
tagline = 'Length: %d, Freq: %s'
1134+
return tagline % (len(self), self.freqstr)
11631135

11641136
def take(self, indices, axis=None):
11651137
"""

pandas/tseries/tests/test_period.py

+1-30
Original file line numberDiff line numberDiff line change
@@ -1798,65 +1798,39 @@ def test_asfreq_nat(self):
17981798
expected = PeriodIndex(['2011Q1', '2011Q1', 'NaT', '2011Q2'], freq='Q')
17991799
self.assertTrue(result.equals(expected))
18001800

1801-
def test_ts_repr(self):
1802-
index = PeriodIndex(freq='A', start='1/1/2001', end='12/31/2010')
1803-
ts = Series(np.random.randn(len(index)), index=index)
1804-
repr(ts) # ??
1805-
1806-
val = period_range('2013Q1', periods=1, freq="Q")
1807-
expected = "<class 'pandas.tseries.period.PeriodIndex'>\nfreq: Q-DEC\n[2013Q1]\nlength: 1"
1808-
assert_equal(repr(val), expected)
1809-
1810-
val = period_range('2013Q1', periods=2, freq="Q")
1811-
expected = "<class 'pandas.tseries.period.PeriodIndex'>\nfreq: Q-DEC\n[2013Q1, 2013Q2]\nlength: 2"
1812-
assert_equal(repr(val), expected)
1813-
1814-
val = period_range('2013Q1', periods=3, freq="Q")
1815-
expected = "<class 'pandas.tseries.period.PeriodIndex'>\nfreq: Q-DEC\n[2013Q1, ..., 2013Q3]\nlength: 3"
1816-
assert_equal(repr(val), expected)
1817-
1818-
def test_period_index_unicode(self):
1801+
def test_period_index_length(self):
18191802
pi = PeriodIndex(freq='A', start='1/1/2001', end='12/1/2009')
18201803
assert_equal(len(pi), 9)
1821-
assert_equal(pi, eval(compat.text_type(pi)))
18221804

18231805
pi = PeriodIndex(freq='Q', start='1/1/2001', end='12/1/2009')
18241806
assert_equal(len(pi), 4 * 9)
1825-
assert_equal(pi, eval(compat.text_type(pi)))
18261807

18271808
pi = PeriodIndex(freq='M', start='1/1/2001', end='12/1/2009')
18281809
assert_equal(len(pi), 12 * 9)
1829-
assert_equal(pi, eval(compat.text_type(pi)))
18301810

18311811
start = Period('02-Apr-2005', 'B')
18321812
i1 = PeriodIndex(start=start, periods=20)
18331813
assert_equal(len(i1), 20)
18341814
assert_equal(i1.freq, start.freq)
18351815
assert_equal(i1[0], start)
1836-
assert_equal(i1, eval(compat.text_type(i1)))
18371816

18381817
end_intv = Period('2006-12-31', 'W')
18391818
i1 = PeriodIndex(end=end_intv, periods=10)
18401819
assert_equal(len(i1), 10)
18411820
assert_equal(i1.freq, end_intv.freq)
18421821
assert_equal(i1[-1], end_intv)
1843-
assert_equal(i1, eval(compat.text_type(i1)))
18441822

18451823
end_intv = Period('2006-12-31', '1w')
18461824
i2 = PeriodIndex(end=end_intv, periods=10)
18471825
assert_equal(len(i1), len(i2))
18481826
self.assertTrue((i1 == i2).all())
18491827
assert_equal(i1.freq, i2.freq)
1850-
assert_equal(i1, eval(compat.text_type(i1)))
1851-
assert_equal(i2, eval(compat.text_type(i2)))
18521828

18531829
end_intv = Period('2006-12-31', ('w', 1))
18541830
i2 = PeriodIndex(end=end_intv, periods=10)
18551831
assert_equal(len(i1), len(i2))
18561832
self.assertTrue((i1 == i2).all())
18571833
assert_equal(i1.freq, i2.freq)
1858-
assert_equal(i1, eval(compat.text_type(i1)))
1859-
assert_equal(i2, eval(compat.text_type(i2)))
18601834

18611835
try:
18621836
PeriodIndex(start=start, end=end_intv)
@@ -1866,7 +1840,6 @@ def test_period_index_unicode(self):
18661840

18671841
end_intv = Period('2005-05-01', 'B')
18681842
i1 = PeriodIndex(start=start, end=end_intv)
1869-
assert_equal(i1, eval(compat.text_type(i1)))
18701843

18711844
try:
18721845
PeriodIndex(start=start)
@@ -1879,12 +1852,10 @@ def test_period_index_unicode(self):
18791852
i2 = PeriodIndex([end_intv, Period('2005-05-05', 'B')])
18801853
assert_equal(len(i2), 2)
18811854
assert_equal(i2[0], end_intv)
1882-
assert_equal(i2, eval(compat.text_type(i2)))
18831855

18841856
i2 = PeriodIndex(np.array([end_intv, Period('2005-05-05', 'B')]))
18851857
assert_equal(len(i2), 2)
18861858
assert_equal(i2[0], end_intv)
1887-
assert_equal(i2, eval(compat.text_type(i2)))
18881859

18891860
# Mixed freq should fail
18901861
vals = [end_intv, Period('2006-12-31', 'w')]

0 commit comments

Comments
 (0)