From 6bd4fb10cedf95113ed5813462d3467670e69e4d Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 20:51:30 -0700 Subject: [PATCH 01/21] TST: parametrize tests --- .../tests/arrays/sparse/test_arithmetics.py | 381 +++++++++--------- 1 file changed, 185 insertions(+), 196 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 7bfedff217719..ae752e95e693a 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -8,6 +8,11 @@ import pandas.util.testing as tm +@pytest.fixture(params=["integer", "block"]) +def kind(request): + return request.param + + @pytest.mark.filterwarnings("ignore:Sparse:FutureWarning") @pytest.mark.filterwarnings("ignore:Series.to_sparse:FutureWarning") class TestSparseArrayArithmetics: @@ -136,99 +141,94 @@ def _check_logical_ops(self, a, b, a_dense, b_dense): self._check_bool_result(a | b_dense) self._assert((a | b_dense).to_dense(), a_dense | b_dense) - def test_float_scalar(self): + def test_float_scalar(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - for kind in ["integer", "block"]: - a = self._klass(values, kind=kind) - self._check_numeric_ops(a, 1, values, 1) - self._check_numeric_ops(a, 0, values, 0) - self._check_numeric_ops(a, 3, values, 3) + a = self._klass(values, kind=kind) + self._check_numeric_ops(a, 1, values, 1) + self._check_numeric_ops(a, 0, values, 0) + self._check_numeric_ops(a, 3, values, 3) - a = self._klass(values, kind=kind, fill_value=0) - self._check_numeric_ops(a, 1, values, 1) - self._check_numeric_ops(a, 0, values, 0) - self._check_numeric_ops(a, 3, values, 3) + a = self._klass(values, kind=kind, fill_value=0) + self._check_numeric_ops(a, 1, values, 1) + self._check_numeric_ops(a, 0, values, 0) + self._check_numeric_ops(a, 3, values, 3) - a = self._klass(values, kind=kind, fill_value=2) - self._check_numeric_ops(a, 1, values, 1) - self._check_numeric_ops(a, 0, values, 0) - self._check_numeric_ops(a, 3, values, 3) + a = self._klass(values, kind=kind, fill_value=2) + self._check_numeric_ops(a, 1, values, 1) + self._check_numeric_ops(a, 0, values, 0) + self._check_numeric_ops(a, 3, values, 3) - def test_float_scalar_comparison(self): + def test_float_scalar_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - for kind in ["integer", "block"]: - a = self._klass(values, kind=kind) - self._check_comparison_ops(a, 1, values, 1) - self._check_comparison_ops(a, 0, values, 0) - self._check_comparison_ops(a, 3, values, 3) + a = self._klass(values, kind=kind) + self._check_comparison_ops(a, 1, values, 1) + self._check_comparison_ops(a, 0, values, 0) + self._check_comparison_ops(a, 3, values, 3) - a = self._klass(values, kind=kind, fill_value=0) - self._check_comparison_ops(a, 1, values, 1) - self._check_comparison_ops(a, 0, values, 0) - self._check_comparison_ops(a, 3, values, 3) + a = self._klass(values, kind=kind, fill_value=0) + self._check_comparison_ops(a, 1, values, 1) + self._check_comparison_ops(a, 0, values, 0) + self._check_comparison_ops(a, 3, values, 3) - a = self._klass(values, kind=kind, fill_value=2) - self._check_comparison_ops(a, 1, values, 1) - self._check_comparison_ops(a, 0, values, 0) - self._check_comparison_ops(a, 3, values, 3) + a = self._klass(values, kind=kind, fill_value=2) + self._check_comparison_ops(a, 1, values, 1) + self._check_comparison_ops(a, 0, values, 0) + self._check_comparison_ops(a, 3, values, 3) - def test_float_same_index(self): + def test_float_same_index(self, kind): # when sp_index are the same - for kind in ["integer", "block"]: - values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) + values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) + rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) - a = self._klass(values, kind=kind) - b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind) + b = self._klass(rvalues, kind=kind) + self._check_numeric_ops(a, b, values, rvalues) - values = self._base([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0]) - rvalues = self._base([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0]) + values = self._base([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0]) + rvalues = self._base([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0]) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind, fill_value=0) + self._check_numeric_ops(a, b, values, rvalues) - def test_float_same_index_comparison(self): + def test_float_same_index_comparison(self, kind): # when sp_index are the same - for kind in ["integer", "block"]: - values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) + values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) + rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) - a = self._klass(values, kind=kind) - b = self._klass(rvalues, kind=kind) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind) + b = self._klass(rvalues, kind=kind) + self._check_comparison_ops(a, b, values, rvalues) - values = self._base([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0]) - rvalues = self._base([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0]) + values = self._base([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0]) + rvalues = self._base([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0]) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind, fill_value=0) + self._check_comparison_ops(a, b, values, rvalues) - def test_float_array(self): + def test_float_array(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) - for kind in ["integer", "block"]: - a = self._klass(values, kind=kind) - b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + a = self._klass(values, kind=kind) + b = self._klass(rvalues, kind=kind) + self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b * 0, values, rvalues * 0) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind) + self._check_numeric_ops(a, b, values, rvalues) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind, fill_value=0) + self._check_numeric_ops(a, b, values, rvalues) - a = self._klass(values, kind=kind, fill_value=1) - b = self._klass(rvalues, kind=kind, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=1) + b = self._klass(rvalues, kind=kind, fill_value=2) + self._check_numeric_ops(a, b, values, rvalues) def test_float_array_different_kind(self): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -251,174 +251,163 @@ def test_float_array_different_kind(self): b = self._klass(rvalues, kind="block", fill_value=2) self._check_numeric_ops(a, b, values, rvalues) - def test_float_array_comparison(self): + def test_float_array_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) - for kind in ["integer", "block"]: - a = self._klass(values, kind=kind) - b = self._klass(rvalues, kind=kind) - self._check_comparison_ops(a, b, values, rvalues) - self._check_comparison_ops(a, b * 0, values, rvalues * 0) + a = self._klass(values, kind=kind) + b = self._klass(rvalues, kind=kind) + self._check_comparison_ops(a, b, values, rvalues) + self._check_comparison_ops(a, b * 0, values, rvalues * 0) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind) + self._check_comparison_ops(a, b, values, rvalues) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind, fill_value=0) + self._check_comparison_ops(a, b, values, rvalues) - a = self._klass(values, kind=kind, fill_value=1) - b = self._klass(rvalues, kind=kind, fill_value=2) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=1) + b = self._klass(rvalues, kind=kind, fill_value=2) + self._check_comparison_ops(a, b, values, rvalues) - def test_int_array(self): + def test_int_array(self, kind): # have to specify dtype explicitly until fixing GH 667 dtype = np.int64 values = self._base([0, 1, 2, 0, 0, 0, 1, 2, 1, 0], dtype=dtype) rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=dtype) - for kind in ["integer", "block"]: - a = self._klass(values, dtype=dtype, kind=kind) - assert a.dtype == SparseDtype(dtype) - b = self._klass(rvalues, dtype=dtype, kind=kind) - assert b.dtype == SparseDtype(dtype) - - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + a = self._klass(values, dtype=dtype, kind=kind) + assert a.dtype == SparseDtype(dtype) + b = self._klass(rvalues, dtype=dtype, kind=kind) + assert b.dtype == SparseDtype(dtype) - a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) - assert a.dtype == SparseDtype(dtype) - b = self._klass(rvalues, dtype=dtype, kind=kind) - assert b.dtype == SparseDtype(dtype) + self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b * 0, values, rvalues * 0) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) + assert a.dtype == SparseDtype(dtype) + b = self._klass(rvalues, dtype=dtype, kind=kind) + assert b.dtype == SparseDtype(dtype) - a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) - assert a.dtype == SparseDtype(dtype) - b = self._klass(rvalues, fill_value=0, dtype=dtype, kind=kind) - assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues) - a = self._klass(values, fill_value=1, dtype=dtype, kind=kind) - assert a.dtype == SparseDtype(dtype, fill_value=1) - b = self._klass(rvalues, fill_value=2, dtype=dtype, kind=kind) - assert b.dtype == SparseDtype(dtype, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) + assert a.dtype == SparseDtype(dtype) + b = self._klass(rvalues, fill_value=0, dtype=dtype, kind=kind) + assert b.dtype == SparseDtype(dtype) + self._check_numeric_ops(a, b, values, rvalues) - def test_int_array_comparison(self): + a = self._klass(values, fill_value=1, dtype=dtype, kind=kind) + assert a.dtype == SparseDtype(dtype, fill_value=1) + b = self._klass(rvalues, fill_value=2, dtype=dtype, kind=kind) + assert b.dtype == SparseDtype(dtype, fill_value=2) + self._check_numeric_ops(a, b, values, rvalues) + def test_int_array_comparison(self, kind): + dtype = "int64" # int32 NI ATM - for dtype in ["int64"]: - values = self._base([0, 1, 2, 0, 0, 0, 1, 2, 1, 0], dtype=dtype) - rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=dtype) - for kind in ["integer", "block"]: - a = self._klass(values, dtype=dtype, kind=kind) - b = self._klass(rvalues, dtype=dtype, kind=kind) - self._check_comparison_ops(a, b, values, rvalues) - self._check_comparison_ops(a, b * 0, values, rvalues * 0) + values = self._base([0, 1, 2, 0, 0, 0, 1, 2, 1, 0], dtype=dtype) + rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=dtype) + + a = self._klass(values, dtype=dtype, kind=kind) + b = self._klass(rvalues, dtype=dtype, kind=kind) + self._check_comparison_ops(a, b, values, rvalues) + self._check_comparison_ops(a, b * 0, values, rvalues * 0) - a = self._klass(values, dtype=dtype, kind=kind, fill_value=0) - b = self._klass(rvalues, dtype=dtype, kind=kind) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, dtype=dtype, kind=kind, fill_value=0) + b = self._klass(rvalues, dtype=dtype, kind=kind) + self._check_comparison_ops(a, b, values, rvalues) - a = self._klass(values, dtype=dtype, kind=kind, fill_value=0) - b = self._klass(rvalues, dtype=dtype, kind=kind, fill_value=0) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, dtype=dtype, kind=kind, fill_value=0) + b = self._klass(rvalues, dtype=dtype, kind=kind, fill_value=0) + self._check_comparison_ops(a, b, values, rvalues) - a = self._klass(values, dtype=dtype, kind=kind, fill_value=1) - b = self._klass(rvalues, dtype=dtype, kind=kind, fill_value=2) - self._check_comparison_ops(a, b, values, rvalues) + a = self._klass(values, dtype=dtype, kind=kind, fill_value=1) + b = self._klass(rvalues, dtype=dtype, kind=kind, fill_value=2) + self._check_comparison_ops(a, b, values, rvalues) - def test_bool_same_index(self): + @pytest.mark.parametrize("fill_value", [True, False, np.nan]) + def test_bool_same_index(self, kind, fill_value): # GH 14000 # when sp_index are the same - for kind in ["integer", "block"]: - values = self._base([True, False, True, True], dtype=np.bool) - rvalues = self._base([True, False, True, True], dtype=np.bool) - - for fill_value in [True, False, np.nan]: - a = self._klass(values, kind=kind, dtype=np.bool, fill_value=fill_value) - b = self._klass( - rvalues, kind=kind, dtype=np.bool, fill_value=fill_value - ) - self._check_logical_ops(a, b, values, rvalues) - - def test_bool_array_logical(self): + values = self._base([True, False, True, True], dtype=np.bool) + rvalues = self._base([True, False, True, True], dtype=np.bool) + + a = self._klass(values, kind=kind, dtype=np.bool, fill_value=fill_value) + b = self._klass(rvalues, kind=kind, dtype=np.bool, fill_value=fill_value) + self._check_logical_ops(a, b, values, rvalues) + + @pytest.mark.parametrize("fill_value", [True, False, np.nan]) + def test_bool_array_logical(self, kind, fill_value): # GH 14000 # when sp_index are the same - for kind in ["integer", "block"]: - values = self._base([True, False, True, False, True, True], dtype=np.bool) - rvalues = self._base([True, False, False, True, False, True], dtype=np.bool) - - for fill_value in [True, False, np.nan]: - a = self._klass(values, kind=kind, dtype=np.bool, fill_value=fill_value) - b = self._klass( - rvalues, kind=kind, dtype=np.bool, fill_value=fill_value - ) - self._check_logical_ops(a, b, values, rvalues) + values = self._base([True, False, True, False, True, True], dtype=np.bool) + rvalues = self._base([True, False, False, True, False, True], dtype=np.bool) - def test_mixed_array_float_int(self): + a = self._klass(values, kind=kind, dtype=np.bool, fill_value=fill_value) + b = self._klass(rvalues, kind=kind, dtype=np.bool, fill_value=fill_value) + self._check_logical_ops(a, b, values, rvalues) - for rdtype in ["int64"]: - values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=rdtype) + def test_mixed_array_float_int(self, kind): + rdtype = "int64" - for kind in ["integer", "block"]: - a = self._klass(values, kind=kind) - b = self._klass(rvalues, kind=kind) - assert b.dtype == SparseDtype(rdtype) + values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) + rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=rdtype) - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + a = self._klass(values, kind=kind) + b = self._klass(rvalues, kind=kind) + assert b.dtype == SparseDtype(rdtype) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind) - assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b * 0, values, rvalues * 0) - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind, fill_value=0) - assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind) + assert b.dtype == SparseDtype(rdtype) + self._check_numeric_ops(a, b, values, rvalues) - a = self._klass(values, kind=kind, fill_value=1) - b = self._klass(rvalues, kind=kind, fill_value=2) - assert b.dtype == SparseDtype(rdtype, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind, fill_value=0) + assert b.dtype == SparseDtype(rdtype) + self._check_numeric_ops(a, b, values, rvalues) - def test_mixed_array_comparison(self): + a = self._klass(values, kind=kind, fill_value=1) + b = self._klass(rvalues, kind=kind, fill_value=2) + assert b.dtype == SparseDtype(rdtype, fill_value=2) + self._check_numeric_ops(a, b, values, rvalues) + def test_mixed_array_comparison(self, kind): + rdtype = "int64" # int32 NI ATM - for rdtype in ["int64"]: - values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=rdtype) - - for kind in ["integer", "block"]: - a = self._klass(values, kind=kind) - b = self._klass(rvalues, kind=kind) - assert b.dtype == SparseDtype(rdtype) - - self._check_comparison_ops(a, b, values, rvalues) - self._check_comparison_ops(a, b * 0, values, rvalues * 0) - - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind) - assert b.dtype == SparseDtype(rdtype) - self._check_comparison_ops(a, b, values, rvalues) - - a = self._klass(values, kind=kind, fill_value=0) - b = self._klass(rvalues, kind=kind, fill_value=0) - assert b.dtype == SparseDtype(rdtype) - self._check_comparison_ops(a, b, values, rvalues) - - a = self._klass(values, kind=kind, fill_value=1) - b = self._klass(rvalues, kind=kind, fill_value=2) - assert b.dtype == SparseDtype(rdtype, fill_value=2) - self._check_comparison_ops(a, b, values, rvalues) + + values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) + rvalues = self._base([2, 0, 2, 3, 0, 0, 1, 5, 2, 0], dtype=rdtype) + + a = self._klass(values, kind=kind) + b = self._klass(rvalues, kind=kind) + assert b.dtype == SparseDtype(rdtype) + + self._check_comparison_ops(a, b, values, rvalues) + self._check_comparison_ops(a, b * 0, values, rvalues * 0) + + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind) + assert b.dtype == SparseDtype(rdtype) + self._check_comparison_ops(a, b, values, rvalues) + + a = self._klass(values, kind=kind, fill_value=0) + b = self._klass(rvalues, kind=kind, fill_value=0) + assert b.dtype == SparseDtype(rdtype) + self._check_comparison_ops(a, b, values, rvalues) + + a = self._klass(values, kind=kind, fill_value=1) + b = self._klass(rvalues, kind=kind, fill_value=2) + assert b.dtype == SparseDtype(rdtype, fill_value=2) + self._check_comparison_ops(a, b, values, rvalues) class TestSparseSeriesArithmetic(TestSparseArrayArithmetics): From d9943be389e412f93ed3cb35f5766430174cda32 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 21:03:50 -0700 Subject: [PATCH 02/21] for loop, preparing to parametrize --- .../tests/arrays/sparse/test_arithmetics.py | 80 +++++++------------ 1 file changed, 31 insertions(+), 49 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index ae752e95e693a..57f9f83c09d66 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -4,6 +4,7 @@ import pytest import pandas as pd +from pandas.core import ops from pandas.core.sparse.api import SparseDtype import pandas.util.testing as tm @@ -28,55 +29,36 @@ def _check_numeric_ops(self, a, b, a_dense, b_dense): # Unfortunately, trying to wrap the computation of each expected # value is with np.errstate() is too tedious. - # sparse & sparse - self._assert((a + b).to_dense(), a_dense + b_dense) - self._assert((b + a).to_dense(), b_dense + a_dense) - - self._assert((a - b).to_dense(), a_dense - b_dense) - self._assert((b - a).to_dense(), b_dense - a_dense) - - self._assert((a * b).to_dense(), a_dense * b_dense) - self._assert((b * a).to_dense(), b_dense * a_dense) - - # pandas uses future division - self._assert((a / b).to_dense(), a_dense * 1.0 / b_dense) - self._assert((b / a).to_dense(), b_dense * 1.0 / a_dense) - - # ToDo: FIXME in GH 13843 - if not (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): - self._assert((a // b).to_dense(), a_dense // b_dense) - self._assert((b // a).to_dense(), b_dense // a_dense) - - self._assert((a % b).to_dense(), a_dense % b_dense) - self._assert((b % a).to_dense(), b_dense % a_dense) - - self._assert((a ** b).to_dense(), a_dense ** b_dense) - self._assert((b ** a).to_dense(), b_dense ** a_dense) - - # sparse & dense - self._assert((a + b_dense).to_dense(), a_dense + b_dense) - self._assert((b_dense + a).to_dense(), b_dense + a_dense) - - self._assert((a - b_dense).to_dense(), a_dense - b_dense) - self._assert((b_dense - a).to_dense(), b_dense - a_dense) - - self._assert((a * b_dense).to_dense(), a_dense * b_dense) - self._assert((b_dense * a).to_dense(), b_dense * a_dense) - - # pandas uses future division - self._assert((a / b_dense).to_dense(), a_dense * 1.0 / b_dense) - self._assert((b_dense / a).to_dense(), b_dense * 1.0 / a_dense) - - # ToDo: FIXME in GH 13843 - if not (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): - self._assert((a // b_dense).to_dense(), a_dense // b_dense) - self._assert((b_dense // a).to_dense(), b_dense // a_dense) - - self._assert((a % b_dense).to_dense(), a_dense % b_dense) - self._assert((b_dense % a).to_dense(), b_dense % a_dense) - - self._assert((a ** b_dense).to_dense(), a_dense ** b_dense) - self._assert((b_dense ** a).to_dense(), b_dense ** a_dense) + for mix in [True, False]: + # True --> sparse & dense + # False --> sparse & sparse + + # sparse & sparse + for op in [operator.add, ops.radd, + operator.sub, ops.rsub, + operator.mul, ops.rmul, + operator.truediv, ops.rtruediv, + operator.floordiv, ops.rfloordiv, + operator.mod, ops.rmod, + operator.pow, ops.rpow]: + + if op in [operator.floordiv, ops.rfloordiv]: + # FIXME: GH#13843 + if (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): + continue + + if mix: + result = op(a, b_dense).to_dense() + else: + result = op(a, b).to_dense() + + if op in [operator.truediv, ops.rtruediv]: + # pandas uses future division + expected = op(a_dense * 1.0, b_dense) + else: + expected = op(a_dense, b_dense) + + self._assert(result, expected) def _check_bool_result(self, res): assert isinstance(res, self._klass) From eb8b6d750be1d269294a7f4431e5b72756b817da Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 21:07:26 -0700 Subject: [PATCH 03/21] parametrize/fixturize mix --- .../tests/arrays/sparse/test_arithmetics.py | 148 +++++++++--------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 57f9f83c09d66..8c1206f4e1654 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -14,6 +14,12 @@ def kind(request): return request.param +@pytest.fixture(params=[True, False]) +def mix(request): + # whether to operate op(sparse, dense) instead of op(sparse, sparse) + return request.param + + @pytest.mark.filterwarnings("ignore:Sparse:FutureWarning") @pytest.mark.filterwarnings("ignore:Series.to_sparse:FutureWarning") class TestSparseArrayArithmetics: @@ -24,41 +30,37 @@ class TestSparseArrayArithmetics: def _assert(self, a, b): tm.assert_numpy_array_equal(a, b) - def _check_numeric_ops(self, a, b, a_dense, b_dense): + def _check_numeric_ops(self, a, b, a_dense, b_dense, mix): with np.errstate(invalid="ignore", divide="ignore"): # Unfortunately, trying to wrap the computation of each expected # value is with np.errstate() is too tedious. - for mix in [True, False]: - # True --> sparse & dense - # False --> sparse & sparse - - # sparse & sparse - for op in [operator.add, ops.radd, - operator.sub, ops.rsub, - operator.mul, ops.rmul, - operator.truediv, ops.rtruediv, - operator.floordiv, ops.rfloordiv, - operator.mod, ops.rmod, - operator.pow, ops.rpow]: - - if op in [operator.floordiv, ops.rfloordiv]: - # FIXME: GH#13843 - if (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): - continue - - if mix: - result = op(a, b_dense).to_dense() - else: - result = op(a, b).to_dense() - - if op in [operator.truediv, ops.rtruediv]: - # pandas uses future division - expected = op(a_dense * 1.0, b_dense) - else: - expected = op(a_dense, b_dense) - - self._assert(result, expected) + # sparse & sparse + for op in [operator.add, ops.radd, + operator.sub, ops.rsub, + operator.mul, ops.rmul, + operator.truediv, ops.rtruediv, + operator.floordiv, ops.rfloordiv, + operator.mod, ops.rmod, + operator.pow, ops.rpow]: + + if op in [operator.floordiv, ops.rfloordiv]: + # FIXME: GH#13843 + if (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): + continue + + if mix: + result = op(a, b_dense).to_dense() + else: + result = op(a, b).to_dense() + + if op in [operator.truediv, ops.rtruediv]: + # pandas uses future division + expected = op(a_dense * 1.0, b_dense) + else: + expected = op(a_dense, b_dense) + + self._assert(result, expected) def _check_bool_result(self, res): assert isinstance(res, self._klass) @@ -123,23 +125,23 @@ def _check_logical_ops(self, a, b, a_dense, b_dense): self._check_bool_result(a | b_dense) self._assert((a | b_dense).to_dense(), a_dense | b_dense) - def test_float_scalar(self, kind): + def test_float_scalar(self, kind, mix): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) a = self._klass(values, kind=kind) - self._check_numeric_ops(a, 1, values, 1) - self._check_numeric_ops(a, 0, values, 0) - self._check_numeric_ops(a, 3, values, 3) + self._check_numeric_ops(a, 1, values, 1, mix) + self._check_numeric_ops(a, 0, values, 0, mix) + self._check_numeric_ops(a, 3, values, 3, mix) a = self._klass(values, kind=kind, fill_value=0) - self._check_numeric_ops(a, 1, values, 1) - self._check_numeric_ops(a, 0, values, 0) - self._check_numeric_ops(a, 3, values, 3) + self._check_numeric_ops(a, 1, values, 1, mix) + self._check_numeric_ops(a, 0, values, 0, mix) + self._check_numeric_ops(a, 3, values, 3, mix) a = self._klass(values, kind=kind, fill_value=2) - self._check_numeric_ops(a, 1, values, 1) - self._check_numeric_ops(a, 0, values, 0) - self._check_numeric_ops(a, 3, values, 3) + self._check_numeric_ops(a, 1, values, 1, mix) + self._check_numeric_ops(a, 0, values, 0, mix) + self._check_numeric_ops(a, 3, values, 3, mix) def test_float_scalar_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -159,21 +161,21 @@ def test_float_scalar_comparison(self, kind): self._check_comparison_ops(a, 0, values, 0) self._check_comparison_ops(a, 3, values, 3) - def test_float_same_index(self, kind): + def test_float_same_index(self, kind, mix): # when sp_index are the same values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) a = self._klass(values, kind=kind) b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) values = self._base([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0]) rvalues = self._base([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0]) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) def test_float_same_index_comparison(self, kind): # when sp_index are the same @@ -191,47 +193,47 @@ def test_float_same_index_comparison(self, kind): b = self._klass(rvalues, kind=kind, fill_value=0) self._check_comparison_ops(a, b, values, rvalues) - def test_float_array(self, kind): + def test_float_array(self, kind, mix): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) a = self._klass(values, kind=kind) b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, kind=kind, fill_value=1) b = self._klass(rvalues, kind=kind, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) - def test_float_array_different_kind(self): + def test_float_array_different_kind(self, mix): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) a = self._klass(values, kind="integer") b = self._klass(rvalues, kind="block") - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) a = self._klass(values, kind="integer", fill_value=0) b = self._klass(rvalues, kind="block") - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, kind="integer", fill_value=0) b = self._klass(rvalues, kind="block", fill_value=0) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, kind="integer", fill_value=1) b = self._klass(rvalues, kind="block", fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) def test_float_array_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -254,7 +256,7 @@ def test_float_array_comparison(self, kind): b = self._klass(rvalues, kind=kind, fill_value=2) self._check_comparison_ops(a, b, values, rvalues) - def test_int_array(self, kind): + def test_int_array(self, kind, mix): # have to specify dtype explicitly until fixing GH 667 dtype = np.int64 @@ -266,27 +268,27 @@ def test_int_array(self, kind): b = self._klass(rvalues, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) assert a.dtype == SparseDtype(dtype) b = self._klass(rvalues, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) assert a.dtype == SparseDtype(dtype) b = self._klass(rvalues, fill_value=0, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, fill_value=1, dtype=dtype, kind=kind) assert a.dtype == SparseDtype(dtype, fill_value=1) b = self._klass(rvalues, fill_value=2, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) def test_int_array_comparison(self, kind): dtype = "int64" @@ -334,7 +336,7 @@ def test_bool_array_logical(self, kind, fill_value): b = self._klass(rvalues, kind=kind, dtype=np.bool, fill_value=fill_value) self._check_logical_ops(a, b, values, rvalues) - def test_mixed_array_float_int(self, kind): + def test_mixed_array_float_int(self, kind, mix): rdtype = "int64" values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -344,23 +346,23 @@ def test_mixed_array_float_int(self, kind): b = self._klass(rvalues, kind=kind) assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues) - self._check_numeric_ops(a, b * 0, values, rvalues * 0) + self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind) assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind, fill_value=0) assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) a = self._klass(values, kind=kind, fill_value=1) b = self._klass(rvalues, kind=kind, fill_value=2) assert b.dtype == SparseDtype(rdtype, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues) + self._check_numeric_ops(a, b, values, rvalues, mix) def test_mixed_array_comparison(self, kind): rdtype = "int64" @@ -400,7 +402,7 @@ class TestSparseSeriesArithmetic(TestSparseArrayArithmetics): def _assert(self, a, b): tm.assert_series_equal(a, b) - def test_alignment(self): + def test_alignment(self, mix): da = pd.Series(np.arange(4)) db = pd.Series(np.arange(4), index=[1, 2, 3, 4]) @@ -408,13 +410,13 @@ def test_alignment(self): sb = pd.SparseSeries( np.arange(4), index=[1, 2, 3, 4], dtype=np.int64, fill_value=0 ) - self._check_numeric_ops(sa, sb, da, db) + self._check_numeric_ops(sa, sb, da, db, mix) sa = pd.SparseSeries(np.arange(4), dtype=np.int64, fill_value=np.nan) sb = pd.SparseSeries( np.arange(4), index=[1, 2, 3, 4], dtype=np.int64, fill_value=np.nan ) - self._check_numeric_ops(sa, sb, da, db) + self._check_numeric_ops(sa, sb, da, db, mix) da = pd.Series(np.arange(4)) db = pd.Series(np.arange(4), index=[10, 11, 12, 13]) @@ -423,13 +425,13 @@ def test_alignment(self): sb = pd.SparseSeries( np.arange(4), index=[10, 11, 12, 13], dtype=np.int64, fill_value=0 ) - self._check_numeric_ops(sa, sb, da, db) + self._check_numeric_ops(sa, sb, da, db, mix) sa = pd.SparseSeries(np.arange(4), dtype=np.int64, fill_value=np.nan) sb = pd.SparseSeries( np.arange(4), index=[10, 11, 12, 13], dtype=np.int64, fill_value=np.nan ) - self._check_numeric_ops(sa, sb, da, db) + self._check_numeric_ops(sa, sb, da, db, mix) @pytest.mark.parametrize("op", [operator.eq, operator.add]) From 3edebac69f952bb7b2e27e75ffaac713818e69ce Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 21:10:09 -0700 Subject: [PATCH 04/21] fixture for op --- .../tests/arrays/sparse/test_arithmetics.py | 142 +++++++++--------- 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 8c1206f4e1654..1bea7dcee72b3 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -20,6 +20,19 @@ def mix(request): return request.param +@pytest.fixture(params=[ + operator.add, ops.radd, + operator.sub, ops.rsub, + operator.mul, ops.rmul, + operator.truediv, ops.rtruediv, + operator.floordiv, ops.rfloordiv, + operator.mod, ops.rmod, + operator.pow, ops.rpow +]) +def op(request): + return request.param + + @pytest.mark.filterwarnings("ignore:Sparse:FutureWarning") @pytest.mark.filterwarnings("ignore:Series.to_sparse:FutureWarning") class TestSparseArrayArithmetics: @@ -30,37 +43,28 @@ class TestSparseArrayArithmetics: def _assert(self, a, b): tm.assert_numpy_array_equal(a, b) - def _check_numeric_ops(self, a, b, a_dense, b_dense, mix): + def _check_numeric_ops(self, a, b, a_dense, b_dense, mix, op): with np.errstate(invalid="ignore", divide="ignore"): # Unfortunately, trying to wrap the computation of each expected # value is with np.errstate() is too tedious. - # sparse & sparse - for op in [operator.add, ops.radd, - operator.sub, ops.rsub, - operator.mul, ops.rmul, - operator.truediv, ops.rtruediv, - operator.floordiv, ops.rfloordiv, - operator.mod, ops.rmod, - operator.pow, ops.rpow]: - - if op in [operator.floordiv, ops.rfloordiv]: - # FIXME: GH#13843 - if (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): - continue - - if mix: - result = op(a, b_dense).to_dense() - else: - result = op(a, b).to_dense() - - if op in [operator.truediv, ops.rtruediv]: - # pandas uses future division - expected = op(a_dense * 1.0, b_dense) - else: - expected = op(a_dense, b_dense) - - self._assert(result, expected) + if op in [operator.floordiv, ops.rfloordiv]: + # FIXME: GH#13843 + if (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): + return # TODO: pytest.skip? xfail? + + if mix: + result = op(a, b_dense).to_dense() + else: + result = op(a, b).to_dense() + + if op in [operator.truediv, ops.rtruediv]: + # pandas uses future division + expected = op(a_dense * 1.0, b_dense) + else: + expected = op(a_dense, b_dense) + + self._assert(result, expected) def _check_bool_result(self, res): assert isinstance(res, self._klass) @@ -125,23 +129,23 @@ def _check_logical_ops(self, a, b, a_dense, b_dense): self._check_bool_result(a | b_dense) self._assert((a | b_dense).to_dense(), a_dense | b_dense) - def test_float_scalar(self, kind, mix): + def test_float_scalar(self, kind, mix, op): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) a = self._klass(values, kind=kind) - self._check_numeric_ops(a, 1, values, 1, mix) - self._check_numeric_ops(a, 0, values, 0, mix) - self._check_numeric_ops(a, 3, values, 3, mix) + self._check_numeric_ops(a, 1, values, 1, mix, op) + self._check_numeric_ops(a, 0, values, 0, mix, op) + self._check_numeric_ops(a, 3, values, 3, mix, op) a = self._klass(values, kind=kind, fill_value=0) - self._check_numeric_ops(a, 1, values, 1, mix) - self._check_numeric_ops(a, 0, values, 0, mix) - self._check_numeric_ops(a, 3, values, 3, mix) + self._check_numeric_ops(a, 1, values, 1, mix, op) + self._check_numeric_ops(a, 0, values, 0, mix, op) + self._check_numeric_ops(a, 3, values, 3, mix, op) a = self._klass(values, kind=kind, fill_value=2) - self._check_numeric_ops(a, 1, values, 1, mix) - self._check_numeric_ops(a, 0, values, 0, mix) - self._check_numeric_ops(a, 3, values, 3, mix) + self._check_numeric_ops(a, 1, values, 1, mix, op) + self._check_numeric_ops(a, 0, values, 0, mix, op) + self._check_numeric_ops(a, 3, values, 3, mix, op) def test_float_scalar_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -161,21 +165,21 @@ def test_float_scalar_comparison(self, kind): self._check_comparison_ops(a, 0, values, 0) self._check_comparison_ops(a, 3, values, 3) - def test_float_same_index(self, kind, mix): + def test_float_same_index(self, kind, mix, op): # when sp_index are the same values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) a = self._klass(values, kind=kind) b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) values = self._base([0.0, 1.0, 2.0, 6.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0]) rvalues = self._base([0.0, 2.0, 3.0, 4.0, 0.0, 0.0, 1.0, 3.0, 2.0, 0.0]) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) def test_float_same_index_comparison(self, kind): # when sp_index are the same @@ -193,47 +197,47 @@ def test_float_same_index_comparison(self, kind): b = self._klass(rvalues, kind=kind, fill_value=0) self._check_comparison_ops(a, b, values, rvalues) - def test_float_array(self, kind, mix): + def test_float_array(self, kind, mix, op): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) a = self._klass(values, kind=kind) b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues, mix) - self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind, fill_value=0) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, kind=kind, fill_value=1) b = self._klass(rvalues, kind=kind, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) - def test_float_array_different_kind(self, mix): + def test_float_array_different_kind(self, mix, op): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) a = self._klass(values, kind="integer") b = self._klass(rvalues, kind="block") - self._check_numeric_ops(a, b, values, rvalues, mix) - self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op) a = self._klass(values, kind="integer", fill_value=0) b = self._klass(rvalues, kind="block") - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, kind="integer", fill_value=0) b = self._klass(rvalues, kind="block", fill_value=0) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, kind="integer", fill_value=1) b = self._klass(rvalues, kind="block", fill_value=2) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) def test_float_array_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -256,7 +260,7 @@ def test_float_array_comparison(self, kind): b = self._klass(rvalues, kind=kind, fill_value=2) self._check_comparison_ops(a, b, values, rvalues) - def test_int_array(self, kind, mix): + def test_int_array(self, kind, mix, op): # have to specify dtype explicitly until fixing GH 667 dtype = np.int64 @@ -268,27 +272,27 @@ def test_int_array(self, kind, mix): b = self._klass(rvalues, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues, mix) - self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op) a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) assert a.dtype == SparseDtype(dtype) b = self._klass(rvalues, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, fill_value=0, dtype=dtype, kind=kind) assert a.dtype == SparseDtype(dtype) b = self._klass(rvalues, fill_value=0, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, fill_value=1, dtype=dtype, kind=kind) assert a.dtype == SparseDtype(dtype, fill_value=1) b = self._klass(rvalues, fill_value=2, dtype=dtype, kind=kind) assert b.dtype == SparseDtype(dtype, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) def test_int_array_comparison(self, kind): dtype = "int64" @@ -336,7 +340,7 @@ def test_bool_array_logical(self, kind, fill_value): b = self._klass(rvalues, kind=kind, dtype=np.bool, fill_value=fill_value) self._check_logical_ops(a, b, values, rvalues) - def test_mixed_array_float_int(self, kind, mix): + def test_mixed_array_float_int(self, kind, mix, op): rdtype = "int64" values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -346,23 +350,23 @@ def test_mixed_array_float_int(self, kind, mix): b = self._klass(rvalues, kind=kind) assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues, mix) - self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) + self._check_numeric_ops(a, b * 0, values, rvalues * 0, mix, op) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind) assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, kind=kind, fill_value=0) b = self._klass(rvalues, kind=kind, fill_value=0) assert b.dtype == SparseDtype(rdtype) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) a = self._klass(values, kind=kind, fill_value=1) b = self._klass(rvalues, kind=kind, fill_value=2) assert b.dtype == SparseDtype(rdtype, fill_value=2) - self._check_numeric_ops(a, b, values, rvalues, mix) + self._check_numeric_ops(a, b, values, rvalues, mix, op) def test_mixed_array_comparison(self, kind): rdtype = "int64" @@ -402,7 +406,7 @@ class TestSparseSeriesArithmetic(TestSparseArrayArithmetics): def _assert(self, a, b): tm.assert_series_equal(a, b) - def test_alignment(self, mix): + def test_alignment(self, mix, op): da = pd.Series(np.arange(4)) db = pd.Series(np.arange(4), index=[1, 2, 3, 4]) @@ -410,13 +414,13 @@ def test_alignment(self, mix): sb = pd.SparseSeries( np.arange(4), index=[1, 2, 3, 4], dtype=np.int64, fill_value=0 ) - self._check_numeric_ops(sa, sb, da, db, mix) + self._check_numeric_ops(sa, sb, da, db, mix, op) sa = pd.SparseSeries(np.arange(4), dtype=np.int64, fill_value=np.nan) sb = pd.SparseSeries( np.arange(4), index=[1, 2, 3, 4], dtype=np.int64, fill_value=np.nan ) - self._check_numeric_ops(sa, sb, da, db, mix) + self._check_numeric_ops(sa, sb, da, db, mix, op) da = pd.Series(np.arange(4)) db = pd.Series(np.arange(4), index=[10, 11, 12, 13]) @@ -425,13 +429,13 @@ def test_alignment(self, mix): sb = pd.SparseSeries( np.arange(4), index=[10, 11, 12, 13], dtype=np.int64, fill_value=0 ) - self._check_numeric_ops(sa, sb, da, db, mix) + self._check_numeric_ops(sa, sb, da, db, mix, op) sa = pd.SparseSeries(np.arange(4), dtype=np.int64, fill_value=np.nan) sb = pd.SparseSeries( np.arange(4), index=[10, 11, 12, 13], dtype=np.int64, fill_value=np.nan ) - self._check_numeric_ops(sa, sb, da, db, mix) + self._check_numeric_ops(sa, sb, da, db, mix, op) @pytest.mark.parametrize("op", [operator.eq, operator.add]) From eb60fc36c7229b09982a540ef48a9faba86990f5 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 21:10:38 -0700 Subject: [PATCH 05/21] blackify --- .../tests/arrays/sparse/test_arithmetics.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 1bea7dcee72b3..296d6e3b80a17 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -20,15 +20,24 @@ def mix(request): return request.param -@pytest.fixture(params=[ - operator.add, ops.radd, - operator.sub, ops.rsub, - operator.mul, ops.rmul, - operator.truediv, ops.rtruediv, - operator.floordiv, ops.rfloordiv, - operator.mod, ops.rmod, - operator.pow, ops.rpow -]) +@pytest.fixture( + params=[ + operator.add, + ops.radd, + operator.sub, + ops.rsub, + operator.mul, + ops.rmul, + operator.truediv, + ops.rtruediv, + operator.floordiv, + ops.rfloordiv, + operator.mod, + ops.rmod, + operator.pow, + ops.rpow, + ] +) def op(request): return request.param @@ -50,7 +59,7 @@ def _check_numeric_ops(self, a, b, a_dense, b_dense, mix, op): if op in [operator.floordiv, ops.rfloordiv]: # FIXME: GH#13843 - if (self._base == pd.Series and a.dtype.subtype == np.dtype("int64")): + if self._base == pd.Series and a.dtype.subtype == np.dtype("int64"): return # TODO: pytest.skip? xfail? if mix: From 3cf8b1ea854a9468510520aa5db56dc6ca3e2040 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 21:15:29 -0700 Subject: [PATCH 06/21] parametrize more --- pandas/tests/arrays/sparse/test_arithmetics.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 296d6e3b80a17..d1cae700a968d 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -138,20 +138,11 @@ def _check_logical_ops(self, a, b, a_dense, b_dense): self._check_bool_result(a | b_dense) self._assert((a | b_dense).to_dense(), a_dense | b_dense) - def test_float_scalar(self, kind, mix, op): + @pytest.mark.parametrize("fill_value", [None, 0, 2]) + def test_float_scalar(self, kind, mix, op, fill_value): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) - a = self._klass(values, kind=kind) - self._check_numeric_ops(a, 1, values, 1, mix, op) - self._check_numeric_ops(a, 0, values, 0, mix, op) - self._check_numeric_ops(a, 3, values, 3, mix, op) - - a = self._klass(values, kind=kind, fill_value=0) - self._check_numeric_ops(a, 1, values, 1, mix, op) - self._check_numeric_ops(a, 0, values, 0, mix, op) - self._check_numeric_ops(a, 3, values, 3, mix, op) - - a = self._klass(values, kind=kind, fill_value=2) + a = self._klass(values, kind=kind, fill_value=fill_value) self._check_numeric_ops(a, 1, values, 1, mix, op) self._check_numeric_ops(a, 0, values, 0, mix, op) self._check_numeric_ops(a, 3, values, 3, mix, op) From c34a9cca1d27fabe8cf39ef710fe963ff7836a16 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sat, 6 Jul 2019 21:19:25 -0700 Subject: [PATCH 07/21] param scalar --- pandas/tests/arrays/sparse/test_arithmetics.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index d1cae700a968d..5c8e1a154b437 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -138,14 +138,13 @@ def _check_logical_ops(self, a, b, a_dense, b_dense): self._check_bool_result(a | b_dense) self._assert((a | b_dense).to_dense(), a_dense | b_dense) + @pytest.mark.parametrize("scalar", [0, 1, 3]) @pytest.mark.parametrize("fill_value", [None, 0, 2]) - def test_float_scalar(self, kind, mix, op, fill_value): + def test_float_scalar(self, kind, mix, op, fill_value, scalar): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) a = self._klass(values, kind=kind, fill_value=fill_value) - self._check_numeric_ops(a, 1, values, 1, mix, op) - self._check_numeric_ops(a, 0, values, 0, mix, op) - self._check_numeric_ops(a, 3, values, 3, mix, op) + self._check_numeric_ops(a, scalar, values, scalar, mix, op) def test_float_scalar_comparison(self, kind): values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) From b59b81a1b8d75591540d75ec3dff62f618207c9b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 7 Jul 2019 07:59:50 -0700 Subject: [PATCH 08/21] docstring, xfail --- pandas/tests/arrays/sparse/test_arithmetics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 5c8e1a154b437..698c37c37e805 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -11,6 +11,7 @@ @pytest.fixture(params=["integer", "block"]) def kind(request): + """kind kwarg to pass to SparseArray/SparseSeries""" return request.param @@ -60,7 +61,7 @@ def _check_numeric_ops(self, a, b, a_dense, b_dense, mix, op): if op in [operator.floordiv, ops.rfloordiv]: # FIXME: GH#13843 if self._base == pd.Series and a.dtype.subtype == np.dtype("int64"): - return # TODO: pytest.skip? xfail? + pytest.xfail("Not defined/working. See GH#13843") if mix: result = op(a, b_dense).to_dense() From f085d6bd76419e6cc9576fe0f871efd8462f9dc8 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 7 Jul 2019 08:04:00 -0700 Subject: [PATCH 09/21] remove defunct comment --- pandas/tests/arrays/sparse/test_arithmetics.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 698c37c37e805..1eeb5017344b5 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -55,9 +55,6 @@ def _assert(self, a, b): def _check_numeric_ops(self, a, b, a_dense, b_dense, mix, op): with np.errstate(invalid="ignore", divide="ignore"): - # Unfortunately, trying to wrap the computation of each expected - # value is with np.errstate() is too tedious. - if op in [operator.floordiv, ops.rfloordiv]: # FIXME: GH#13843 if self._base == pd.Series and a.dtype.subtype == np.dtype("int64"): From f03bd93d21609bea4d914cb7ee390f643b803d53 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 8 Jul 2019 16:21:33 -0700 Subject: [PATCH 10/21] implement all_arithmetic_functions fixture --- pandas/conftest.py | 30 +++++++++++ .../tests/arrays/sparse/test_arithmetics.py | 50 ++++++++----------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index 29833ab2fc0fa..f189947fb1817 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -1,5 +1,6 @@ from datetime import date, time, timedelta, timezone from decimal import Decimal +import operator import os from dateutil.tz import tzlocal, tzutc @@ -12,6 +13,7 @@ import pandas.util._test_decorators as td import pandas as pd +from pandas.core import ops from pandas import DataFrame import pandas.util.testing as tm @@ -163,6 +165,34 @@ def all_arithmetic_operators(request): return request.param +@pytest.fixture( + params=[ + operator.add, + ops.radd, + operator.sub, + ops.rsub, + operator.mul, + ops.rmul, + operator.truediv, + ops.rtruediv, + operator.floordiv, + ops.rfloordiv, + operator.mod, + ops.rmod, + operator.pow, + ops.rpow, + ] +) +def all_arithmetic_functions(request): + """ + Fixture for operator and roperator arithmetic functions. + + Note: This includes divmod and rdivmod, whereas all_arithmetic_operators + does not. + """ + return request.param + + _all_numeric_reductions = [ "sum", "max", diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 1eeb5017344b5..0f8f3d261c3b3 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -21,28 +21,6 @@ def mix(request): return request.param -@pytest.fixture( - params=[ - operator.add, - ops.radd, - operator.sub, - ops.rsub, - operator.mul, - ops.rmul, - operator.truediv, - ops.rtruediv, - operator.floordiv, - ops.rfloordiv, - operator.mod, - ops.rmod, - operator.pow, - ops.rpow, - ] -) -def op(request): - return request.param - - @pytest.mark.filterwarnings("ignore:Sparse:FutureWarning") @pytest.mark.filterwarnings("ignore:Series.to_sparse:FutureWarning") class TestSparseArrayArithmetics: @@ -138,7 +116,10 @@ def _check_logical_ops(self, a, b, a_dense, b_dense): @pytest.mark.parametrize("scalar", [0, 1, 3]) @pytest.mark.parametrize("fill_value", [None, 0, 2]) - def test_float_scalar(self, kind, mix, op, fill_value, scalar): + def test_float_scalar( + self, kind, mix, all_arithmetic_functions, fill_value, scalar + ): + op = all_arithmetic_functions values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) a = self._klass(values, kind=kind, fill_value=fill_value) @@ -162,8 +143,9 @@ def test_float_scalar_comparison(self, kind): self._check_comparison_ops(a, 0, values, 0) self._check_comparison_ops(a, 3, values, 3) - def test_float_same_index(self, kind, mix, op): + def test_float_same_index(self, kind, mix, all_arithmetic_functions): # when sp_index are the same + op = all_arithmetic_functions values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([np.nan, 2, 3, 4, np.nan, 0, 1, 3, 2, np.nan]) @@ -194,7 +176,9 @@ def test_float_same_index_comparison(self, kind): b = self._klass(rvalues, kind=kind, fill_value=0) self._check_comparison_ops(a, b, values, rvalues) - def test_float_array(self, kind, mix, op): + def test_float_array(self, kind, mix, all_arithmetic_functions): + op = all_arithmetic_functions + values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) @@ -215,7 +199,9 @@ def test_float_array(self, kind, mix, op): b = self._klass(rvalues, kind=kind, fill_value=2) self._check_numeric_ops(a, b, values, rvalues, mix, op) - def test_float_array_different_kind(self, mix, op): + def test_float_array_different_kind(self, mix, all_arithmetic_functions): + op = all_arithmetic_functions + values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) rvalues = self._base([2, np.nan, 2, 3, np.nan, 0, 1, 5, 2, np.nan]) @@ -257,7 +243,9 @@ def test_float_array_comparison(self, kind): b = self._klass(rvalues, kind=kind, fill_value=2) self._check_comparison_ops(a, b, values, rvalues) - def test_int_array(self, kind, mix, op): + def test_int_array(self, kind, mix, all_arithmetic_functions): + op = all_arithmetic_functions + # have to specify dtype explicitly until fixing GH 667 dtype = np.int64 @@ -337,7 +325,9 @@ def test_bool_array_logical(self, kind, fill_value): b = self._klass(rvalues, kind=kind, dtype=np.bool, fill_value=fill_value) self._check_logical_ops(a, b, values, rvalues) - def test_mixed_array_float_int(self, kind, mix, op): + def test_mixed_array_float_int(self, kind, mix, all_arithmetic_functions): + op = all_arithmetic_functions + rdtype = "int64" values = self._base([np.nan, 1, 2, 0, np.nan, 0, 1, 2, 1, np.nan]) @@ -403,7 +393,9 @@ class TestSparseSeriesArithmetic(TestSparseArrayArithmetics): def _assert(self, a, b): tm.assert_series_equal(a, b) - def test_alignment(self, mix, op): + def test_alignment(self, mix, all_arithmetic_functions): + op = all_arithmetic_functions + da = pd.Series(np.arange(4)) db = pd.Series(np.arange(4), index=[1, 2, 3, 4]) From cd2f56466922bad3c3b20c6dfae1367ff264e0a3 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 8 Jul 2019 17:43:41 -0700 Subject: [PATCH 11/21] isort fixup --- pandas/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/conftest.py b/pandas/conftest.py index f189947fb1817..ef2758d263e1a 100644 --- a/pandas/conftest.py +++ b/pandas/conftest.py @@ -13,8 +13,8 @@ import pandas.util._test_decorators as td import pandas as pd -from pandas.core import ops from pandas import DataFrame +from pandas.core import ops import pandas.util.testing as tm hypothesis.settings.register_profile( From e362a086d619e9141bffb99edf3e032811b01d8d Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 8 Jul 2019 18:22:48 -0700 Subject: [PATCH 12/21] check early for non-scalar default_fill_value --- pandas/core/sparse/frame.py | 5 ++++- pandas/tests/sparse/frame/test_frame.py | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pandas/core/sparse/frame.py b/pandas/core/sparse/frame.py index f195e4b5f4e37..60060a4a2d1fa 100644 --- a/pandas/core/sparse/frame.py +++ b/pandas/core/sparse/frame.py @@ -6,6 +6,7 @@ import numpy as np +from pandas._libs.lib import is_scalar, item_from_zerodim from pandas._libs.sparse import BlockIndex, get_blocks from pandas.compat.numpy import function as nv from pandas.util._decorators import Appender @@ -74,6 +75,8 @@ def __init__( dtype=None, copy=False, ): + if not is_scalar(default_fill_value): + raise ValueError("'default_fill_value' must be a scalar") warnings.warn(depr_msg, FutureWarning, stacklevel=2) # pick up the defaults from the Sparse structures @@ -666,7 +669,7 @@ def _get_op_result_fill_value(self, other, func): fill_value = np.nan else: fill_value = func(np.float64(own_default), np.float64(other.fill_value)) - + fill_value = item_from_zerodim(fill_value) else: raise NotImplementedError(type(other)) diff --git a/pandas/tests/sparse/frame/test_frame.py b/pandas/tests/sparse/frame/test_frame.py index 6527d41eac841..c3f2c0a960197 100644 --- a/pandas/tests/sparse/frame/test_frame.py +++ b/pandas/tests/sparse/frame/test_frame.py @@ -136,6 +136,12 @@ def test_constructor(self, float_frame, float_frame_int_kind, float_frame_fill0) repr(float_frame) + def test_constructor_fill_value_scalar(self): + d = {"b": [2, 3], "a": [0, 1]} + fill_value = np.array(np.nan) + with pytest.raises(ValueError, match="must be a scalar"): + SparseDataFrame(data=d, default_fill_value=fill_value) + def test_constructor_dict_order(self): # GH19018 # initialization ordering: by insertion order if python>= 3.6, else From 7c6e127d3a30f1a86fb157a1b311285a83fdbf20 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 8 Jul 2019 09:33:45 -0700 Subject: [PATCH 13/21] try to bring dispatch_fill_zeros, dispatch_mnissing in sync, remove unnecessary kwargs --- pandas/core/ops/__init__.py | 32 ++------------------------------ pandas/core/ops/missing.py | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 39 deletions(-) diff --git a/pandas/core/ops/__init__.py b/pandas/core/ops/__init__.py index f9112dbb1e4ab..bca8e435f10d6 100644 --- a/pandas/core/ops/__init__.py +++ b/pandas/core/ops/__init__.py @@ -234,32 +234,6 @@ def _gen_eval_kwargs(name): return kwargs -def _gen_fill_zeros(name): - """ - Find the appropriate fill value to use when filling in undefined values - in the results of the given operation caused by operating on - (generally dividing by) zero. - - Parameters - ---------- - name : str - - Returns - ------- - fill_value : {None, np.nan, np.inf} - """ - name = name.strip("__") - if "div" in name: - # truediv, floordiv, and reversed variants - fill_value = np.inf - elif "mod" in name: - # mod, rmod - fill_value = np.nan - else: - fill_value = None - return fill_value - - def _get_frame_op_default_axis(name): """ Only DataFrame cares about default_axis, specifically: @@ -1632,7 +1606,6 @@ def _arith_method_SERIES(cls, op, special): str_rep = _get_opstr(op, cls) op_name = _get_op_name(op, special) eval_kwargs = _gen_eval_kwargs(op_name) - fill_zeros = _gen_fill_zeros(op_name) construct_result = ( _construct_divmod_result if op in [divmod, rdivmod] else _construct_result ) @@ -1663,7 +1636,7 @@ def na_op(x, y): except TypeError: result = masked_arith_op(x, y, op) - return missing.dispatch_fill_zeros(op, x, y, result, fill_zeros) + return missing.dispatch_fill_zeros(op, x, y, result) def wrapper(left, right): if isinstance(right, ABCDataFrame): @@ -2154,7 +2127,6 @@ def _arith_method_FRAME(cls, op, special): str_rep = _get_opstr(op, cls) op_name = _get_op_name(op, special) eval_kwargs = _gen_eval_kwargs(op_name) - fill_zeros = _gen_fill_zeros(op_name) default_axis = _get_frame_op_default_axis(op_name) def na_op(x, y): @@ -2165,7 +2137,7 @@ def na_op(x, y): except TypeError: result = masked_arith_op(x, y, op) - return missing.dispatch_fill_zeros(op, x, y, result, fill_zeros) + return missing.dispatch_fill_zeros(op, x, y, result) if op_name in _op_descriptions: # i.e. include "add" but not "__add__" diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 608c2550994f1..11a8975713104 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -27,7 +27,7 @@ from pandas.core.dtypes.common import is_float_dtype, is_integer_dtype, is_scalar -from .roperator import rdivmod +from .roperator import rdivmod, rfloordiv, rmod def fill_zeros(result, x, y, name, fill): @@ -85,7 +85,7 @@ def fill_zeros(result, x, y, name, fill): return result -def mask_zero_div_zero(x, y, result, copy=False): +def mask_zero_div_zero(x, y, result): """ Set results of 0 / 0 or 0 // 0 to np.nan, regardless of the dtypes of the numerator or the denominator. @@ -95,9 +95,6 @@ def mask_zero_div_zero(x, y, result, copy=False): x : ndarray y : ndarray result : ndarray - copy : bool (default False) - Whether to always create a new array or try to fill in the existing - array if possible. Returns ------- @@ -130,7 +127,7 @@ def mask_zero_div_zero(x, y, result, copy=False): 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=copy).ravel() + result = result.astype("float64", copy=False).ravel() np.putmask(result, nan_mask, np.nan) np.putmask(result, posinf_mask, np.inf) @@ -172,7 +169,7 @@ def dispatch_missing(op, left, right, result): # FIXME: de-duplicate with dispatch_missing -def dispatch_fill_zeros(op, left, right, result, fill_value): +def dispatch_fill_zeros(op, left, right, result): """ Call fill_zeros with the appropriate fill value depending on the operation, with special logic for divmod and rdivmod. @@ -187,6 +184,12 @@ def dispatch_fill_zeros(op, left, right, result, fill_value): fill_zeros(result[0], left, right, "__rfloordiv__", np.inf), fill_zeros(result[1], left, right, "__rmod__", np.nan), ) - else: - result = fill_zeros(result, left, right, op.__name__, fill_value) + elif op is operator.floordiv: + result = fill_zeros(result, left, right, "__floordiv__", np.inf) + elif op is op is rfloordiv: + result = fill_zeros(result, left, right, "__rfloordiv__", np.inf) + elif op is operator.mod: + result = fill_zeros(result, left, right, "__mod__", np.nan) + elif op is rmod: + result = fill_zeros(result, left, right, "__rmod__", np.nan) return result From 97611c345fe669a348205f1faec1b0fd3613e390 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 8 Jul 2019 09:36:10 -0700 Subject: [PATCH 14/21] standardize calls --- pandas/core/ops/missing.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 11a8975713104..06128c9c24c20 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -154,16 +154,15 @@ def dispatch_missing(op, left, right, result): ------- result : ndarray """ - opstr = "__{opname}__".format(opname=op.__name__).replace("____", "__") if op is operator.floordiv: # Note: no need to do this for truediv; in py3 numpy behaves the way # we want. result = mask_zero_div_zero(left, right, result) elif op is operator.mod: - result = fill_zeros(result, left, right, opstr, np.nan) + result = fill_zeros(result, left, right, "__mod__", np.nan) elif op is divmod: res0 = mask_zero_div_zero(left, right, result[0]) - res1 = fill_zeros(result[1], left, right, opstr, np.nan) + res1 = fill_zeros(result[1], left, right, "__divmod__", np.nan) result = (res0, res1) return result @@ -185,8 +184,12 @@ def dispatch_fill_zeros(op, left, right, result): fill_zeros(result[1], left, right, "__rmod__", np.nan), ) elif op is operator.floordiv: + # Note: no need to do this for truediv; in py3 numpy behaves the way + # we want. result = fill_zeros(result, left, right, "__floordiv__", np.inf) elif op is op is rfloordiv: + # Note: no need to do this for rtruediv; in py3 numpy behaves the way + # we want. result = fill_zeros(result, left, right, "__rfloordiv__", np.inf) elif op is operator.mod: result = fill_zeros(result, left, right, "__mod__", np.nan) From c30b40c4053418bd86bfbd042122ad2b8c2b14b6 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 9 Jul 2019 13:06:10 -0700 Subject: [PATCH 15/21] use pandas convention for divmod --- pandas/core/ops/missing.py | 10 ++++++---- pandas/tests/arithmetic/test_numeric.py | 11 +++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 06128c9c24c20..8bbb29b4d7ad6 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -122,8 +122,9 @@ def mask_zero_div_zero(x, y, result): zpos_mask = zmask & ~zneg_mask nan_mask = (zmask & (x == 0)).ravel() - neginf_mask = ((zpos_mask & (x < 0)) | (zneg_mask & (x > 0))).ravel() - posinf_mask = ((zpos_mask & (x > 0)) | (zneg_mask & (x < 0))).ravel() + 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() 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 @@ -175,12 +176,13 @@ def dispatch_fill_zeros(op, left, right, result): """ if op is divmod: result = ( - fill_zeros(result[0], left, right, "__floordiv__", np.inf), + mask_zero_div_zero(left, right, result[0]), fill_zeros(result[1], left, right, "__mod__", np.nan), ) elif op is rdivmod: result = ( - fill_zeros(result[0], left, right, "__rfloordiv__", np.inf), + # TODO: do we need to switch left/right? + mask_zero_div_zero(right, left, result[0]), fill_zeros(result[1], left, right, "__rmod__", np.nan), ) elif op is operator.floordiv: diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 1fbecbab469e4..6b3a50af922da 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -332,6 +332,9 @@ def test_ser_divmod_zero(self, dtype1, any_real_dtype): right = pd.Series([0, 2]).astype(dtype2) expected = left // right, left % right + expected = list(expected) + expected[0] = expected[0].astype(np.float64) + expected[0][0] = np.inf result = divmod(left, right) tm.assert_series_equal(result[0], expected[0]) @@ -931,13 +934,9 @@ def check(series, other): tser = tm.makeTimeSeries().rename("ts") check(tser, tser * 2) - check(tser, tser * 0) check(tser, tser[::2]) check(tser, 5) - @pytest.mark.xfail( - reason="Series division does not yet fill 1/0 consistently; Index does." - ) def test_series_divmod_zero(self): # Check that divmod uses pandas convention for division by zero, # which does not match numpy. @@ -950,8 +949,8 @@ def test_series_divmod_zero(self): other = tser * 0 result = divmod(tser, other) - exp1 = pd.Series([np.inf] * len(tser), index=tser.index) - exp2 = pd.Series([np.nan] * len(tser), index=tser.index) + exp1 = pd.Series([np.inf] * len(tser), index=tser.index, name="ts") + exp2 = pd.Series([np.nan] * len(tser), index=tser.index, name="ts") tm.assert_series_equal(result[0], exp1) tm.assert_series_equal(result[1], exp2) From 1ed12b88238e4972f2424f60c5f068f12fa37666 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 9 Jul 2019 13:06:31 -0700 Subject: [PATCH 16/21] remove comment --- pandas/core/ops/missing.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 8bbb29b4d7ad6..276426d82a622 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -181,7 +181,6 @@ def dispatch_fill_zeros(op, left, right, result): ) elif op is rdivmod: result = ( - # TODO: do we need to switch left/right? mask_zero_div_zero(right, left, result[0]), fill_zeros(result[1], left, right, "__rmod__", np.nan), ) From 591eaaad6ec3c436dd47c96e5691815ab21b2433 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 10 Jul 2019 07:09:27 -0700 Subject: [PATCH 17/21] patch sparse and IntegerArray tests --- pandas/core/ops/missing.py | 8 ++++++-- pandas/tests/arithmetic/test_numeric.py | 15 +++++++-------- pandas/tests/arrays/sparse/test_arithmetics.py | 6 ++++++ pandas/tests/arrays/test_integer.py | 4 ++++ pandas/tests/sparse/frame/test_frame.py | 14 +++++++++++--- pandas/tests/sparse/series/test_series.py | 15 ++++++++++----- 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 276426d82a622..50dce941ec337 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -110,6 +110,10 @@ def mask_zero_div_zero(x, y, result): >>> mask_zero_div_zero(x, y, result) array([ inf, nan, -inf]) """ + if not isinstance(result, np.ndarray): + # e.g. SparseArray would raise TypeError with np.putmask + return result + if is_scalar(y): y = np.array(y) @@ -187,11 +191,11 @@ def dispatch_fill_zeros(op, left, right, result): elif op is operator.floordiv: # Note: no need to do this for truediv; in py3 numpy behaves the way # we want. - result = fill_zeros(result, left, right, "__floordiv__", np.inf) + result = mask_zero_div_zero(left, right, result) elif op is op is rfloordiv: # Note: no need to do this for rtruediv; in py3 numpy behaves the way # we want. - result = fill_zeros(result, left, right, "__rfloordiv__", np.inf) + result = mask_zero_div_zero(right, left, result) elif op is operator.mod: result = fill_zeros(result, left, right, "__mod__", np.nan) elif op is rmod: diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 6b3a50af922da..b237868d8bea8 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -884,17 +884,16 @@ def check(series, other): _check_op(series, other, operator.pow, pos_only=True) - _check_op(series, other, lambda x, y: operator.add(y, x)) - _check_op(series, other, lambda x, y: operator.sub(y, x)) - _check_op(series, other, lambda x, y: operator.truediv(y, x)) - _check_op(series, other, lambda x, y: operator.floordiv(y, x)) - _check_op(series, other, lambda x, y: operator.mul(y, x)) - _check_op(series, other, lambda x, y: operator.pow(y, x), pos_only=True) - _check_op(series, other, lambda x, y: operator.mod(y, x)) + _check_op(series, other, ops.radd) + _check_op(series, other, ops.rsub) + _check_op(series, other, ops.rtruediv) + _check_op(series, other, ops.rfloordiv) + _check_op(series, other, ops.rmul) + _check_op(series, other, ops.rpow, pos_only=True) + _check_op(series, other, ops.rmod) tser = tm.makeTimeSeries().rename("ts") check(tser, tser * 2) - check(tser, tser * 0) check(tser, tser[::2]) check(tser, 5) diff --git a/pandas/tests/arrays/sparse/test_arithmetics.py b/pandas/tests/arrays/sparse/test_arithmetics.py index 0f8f3d261c3b3..57e5a35d99e48 100644 --- a/pandas/tests/arrays/sparse/test_arithmetics.py +++ b/pandas/tests/arrays/sparse/test_arithmetics.py @@ -49,6 +49,12 @@ def _check_numeric_ops(self, a, b, a_dense, b_dense, mix, op): else: expected = op(a_dense, b_dense) + if op in [operator.floordiv, ops.rfloordiv]: + # Series sets 1//0 to np.inf, which SparseArray does not do (yet) + mask = np.isinf(expected) + if mask.any(): + expected[mask] = np.nan + self._assert(result, expected) def _check_bool_result(self, res): diff --git a/pandas/tests/arrays/test_integer.py b/pandas/tests/arrays/test_integer.py index dfdb08fa78cbc..8fbfb4c12f4b2 100644 --- a/pandas/tests/arrays/test_integer.py +++ b/pandas/tests/arrays/test_integer.py @@ -179,6 +179,10 @@ def _check_op_float(self, result, expected, mask, s, op_name, other): # check comparisons that are resulting in float dtypes expected[mask] = np.nan + if "floordiv" in op_name: + # Series op sets 1//0 to np.inf, which IntegerArray does not do (yet) + mask2 = np.isinf(expected) & np.isnan(result) + expected[mask2] = np.nan tm.assert_series_equal(result, expected) def _check_op_integer(self, result, expected, mask, s, op_name, other): diff --git a/pandas/tests/sparse/frame/test_frame.py b/pandas/tests/sparse/frame/test_frame.py index c3f2c0a960197..fb99d688e42e5 100644 --- a/pandas/tests/sparse/frame/test_frame.py +++ b/pandas/tests/sparse/frame/test_frame.py @@ -1,4 +1,5 @@ import operator +from types import LambdaType import numpy as np from numpy import nan @@ -9,6 +10,7 @@ import pandas as pd from pandas import DataFrame, Series, bdate_range, compat +from pandas.core import ops from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.sparse import frame as spf from pandas.core.sparse.api import ( @@ -424,6 +426,11 @@ def _compare_to_dense(a, b, da, db, op): sparse_result = op(a, b) dense_result = op(da, db) + if op in [operator.floordiv, ops.rfloordiv] or isinstance(op, LambdaType): + # Series sets 1//0 to np.inf, which SparseArray does not do (yet) + mask = np.isinf(dense_result) & ~np.isinf(sparse_result.to_dense()) + dense_result[mask] = np.nan + fill = sparse_result.default_fill_value dense_result = dense_result.to_sparse(fill_value=fill) tm.assert_sp_frame_equal(sparse_result, dense_result, exact_indices=False) @@ -436,7 +443,6 @@ def _compare_to_dense(a, b, da, db, op): ) opnames = ["add", "sub", "mul", "truediv", "floordiv"] - ops = [getattr(operator, name) for name in opnames] fidx = frame.index @@ -466,6 +472,7 @@ def _compare_to_dense(a, b, da, db, op): f = lambda a, b: getattr(a, op)(b, axis="index") _compare_to_dense(frame, s, frame.to_dense(), s.to_dense(), f) + # FIXME: dont leave commented-out # rops are not implemented # _compare_to_dense(s, frame, s.to_dense(), # frame.to_dense(), f) @@ -479,13 +486,14 @@ def _compare_to_dense(a, b, da, db, op): frame.xs(fidx[5])[:2], ] - for op in ops: + for name in opnames: + op = getattr(operator, name) for s in series: _compare_to_dense(frame, s, frame.to_dense(), s, op) _compare_to_dense(s, frame, s, frame.to_dense(), op) # it works! - result = frame + frame.loc[:, ["A", "B"]] # noqa + frame + frame.loc[:, ["A", "B"]] def test_op_corners(self, float_frame, empty_frame): empty = empty_frame + empty_frame diff --git a/pandas/tests/sparse/series/test_series.py b/pandas/tests/sparse/series/test_series.py index eb217283c7a83..531a1ec67a25b 100644 --- a/pandas/tests/sparse/series/test_series.py +++ b/pandas/tests/sparse/series/test_series.py @@ -12,6 +12,7 @@ import pandas as pd from pandas import DataFrame, Series, SparseDtype, SparseSeries, bdate_range, isna +from pandas.core import ops from pandas.core.reshape.util import cartesian_product import pandas.core.sparse.frame as spf from pandas.tests.series.test_api import SharedWithSparse @@ -563,6 +564,10 @@ def _check_op(a, b, op): adense = a.to_dense() if isinstance(a, SparseSeries) else a bdense = b.to_dense() if isinstance(b, SparseSeries) else b dense_result = op(adense, bdense) + if "floordiv" in op.__name__: + # Series sets 1//0 to np.inf, which SparseSeries does not do (yet) + mask = np.isinf(dense_result) + dense_result[mask] = np.nan tm.assert_almost_equal(sp_result.to_dense(), dense_result) def check(a, b): @@ -572,11 +577,11 @@ def check(a, b): _check_op(a, b, operator.floordiv) _check_op(a, b, operator.mul) - _check_op(a, b, lambda x, y: operator.add(y, x)) - _check_op(a, b, lambda x, y: operator.sub(y, x)) - _check_op(a, b, lambda x, y: operator.truediv(y, x)) - _check_op(a, b, lambda x, y: operator.floordiv(y, x)) - _check_op(a, b, lambda x, y: operator.mul(y, x)) + _check_op(a, b, ops.radd) + _check_op(a, b, ops.rsub) + _check_op(a, b, ops.rtruediv) + _check_op(a, b, ops.rfloordiv) + _check_op(a, b, ops.rmul) # FIXME: don't leave commented-out # NaN ** 0 = 1 in C? From 2580048a36279361fee381a8dd8d3da8ecdf9f7b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 10 Jul 2019 09:35:17 -0700 Subject: [PATCH 18/21] fixup for docs --- pandas/core/ops/missing.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pandas/core/ops/missing.py b/pandas/core/ops/missing.py index 50dce941ec337..3698958261555 100644 --- a/pandas/core/ops/missing.py +++ b/pandas/core/ops/missing.py @@ -111,13 +111,18 @@ def mask_zero_div_zero(x, y, result): array([ inf, nan, -inf]) """ if not isinstance(result, np.ndarray): - # e.g. SparseArray would raise TypeError with np.putmask + # FIXME: SparseArray would raise TypeError with np.putmask return result if is_scalar(y): y = np.array(y) zmask = y == 0 + + if isinstance(zmask, bool): + # FIXME: numpy did not evaluate pointwise, seen in docs build + return result + if zmask.any(): shape = result.shape From 6b411c4f93b7552d0f4c8ec1cc87bb95a24e1ae2 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 10 Jul 2019 17:13:38 -0700 Subject: [PATCH 19/21] requested comments --- pandas/tests/arithmetic/test_numeric.py | 2 ++ pandas/tests/sparse/frame/test_frame.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index b237868d8bea8..2b23790e4ccd3 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -331,6 +331,8 @@ def test_ser_divmod_zero(self, dtype1, any_real_dtype): left = pd.Series([1, 1]).astype(dtype1) right = pd.Series([0, 2]).astype(dtype2) + # GH#27321 pandas convention is to set 1 // 0 to np.inf, as opposed + # to numpy which sets to np.nan; patch `expected[0]` below expected = left // right, left % right expected = list(expected) expected[0] = expected[0].astype(np.float64) diff --git a/pandas/tests/sparse/frame/test_frame.py b/pandas/tests/sparse/frame/test_frame.py index c7b89d44bd4e7..5682c74a8b692 100644 --- a/pandas/tests/sparse/frame/test_frame.py +++ b/pandas/tests/sparse/frame/test_frame.py @@ -426,8 +426,10 @@ def _compare_to_dense(a, b, da, db, op): sparse_result = op(a, b) dense_result = op(da, db) + # catch lambdas but not non-lambdas e.g. operator.add if op in [operator.floordiv, ops.rfloordiv] or isinstance(op, LambdaType): - # Series sets 1//0 to np.inf, which SparseArray does not do (yet) + # GH#27231 Series sets 1//0 to np.inf, which SparseArray + # does not do (yet) mask = np.isinf(dense_result) & ~np.isinf(sparse_result.to_dense()) dense_result[mask] = np.nan From 77c2383f92897496cc2b9f43f38eeb329e70ef2b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 10 Jul 2019 17:16:03 -0700 Subject: [PATCH 20/21] whatsnew --- doc/source/whatsnew/v0.25.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 042c97a0c98b1..ebe8b4770f6aa 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -1009,6 +1009,7 @@ Numeric - Bug in :meth:`~pandas.eval` when comparing floats with scalar operators, for example: ``x < -0.1`` (:issue:`25928`) - Fixed bug where casting all-boolean array to integer extension array failed (:issue:`25211`) - Bug in ``divmod`` with a :class:`Series` object containing zeros incorrectly raising ``AttributeError`` (:issue:`26987`) +- Inconsistency in :class:`Series` floor-division (`//`) and ``divmod`` filling positive//zero with ``NaN`` instead of ``Inf`` (:issue:`27321`) - Conversion From 14010c802d8cb36d663f702db4b356f654162457 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 10 Jul 2019 20:22:10 -0700 Subject: [PATCH 21/21] dummy commit to force ci