Skip to content

Commit f953727

Browse files
committed
BUG: fix concat failure with PeriodIndex, close #1815
1 parent 458db04 commit f953727

File tree

5 files changed

+80
-14
lines changed

5 files changed

+80
-14
lines changed

RELEASE.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ pandas 0.9.0
142142
- Fix float64/float32 merging bug (#1849)
143143
- Fixes to Period.start_time for non-daily frequencies (#1857)
144144
- Fix failure when converter used on index_col in read_csv (#1835)
145+
- Implement PeriodIndex.append so that pandas.concat works correctly (#1815)
145146

146147
pandas 0.8.1
147148
============

pandas/core/index.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -342,12 +342,24 @@ def append(self, other):
342342
name = None
343343
break
344344

345-
to_concat = _ensure_compat_concat(to_concat)
345+
to_concat = self._ensure_compat_concat(to_concat)
346346
to_concat = [x.values if isinstance(x, Index) else x
347347
for x in to_concat]
348348

349349
return Index(np.concatenate(to_concat), name=name)
350350

351+
@staticmethod
352+
def _ensure_compat_concat(indexes):
353+
from pandas.tseries.api import DatetimeIndex, PeriodIndex
354+
klasses = DatetimeIndex, PeriodIndex
355+
356+
is_ts = [isinstance(idx, klasses) for idx in indexes]
357+
358+
if any(is_ts) and not all(is_ts):
359+
return [_maybe_box(idx) for idx in indexes]
360+
361+
return indexes
362+
351363
def take(self, indexer, axis=0):
352364
"""
353365
Analogous to ndarray.take
@@ -2516,16 +2528,11 @@ def _get_consensus_names(indexes):
25162528
break
25172529
return consensus_name
25182530

2519-
def _ensure_compat_concat(indexes):
2520-
from pandas.tseries.index import DatetimeIndex
2521-
is_m8 = [isinstance(idx, DatetimeIndex) for idx in indexes]
2522-
if any(is_m8) and not all(is_m8):
2523-
return [_maybe_box_dtindex(idx) for idx in indexes]
2524-
return indexes
2531+
def _maybe_box(idx):
2532+
from pandas.tseries.api import DatetimeIndex, PeriodIndex
2533+
klasses = DatetimeIndex, PeriodIndex
25252534

2526-
def _maybe_box_dtindex(idx):
2527-
from pandas.tseries.index import DatetimeIndex
2528-
if isinstance(idx, DatetimeIndex):
2535+
if isinstance(idx, klasses):
25292536
return idx.asobject
25302537
return idx
25312538

pandas/tseries/index.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,6 @@ def append(self, other):
601601
-------
602602
appended : Index
603603
"""
604-
from pandas.core.index import _ensure_compat_concat
605-
606604
name = self.name
607605
to_concat = [self]
608606

@@ -616,7 +614,7 @@ def append(self, other):
616614
name = None
617615
break
618616

619-
to_concat = _ensure_compat_concat(to_concat)
617+
to_concat = self._ensure_compat_concat(to_concat)
620618
to_concat = [x.values if isinstance(x, Index) else x
621619
for x in to_concat]
622620

pandas/tseries/period.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from pandas.tseries.frequencies import (get_freq_code as _gfc, to_offset,
66
_month_numbers, FreqGroup)
7-
from pandas.tseries.index import DatetimeIndex, Int64Index
7+
from pandas.tseries.index import DatetimeIndex, Int64Index, Index
88
from pandas.tseries.tools import parse_time_string
99
import pandas.tseries.frequencies as _freq_mod
1010

@@ -632,6 +632,11 @@ def _box_values(self, values):
632632
f = lambda x: Period(ordinal=x, freq=self.freq)
633633
return lib.map_infer(values, f)
634634

635+
@property
636+
def asobject(self):
637+
from pandas.core.index import Index
638+
return Index(self._box_values(self.values), dtype=object)
639+
635640
def astype(self, dtype):
636641
dtype = np.dtype(dtype)
637642
if dtype == np.object_:
@@ -982,6 +987,45 @@ def take(self, indices, axis=None):
982987
taken.name = self.name
983988
return taken
984989

990+
def append(self, other):
991+
"""
992+
Append a collection of Index options together
993+
994+
Parameters
995+
----------
996+
other : Index or list/tuple of indices
997+
998+
Returns
999+
-------
1000+
appended : Index
1001+
"""
1002+
name = self.name
1003+
to_concat = [self]
1004+
1005+
if isinstance(other, (list, tuple)):
1006+
to_concat = to_concat + list(other)
1007+
else:
1008+
to_concat.append(other)
1009+
1010+
for obj in to_concat:
1011+
if isinstance(obj, Index) and obj.name != name:
1012+
name = None
1013+
break
1014+
1015+
to_concat = self._ensure_compat_concat(to_concat)
1016+
1017+
if isinstance(to_concat[0], PeriodIndex):
1018+
if len(set([x.freq for x in to_concat])) > 1:
1019+
# box
1020+
to_concat = [x.asobject for x in to_concat]
1021+
else:
1022+
cat_values = np.concatenate([x.values for x in to_concat])
1023+
return PeriodIndex(cat_values, freq=self.freq, name=name)
1024+
1025+
to_concat = [x.values if isinstance(x, Index) else x
1026+
for x in to_concat]
1027+
return Index(com._concat_compat(to_concat), name=name)
1028+
9851029

9861030
def _get_ordinal_range(start, end, periods, freq):
9871031
if com._count_not_none(start, end, periods) < 2:

pandas/tseries/tests/test_period.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,22 @@ def test_to_datetime_1703(self):
18481848
result = index.to_datetime()
18491849
self.assertEquals(result[0], Timestamp('1/1/2012'))
18501850

1851+
def test_append_concat(self):
1852+
# #1815
1853+
d1 = date_range('12/31/1990', '12/31/1999', freq='A-DEC')
1854+
d2 = date_range('12/31/2000', '12/31/2009', freq='A-DEC')
1855+
1856+
s1 = Series(np.random.randn(10), d1)
1857+
s2 = Series(np.random.randn(10), d2)
1858+
1859+
s1 = s1.to_period()
1860+
s2 = s2.to_period()
1861+
1862+
# drops index
1863+
result = pd.concat([s1,s2])
1864+
self.assert_(isinstance(result.index, PeriodIndex))
1865+
self.assertEquals(result.index[0], s1.index[0])
1866+
18511867
def _permute(obj):
18521868
return obj.take(np.random.permutation(len(obj)))
18531869

0 commit comments

Comments
 (0)