From f49731c5f4f6117238bc0378252d7f81c937714a Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 16:23:57 -0700 Subject: [PATCH 1/3] Treat 1D vectors as columns in some cases --- pandas/core/frame.py | 2 +- pandas/core/ops.py | 42 +++++- pandas/tests/arithmetic/conftest.py | 16 --- pandas/tests/arithmetic/test_datetime64.py | 4 +- pandas/tests/arithmetic/test_period.py | 8 +- pandas/tests/arithmetic/test_timedelta64.py | 136 ++++++++------------ pandas/tests/frame/test_operators.py | 29 ++--- 7 files changed, 106 insertions(+), 131 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6dd9174028f18..d51b60f6e788c 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -4960,7 +4960,7 @@ def _combine_match_index(self, other, func, level=None): copy=False) assert left.index.equals(right.index) - if left._is_mixed_type or right._is_mixed_type: + if ops.should_series_dispatch(left, right, func): # operate column-wise; avoid costly object-casting in `.values` return ops.dispatch_to_series(left, right, func) else: diff --git a/pandas/core/ops.py b/pandas/core/ops.py index f29b4410fbf54..3ebbba3ed9107 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -928,12 +928,14 @@ def should_series_dispatch(left, right, op): if left._is_mixed_type or right._is_mixed_type: return True - if not len(left.columns) or not len(right.columns): + if not len(left.columns) or (isinstance(right, ABCDataFrame) and + not len(right.columns)): # ensure obj.dtypes[0] exists for each obj return False ldtype = left.dtypes.iloc[0] - rdtype = right.dtypes.iloc[0] + rdtype = (right.dtypes.iloc[0] + if isinstance(right, ABCDataFrame) else right.dtype) if ((is_timedelta64_dtype(ldtype) and is_integer_dtype(rdtype)) or (is_timedelta64_dtype(rdtype) and is_integer_dtype(ldtype))): @@ -1767,6 +1769,25 @@ def _combine_series_frame(self, other, func, fill_value=None, axis=None, return self._combine_match_columns(other, func, level=level) +def _treat_as_column(left, right, axis=None): + # Only if it is _impossible_ to treat `right` as row-like and a precise + # fit to treat it as column-like do we treat it as a column. + if axis is not None: + return False + + if np.ndim(right) != 1: + return False + + if len(right) == len(left.columns): + return False + + if isinstance(right, ABCSeries): + # require an exact match + return right.index.equals(left.index) + + return len(right) == len(left) + + def _align_method_FRAME(left, right, axis): """ convert rhs to meet lhs dims if input is list, tuple or np.ndarray """ @@ -1850,7 +1871,12 @@ def na_op(x, y): doc = _arith_doc_FRAME % op_name @Appender(doc) - def f(self, other, axis=default_axis, level=None, fill_value=None): + def f(self, other, axis=None, level=None, fill_value=None): + + if _treat_as_column(self, other, axis): + axis = "index" + else: + axis = axis if axis is not None else default_axis other = _align_method_FRAME(self, other, axis) @@ -1861,7 +1887,7 @@ def f(self, other, axis=default_axis, level=None, fill_value=None): elif isinstance(other, ABCSeries): # For these values of `axis`, we end up dispatching to Series op, # so do not want the masked op. - pass_op = op if axis in [0, "columns", None] else na_op + pass_op = op if axis in [0, "columns", "index", None] else na_op return _combine_series_frame(self, other, pass_op, fill_value=fill_value, axis=axis, level=level) @@ -1923,7 +1949,11 @@ def _comp_method_FRAME(cls, func, special): @Appender('Wrapper for comparison method {name}'.format(name=op_name)) def f(self, other): - other = _align_method_FRAME(self, other, axis=None) + axis = None + if _treat_as_column(self, other, axis=axis): + axis = "index" + + other = _align_method_FRAME(self, other, axis=axis) if isinstance(other, ABCDataFrame): # Another DataFrame @@ -1934,7 +1964,7 @@ def f(self, other): elif isinstance(other, ABCSeries): return _combine_series_frame(self, other, func, - fill_value=None, axis=None, + fill_value=None, axis=axis, level=None) else: diff --git a/pandas/tests/arithmetic/conftest.py b/pandas/tests/arithmetic/conftest.py index b800b66e8edea..f2819c68e185d 100644 --- a/pandas/tests/arithmetic/conftest.py +++ b/pandas/tests/arithmetic/conftest.py @@ -153,19 +153,3 @@ 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_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 36bb0aca066fb..d0298ea0c2d37 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -1365,10 +1365,8 @@ 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) diff --git a/pandas/tests/arithmetic/test_period.py b/pandas/tests/arithmetic/test_period.py index fe98b74499983..cf402f0cf3549 100644 --- a/pandas/tests/arithmetic/test_period.py +++ b/pandas/tests/arithmetic/test_period.py @@ -344,9 +344,7 @@ 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? @@ -388,9 +386,7 @@ 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? diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 14a55785c243b..d88430e371ee1 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -18,6 +18,13 @@ DataFrame) +def assert_dtype(result, dtype, box): + if box is not pd.DataFrame: + assert result.dtype == dtype + else: + assert result.dtypes[0] == dtype + + # ------------------------------------------------------------------ # Timedelta64[ns] dtype Comparisons @@ -364,10 +371,6 @@ def test_td64arr_add_str_invalid(self, box): 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) @@ -390,9 +393,8 @@ 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) @@ -460,9 +462,7 @@ 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 @@ -478,9 +478,7 @@ 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 @@ -521,9 +519,8 @@ 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) err = TypeError if box is not pd.Index else NullFrequencyError @@ -577,9 +574,6 @@ def test_td64arr_add_sub_numeric_scalar_invalid(self, box, scalar, tdser): # 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) err = TypeError if box is pd.Index and not dtype.startswith('float'): @@ -652,9 +646,7 @@ 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 @@ -668,9 +660,7 @@ def test_td64arr_add_td64_array(self, box_df_broadcast_failure): 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 @@ -688,49 +678,38 @@ def test_td64arr_sub_td64_array(self, box_df_broadcast_failure): @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 + exname = names[2] if box is not pd.DataFrame else names[1] + 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]) + name=exname) ser = tm.box_expected(ser, box) expected = tm.box_expected(expected, box) result = tdi + ser tm.assert_equal(result, expected) - if box is not pd.DataFrame: - assert result.dtype == 'timedelta64[ns]' - else: - assert result.dtypes[0] == 'timedelta64[ns]' + assert_dtype(result, 'timedelta64[ns]', box) result = ser + tdi tm.assert_equal(result, expected) - if box is not pd.DataFrame: - assert result.dtype == 'timedelta64[ns]' - else: - assert result.dtypes[0] == 'timedelta64[ns]' + assert_dtype(result, 'timedelta64[ns]', box) expected = Series([Timedelta(hours=-3), Timedelta(days=1, hours=-4)], - name=names[2]) + name=exname) expected = tm.box_expected(expected, box) result = tdi - ser tm.assert_equal(result, expected) - if box is not pd.DataFrame: - assert result.dtype == 'timedelta64[ns]' - else: - assert result.dtypes[0] == 'timedelta64[ns]' + assert_dtype(result, 'timedelta64[ns]', box) result = ser - tdi tm.assert_equal(result, -expected) - if box is not pd.DataFrame: - assert result.dtype == 'timedelta64[ns]' - else: - assert result.dtypes[0] == 'timedelta64[ns]' + assert_dtype(result, 'timedelta64[ns]', box) def test_td64arr_sub_NaT(self, box): # GH#18808 @@ -804,16 +783,18 @@ 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_df_fail): # GH#18849, GH#19744 - box = box_df_broadcast_failure + box = box_df_fail + exname = names[2] if box is not pd.DataFrame else names[1] + 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)], name=names[1]) expected = TimedeltaIndex([tdi[n] + other[n] for n in range(len(tdi))], - freq='infer', name=names[2]) + freq='infer', name=exname) tdi = tm.box_expected(tdi, box) expected = tm.box_expected(expected, box) @@ -827,9 +808,9 @@ 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_df_fail): # GH#18849 - box = box_df_broadcast_failure + box = box_df_fail 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)]) @@ -850,16 +831,18 @@ def test_td64arr_add_offset_array(self, box_df_broadcast_failure): @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_df_fail): # GH#18824, GH#19744 - box = box_df_broadcast_failure + box = box_df_fail + exname = names[2] if box is not pd.DataFrame else names[0] + 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)], name=names[1]) expected = TimedeltaIndex([tdi[n] - other[n] for n in range(len(tdi))], - freq='infer', name=names[2]) + freq='infer', name=exname) tdi = tm.box_expected(tdi, box) expected = tm.box_expected(expected, box) @@ -868,9 +851,10 @@ 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_df_fail): # GH#18824 - box = box_df_broadcast_failure + box = box_df_fail + 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)]) @@ -891,6 +875,7 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): # GH#18849 box = box_df_fail box2 = Series if box is pd.Index else box + exname = names[2] if box is not pd.DataFrame else names[0] tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00'], name=names[0]) @@ -898,7 +883,7 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): name=names[1]) expected_add = Series([tdi[n] + other[n] for n in range(len(tdi))], - name=names[2]) + name=exname) tdi = tm.box_expected(tdi, box) expected_add = tm.box_expected(expected_add, box2) @@ -912,7 +897,7 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): # TODO: separate/parametrize add/sub test? expected_sub = Series([tdi[n] - other[n] for n in range(len(tdi))], - name=names[2]) + name=exname) expected_sub = tm.box_expected(expected_sub, box2) with tm.assert_produces_warning(PerformanceWarning): @@ -922,9 +907,6 @@ def test_td64arr_with_offset_series(self, names, box_df_fail): @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) @@ -1002,8 +984,7 @@ 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) @@ -1014,8 +995,7 @@ def test_tdi_mul_int_array(self, box_df_broadcast_failure): 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) @@ -1027,8 +1007,7 @@ def test_tdi_mul_int_series(self, box_df_fail): 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) @@ -1048,10 +1027,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): - # RangeIndex fails to return NotImplemented, for others - # DataFrame tries to broadcast incorrectly - box = box_df_fail + def test_tdi_rmul_arraylike(self, other, box): + if box is pd.DataFrame and isinstance(other, pd.RangeIndex): + pytest.xfail(reason="Fails to return NotImplemented") tdi = TimedeltaIndex(['1 Day'] * 10) expected = timedelta_range('1 days', '10 days') @@ -1264,11 +1242,9 @@ 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'], @@ -1282,14 +1258,6 @@ def test_td64arr_rmul_numeric_array(self, op, box_df_fail, 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']) @@ -1317,9 +1285,10 @@ 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 + exname = names[2] if box is not pd.DataFrame else names[0] + tdi = TimedeltaIndex(['0days', '1day', '2days', '3days', '4days'], name=names[0]) # TODO: Should we be parametrizing over types for `ser` too? @@ -1327,7 +1296,7 @@ def test_td64arr_mul_int_series(self, box_df_fail, names): expected = Series(['0days', '1day', '4days', '9days', '16days'], dtype='timedelta64[ns]', - name=names[2]) + name=exname) tdi = tm.box_expected(tdi, box) box = Series if (box is pd.Index and type(ser) is Series) else box @@ -1336,8 +1305,7 @@ 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) + result = tdi * ser tm.assert_equal(result, expected) # TODO: Should we be parametrizing over types for `ser` too? diff --git a/pandas/tests/frame/test_operators.py b/pandas/tests/frame/test_operators.py index b2781952ea86d..44d87258f12cc 100644 --- a/pandas/tests/frame/test_operators.py +++ b/pandas/tests/frame/test_operators.py @@ -805,16 +805,15 @@ def test_boolean_comparison(self): result = df.values > b assert_numpy_array_equal(result, expected.values) - msg1d = 'Unable to coerce to Series, length must be 2: given 3' msg2d = 'Unable to coerce to DataFrame, shape must be' msg2db = 'operands could not be broadcast together with shapes' - with tm.assert_raises_regex(ValueError, msg1d): - # wrong shape - df > lst - with tm.assert_raises_regex(ValueError, msg1d): - # wrong shape - result = df > tup + # operate column-by-column + result = df > lst + assert_frame_equal(result, expected) + + result = df > tup + assert_frame_equal(result, expected) # broadcasts like ndarray (GH#23000) result = df > b_r @@ -834,11 +833,11 @@ def test_boolean_comparison(self): result = df == b assert_frame_equal(result, expected) - with tm.assert_raises_regex(ValueError, msg1d): - result = df == lst + result = df == lst + assert_frame_equal(result, expected) - with tm.assert_raises_regex(ValueError, msg1d): - result = df == tup + result = df == tup + assert_frame_equal(result, expected) # broadcasts like ndarray (GH#23000) result = df == b_r @@ -858,11 +857,11 @@ def test_boolean_comparison(self): expected.index = df.index expected.columns = df.columns - with tm.assert_raises_regex(ValueError, msg1d): - result = df == lst + result = df == lst + assert_frame_equal(result, expected) - with tm.assert_raises_regex(ValueError, msg1d): - result = df == tup + result = df == tup + assert_frame_equal(result, expected) def test_combine_generic(self): df1 = self.frame From b5b4c85deadc3b13669630ae328fd70abfe6f847 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 23 Oct 2018 16:28:53 -0700 Subject: [PATCH 2/3] more precise conditions --- pandas/core/ops.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 3ebbba3ed9107..4d9d16f6c9cb5 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -1778,14 +1778,12 @@ def _treat_as_column(left, right, axis=None): if np.ndim(right) != 1: return False - if len(right) == len(left.columns): - return False - if isinstance(right, ABCSeries): # require an exact match - return right.index.equals(left.index) + return (right.index.equals(left.index) and + not left.index.equals(left.columns)) - return len(right) == len(left) + return len(right) == len(left) != len(left.columns) def _align_method_FRAME(left, right, axis): From 9100010585fce40a97d823c7b506e064260ebdb0 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 23 Oct 2018 19:56:27 -0700 Subject: [PATCH 3/3] Whitespace fixup --- pandas/tests/arithmetic/test_timedelta64.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index d88430e371ee1..935da400526fe 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -22,7 +22,7 @@ def assert_dtype(result, dtype, box): if box is not pd.DataFrame: assert result.dtype == dtype else: - assert result.dtypes[0] == dtype + assert result.dtypes[0] == dtype # ------------------------------------------------------------------