From ba9240c6fff9e92c43c20629dda5cda3c06f8094 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 16:18:47 -0800 Subject: [PATCH 1/7] separate numeric tests so we can isolate division by zero --- pandas/tests/series/test_operators.py | 195 ++++++++++++++------------ 1 file changed, 108 insertions(+), 87 deletions(-) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 7505e6b0cec3b..2fe6b1bc6bc70 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -595,77 +595,103 @@ def test_divide_decimal(self): assert_series_equal(expected, s) - def test_div(self): + def test_i8_ser_div_i8_ser(self): + # no longer do integer div for any ops, but deal with the 0's + first = Series([3, 4, 5, 8], name='first') + second = Series([0, 0, 0, 3], name='second') + with np.errstate(all='ignore'): - # no longer do integer div for any ops, but deal with the 0's - p = DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - result = p['first'] / p['second'] - expected = Series( - p['first'].values.astype(float) / p['second'].values, - dtype='float64') - expected.iloc[0:3] = np.inf - assert_series_equal(result, expected) + expected = Series(first.values.astype(np.float64) / second.values, + dtype='float64') + expected.iloc[0:3] = np.inf - result = p['first'] / 0 - expected = Series(np.inf, index=p.index, name='first') - assert_series_equal(result, expected) + result = first / second + assert_series_equal(result, expected) - p = p.astype('float64') - result = p['first'] / p['second'] - expected = Series(p['first'].values / p['second'].values) - assert_series_equal(result, expected) + def test_f8_ser_div_f8_ser(self): + # no longer do integer div for any ops, but deal with the 0's + first = Series([3, 4, 5, 8], name='first', dtype='float64') + second = Series([0, 0, 0, 3], name='second', dtype='float64') - p = DataFrame({'first': [3, 4, 5, 8], 'second': [1, 1, 1, 1]}) - result = p['first'] / p['second'] - assert_series_equal(result, p['first'].astype('float64'), - check_names=False) - assert result.name is None - assert not result.equals(p['second'] / p['first']) - - # inf signing - s = Series([np.nan, 1., -1.]) - result = s / 0 - expected = Series([np.nan, np.inf, -np.inf]) - assert_series_equal(result, expected) + result = first / second + with np.errstate(all='ignore'): + expected = Series(first.values / second.values) + assert_series_equal(result, expected) - # float/integer issue - # GH 7785 - p = DataFrame({'first': (1, 0), 'second': (-0.01, -0.02)}) - expected = Series([-0.01, -np.inf]) + def test_ser_div_ser_name_propagation(self): + first = Series([3, 4, 5, 8], name='first') + second = Series([1, 1, 1, 1], name='second') - result = p['second'].div(p['first']) - assert_series_equal(result, expected, check_names=False) + expected = Series([3, 4, 5, 8], dtype='float64', name=None) + result = first / second + assert_series_equal(result, expected) + assert not result.equals(second / first) - result = p['second'] / p['first'] - assert_series_equal(result, expected) + def test_int_div_pyint_zero(self): + ser = Series([3, 4, 5, 8], name='first') + result = ser / 0 + expected = Series(np.inf, index=ser.index, name='first') + assert_series_equal(result, expected) - # GH 9144 - s = Series([-1, 0, 1]) + def test_div_zero_inf_signs(self): + # inf signing + ser = Series([np.nan, 1., -1.]) + result = ser / 0 + expected = Series([np.nan, np.inf, -np.inf]) + assert_series_equal(result, expected) - result = 0 / s - expected = Series([0.0, nan, 0.0]) - assert_series_equal(result, expected) + def test_div_equiv_binop(self): + # Test Series.div as well as Series.__div__ + # float/integer issue + # GH#7785 + first = pd.Series([1, 0], name='first') + second = pd.Series([-0.01, -0.02], name='second') + expected = Series([-0.01, -np.inf]) - result = s / 0 - expected = Series([-inf, nan, inf]) - assert_series_equal(result, expected) + result = second.div(first) + assert_series_equal(result, expected, check_names=False) - result = s // 0 - expected = Series([-inf, nan, inf]) - assert_series_equal(result, expected) + result = second / first + assert_series_equal(result, expected) - # GH 8674 - zero_array = np.array([0] * 5) - data = np.random.randn(5) - expected = pd.Series([0.] * 5) - result = zero_array / pd.Series(data) - assert_series_equal(result, expected) + def test_rdiv_zero_compat(self): + # GH#8674 + zero_array = np.array([0] * 5) + data = np.random.randn(5) + expected = pd.Series([0.] * 5) - result = pd.Series(zero_array) / data - assert_series_equal(result, expected) + result = zero_array / pd.Series(data) + assert_series_equal(result, expected) - result = pd.Series(zero_array) / pd.Series(data) - assert_series_equal(result, expected) + result = pd.Series(zero_array) / data + assert_series_equal(result, expected) + + result = pd.Series(zero_array) / pd.Series(data) + assert_series_equal(result, expected) + + def test_div_zero(self): + # GH#9144 + ser = Series([-1, 0, 1]) + expected = Series([-np.inf, np.nan, np.inf]) + + result = ser / 0 + assert_series_equal(result, expected) + + def test_rdiv_zero(self): + # GH#9144 + ser = Series([-1, 0, 1]) + expected = Series([0.0, np.nan, 0.0]) + + result = 0 / ser + assert_series_equal(result, expected) + + def test_floordiv_div(self): + # GH#9144 + ser = Series([-1, 0, 1]) + + result = ser // 0 + expected = Series([-inf, nan, inf]) + assert_series_equal(result, expected) class TestTimedeltaSeriesArithmeticWithIntegers(object): @@ -1574,28 +1600,28 @@ def test_dt64_series_add_intlike(self, tz): class TestSeriesOperators(TestData): - def test_op_method(self): - def check(series, other, check_reverse=False): - simple_ops = ['add', 'sub', 'mul', 'floordiv', 'truediv', 'pow'] - if not compat.PY3: - simple_ops.append('div') + @pytest.mark.parametrize('opname', ['add', 'sub', 'mul', 'floordiv', + 'truediv', 'div', 'pow']) + def test_op_method(self, opname): + if opname == 'div' and not compat.PY3: + pytest.skip('div test only for Py3') - for opname in simple_ops: - op = getattr(Series, opname) - - if op == 'div': - alt = operator.truediv - else: - alt = getattr(operator, opname) + def check(series, other, check_reverse=False): + op = getattr(Series, opname) - result = op(series, other) - expected = alt(series, other) + if op == 'div': + alt = operator.truediv + else: + alt = getattr(operator, opname) + + result = op(series, other) + expected = alt(series, other) + assert_almost_equal(result, expected) + if check_reverse: + rop = getattr(Series, "r" + opname) + result = rop(series, other) + expected = alt(other, series) assert_almost_equal(result, expected) - if check_reverse: - rop = getattr(Series, "r" + opname) - result = rop(series, other) - expected = alt(other, series) - assert_almost_equal(result, expected) check(self.ts, self.ts * 2) check(self.ts, self.ts[::2]) @@ -1992,20 +2018,15 @@ def test_operators_corner(self): index=self.ts.index[:-5], name='ts') tm.assert_series_equal(added[:-5], expected) - def test_operators_reverse_object(self): + @pytest.mark.parametrize('op', [operator.add, operator.sub, operator.mul, + operator.truediv, operator.floordiv]) + def test_operators_reverse_object(self, op): # GH 56 arr = Series(np.random.randn(10), index=np.arange(10), dtype=object) - def _check_op(arr, op): - result = op(1., arr) - expected = op(1., arr.astype(float)) - assert_series_equal(result.astype(float), expected) - - _check_op(arr, operator.add) - _check_op(arr, operator.sub) - _check_op(arr, operator.mul) - _check_op(arr, operator.truediv) - _check_op(arr, operator.floordiv) + result = op(1., arr) + expected = op(1., arr.astype(float)) + assert_series_equal(result.astype(float), expected) def test_arith_ops_df_compat(self): # GH 1134 From c19dff1a0dbbf10bde255f8cf1d619601fa401ba Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 22 Jan 2018 09:55:47 -0800 Subject: [PATCH 2/7] fix flipped condition --- pandas/tests/series/test_operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 2fe6b1bc6bc70..1cbf88ba31320 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -1603,7 +1603,7 @@ class TestSeriesOperators(TestData): @pytest.mark.parametrize('opname', ['add', 'sub', 'mul', 'floordiv', 'truediv', 'div', 'pow']) def test_op_method(self, opname): - if opname == 'div' and not compat.PY3: + if opname == 'div' and compat.PY3: pytest.skip('div test only for Py3') def check(series, other, check_reverse=False): From 41820898a15a3cb7a6297942c9826cdaabce4fbd Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 22 Jan 2018 10:04:31 -0800 Subject: [PATCH 3/7] comment --- pandas/tests/series/test_operators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 1cbf88ba31320..2fa3f6631f976 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -1603,6 +1603,7 @@ class TestSeriesOperators(TestData): @pytest.mark.parametrize('opname', ['add', 'sub', 'mul', 'floordiv', 'truediv', 'div', 'pow']) def test_op_method(self, opname): + # check that Series.{opname} behaves like Series.__{opname}__, if opname == 'div' and compat.PY3: pytest.skip('div test only for Py3') From 6f734bc2ab27579adb692096204e2cf8bdece5a3 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 22 Jan 2018 10:15:56 -0800 Subject: [PATCH 4/7] parametrize and dedup tests --- pandas/tests/series/test_operators.py | 50 +++++++++------------------ 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 2fa3f6631f976..5420eaed23a01 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -595,10 +595,15 @@ def test_divide_decimal(self): assert_series_equal(expected, s) - def test_i8_ser_div_i8_ser(self): + @pytest.mark.parametrize('dtype2', [np.int64, np.int32, np.int16, np.int8, + np.float64, np.float32, np.float16, + np.uint64, np.uint32, + np.uint16, np.uint8]) + @pytest.mark.parametrize('dtype1', [np.int64, np.float64, np.uint64]) + def test_ser_div_ser(self, dtype1, dtype2): # no longer do integer div for any ops, but deal with the 0's - first = Series([3, 4, 5, 8], name='first') - second = Series([0, 0, 0, 3], name='second') + first = Series([3, 4, 5, 8], name='first').astype(dtype1) + second = Series([0, 0, 0, 3], name='second').astype(dtype2) with np.errstate(all='ignore'): expected = Series(first.values.astype(np.float64) / second.values, @@ -608,16 +613,6 @@ def test_i8_ser_div_i8_ser(self): result = first / second assert_series_equal(result, expected) - def test_f8_ser_div_f8_ser(self): - # no longer do integer div for any ops, but deal with the 0's - first = Series([3, 4, 5, 8], name='first', dtype='float64') - second = Series([0, 0, 0, 3], name='second', dtype='float64') - - result = first / second - with np.errstate(all='ignore'): - expected = Series(first.values / second.values) - assert_series_equal(result, expected) - def test_ser_div_ser_name_propagation(self): first = Series([3, 4, 5, 8], name='first') second = Series([1, 1, 1, 1], name='second') @@ -627,19 +622,6 @@ def test_ser_div_ser_name_propagation(self): assert_series_equal(result, expected) assert not result.equals(second / first) - def test_int_div_pyint_zero(self): - ser = Series([3, 4, 5, 8], name='first') - result = ser / 0 - expected = Series(np.inf, index=ser.index, name='first') - assert_series_equal(result, expected) - - def test_div_zero_inf_signs(self): - # inf signing - ser = Series([np.nan, 1., -1.]) - result = ser / 0 - expected = Series([np.nan, np.inf, -np.inf]) - assert_series_equal(result, expected) - def test_div_equiv_binop(self): # Test Series.div as well as Series.__div__ # float/integer issue @@ -669,28 +651,28 @@ def test_rdiv_zero_compat(self): result = pd.Series(zero_array) / pd.Series(data) assert_series_equal(result, expected) - def test_div_zero(self): - # GH#9144 - ser = Series([-1, 0, 1]) - expected = Series([-np.inf, np.nan, np.inf]) + def test_div_zero_inf_signs(self): + # GH#9144, inf signing + ser = Series([-1, 0, 1], name='first') + expected = Series([-np.inf, np.nan, np.inf], name='first') result = ser / 0 assert_series_equal(result, expected) def test_rdiv_zero(self): # GH#9144 - ser = Series([-1, 0, 1]) - expected = Series([0.0, np.nan, 0.0]) + ser = Series([-1, 0, 1], name='first') + expected = Series([0.0, np.nan, 0.0], name='first') result = 0 / ser assert_series_equal(result, expected) def test_floordiv_div(self): # GH#9144 - ser = Series([-1, 0, 1]) + ser = Series([-1, 0, 1], name='first') result = ser // 0 - expected = Series([-inf, nan, inf]) + expected = Series([-inf, nan, inf], name='first') assert_series_equal(result, expected) From 393a6c830201f80da94d58ba4ee736304103f165 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 24 Jan 2018 08:47:49 -0800 Subject: [PATCH 5/7] parametrize with lambdas --- pandas/tests/series/test_operators.py | 40 ++++++++++++++------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 5420eaed23a01..3c2738a87767e 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -1582,34 +1582,36 @@ def test_dt64_series_add_intlike(self, tz): class TestSeriesOperators(TestData): + @pytest.mark.parametrize('ts', [lambda x: (x, x * 2, False), + lambda x: (x, x[::2], False), + lambda x: (x, 5, True), + lambda x: (tm.makeFloatSeries(), + tm.makeFloatSeries(), + True)]) @pytest.mark.parametrize('opname', ['add', 'sub', 'mul', 'floordiv', 'truediv', 'div', 'pow']) - def test_op_method(self, opname): + def test_op_method(self, opname, ts): # check that Series.{opname} behaves like Series.__{opname}__, + series, other, check_reverse = ts(self.ts) + if opname == 'div' and compat.PY3: pytest.skip('div test only for Py3') - def check(series, other, check_reverse=False): - op = getattr(Series, opname) + op = getattr(Series, opname) - if op == 'div': - alt = operator.truediv - else: - alt = getattr(operator, opname) + if op == 'div': + alt = operator.truediv + else: + alt = getattr(operator, opname) - result = op(series, other) - expected = alt(series, other) + result = op(series, other) + expected = alt(series, other) + assert_almost_equal(result, expected) + if check_reverse: + rop = getattr(Series, "r" + opname) + result = rop(series, other) + expected = alt(other, series) assert_almost_equal(result, expected) - if check_reverse: - rop = getattr(Series, "r" + opname) - result = rop(series, other) - expected = alt(other, series) - assert_almost_equal(result, expected) - - check(self.ts, self.ts * 2) - check(self.ts, self.ts[::2]) - check(self.ts, 5, check_reverse=True) - check(tm.makeFloatSeries(), tm.makeFloatSeries(), check_reverse=True) def test_neg(self): assert_series_equal(-self.series, -1 * self.series) From 5e1d39fb401957e0c5f7f50a0557e0f759080e59 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 24 Jan 2018 18:22:13 -0800 Subject: [PATCH 6/7] requested formatting edits --- pandas/tests/series/test_operators.py | 36 +++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 3c2738a87767e..b895f34034ee4 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -595,10 +595,14 @@ def test_divide_decimal(self): assert_series_equal(expected, s) - @pytest.mark.parametrize('dtype2', [np.int64, np.int32, np.int16, np.int8, - np.float64, np.float32, np.float16, - np.uint64, np.uint32, - np.uint16, np.uint8]) + @pytest.mark.parametrize( + 'dtype2', + [ + np.int64, np.int32, np.int16, np.int8, + np.float64, np.float32, np.float16, + np.uint64, np.uint32, + np.uint16, np.uint8 + ]) @pytest.mark.parametrize('dtype1', [np.int64, np.float64, np.uint64]) def test_ser_div_ser(self, dtype1, dtype2): # no longer do integer div for any ops, but deal with the 0's @@ -607,19 +611,11 @@ def test_ser_div_ser(self, dtype1, dtype2): with np.errstate(all='ignore'): expected = Series(first.values.astype(np.float64) / second.values, - dtype='float64') + dtype='float64', name=None) expected.iloc[0:3] = np.inf result = first / second assert_series_equal(result, expected) - - def test_ser_div_ser_name_propagation(self): - first = Series([3, 4, 5, 8], name='first') - second = Series([1, 1, 1, 1], name='second') - - expected = Series([3, 4, 5, 8], dtype='float64', name=None) - result = first / second - assert_series_equal(result, expected) assert not result.equals(second / first) def test_div_equiv_binop(self): @@ -1582,12 +1578,14 @@ def test_dt64_series_add_intlike(self, tz): class TestSeriesOperators(TestData): - @pytest.mark.parametrize('ts', [lambda x: (x, x * 2, False), - lambda x: (x, x[::2], False), - lambda x: (x, 5, True), - lambda x: (tm.makeFloatSeries(), - tm.makeFloatSeries(), - True)]) + @pytest.mark.parametrize( + 'ts', + [ + lambda x: (x, x * 2, False), + lambda x: (x, x[::2], False), + lambda x: (x, 5, True), + lambda x: (tm.makeFloatSeries(), tm.makeFloatSeries(), True) + ]) @pytest.mark.parametrize('opname', ['add', 'sub', 'mul', 'floordiv', 'truediv', 'div', 'pow']) def test_op_method(self, opname, ts): From 4b78542dd08b22fc5a531f7d9853d2208d2eb17c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 1 Feb 2018 08:33:33 -0800 Subject: [PATCH 7/7] suggested reformat --- pandas/tests/series/test_operators.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index 5c553311d039c..554b3e15d8f10 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -1583,16 +1583,20 @@ class TestSeriesOperators(TestData): @pytest.mark.parametrize( 'ts', [ - lambda x: (x, x * 2, False), - lambda x: (x, x[::2], False), - lambda x: (x, 5, True), - lambda x: (tm.makeFloatSeries(), tm.makeFloatSeries(), True) + (lambda x: x, lambda x: x * 2, False), + (lambda x: x, lambda x: x[::2], False), + (lambda x: x, lambda x: 5, True), + (lambda x: tm.makeFloatSeries(), + lambda x: tm.makeFloatSeries(), + True) ]) @pytest.mark.parametrize('opname', ['add', 'sub', 'mul', 'floordiv', 'truediv', 'div', 'pow']) def test_op_method(self, opname, ts): # check that Series.{opname} behaves like Series.__{opname}__, - series, other, check_reverse = ts(self.ts) + series = ts[0](self.ts) + other = ts[1](self.ts) + check_reverse = ts[2] if opname == 'div' and compat.PY3: pytest.skip('div test only for Py3')