From f7ae4442819153674bf57531bde70aeeb6251860 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 15:10:23 -0700 Subject: [PATCH 01/12] remove unused errors kwarg --- pandas/core/frame.py | 2 +- pandas/core/ops.py | 3 +-- pandas/core/sparse/frame.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6dd9174028f18..d3dcae48dc6a0 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4978,7 +4978,7 @@ def _combine_match_columns(self, other, func, level=None): assert left.columns.equals(right.index) return ops.dispatch_to_series(left, right, func, axis="columns") - def _combine_const(self, other, func, errors='raise'): + def _combine_const(self, other, func): assert lib.is_scalar(other) or np.ndim(other) == 0 return ops.dispatch_to_series(self, other, func) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index f29b4410fbf54..fc862c90402e1 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -1940,8 +1940,7 @@ def f(self, other): # straight boolean comparisons we want to allow all columns # (regardless of dtype to pass thru) See #4537 for discussion. - res = self._combine_const(other, func, - errors='ignore') + res = self._combine_const(other, func) return res.fillna(True).astype(bool) f.__name__ = op_name diff --git a/pandas/core/sparse/frame.py b/pandas/core/sparse/frame.py index e46df2b2bde70..56a5f4f49243f 100644 --- a/pandas/core/sparse/frame.py +++ b/pandas/core/sparse/frame.py @@ -642,7 +642,7 @@ def _combine_match_columns(self, other, func, level=None): new_data, index=self.index, columns=union, default_fill_value=self.default_fill_value).__finalize__(self) - def _combine_const(self, other, func, errors='raise'): + def _combine_const(self, other, func): return self._apply_columns(lambda x: func(x, other)) def _reindex_index(self, index, method, copy, level, fill_value=np.nan, From cdb7c5f030ce19d4a0d3568d5d230bd514a7fe00 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 16:31:09 -0700 Subject: [PATCH 02/12] Make strict copies for xfailed tests --- pandas/tests/arithmetic/conftest.py | 3 +- pandas/tests/arithmetic/test_numeric.py | 31 +++++++- pandas/tests/arithmetic/test_timedelta64.py | 79 +++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/pandas/tests/arithmetic/conftest.py b/pandas/tests/arithmetic/conftest.py index b800b66e8edea..dfb50583af948 100644 --- a/pandas/tests/arithmetic/conftest.py +++ b/pandas/tests/arithmetic/conftest.py @@ -84,7 +84,8 @@ def three_days(request): pd.Timedelta(hours=2).to_pytimedelta(), pd.Timedelta(seconds=2 * 3600), np.timedelta64(2, 'h'), - np.timedelta64(120, 'm')]) + np.timedelta64(120, 'm')], + ids=lambda x: str(x)) def two_hours(request): """ Several timedelta-like and DateOffset objects that each represent diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 25845dd8b3151..7ca999cd4bf4f 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -151,7 +151,7 @@ def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box): index = numeric_idx[1:3] broken = (isinstance(three_days, np.timedelta64) and - three_days.dtype != 'm8[ns]') + three_days.dtype == 'm8[D]') broken = broken or isinstance(three_days, pd.offsets.Tick) if box is not pd.Index and broken: # np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D') @@ -169,6 +169,35 @@ def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box): with pytest.raises(TypeError): index / three_days + @pytest.mark.xfail(reason="timedelta64 not converted to nanos; " + "Tick division is not implemented", + strict=True) + @pytest.mark.parametrize('box', [pd.Series, pd.DataFrame]) + def test_numeric_arr_rdiv_tdscalar_failing(self, three_days, + numeric_idx, box): + # This is a duplicate of test_numeric_arr_rdiv_tdscalar specific to + # failing cases. This allows us to make the "xfail" strict and be + # notified if/when these cases are fixed. + index = numeric_idx[1:3] + + # np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D') + broken = (isinstance(three_days, np.timedelta64) and + three_days.dtype == 'm8[D]') + broken = broken or isinstance(three_days, pd.offsets.Tick) + if not broken: + # raise a fake exception for the working cases + assert False + + expected = TimedeltaIndex(['3 Days', '36 Hours']) + + index = tm.box_expected(index, box) + expected = tm.box_expected(expected, box) + + result = three_days / index + tm.assert_equal(result, expected) + + with pytest.raises(TypeError): + index / three_days # ------------------------------------------------------------------ # Arithmetic diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 14a55785c243b..239de7572a904 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -359,6 +359,24 @@ def test_td64arr_add_str_invalid(self, box): with pytest.raises(TypeError): 'a' + tdi + @pytest.mark.xfail(reason="Tries to broadcast", + strict=True, raises=ValueError) + @pytest.mark.parametrize('op', [operator.add, ops.radd, + operator.sub, ops.rsub], + ids=lambda x: x.__name__) + def test_td64arr_add_sub_float_fail(self, op): + # This is a duplicate of test_td64arr_add_sub_float specific to + # the failing cases. This allows us to make the "xfail" strict and + # get alerted if/when the broken cases are fixed. + box = pd.DataFrame + other = np.array([2.0, 3.0]) + + tdi = TimedeltaIndex(['-1 days', '-1 days']) + tdi = tm.box_expected(tdi, box) + + with pytest.raises(TypeError): + op(tdi, other) + @pytest.mark.parametrize('other', [3.14, np.array([2.0, 3.0])]) @pytest.mark.parametrize('op', [operator.add, ops.radd, operator.sub, ops.rsub], @@ -567,6 +585,38 @@ def test_td64arr_add_sub_numeric_scalar_invalid(self, box, scalar, tdser): with pytest.raises(err): scalar - tdser + @pytest.mark.xfail(reason="Tries to broadcast incorrectly", + strict=True, raises=ValueError) + @pytest.mark.parametrize('dtype', ['int64', 'int32', 'int16', + 'uint64', 'uint32', 'uint16', 'uint8', + 'float64', 'float32', 'float16']) + @pytest.mark.parametrize('vec', [ + np.array([1, 2, 3]), + pd.Index([1, 2, 3]), + ], ids=lambda x: type(x).__name__) + def test_td64arr_add_sub_numeric_arr_invalid_failing(self, vec, + dtype, tdser): + # This is a duplicate of test_td64arr_add_sub_numeric_arr_invalid + # specific to the failing cases. This lets us have a "strict" xfail + # and get alerted if/when the cases are fixed. + box = pd.DataFrame + + tdser = tm.box_expected(tdser, box) + err = TypeError + if box is pd.Index and not dtype.startswith('float'): + err = NullFrequencyError + + vector = vec.astype(dtype) + # TODO: parametrize over these four ops? + with pytest.raises(err): + tdser + vector + with pytest.raises(err): + vector + tdser + with pytest.raises(err): + tdser - vector + with pytest.raises(err): + vector - tdser + @pytest.mark.parametrize('dtype', ['int64', 'int32', 'int16', 'uint64', 'uint32', 'uint16', 'uint8', 'float64', 'float32', 'float16']) @@ -919,6 +969,35 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): res3 = tdi - other tm.assert_equal(res3, expected_sub) + @pytest.mark.xfail(reason="Attempts to broadcast incorrectly", + strict=True, raises=ValueError) + @pytest.mark.parametrize('obox', [np.array, pd.Index]) + def test_td64arr_addsub_anchored_offset_arraylike_fail(self, obox): + # This is a duplicate of test_td64arr_addsub_anchored_offset_arraylike + # specific to the failing cases, so that we can have a "strict" xfail, + # and be alerted if/when the case is fixed. + box = pd.DataFrame + + tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00']) + tdi = tm.box_expected(tdi, box) + + anchored = obox([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) + + # addition/subtraction ops with anchored offsets should issue + # a PerformanceWarning and _then_ raise a TypeError. + with pytest.raises(TypeError): + with tm.assert_produces_warning(PerformanceWarning): + tdi + anchored + with pytest.raises(TypeError): + with tm.assert_produces_warning(PerformanceWarning): + anchored + tdi + with pytest.raises(TypeError): + with tm.assert_produces_warning(PerformanceWarning): + tdi - anchored + with pytest.raises(TypeError): + with tm.assert_produces_warning(PerformanceWarning): + anchored - tdi + @pytest.mark.parametrize('obox', [np.array, pd.Index, pd.Series]) def test_td64arr_addsub_anchored_offset_arraylike(self, obox, box): # GH#18824 From 3117c8ee963a93e737577da657734703ffbdda3c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 16:31:32 -0700 Subject: [PATCH 03/12] Fix RangeIndex with DataFrame op to return NotImplemented --- pandas/core/indexes/range.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 2b2f9ca51ce12..73bcbf1ceef7e 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -16,7 +16,8 @@ from pandas.core.dtypes.common import ( is_int64_dtype, is_integer, is_scalar, is_timedelta64_dtype ) -from pandas.core.dtypes.generic import ABCSeries, ABCTimedeltaIndex +from pandas.core.dtypes.generic import ( + ABCSeries, ABCTimedeltaIndex, ABCDataFrame) from pandas.core.indexes.base import Index, _index_shared_docs from pandas.core.indexes.numeric import Int64Index from pandas.util._decorators import Appender, cache_readonly @@ -557,6 +558,9 @@ def __getitem__(self, key): return super_getitem(key) def __floordiv__(self, other): + if isinstance(other, ABCDataFrame): + return NotImplemented + if is_integer(other) and other != 0: if (len(self) == 0 or self._start % other == 0 and @@ -588,7 +592,7 @@ def _make_evaluate_binop(op, step=False): """ def _evaluate_numeric_binop(self, other): - if isinstance(other, ABCSeries): + if isinstance(other, (ABCSeries, ABCDataFrame)): return NotImplemented elif isinstance(other, ABCTimedeltaIndex): # Defer to TimedeltaIndex implementation From 1354fcecc9743757edeaf9fba72b2840bc82a5bb Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 16:58:02 -0700 Subject: [PATCH 04/12] Fix reversed ops with np.timedelta64 and Tick --- pandas/core/ops.py | 9 +++++- pandas/tests/arithmetic/test_numeric.py | 37 ------------------------- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index fc862c90402e1..3c14446882315 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -45,6 +45,8 @@ ABCIndex, ABCIndexClass, ABCSparseSeries, ABCSparseArray) +from pandas.tseries.offsets import Tick + # ----------------------------------------------------------------------------- # Ops Wrapping Utilities @@ -125,11 +127,16 @@ def maybe_upcast_for_op(obj): Be careful to call this *after* determining the `name` attribute to be attached to the result of the arithmetic operation. """ - if type(obj) is datetime.timedelta: + if type(obj) is datetime.timedelta or isinstance(obj, Tick): # GH#22390 cast up to Timedelta to rely on Timedelta # implementation; otherwise operation against numeric-dtype # raises TypeError return pd.Timedelta(obj) + elif isinstance(obj, np.timedelta64): + # In particular non-nanosecond timedelta64 needs to be cast to + # nanoseconds, or else we get undesired behavior like + # np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D') + return pd.Timedelta(obj) elif isinstance(obj, np.ndarray) and is_timedelta64_dtype(obj): # GH#22390 Unfortunately we need to special-case right-hand # timedelta64 dtypes because numpy casts integer dtypes to diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 7ca999cd4bf4f..b8b88630a4344 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -150,14 +150,6 @@ def test_numeric_arr_mul_tdscalar(self, scalar_td, numeric_idx, box): def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box): index = numeric_idx[1:3] - broken = (isinstance(three_days, np.timedelta64) and - three_days.dtype == 'm8[D]') - broken = broken or isinstance(three_days, pd.offsets.Tick) - if box is not pd.Index and broken: - # np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D') - raise pytest.xfail("timedelta64 not converted to nanos; " - "Tick division not implemented") - expected = TimedeltaIndex(['3 Days', '36 Hours']) index = tm.box_expected(index, box) @@ -169,35 +161,6 @@ def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box): with pytest.raises(TypeError): index / three_days - @pytest.mark.xfail(reason="timedelta64 not converted to nanos; " - "Tick division is not implemented", - strict=True) - @pytest.mark.parametrize('box', [pd.Series, pd.DataFrame]) - def test_numeric_arr_rdiv_tdscalar_failing(self, three_days, - numeric_idx, box): - # This is a duplicate of test_numeric_arr_rdiv_tdscalar specific to - # failing cases. This allows us to make the "xfail" strict and be - # notified if/when these cases are fixed. - index = numeric_idx[1:3] - - # np.timedelta64(3, 'D') / 2 == np.timedelta64(1, 'D') - broken = (isinstance(three_days, np.timedelta64) and - three_days.dtype == 'm8[D]') - broken = broken or isinstance(three_days, pd.offsets.Tick) - if not broken: - # raise a fake exception for the working cases - assert False - - expected = TimedeltaIndex(['3 Days', '36 Hours']) - - index = tm.box_expected(index, box) - expected = tm.box_expected(expected, box) - - result = three_days / index - tm.assert_equal(result, expected) - - with pytest.raises(TypeError): - index / three_days # ------------------------------------------------------------------ # Arithmetic From f0c280df44e8dbadb49b49f8ae5c0ff894fc553f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 17:04:01 -0700 Subject: [PATCH 05/12] update xfails --- pandas/tests/arithmetic/test_timedelta64.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 239de7572a904..0e803acca8bdc 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -1128,7 +1128,6 @@ def test_tdi_mul_float_series(self, box_df_fail): pd.RangeIndex(1, 11) ], ids=lambda x: type(x).__name__) def test_tdi_rmul_arraylike(self, other, box_df_fail): - # RangeIndex fails to return NotImplemented, for others # DataFrame tries to broadcast incorrectly box = box_df_fail @@ -1415,9 +1414,10 @@ def test_td64arr_mul_int_series(self, box_df_fail, names): result = ser * tdi tm.assert_equal(result, expected) - # The direct operation tdi * ser still needs to be fixed. result = ser.__rmul__(tdi) tm.assert_equal(result, expected) + result = tdi * ser + tm.assert_equal(result, expected) # TODO: Should we be parametrizing over types for `ser` too? @pytest.mark.parametrize('names', [(None, None, None), From 28e588b5a243bd705cfad62c196350d7592105a0 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 17:19:23 -0700 Subject: [PATCH 06/12] update to more specific xfail --- pandas/tests/arithmetic/test_timedelta64.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 0e803acca8bdc..30adee1d1f19a 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -1127,9 +1127,9 @@ def test_tdi_mul_float_series(self, box_df_fail): pd.Float64Index(range(1, 11)), pd.RangeIndex(1, 11) ], ids=lambda x: type(x).__name__) - def test_tdi_rmul_arraylike(self, other, box_df_fail): + def test_tdi_rmul_arraylike(self, other, box_df_broadcast_failure): # DataFrame tries to broadcast incorrectly - box = box_df_fail + box = box_df_broadcast_failure tdi = TimedeltaIndex(['1 Day'] * 10) expected = timedelta_range('1 days', '10 days') From 50c4c960222bdc0bcd673a0b428a7086b9332d4c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 17:22:06 -0700 Subject: [PATCH 07/12] Implement transpose option for box_expected --- pandas/tests/arithmetic/test_timedelta64.py | 10 ++++------ pandas/util/testing.py | 9 ++++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 30adee1d1f19a..ccbfe8acf8a87 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -1342,20 +1342,18 @@ def test_td64arr_div_numeric_scalar(self, box, two, tdser): Series([20, 30, 40])], ids=lambda x: type(x).__name__) @pytest.mark.parametrize('op', [operator.mul, ops.rmul]) - def test_td64arr_rmul_numeric_array(self, op, box_df_fail, - vector, dtype, tdser): + def test_td64arr_rmul_numeric_array(self, op, box, vector, dtype, tdser): # GH#4521 # divide/multiply by integers - box = box_df_fail # broadcasts incorrectly but doesn't raise vector = vector.astype(dtype) expected = Series(['1180 Days', '1770 Days', 'NaT'], dtype='timedelta64[ns]') - tdser = tm.box_expected(tdser, box) + tdser = tm.box_expected(tdser, box, transpose=True) # TODO: Make this up-casting more systematic? - box = Series if (box is pd.Index and type(vector) is Series) else box - expected = tm.box_expected(expected, box) + box2 = Series if (box is pd.Index and type(vector) is Series) else box + expected = tm.box_expected(expected, box2, transpose=True) result = op(vector, tdser) tm.assert_equal(result, expected) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index b5ec0912c5c26..055a8418137d2 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1544,7 +1544,7 @@ def assert_equal(left, right, **kwargs): raise NotImplementedError(type(left)) -def box_expected(expected, box_cls): +def box_expected(expected, box_cls, transpose=False): """ Helper function to wrap the expected output of a test in a given box_class. @@ -1552,6 +1552,8 @@ def box_expected(expected, box_cls): ---------- expected : np.ndarray, Index, Series box_cls : {Index, Series, DataFrame} + transpose : bool + whether to transpose the result; only relevant for DataFrame Returns ------- @@ -1563,6 +1565,11 @@ def box_expected(expected, box_cls): expected = pd.Series(expected) elif box_cls is pd.DataFrame: expected = pd.Series(expected).to_frame() + if transpose: + # For tests in which a DataFrame operates with a 1-dimensional + # array-like other, broadcasting conventions require us to transpose + # both the DataFrame operand and the expected result. + expected = expected.T else: raise NotImplementedError(box_cls) return expected From c4285a7d9dad9a9a1c5069df5ce8cd5bd26f5d3f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 17:45:17 -0700 Subject: [PATCH 08/12] use transpose=True to fix many xfails --- pandas/tests/arithmetic/test_datetime64.py | 6 +- pandas/tests/arithmetic/test_period.py | 12 +- pandas/tests/arithmetic/test_timedelta64.py | 201 +++++--------------- 3 files changed, 50 insertions(+), 169 deletions(-) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 36bb0aca066fb..8be63479443fd 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1365,14 +1365,12 @@ def test_sub_period(self, freq, box): operator.sub, ops.rsub]) @pytest.mark.parametrize('pi_freq', ['D', 'W', 'Q', 'H']) @pytest.mark.parametrize('dti_freq', [None, 'D']) - def test_dti_sub_pi(self, dti_freq, pi_freq, op, box_df_broadcast_failure): + def test_dti_sub_pi(self, dti_freq, pi_freq, op, box): # GH#20049 subtracting PeriodIndex should raise TypeError - box = box_df_broadcast_failure - dti = pd.DatetimeIndex(['2011-01-01', '2011-01-02'], freq=dti_freq) pi = dti.to_period(pi_freq) - dti = tm.box_expected(dti, box) + dti = tm.box_expected(dti, box, transpose=True) # TODO: Also box pi? with pytest.raises(TypeError): op(dti, pi) diff --git a/pandas/tests/arithmetic/test_period.py b/pandas/tests/arithmetic/test_period.py index fe98b74499983..5fbb69ba52c28 100644 --- a/pandas/tests/arithmetic/test_period.py +++ b/pandas/tests/arithmetic/test_period.py @@ -344,14 +344,12 @@ class TestPeriodIndexArithmetic(object): # PeriodIndex - other is defined for integers, timedelta-like others, # and PeriodIndex (with matching freq) - def test_parr_add_iadd_parr_raises(self, box_df_broadcast_failure): - box = box_df_broadcast_failure - + def test_parr_add_iadd_parr_raises(self, box): rng = pd.period_range('1/1/2000', freq='D', periods=5) other = pd.period_range('1/6/2000', freq='D', periods=5) # TODO: parametrize over boxes for other? - rng = tm.box_expected(rng, box) + rng = tm.box_expected(rng, box, transpose=True) # An earlier implementation of PeriodIndex addition performed # a set operation (union). This has since been changed to # raise a TypeError. See GH#14164 and GH#13077 for historical @@ -388,14 +386,12 @@ def test_pi_sub_pi_with_nat(self): expected = pd.Index([pd.NaT, 0 * off, 0 * off, 0 * off, 0 * off]) tm.assert_index_equal(result, expected) - def test_parr_sub_pi_mismatched_freq(self, box_df_broadcast_failure): - box = box_df_broadcast_failure - + def test_parr_sub_pi_mismatched_freq(self, box): rng = pd.period_range('1/1/2000', freq='D', periods=5) other = pd.period_range('1/6/2000', freq='H', periods=5) # TODO: parametrize over boxes for other? - rng = tm.box_expected(rng, box) + rng = tm.box_expected(rng, box, transpose=True) with pytest.raises(period.IncompatibleFrequency): rng - other diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index ccbfe8acf8a87..001e4755c7b13 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -359,35 +359,13 @@ def test_td64arr_add_str_invalid(self, box): with pytest.raises(TypeError): 'a' + tdi - @pytest.mark.xfail(reason="Tries to broadcast", - strict=True, raises=ValueError) - @pytest.mark.parametrize('op', [operator.add, ops.radd, - operator.sub, ops.rsub], - ids=lambda x: x.__name__) - def test_td64arr_add_sub_float_fail(self, op): - # This is a duplicate of test_td64arr_add_sub_float specific to - # the failing cases. This allows us to make the "xfail" strict and - # get alerted if/when the broken cases are fixed. - box = pd.DataFrame - other = np.array([2.0, 3.0]) - - tdi = TimedeltaIndex(['-1 days', '-1 days']) - tdi = tm.box_expected(tdi, box) - - with pytest.raises(TypeError): - op(tdi, other) - @pytest.mark.parametrize('other', [3.14, np.array([2.0, 3.0])]) @pytest.mark.parametrize('op', [operator.add, ops.radd, operator.sub, ops.rsub], ids=lambda x: x.__name__) def test_td64arr_add_sub_float(self, box, op, other): - if box is pd.DataFrame and isinstance(other, np.ndarray): - pytest.xfail("Tries to broadcast, raising " - "ValueError instead of TypeError") - tdi = TimedeltaIndex(['-1 days', '-1 days']) - tdi = tm.box_expected(tdi, box) + tdi = tm.box_expected(tdi, box, transpose=True) with pytest.raises(TypeError): op(tdi, other) @@ -408,15 +386,14 @@ def test_td64arr_sub_period(self, box, freq): @pytest.mark.parametrize('pi_freq', ['D', 'W', 'Q', 'H']) @pytest.mark.parametrize('tdi_freq', [None, 'H']) - def test_td64arr_sub_pi(self, box_df_broadcast_failure, tdi_freq, pi_freq): + def test_td64arr_sub_pi(self, box, tdi_freq, pi_freq): # GH#20049 subtracting PeriodIndex should raise TypeError - box = box_df_broadcast_failure tdi = TimedeltaIndex(['1 hours', '2 hours'], freq=tdi_freq) dti = Timestamp('2018-03-07 17:16:40') + tdi pi = dti.to_period(pi_freq) # TODO: parametrize over box for pi? - tdi = tm.box_expected(tdi, box) + tdi = tm.box_expected(tdi, box, transpose=True) with pytest.raises(TypeError): tdi - pi @@ -478,16 +455,14 @@ def test_td64arr_add_sub_timestamp(self, box): with pytest.raises(TypeError): tdser - ts - def test_tdi_sub_dt64_array(self, box_df_broadcast_failure): - box = box_df_broadcast_failure - + def test_tdi_sub_dt64_array(self, box): dti = pd.date_range('2016-01-01', periods=3) tdi = dti - dti.shift(1) dtarr = dti.values expected = pd.DatetimeIndex(dtarr) - tdi - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) with pytest.raises(TypeError): tdi - dtarr @@ -496,16 +471,14 @@ def test_tdi_sub_dt64_array(self, box_df_broadcast_failure): result = dtarr - tdi tm.assert_equal(result, expected) - def test_tdi_add_dt64_array(self, box_df_broadcast_failure): - box = box_df_broadcast_failure - + def test_tdi_add_dt64_array(self, box): dti = pd.date_range('2016-01-01', periods=3) tdi = dti - dti.shift(1) dtarr = dti.values expected = pd.DatetimeIndex(dtarr) + tdi - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) result = tdi + dtarr tm.assert_equal(result, expected) @@ -539,11 +512,10 @@ def test_td64arr_rsub_int_series_invalid(self, box, tdser): with pytest.raises(err): Series([2, 3, 4]) - tdser - def test_td64arr_add_intlike(self, box_df_broadcast_failure): + def test_td64arr_add_intlike(self, box): # GH#19123 - box = box_df_broadcast_failure tdi = TimedeltaIndex(['59 days', '59 days', 'NaT']) - ser = tm.box_expected(tdi, box) + ser = tm.box_expected(tdi, box, transpose=True) err = TypeError if box is not pd.Index else NullFrequencyError other = Series([20, 30, 40], dtype='uint8') @@ -585,38 +557,6 @@ def test_td64arr_add_sub_numeric_scalar_invalid(self, box, scalar, tdser): with pytest.raises(err): scalar - tdser - @pytest.mark.xfail(reason="Tries to broadcast incorrectly", - strict=True, raises=ValueError) - @pytest.mark.parametrize('dtype', ['int64', 'int32', 'int16', - 'uint64', 'uint32', 'uint16', 'uint8', - 'float64', 'float32', 'float16']) - @pytest.mark.parametrize('vec', [ - np.array([1, 2, 3]), - pd.Index([1, 2, 3]), - ], ids=lambda x: type(x).__name__) - def test_td64arr_add_sub_numeric_arr_invalid_failing(self, vec, - dtype, tdser): - # This is a duplicate of test_td64arr_add_sub_numeric_arr_invalid - # specific to the failing cases. This lets us have a "strict" xfail - # and get alerted if/when the cases are fixed. - box = pd.DataFrame - - tdser = tm.box_expected(tdser, box) - err = TypeError - if box is pd.Index and not dtype.startswith('float'): - err = NullFrequencyError - - vector = vec.astype(dtype) - # TODO: parametrize over these four ops? - with pytest.raises(err): - tdser + vector - with pytest.raises(err): - vector + tdser - with pytest.raises(err): - tdser - vector - with pytest.raises(err): - vector - tdser - @pytest.mark.parametrize('dtype', ['int64', 'int32', 'int16', 'uint64', 'uint32', 'uint16', 'uint8', 'float64', 'float32', 'float16']) @@ -627,10 +567,7 @@ def test_td64arr_add_sub_numeric_arr_invalid_failing(self, vec, # TODO: Add DataFrame in here? ], ids=lambda x: type(x).__name__) def test_td64arr_add_sub_numeric_arr_invalid(self, box, vec, dtype, tdser): - if box is pd.DataFrame and not isinstance(vec, Series): - raise pytest.xfail(reason="Tries to broadcast incorrectly") - - tdser = tm.box_expected(tdser, box) + tdser = tm.box_expected(tdser, box, transpose=True) err = TypeError if box is pd.Index and not dtype.startswith('float'): err = NullFrequencyError @@ -702,32 +639,28 @@ def test_timedelta64_operations_with_timedeltas(self): # roundtrip tm.assert_series_equal(result + td2, td1) - def test_td64arr_add_td64_array(self, box_df_broadcast_failure): - box = box_df_broadcast_failure - + def test_td64arr_add_td64_array(self, box): dti = pd.date_range('2016-01-01', periods=3) tdi = dti - dti.shift(1) tdarr = tdi.values expected = 2 * tdi - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) result = tdi + tdarr tm.assert_equal(result, expected) result = tdarr + tdi tm.assert_equal(result, expected) - def test_td64arr_sub_td64_array(self, box_df_broadcast_failure): - box = box_df_broadcast_failure - + def test_td64arr_sub_td64_array(self, box): dti = pd.date_range('2016-01-01', periods=3) tdi = dti - dti.shift(1) tdarr = tdi.values expected = 0 * tdi - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) result = tdi - tdarr tm.assert_equal(result, expected) @@ -877,23 +810,23 @@ def test_td64arr_add_offset_index(self, names, box_df_broadcast_failure): # TODO: combine with test_td64arr_add_offset_index by parametrizing # over second box? - def test_td64arr_add_offset_array(self, box_df_broadcast_failure): + def test_td64arr_add_offset_array(self, box): # GH#18849 - box = box_df_broadcast_failure tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00']) other = np.array([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)]) expected = TimedeltaIndex([tdi[n] + other[n] for n in range(len(tdi))], freq='infer') - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) - with tm.assert_produces_warning(PerformanceWarning): + exwarning = PerformanceWarning if box is not pd.DataFrame else None + with tm.assert_produces_warning(exwarning): res = tdi + other tm.assert_equal(res, expected) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(exwarning): res2 = other + tdi tm.assert_equal(res2, expected) @@ -918,19 +851,19 @@ def test_td64arr_sub_offset_index(self, names, box_df_broadcast_failure): res = tdi - other tm.assert_equal(res, expected) - def test_td64arr_sub_offset_array(self, box_df_broadcast_failure): + def test_td64arr_sub_offset_array(self, box): # GH#18824 - box = box_df_broadcast_failure tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00']) other = np.array([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)]) expected = TimedeltaIndex([tdi[n] - other[n] for n in range(len(tdi))], freq='infer') - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) - with tm.assert_produces_warning(PerformanceWarning): + exwarning = PerformanceWarning if box is not pd.DataFrame else None + with tm.assert_produces_warning(exwarning): res = tdi - other tm.assert_equal(res, expected) @@ -969,43 +902,11 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): res3 = tdi - other tm.assert_equal(res3, expected_sub) - @pytest.mark.xfail(reason="Attempts to broadcast incorrectly", - strict=True, raises=ValueError) - @pytest.mark.parametrize('obox', [np.array, pd.Index]) - def test_td64arr_addsub_anchored_offset_arraylike_fail(self, obox): - # This is a duplicate of test_td64arr_addsub_anchored_offset_arraylike - # specific to the failing cases, so that we can have a "strict" xfail, - # and be alerted if/when the case is fixed. - box = pd.DataFrame - - tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00']) - tdi = tm.box_expected(tdi, box) - - anchored = obox([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) - - # addition/subtraction ops with anchored offsets should issue - # a PerformanceWarning and _then_ raise a TypeError. - with pytest.raises(TypeError): - with tm.assert_produces_warning(PerformanceWarning): - tdi + anchored - with pytest.raises(TypeError): - with tm.assert_produces_warning(PerformanceWarning): - anchored + tdi - with pytest.raises(TypeError): - with tm.assert_produces_warning(PerformanceWarning): - tdi - anchored - with pytest.raises(TypeError): - with tm.assert_produces_warning(PerformanceWarning): - anchored - tdi - @pytest.mark.parametrize('obox', [np.array, pd.Index, pd.Series]) def test_td64arr_addsub_anchored_offset_arraylike(self, obox, box): # GH#18824 - if box is pd.DataFrame and obox is not pd.Series: - raise pytest.xfail(reason="Attempts to broadcast incorrectly") - tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00']) - tdi = tm.box_expected(tdi, box) + tdi = tm.box_expected(tdi, box, transpose=True) anchored = obox([pd.offsets.MonthEnd(), pd.offsets.Day(n=2)]) @@ -1081,40 +982,37 @@ def test_tdi_mul_int_array_zerodim(self, box): result = idx * np.array(5, dtype='int64') tm.assert_equal(result, expected) - def test_tdi_mul_int_array(self, box_df_broadcast_failure): - box = box_df_broadcast_failure + def test_tdi_mul_int_array(self, box): rng5 = np.arange(5, dtype='int64') idx = TimedeltaIndex(rng5) expected = TimedeltaIndex(rng5 ** 2) - idx = tm.box_expected(idx, box) - expected = tm.box_expected(expected, box) + idx = tm.box_expected(idx, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) result = idx * rng5 tm.assert_equal(result, expected) - def test_tdi_mul_int_series(self, box_df_fail): - box = box_df_fail + def test_tdi_mul_int_series(self, box): idx = TimedeltaIndex(np.arange(5, dtype='int64')) expected = TimedeltaIndex(np.arange(5, dtype='int64') ** 2) - idx = tm.box_expected(idx, box) + idx = tm.box_expected(idx, box, transpose=True) box2 = pd.Series if box is pd.Index else box - expected = tm.box_expected(expected, box2) + expected = tm.box_expected(expected, box2, transpose=True) result = idx * pd.Series(np.arange(5, dtype='int64')) tm.assert_equal(result, expected) - def test_tdi_mul_float_series(self, box_df_fail): - box = box_df_fail + def test_tdi_mul_float_series(self, box): idx = TimedeltaIndex(np.arange(5, dtype='int64')) - idx = tm.box_expected(idx, box) + idx = tm.box_expected(idx, box, transpose=True) rng5f = np.arange(5, dtype='float64') expected = TimedeltaIndex(rng5f * (rng5f + 0.1)) box2 = pd.Series if box is pd.Index else box - expected = tm.box_expected(expected, box2) + expected = tm.box_expected(expected, box2, transpose=True) result = idx * Series(rng5f + 0.1) tm.assert_equal(result, expected) @@ -1127,15 +1025,12 @@ def test_tdi_mul_float_series(self, box_df_fail): pd.Float64Index(range(1, 11)), pd.RangeIndex(1, 11) ], ids=lambda x: type(x).__name__) - def test_tdi_rmul_arraylike(self, other, box_df_broadcast_failure): - # DataFrame tries to broadcast incorrectly - box = box_df_broadcast_failure - + def test_tdi_rmul_arraylike(self, other, box): tdi = TimedeltaIndex(['1 Day'] * 10) expected = timedelta_range('1 days', '10 days') - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) result = other * tdi tm.assert_equal(result, expected) @@ -1358,14 +1253,6 @@ def test_td64arr_rmul_numeric_array(self, op, box, vector, dtype, tdser): result = op(vector, tdser) tm.assert_equal(result, expected) - @pytest.mark.parametrize('box', [ - pd.Index, - Series, - pytest.param(pd.DataFrame, - marks=pytest.mark.xfail(reason="broadcasts along " - "wrong axis", - strict=True)) - ], ids=lambda x: x.__name__) @pytest.mark.parametrize('dtype', ['int64', 'int32', 'int16', 'uint64', 'uint32', 'uint16', 'uint8', 'float64', 'float32', 'float16']) @@ -1380,9 +1267,9 @@ def test_td64arr_div_numeric_array(self, box, vector, dtype, tdser): expected = Series(['2.95D', '1D 23H 12m', 'NaT'], dtype='timedelta64[ns]') - tdser = tm.box_expected(tdser, box) + tdser = tm.box_expected(tdser, box, transpose=True) box = Series if (box is pd.Index and type(vector) is Series) else box - expected = tm.box_expected(expected, box) + expected = tm.box_expected(expected, box, transpose=True) result = tdser / vector tm.assert_equal(result, expected) From a79486548b885c8571a3177032331a6977762666 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 18:21:53 -0700 Subject: [PATCH 09/12] use transpose in box_expected to fix most remaining xfails --- pandas/tests/arithmetic/conftest.py | 28 -------- pandas/tests/arithmetic/test_timedelta64.py | 77 ++++++++++++--------- 2 files changed, 44 insertions(+), 61 deletions(-) diff --git a/pandas/tests/arithmetic/conftest.py b/pandas/tests/arithmetic/conftest.py index dfb50583af948..76b4022ce6f0a 100644 --- a/pandas/tests/arithmetic/conftest.py +++ b/pandas/tests/arithmetic/conftest.py @@ -142,31 +142,3 @@ def box(request): behavior with respect to arithmetic operations. """ return request.param - - -@pytest.fixture(params=[pd.Index, - pd.Series, - pytest.param(pd.DataFrame, - marks=pytest.mark.xfail(strict=True))], - ids=lambda x: x.__name__) -def box_df_fail(request): - """ - Fixture equivalent to `box` fixture but xfailing the DataFrame case. - """ - return request.param - - -@pytest.fixture(params=[ - pd.Index, - pd.Series, - pytest.param(pd.DataFrame, - marks=pytest.mark.xfail(reason="Tries to broadcast " - "incorrectly", - strict=True, raises=ValueError)) -], ids=lambda x: x.__name__) -def box_df_broadcast_failure(request): - """ - Fixture equivalent to `box` but with the common failing case where - the DataFrame operation tries to broadcast incorrectly. - """ - return request.param diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 001e4755c7b13..97b37795e8c6a 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -671,17 +671,19 @@ def test_td64arr_sub_td64_array(self, box): @pytest.mark.parametrize('names', [(None, None, None), ('Egon', 'Venkman', None), ('NCC1701D', 'NCC1701D', 'NCC1701D')]) - def test_td64arr_add_sub_tdi(self, box_df_broadcast_failure, names): + def test_td64arr_add_sub_tdi(self, box, names): # GH#17250 make sure result dtype is correct # GH#19043 make sure names are propagated correctly - box = box_df_broadcast_failure + if box is pd.DataFrame and names[0] != names[1]: + return + tdi = TimedeltaIndex(['0 days', '1 day'], name=names[0]) ser = Series([Timedelta(hours=3), Timedelta(hours=4)], name=names[1]) expected = Series([Timedelta(hours=3), Timedelta(days=1, hours=4)], name=names[2]) - ser = tm.box_expected(ser, box) - expected = tm.box_expected(expected, box) + ser = tm.box_expected(ser, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) result = tdi + ser tm.assert_equal(result, expected) @@ -699,7 +701,7 @@ def test_td64arr_add_sub_tdi(self, box_df_broadcast_failure, names): expected = Series([Timedelta(hours=-3), Timedelta(days=1, hours=-4)], name=names[2]) - expected = tm.box_expected(expected, box) + expected = tm.box_expected(expected, box, transpose=True) result = tdi - ser tm.assert_equal(result, expected) @@ -787,9 +789,11 @@ def test_timedelta64_operations_with_DateOffset(self): @pytest.mark.parametrize('names', [(None, None, None), ('foo', 'bar', None), ('foo', 'foo', 'foo')]) - def test_td64arr_add_offset_index(self, names, box_df_broadcast_failure): + def test_td64arr_add_offset_index(self, names, box): # GH#18849, GH#19744 - box = box_df_broadcast_failure + if box is pd.DataFrame and names[0] != names[1]: + return + tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00'], name=names[0]) other = pd.Index([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)], @@ -797,14 +801,15 @@ def test_td64arr_add_offset_index(self, names, box_df_broadcast_failure): expected = TimedeltaIndex([tdi[n] + other[n] for n in range(len(tdi))], freq='infer', name=names[2]) - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) - with tm.assert_produces_warning(PerformanceWarning): + exwarning = PerformanceWarning if box is not pd.DataFrame else None + with tm.assert_produces_warning(exwarning): res = tdi + other tm.assert_equal(res, expected) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(exwarning): res2 = other + tdi tm.assert_equal(res2, expected) @@ -833,9 +838,11 @@ def test_td64arr_add_offset_array(self, box): @pytest.mark.parametrize('names', [(None, None, None), ('foo', 'bar', None), ('foo', 'foo', 'foo')]) - def test_td64arr_sub_offset_index(self, names, box_df_broadcast_failure): + def test_td64arr_sub_offset_index(self, names, box): # GH#18824, GH#19744 - box = box_df_broadcast_failure + if box is pd.DataFrame and names[0] != names[1]: + return + tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00'], name=names[0]) other = pd.Index([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)], @@ -844,10 +851,11 @@ def test_td64arr_sub_offset_index(self, names, box_df_broadcast_failure): expected = TimedeltaIndex([tdi[n] - other[n] for n in range(len(tdi))], freq='infer', name=names[2]) - tdi = tm.box_expected(tdi, box) - expected = tm.box_expected(expected, box) + tdi = tm.box_expected(tdi, box, transpose=True) + expected = tm.box_expected(expected, box, transpose=True) - with tm.assert_produces_warning(PerformanceWarning): + exwarning = PerformanceWarning if box is not pd.DataFrame else None + with tm.assert_produces_warning(exwarning): res = tdi - other tm.assert_equal(res, expected) @@ -870,10 +878,13 @@ def test_td64arr_sub_offset_array(self, box): @pytest.mark.parametrize('names', [(None, None, None), ('foo', 'bar', None), ('foo', 'foo', 'foo')]) - def test_td64arr_with_offset_series(self, names, box_df_fail): + def test_td64arr_with_offset_series(self, names, box): # GH#18849 - box = box_df_fail + if box is pd.DataFrame and names[0] != names[1]: + return + box2 = Series if box is pd.Index else box + exwarning = PerformanceWarning if box is not pd.DataFrame else None tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00'], name=names[0]) @@ -882,23 +893,23 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): expected_add = Series([tdi[n] + other[n] for n in range(len(tdi))], name=names[2]) - tdi = tm.box_expected(tdi, box) - expected_add = tm.box_expected(expected_add, box2) + expected_sub = Series([tdi[n] - other[n] for n in range(len(tdi))], + name=names[2]) - with tm.assert_produces_warning(PerformanceWarning): + tdi = tm.box_expected(tdi, box, transpose=True) + expected_add = tm.box_expected(expected_add, box2, transpose=True) + + with tm.assert_produces_warning(exwarning): res = tdi + other tm.assert_equal(res, expected_add) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(exwarning): res2 = other + tdi tm.assert_equal(res2, expected_add) - # TODO: separate/parametrize add/sub test? - expected_sub = Series([tdi[n] - other[n] for n in range(len(tdi))], - name=names[2]) - expected_sub = tm.box_expected(expected_sub, box2) + expected_sub = tm.box_expected(expected_sub, box2, transpose=True) - with tm.assert_produces_warning(PerformanceWarning): + with tm.assert_produces_warning(exwarning): res3 = tdi - other tm.assert_equal(res3, expected_sub) @@ -1280,9 +1291,11 @@ def test_td64arr_div_numeric_array(self, box, vector, dtype, tdser): @pytest.mark.parametrize('names', [(None, None, None), ('Egon', 'Venkman', None), ('NCC1701D', 'NCC1701D', 'NCC1701D')]) - def test_td64arr_mul_int_series(self, box_df_fail, names): + def test_td64arr_mul_int_series(self, box, names): # GH#19042 test for correct name attachment - box = box_df_fail # broadcasts along wrong axis, but doesn't raise + if box is pd.DataFrame and names[0] != names[1]: + return + tdi = TimedeltaIndex(['0days', '1day', '2days', '3days', '4days'], name=names[0]) # TODO: Should we be parametrizing over types for `ser` too? @@ -1292,15 +1305,13 @@ def test_td64arr_mul_int_series(self, box_df_fail, names): dtype='timedelta64[ns]', name=names[2]) - tdi = tm.box_expected(tdi, box) + tdi = tm.box_expected(tdi, box, transpose=True) box = Series if (box is pd.Index and type(ser) is Series) else box - expected = tm.box_expected(expected, box) + expected = tm.box_expected(expected, box, transpose=True) result = ser * tdi tm.assert_equal(result, expected) - result = ser.__rmul__(tdi) - tm.assert_equal(result, expected) result = tdi * ser tm.assert_equal(result, expected) From 370c8778e5f1b3bc06a41e3953116da07ea37db4 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 18:34:09 -0700 Subject: [PATCH 10/12] flake8 fixup --- pandas/util/testing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 055a8418137d2..46d313f438e3c 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1567,8 +1567,8 @@ def box_expected(expected, box_cls, transpose=False): expected = pd.Series(expected).to_frame() if transpose: # For tests in which a DataFrame operates with a 1-dimensional - # array-like other, broadcasting conventions require us to transpose - # both the DataFrame operand and the expected result. + # array-like other, broadcasting conventions require us to + # transpose both the DataFrame operand and the expected result. expected = expected.T else: raise NotImplementedError(box_cls) From 870862cc42095596e27d557b39096e7b318af9e6 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 20:00:33 -0700 Subject: [PATCH 11/12] fix incorrectly-xfailed test --- pandas/tests/internals/test_internals.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/tests/internals/test_internals.py b/pandas/tests/internals/test_internals.py index b6a83b786bab2..e2c9ebef72bc1 100644 --- a/pandas/tests/internals/test_internals.py +++ b/pandas/tests/internals/test_internals.py @@ -1243,7 +1243,6 @@ def test_binop_other(self, op, value, dtype): (operator.mul, ' Date: Tue, 23 Oct 2018 20:11:17 -0700 Subject: [PATCH 12/12] tests for RangeIndex returning NotImplemented --- pandas/core/indexes/range.py | 2 +- pandas/tests/indexes/test_range.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 73bcbf1ceef7e..5ee4d3407eb00 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -558,7 +558,7 @@ def __getitem__(self, key): return super_getitem(key) def __floordiv__(self, other): - if isinstance(other, ABCDataFrame): + if isinstance(other, (ABCSeries, ABCDataFrame)): return NotImplemented if is_integer(other) and other != 0: diff --git a/pandas/tests/indexes/test_range.py b/pandas/tests/indexes/test_range.py index 0e47fcd52f625..fd2df39b1e7e7 100644 --- a/pandas/tests/indexes/test_range.py +++ b/pandas/tests/indexes/test_range.py @@ -189,6 +189,25 @@ def test_constructor_name(self): assert copy.name == 'copy' assert new.name == 'new' + # TODO: mod, divmod? + @pytest.mark.parametrize('op', [operator.add, operator.sub, + operator.mul, operator.floordiv, + operator.truediv, operator.pow]) + def test_arithmetic_with_frame_or_series(self, op): + # check that we return NotImplemented when operating with Series + # or DataFrame + index = pd.RangeIndex(5) + other = pd.Series(np.random.randn(5)) + + expected = op(pd.Series(index), other) + result = op(index, other) + tm.assert_series_equal(result, expected) + + other = pd.DataFrame(np.random.randn(2, 5)) + expected = op(pd.DataFrame([index, index]), other) + result = op(index, other) + tm.assert_frame_equal(result, expected) + def test_numeric_compat2(self): # validate that we are handling the RangeIndex overrides to numeric ops # and returning RangeIndex where possible