Skip to content

Commit 196b28a

Browse files
authored
REF: handle length-0 setops cases higher up (#42362)
1 parent f175b33 commit 196b28a

File tree

4 files changed

+35
-15
lines changed

4 files changed

+35
-15
lines changed

pandas/core/indexes/base.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3070,6 +3070,30 @@ def intersection(self, other, sort=False):
30703070
return self.unique()._get_reconciled_name_object(other)
30713071
return self._get_reconciled_name_object(other)
30723072

3073+
if len(self) == 0 or len(other) == 0:
3074+
# fastpath; we need to be careful about having commutativity
3075+
3076+
if self._is_multi or other._is_multi:
3077+
# _convert_can_do_setop ensures that we have both or neither
3078+
# We retain self.levels
3079+
return self[:0].rename(result_name)
3080+
3081+
dtype = self._find_common_type_compat(other)
3082+
if is_dtype_equal(self.dtype, dtype):
3083+
# Slicing allows us to retain DTI/TDI.freq, RangeIndex
3084+
3085+
# Note: self[:0] vs other[:0] affects
3086+
# 1) which index's `freq` we get in DTI/TDI cases
3087+
# This may be a historical artifact, i.e. no documented
3088+
# reason for this choice.
3089+
# 2) The `step` we get in RangeIndex cases
3090+
if len(self) == 0:
3091+
return self[:0].rename(result_name)
3092+
else:
3093+
return other[:0].rename(result_name)
3094+
3095+
return Index([], dtype=dtype, name=result_name)
3096+
30733097
elif not self._should_compare(other):
30743098
# We can infer that the intersection is empty.
30753099
if isinstance(self, ABCMultiIndex):

pandas/core/indexes/datetimelike.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -532,15 +532,11 @@ def is_type_compatible(self, kind: str) -> bool:
532532

533533
def _intersection(self, other: Index, sort=False) -> Index:
534534
"""
535-
intersection specialized to the case with matching dtypes.
535+
intersection specialized to the case with matching dtypes and both non-empty.
536536
"""
537537
other = cast("DatetimeTimedeltaMixin", other)
538-
if len(self) == 0:
539-
return self.copy()._get_reconciled_name_object(other)
540-
if len(other) == 0:
541-
return other.copy()._get_reconciled_name_object(self)
542538

543-
elif not self._can_fast_intersect(other):
539+
if not self._can_fast_intersect(other):
544540
result = Index._intersection(self, other, sort=sort)
545541
# We need to invalidate the freq because Index._intersection
546542
# uses _shallow_copy on a view of self._data, which will preserve
@@ -550,6 +546,11 @@ def _intersection(self, other: Index, sort=False) -> Index:
550546
result = self._wrap_setop_result(other, result)
551547
return result._with_freq(None)._with_freq("infer")
552548

549+
else:
550+
return self._fast_intersect(other, sort)
551+
552+
def _fast_intersect(self, other, sort):
553+
553554
# to make our life easier, "sort" the two ranges
554555
if self[0] <= other[0]:
555556
left, right = self, other
@@ -627,11 +628,7 @@ def _can_fast_union(self: _T, other: _T) -> bool:
627628
return (right_start == left_end + freq) or right_start in left
628629

629630
def _fast_union(self: _T, other: _T, sort=None) -> _T:
630-
if len(other) == 0:
631-
return self.view(type(self))
632-
633-
if len(self) == 0:
634-
return other.view(type(self))
631+
# Caller is responsible for ensuring self and other are non-empty
635632

636633
# to make our life easier, "sort" the two ranges
637634
if self[0] <= other[0]:

pandas/core/indexes/datetimes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,8 @@ def union_many(self, others):
405405

406406
this, other = this._maybe_utc_convert(other)
407407

408-
if this._can_fast_union(other):
408+
if len(self) and len(other) and this._can_fast_union(other):
409+
# union already has fastpath handling for empty cases
409410
this = this._fast_union(other)
410411
else:
411412
this = Index.union(this, other)

pandas/core/indexes/range.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -549,14 +549,12 @@ def equals(self, other: object) -> bool:
549549
# Set Operations
550550

551551
def _intersection(self, other: Index, sort=False):
552+
# caller is responsible for checking self and other are both non-empty
552553

553554
if not isinstance(other, RangeIndex):
554555
# Int64Index
555556
return super()._intersection(other, sort=sort)
556557

557-
if not len(self) or not len(other):
558-
return self._simple_new(_empty_range)
559-
560558
first = self._range[::-1] if self.step < 0 else self._range
561559
second = other._range[::-1] if other.step < 0 else other._range
562560

0 commit comments

Comments
 (0)