Skip to content

Commit fb62fcf

Browse files
ccharlesgbTomAugspurger
authored andcommitted
BUG: _can_use_numexpr fails when passed large Series (#27773)
* BUG: _can_use_numexpr did not handle Series case correctly
1 parent 489d1ff commit fb62fcf

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

doc/source/whatsnew/v0.25.1.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Numeric
5353
^^^^^^^
5454
- Bug in :meth:`Series.interpolate` when using a timezone aware :class:`DatetimeIndex` (:issue:`27548`)
5555
- Bug when printing negative floating point complex numbers would raise an ``IndexError`` (:issue:`27484`)
56-
-
56+
- Bug where :class:`DataFrame` arithmetic operators such as :meth:`DataFrame.mul` with a :class:`Series` with axis=1 would raise an ``AttributeError`` on :class:`DataFrame` larger than the minimum threshold to invoke numexpr (:issue:`27636`)
5757
-
5858

5959
Conversion

pandas/core/computation/expressions.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,17 @@ def _can_use_numexpr(op, op_str, a, b, dtype_check):
7676

7777
# required min elements (otherwise we are adding overhead)
7878
if np.prod(a.shape) > _MIN_ELEMENTS:
79-
8079
# check for dtype compatibility
8180
dtypes = set()
8281
for o in [a, b]:
83-
if hasattr(o, "dtypes"):
82+
# Series implements dtypes, check for dimension count as well
83+
if hasattr(o, "dtypes") and o.ndim > 1:
8484
s = o.dtypes.value_counts()
8585
if len(s) > 1:
8686
return False
8787
dtypes |= set(s.index.astype(str))
88-
elif isinstance(o, np.ndarray):
88+
# ndarray and Series Case
89+
elif hasattr(o, "dtype"):
8990
dtypes |= {o.dtype.name}
9091

9192
# allowed are a superset

pandas/tests/test_expressions.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def run_arithmetic(self, df, other, assert_func, check_dtype=False, test_flex=Tr
6666
operator_name = "truediv"
6767

6868
if test_flex:
69-
op = lambda x, y: getattr(df, arith)(y)
69+
op = lambda x, y: getattr(x, arith)(y)
7070
op.__name__ = arith
7171
else:
7272
op = getattr(operator, operator_name)
@@ -318,7 +318,6 @@ def testit():
318318
for f in [self.frame, self.frame2, self.mixed, self.mixed2]:
319319

320320
for cond in [True, False]:
321-
322321
c = np.empty(f.shape, dtype=np.bool_)
323322
c.fill(cond)
324323
result = expr.where(c, f.values, f.values + 1)
@@ -431,3 +430,29 @@ def test_bool_ops_column_name_dtype(self, test_input, expected):
431430
# GH 22383 - .ne fails if columns containing column name 'dtype'
432431
result = test_input.loc[:, ["a", "dtype"]].ne(test_input.loc[:, ["a", "dtype"]])
433432
assert_frame_equal(result, expected)
433+
434+
@pytest.mark.parametrize(
435+
"arith", ("add", "sub", "mul", "mod", "truediv", "floordiv")
436+
)
437+
@pytest.mark.parametrize("axis", (0, 1))
438+
def test_frame_series_axis(self, axis, arith):
439+
# GH#26736 Dataframe.floordiv(Series, axis=1) fails
440+
if axis == 1 and arith == "floordiv":
441+
pytest.xfail("'floordiv' does not succeed with axis=1 #27636")
442+
443+
df = self.frame
444+
if axis == 1:
445+
other = self.frame.iloc[0, :]
446+
else:
447+
other = self.frame.iloc[:, 0]
448+
449+
expr._MIN_ELEMENTS = 0
450+
451+
op_func = getattr(df, arith)
452+
453+
expr.set_use_numexpr(False)
454+
expected = op_func(other, axis=axis)
455+
expr.set_use_numexpr(True)
456+
457+
result = op_func(other, axis=axis)
458+
assert_frame_equal(expected, result)

0 commit comments

Comments
 (0)