Skip to content

REF: handle length-0 setops cases higher up #42362

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3070,6 +3070,30 @@ def intersection(self, other, sort=False):
return self.unique()._get_reconciled_name_object(other)
return self._get_reconciled_name_object(other)

if len(self) == 0 or len(other) == 0:
# fastpath; we need to be careful about having commutativity

if self._is_multi or other._is_multi:
# _convert_can_do_setop ensures that we have both or neither
# We retain self.levels
return self[:0].rename(result_name)

dtype = self._find_common_type_compat(other)
if is_dtype_equal(self.dtype, dtype):
# Slicing allows us to retain DTI/TDI.freq, RangeIndex

# Note: self[:0] vs other[:0] affects
# 1) which index's `freq` we get in DTI/TDI cases
# This may be a historical artifact, i.e. no documented
# reason for this choice.
# 2) The `step` we get in RangeIndex cases
if len(self) == 0:
return self[:0].rename(result_name)
else:
return other[:0].rename(result_name)

return Index([], dtype=dtype, name=result_name)

elif not self._should_compare(other):
# We can infer that the intersection is empty.
if isinstance(self, ABCMultiIndex):
Expand Down
19 changes: 8 additions & 11 deletions pandas/core/indexes/datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,15 +532,11 @@ def is_type_compatible(self, kind: str) -> bool:

def _intersection(self, other: Index, sort=False) -> Index:
"""
intersection specialized to the case with matching dtypes.
intersection specialized to the case with matching dtypes and both non-empty.
"""
other = cast("DatetimeTimedeltaMixin", other)
if len(self) == 0:
return self.copy()._get_reconciled_name_object(other)
if len(other) == 0:
return other.copy()._get_reconciled_name_object(self)

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

else:
return self._fast_intersect(other, sort)

def _fast_intersect(self, other, sort):

# to make our life easier, "sort" the two ranges
if self[0] <= other[0]:
left, right = self, other
Expand Down Expand Up @@ -627,11 +628,7 @@ def _can_fast_union(self: _T, other: _T) -> bool:
return (right_start == left_end + freq) or right_start in left

def _fast_union(self: _T, other: _T, sort=None) -> _T:
if len(other) == 0:
return self.view(type(self))

if len(self) == 0:
return other.view(type(self))
# Caller is responsible for ensuring self and other are non-empty

# to make our life easier, "sort" the two ranges
if self[0] <= other[0]:
Expand Down
3 changes: 2 additions & 1 deletion pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,8 @@ def union_many(self, others):

this, other = this._maybe_utc_convert(other)

if this._can_fast_union(other):
if len(self) and len(other) and this._can_fast_union(other):
# union already has fastpath handling for empty cases
this = this._fast_union(other)
else:
this = Index.union(this, other)
Expand Down
4 changes: 1 addition & 3 deletions pandas/core/indexes/range.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,12 @@ def equals(self, other: object) -> bool:
# Set Operations

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

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

if not len(self) or not len(other):
return self._simple_new(_empty_range)

first = self._range[::-1] if self.step < 0 else self._range
second = other._range[::-1] if other.step < 0 else other._range

Expand Down