Skip to content

Commit 30276bf

Browse files
authored
ENH: PeriodIndex setops with incompatible freq cast instead of raise (#39306)
1 parent f958cf5 commit 30276bf

File tree

6 files changed

+16
-46
lines changed

6 files changed

+16
-46
lines changed

doc/source/whatsnew/v1.3.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ Interval
273273
- Bug in :meth:`IntervalIndex.intersection` and :meth:`IntervalIndex.symmetric_difference` always returning object-dtype when operating with :class:`CategoricalIndex` (:issue:`38653`, :issue:`38741`)
274274
- Bug in :meth:`IntervalIndex.intersection` returning duplicates when at least one of both Indexes has duplicates which are present in the other (:issue:`38743`)
275275
- :meth:`IntervalIndex.union`, :meth:`IntervalIndex.intersection`, :meth:`IntervalIndex.difference`, and :meth:`IntervalIndex.symmetric_difference` now cast to the appropriate dtype instead of raising ``TypeError`` when operating with another :class:`IntervalIndex` with incompatible dtype (:issue:`39267`)
276+
- :meth:`PeriodIndex.union`, :meth:`PeriodIndex.intersection`, :meth:`PeriodIndex.symmetric_difference`, :meth:`PeriodIndex.difference` now cast to object dtype instead of raising ``IncompatibleFrequency`` when opearting with another :class:`PeriodIndex` with incompatible dtype (:issue:`??`)
276277

277278
Indexing
278279
^^^^^^^^

pandas/core/indexes/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2859,7 +2859,7 @@ def _union(self, other, sort):
28592859
if sort is None and self.is_monotonic and other.is_monotonic:
28602860
try:
28612861
result = self._outer_indexer(lvals, rvals)[0]
2862-
except TypeError:
2862+
except (TypeError, IncompatibleFrequency):
28632863
# incomparable objects
28642864
result = list(lvals)
28652865

@@ -3163,6 +3163,7 @@ def symmetric_difference(self, other, result_name=None, sort=None):
31633163

31643164
return Index(the_diff, name=result_name)
31653165

3166+
@final
31663167
def _assert_can_do_setop(self, other):
31673168
if not is_list_like(other):
31683169
raise TypeError("Input must be Index or array-like")

pandas/core/indexes/period.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -428,27 +428,6 @@ def insert(self, loc: int, item):
428428

429429
return DatetimeIndexOpsMixin.insert(self, loc, item)
430430

431-
def join(self, other, how="left", level=None, return_indexers=False, sort=False):
432-
"""
433-
See Index.join
434-
"""
435-
self._assert_can_do_setop(other)
436-
437-
if not isinstance(other, PeriodIndex):
438-
return self.astype(object).join(
439-
other, how=how, level=level, return_indexers=return_indexers, sort=sort
440-
)
441-
442-
# _assert_can_do_setop ensures we have matching dtype
443-
result = super().join(
444-
other,
445-
how=how,
446-
level=level,
447-
return_indexers=return_indexers,
448-
sort=sort,
449-
)
450-
return result
451-
452431
# ------------------------------------------------------------------------
453432
# Indexing Methods
454433

@@ -610,14 +589,6 @@ def _get_string_slice(self, key: str):
610589
# ------------------------------------------------------------------------
611590
# Set Operation Methods
612591

613-
def _assert_can_do_setop(self, other):
614-
super()._assert_can_do_setop(other)
615-
616-
# *Can't* use PeriodIndexes of different freqs
617-
# *Can* use PeriodIndex/DatetimeIndex
618-
if isinstance(other, PeriodIndex) and self.freq != other.freq:
619-
raise raise_on_incompatible(self, other)
620-
621592
def _setop(self, other, sort, opname: str):
622593
"""
623594
Perform a set operation by dispatching to the Int64Index implementation.

pandas/tests/indexes/period/test_join.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ def test_join_does_not_recur(self):
3939
def test_join_mismatched_freq_raises(self):
4040
index = period_range("1/1/2000", "1/20/2000", freq="D")
4141
index3 = period_range("1/1/2000", "1/20/2000", freq="2D")
42-
msg = r".*Input has different freq=2D from PeriodIndex\(freq=D\)"
42+
msg = r".*Input has different freq=2D from Period\(freq=D\)"
4343
with pytest.raises(IncompatibleFrequency, match=msg):
4444
index.join(index3)

pandas/tests/indexes/period/test_setops.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import numpy as np
2-
import pytest
3-
4-
from pandas._libs.tslibs import IncompatibleFrequency
52

63
import pandas as pd
74
from pandas import PeriodIndex, date_range, period_range
@@ -145,12 +142,12 @@ def test_union_misc(self, sort):
145142
tm.assert_index_equal(result, index)
146143
assert tm.equalContents(result, index)
147144

148-
# raise if different frequencies
145+
# cast if different frequencies
149146
index = period_range("1/1/2000", "1/20/2000", freq="D")
150147
index2 = period_range("1/1/2000", "1/20/2000", freq="W-WED")
151-
msg = r"Input has different freq=W-WED from PeriodIndex\(freq=D\)"
152-
with pytest.raises(IncompatibleFrequency, match=msg):
153-
index.union(index2, sort=sort)
148+
result = index.union(index2, sort=sort)
149+
expected = index.astype(object).union(index2.astype(object), sort=sort)
150+
tm.assert_index_equal(result, expected)
154151

155152
# TODO: belongs elsewhere
156153
def test_union_dataframe_index(self):
@@ -178,17 +175,17 @@ def test_intersection(self, sort):
178175
tm.assert_index_equal(result, index[10:-5])
179176
assert tm.equalContents(result, index[10:-5])
180177

181-
# raise if different frequencies
178+
# cast if different frequencies
182179
index = period_range("1/1/2000", "1/20/2000", freq="D")
183180
index2 = period_range("1/1/2000", "1/20/2000", freq="W-WED")
184-
msg = r"Input has different freq=W-WED from PeriodIndex\(freq=D\)"
185-
with pytest.raises(IncompatibleFrequency, match=msg):
186-
index.intersection(index2, sort=sort)
181+
182+
result = index.intersection(index2, sort=sort)
183+
expected = pd.Index([], dtype=object)
184+
tm.assert_index_equal(result, expected)
187185

188186
index3 = period_range("1/1/2000", "1/20/2000", freq="2D")
189-
msg = r"Input has different freq=2D from PeriodIndex\(freq=D\)"
190-
with pytest.raises(IncompatibleFrequency, match=msg):
191-
index.intersection(index3, sort=sort)
187+
result = index.intersection(index3, sort=sort)
188+
tm.assert_index_equal(result, expected)
192189

193190
def test_intersection_cases(self, sort):
194191
base = period_range("6/1/2000", "6/30/2000", freq="D", name="idx")

pandas/tests/series/test_arithmetic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def test_add_series_with_period_index(self):
152152
result = ts + _permute(ts[::2])
153153
tm.assert_series_equal(result, expected)
154154

155-
msg = "Input has different freq=D from PeriodIndex\\(freq=A-DEC\\)"
155+
msg = "Input has different freq=D from Period\\(freq=A-DEC\\)"
156156
with pytest.raises(IncompatibleFrequency, match=msg):
157157
ts + ts.asfreq("D", how="end")
158158

0 commit comments

Comments
 (0)