diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index bcebe3ab024ba..9cca709552439 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -627,6 +627,7 @@ Numeric - Bug in :class:`DataFrame` arithmetic ops with a subclass whose :meth:`_constructor` attribute is a callable other than the subclass itself (:issue:`43201`) - Bug in arithmetic operations involving :class:`RangeIndex` where the result would have the incorrect ``name`` (:issue:`43962`) - Bug in arithmetic operations involving :class:`Series` where the result could have the incorrect ``name`` when the operands having matching NA or matching tuple names (:issue:`44459`) +- Bug in division with ``IntegerDtype`` or ``BooleanDtype`` array and NA scalar incorrectly raising (:issue:`44685`) - Conversion diff --git a/pandas/core/arrays/boolean.py b/pandas/core/arrays/boolean.py index c09d4486afcae..09cdb985ddb2e 100644 --- a/pandas/core/arrays/boolean.py +++ b/pandas/core/arrays/boolean.py @@ -508,6 +508,8 @@ def _arith_method(self, other, op): # actual op, so we need to choose the resulting dtype manually if op_name in {"floordiv", "rfloordiv", "mod", "rmod", "pow", "rpow"}: dtype = "int8" + elif op_name in {"truediv", "rtruediv"}: + dtype = "float64" else: dtype = "bool" result = np.zeros(len(self._data), dtype=dtype) diff --git a/pandas/core/arrays/numeric.py b/pandas/core/arrays/numeric.py index eb955e4d42bc5..b17d40b35903b 100644 --- a/pandas/core/arrays/numeric.py +++ b/pandas/core/arrays/numeric.py @@ -136,6 +136,11 @@ def _arith_method(self, other, op): if other is libmissing.NA: result = np.ones_like(self._data) + if "truediv" in op_name and self.dtype.kind != "f": + # The actual data here doesn't matter since the mask + # will be all-True, but since this is division, we want + # to end up with floating dtype. + result = result.astype(np.float64) else: with np.errstate(all="ignore"): result = op(self._data, other) diff --git a/pandas/tests/arrays/masked/test_arithmetic.py b/pandas/tests/arrays/masked/test_arithmetic.py index fae956693093b..8ad535eeb6b1c 100644 --- a/pandas/tests/arrays/masked/test_arithmetic.py +++ b/pandas/tests/arrays/masked/test_arithmetic.py @@ -49,10 +49,6 @@ def test_array_scalar_like_equivalence(data, all_arithmetic_operators): def test_array_NA(data, all_arithmetic_operators, request): - if "truediv" in all_arithmetic_operators and data[0].dtype.kind != "f": - mark = pytest.mark.xfail(reason="division with pd.NA fails") - request.node.add_marker(mark) - data, _ = data op = tm.get_op_from_name(all_arithmetic_operators) check_skip(data, all_arithmetic_operators) @@ -169,14 +165,22 @@ def test_error_len_mismatch(data, all_arithmetic_operators): def test_unary_op_does_not_propagate_mask(data, op, request): # https://github.com/pandas-dev/pandas/issues/39943 data, _ = data - if data.dtype in ["Float32", "Float64"] and op == "__invert__": - request.node.add_marker( - pytest.mark.xfail( - raises=TypeError, reason="invert is not implemented for float ea dtypes" - ) - ) - s = pd.Series(data) - result = getattr(s, op)() + ser = pd.Series(data) + + if op == "__invert__" and data.dtype.kind == "f": + # we follow numpy in raising + msg = "ufunc 'invert' not supported for the input types" + with pytest.raises(TypeError, match=msg): + getattr(ser, op)() + with pytest.raises(TypeError, match=msg): + getattr(data, op)() + with pytest.raises(TypeError, match=msg): + # Check that this is still the numpy behavior + getattr(data._data, op)() + + return + + result = getattr(ser, op)() expected = result.copy(deep=True) - s[0] = None + ser[0] = None tm.assert_series_equal(result, expected) diff --git a/pandas/tests/extension/test_boolean.py b/pandas/tests/extension/test_boolean.py index 05455905860d2..b86122c8d1ee1 100644 --- a/pandas/tests/extension/test_boolean.py +++ b/pandas/tests/extension/test_boolean.py @@ -150,14 +150,6 @@ def check_opname(self, s, op_name, other, exc=None): # overwriting to indicate ops don't raise an error super().check_opname(s, op_name, other, exc=None) - @pytest.mark.skip(reason="Tested in tests/arrays/test_boolean.py") - def test_compare_scalar(self, data, comparison_op): - pass - - @pytest.mark.skip(reason="Tested in tests/arrays/test_boolean.py") - def test_compare_array(self, data, comparison_op): - pass - class TestReshaping(base.BaseReshapingTests): pass @@ -378,7 +370,6 @@ def check_reduce(self, s, op_name, skipna): tm.assert_almost_equal(result, expected) -@pytest.mark.skip(reason="Tested in tests/reductions/test_reductions.py") class TestBooleanReduce(base.BaseBooleanReduceTests): pass diff --git a/pandas/tests/extension/test_floating.py b/pandas/tests/extension/test_floating.py index 2b08c5b7be450..b72ad2739949e 100644 --- a/pandas/tests/extension/test_floating.py +++ b/pandas/tests/extension/test_floating.py @@ -89,10 +89,7 @@ def data_for_grouping(dtype): class TestDtype(base.BaseDtypeTests): - @pytest.mark.skip(reason="using multiple dtypes") - def test_is_dtype_unboxes_dtype(self): - # we have multiple dtypes, so skip - pass + pass class TestArithmeticOps(base.BaseArithmeticOpsTests): diff --git a/pandas/tests/extension/test_integer.py b/pandas/tests/extension/test_integer.py index 7d343aab3c7a0..6266462adc599 100644 --- a/pandas/tests/extension/test_integer.py +++ b/pandas/tests/extension/test_integer.py @@ -101,10 +101,7 @@ def data_for_grouping(dtype): class TestDtype(base.BaseDtypeTests): - @pytest.mark.skip(reason="using multiple dtypes") - def test_is_dtype_unboxes_dtype(self): - # we have multiple dtypes, so skip - pass + pass class TestArithmeticOps(base.BaseArithmeticOpsTests): @@ -196,6 +193,7 @@ class TestMissing(base.BaseMissingTests): class TestMethods(base.BaseMethodsTests): @pytest.mark.skip(reason="uses nullable integer") + @pytest.mark.parametrize("dropna", [True, False]) def test_value_counts(self, all_data, dropna): all_data = all_data[:10] if dropna: