diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index b41fbe880a65d..266eed8a50ceb 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -119,6 +119,7 @@ Timezones Numeric ^^^^^^^ +- Bug in :meth:`DataFrame.floordiv` with ``axis=0`` not treating division-by-zero like :meth:`Series.floordiv` (:issue:`31271`) - - diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 012fb1d0c2eb7..9ff3f8c2316f1 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -5341,7 +5341,7 @@ def reorder_levels(self, order, axis=0) -> "DataFrame": # ---------------------------------------------------------------------- # Arithmetic / combination related - def _combine_frame(self, other, func, fill_value=None, level=None): + def _combine_frame(self, other: "DataFrame", func, fill_value=None): # at this point we have `self._indexed_same(other)` if fill_value is None: @@ -5368,7 +5368,7 @@ def _arith_op(left, right): return new_data - def _combine_match_index(self, other, func): + def _combine_match_index(self, other: Series, func): # at this point we have `self.index.equals(other.index)` if ops.should_series_dispatch(self, other, func): @@ -5376,8 +5376,10 @@ def _combine_match_index(self, other, func): new_data = ops.dispatch_to_series(self, other, func) else: # fastpath --> operate directly on values + other_vals = other.values.reshape(-1, 1) with np.errstate(all="ignore"): - new_data = func(self.values.T, other.values).T + new_data = func(self.values, other_vals) + new_data = dispatch_fill_zeros(func, self.values, other_vals, new_data) return new_data def _construct_result(self, result) -> "DataFrame": diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 5039ffab33fbd..854d6072eea36 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -109,26 +109,23 @@ def mask_zero_div_zero(x, y, result): return result if zmask.any(): - shape = result.shape # Flip sign if necessary for -0.0 zneg_mask = zmask & np.signbit(y) zpos_mask = zmask & ~zneg_mask - nan_mask = (zmask & (x == 0)).ravel() + nan_mask = zmask & (x == 0) with np.errstate(invalid="ignore"): - neginf_mask = ((zpos_mask & (x < 0)) | (zneg_mask & (x > 0))).ravel() - posinf_mask = ((zpos_mask & (x > 0)) | (zneg_mask & (x < 0))).ravel() + neginf_mask = (zpos_mask & (x < 0)) | (zneg_mask & (x > 0)) + posinf_mask = (zpos_mask & (x > 0)) | (zneg_mask & (x < 0)) if nan_mask.any() or neginf_mask.any() or posinf_mask.any(): # Fill negative/0 with -inf, positive/0 with +inf, 0/0 with NaN - result = result.astype("float64", copy=False).ravel() - - np.putmask(result, nan_mask, np.nan) - np.putmask(result, posinf_mask, np.inf) - np.putmask(result, neginf_mask, -np.inf) + result = result.astype("float64", copy=False) - result = result.reshape(shape) + result[nan_mask] = np.nan + result[posinf_mask] = np.inf + result[neginf_mask] = -np.inf return result diff --git a/pandas/tests/frame/test_arithmetic.py b/pandas/tests/frame/test_arithmetic.py index 659b55756c4b6..c6eacf2bbcd84 100644 --- a/pandas/tests/frame/test_arithmetic.py +++ b/pandas/tests/frame/test_arithmetic.py @@ -332,6 +332,21 @@ def test_df_flex_cmp_constant_return_types_empty(self, opname): class TestFrameFlexArithmetic: + def test_floordiv_axis0(self): + # make sure we df.floordiv(ser, axis=0) matches column-wise result + arr = np.arange(3) + ser = pd.Series(arr) + df = pd.DataFrame({"A": ser, "B": ser}) + + result = df.floordiv(ser, axis=0) + + expected = pd.DataFrame({col: df[col] // ser for col in df.columns}) + + tm.assert_frame_equal(result, expected) + + result2 = df.floordiv(ser.values, axis=0) + tm.assert_frame_equal(result2, expected) + def test_df_add_td64_columnwise(self): # GH 22534 Check that column-wise addition broadcasts correctly dti = pd.date_range("2016-01-01", periods=10)