Skip to content

Commit 84136d5

Browse files
jbrockmendeljreback
authored andcommitted
Dispatch Index ops to Series (#27352)
1 parent 23a6684 commit 84136d5

File tree

5 files changed

+30
-68
lines changed

5 files changed

+30
-68
lines changed

pandas/core/indexes/base.py

+12-18
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
from pandas.core.indexes.frozen import FrozenList
7171
import pandas.core.missing as missing
7272
from pandas.core.ops import get_op_result_name, make_invalid_op
73-
from pandas.core.ops.missing import dispatch_missing
7473
import pandas.core.sorting as sorting
7574
from pandas.core.strings import StringMethods
7675

@@ -144,27 +143,18 @@ def index_arithmetic_method(self, other):
144143
out = op(self, other)
145144
return Index(out, name=self.name)
146145

147-
other = self._validate_for_numeric_binop(other, op)
148-
149146
# handle time-based others
150147
if isinstance(other, (ABCDateOffset, np.timedelta64, timedelta)):
151148
return self._evaluate_with_timedelta_like(other, op)
152-
elif isinstance(other, (datetime, np.datetime64)):
153-
return self._evaluate_with_datetime_like(other, op)
154149

155-
values = self.values
156-
with np.errstate(all="ignore"):
157-
result = op(values, other)
150+
other = self._validate_for_numeric_binop(other, op)
158151

159-
result = dispatch_missing(op, values, other, result)
152+
from pandas import Series
160153

161-
attrs = self._get_attributes_dict()
162-
attrs = self._maybe_update_attributes(attrs)
163-
if op is divmod:
164-
result = (Index(result[0], **attrs), Index(result[1], **attrs))
165-
else:
166-
result = Index(result, **attrs)
167-
return result
154+
result = op(Series(self), other)
155+
if isinstance(result, tuple):
156+
return (Index(result[0]), Index(result[1]))
157+
return Index(result)
168158

169159
name = "__{name}__".format(name=op.__name__)
170160
# TODO: docstring?
@@ -2361,10 +2351,14 @@ def _get_unique_index(self, dropna=False):
23612351
def __add__(self, other):
23622352
if isinstance(other, (ABCSeries, ABCDataFrame)):
23632353
return NotImplemented
2364-
return Index(np.array(self) + other)
2354+
from pandas import Series
2355+
2356+
return Index(Series(self) + other)
23652357

23662358
def __radd__(self, other):
2367-
return Index(other + np.array(self))
2359+
from pandas import Series
2360+
2361+
return Index(other + Series(self))
23682362

23692363
def __iadd__(self, other):
23702364
# alias for __add__

pandas/core/ops/__init__.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
)
3939
from pandas.core.dtypes.generic import (
4040
ABCDataFrame,
41+
ABCDatetimeArray,
4142
ABCIndex,
4243
ABCIndexClass,
4344
ABCSeries,
@@ -1702,10 +1703,14 @@ def wrapper(left, right):
17021703
# does inference in the case where `result` has object-dtype.
17031704
return construct_result(left, result, index=left.index, name=res_name)
17041705

1706+
elif isinstance(right, (ABCDatetimeArray, pd.DatetimeIndex)):
1707+
result = op(left._values, right)
1708+
return construct_result(left, result, index=left.index, name=res_name)
1709+
17051710
lvalues = left.values
17061711
rvalues = right
1707-
if isinstance(rvalues, ABCSeries):
1708-
rvalues = rvalues.values
1712+
if isinstance(rvalues, (ABCSeries, ABCIndexClass)):
1713+
rvalues = rvalues._values
17091714

17101715
with np.errstate(all="ignore"):
17111716
result = na_op(lvalues, rvalues)

pandas/core/ops/missing.py

+10-24
Original file line numberDiff line numberDiff line change
@@ -148,40 +148,26 @@ def mask_zero_div_zero(x, y, result):
148148
return result
149149

150150

151-
def dispatch_missing(op, left, right, result):
151+
def dispatch_fill_zeros(op, left, right, result):
152152
"""
153-
Fill nulls caused by division by zero, casting to a different dtype
154-
if necessary.
153+
Call fill_zeros with the appropriate fill value depending on the operation,
154+
with special logic for divmod and rdivmod.
155155
156156
Parameters
157157
----------
158158
op : function (operator.add, operator.div, ...)
159-
left : object (Index for non-reversed ops)
160-
right : object (Index fof reversed ops)
159+
left : object (np.ndarray for non-reversed ops)
160+
right : object (np.ndarray for reversed ops)
161161
result : ndarray
162162
163163
Returns
164164
-------
165-
result : ndarray
166-
"""
167-
if op is operator.floordiv:
168-
# Note: no need to do this for truediv; in py3 numpy behaves the way
169-
# we want.
170-
result = mask_zero_div_zero(left, right, result)
171-
elif op is operator.mod:
172-
result = fill_zeros(result, left, right, "__mod__", np.nan)
173-
elif op is divmod:
174-
res0 = mask_zero_div_zero(left, right, result[0])
175-
res1 = fill_zeros(result[1], left, right, "__divmod__", np.nan)
176-
result = (res0, res1)
177-
return result
178-
165+
result : np.ndarray
179166
180-
# FIXME: de-duplicate with dispatch_missing
181-
def dispatch_fill_zeros(op, left, right, result):
182-
"""
183-
Call fill_zeros with the appropriate fill value depending on the operation,
184-
with special logic for divmod and rdivmod.
167+
Notes
168+
-----
169+
For divmod and rdivmod, the `result` parameter and returned `result`
170+
is a 2-tuple of ndarray objects.
185171
"""
186172
if op is divmod:
187173
result = (

pandas/tests/arithmetic/test_object.py

-24
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,6 @@ def test_add_extension_scalar(self, other, box, op):
103103
result = op(arr, other)
104104
tm.assert_equal(result, expected)
105105

106-
@pytest.mark.parametrize(
107-
"box",
108-
[
109-
pytest.param(
110-
pd.Index,
111-
marks=pytest.mark.xfail(reason="Does not mask nulls", raises=TypeError),
112-
),
113-
pd.Series,
114-
pd.DataFrame,
115-
],
116-
ids=lambda x: x.__name__,
117-
)
118106
def test_objarr_add_str(self, box):
119107
ser = pd.Series(["x", np.nan, "x"])
120108
expected = pd.Series(["xa", np.nan, "xa"])
@@ -125,18 +113,6 @@ def test_objarr_add_str(self, box):
125113
result = ser + "a"
126114
tm.assert_equal(result, expected)
127115

128-
@pytest.mark.parametrize(
129-
"box",
130-
[
131-
pytest.param(
132-
pd.Index,
133-
marks=pytest.mark.xfail(reason="Does not mask nulls", raises=TypeError),
134-
),
135-
pd.Series,
136-
pd.DataFrame,
137-
],
138-
ids=lambda x: x.__name__,
139-
)
140116
def test_objarr_radd_str(self, box):
141117
ser = pd.Series(["x", np.nan, "x"])
142118
expected = pd.Series(["ax", np.nan, "ax"])

pandas/tests/indexes/test_common.py

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ def test_set_name_methods(self, indices):
144144
assert res is None
145145
assert indices.name == new_name
146146
assert indices.names == [new_name]
147+
# FIXME: dont leave commented-out
147148
# with pytest.raises(TypeError, match="list-like"):
148149
# # should still fail even if it would be the right length
149150
# ind.set_names("a")

0 commit comments

Comments
 (0)