diff --git a/pandas/tests/frame/test_arithmetic.py b/pandas/tests/frame/test_arithmetic.py index 3d03a70553d2d..8d52bc9532136 100644 --- a/pandas/tests/frame/test_arithmetic.py +++ b/pandas/tests/frame/test_arithmetic.py @@ -89,116 +89,6 @@ def test_df_add_flex_filled_mixed_dtypes(self): tm.assert_frame_equal(result, expected) -class TestFrameMulDiv(object): - """Tests for DataFrame multiplication and division""" - # ------------------------------------------------------------------ - # Mod By Zero - - def test_df_mod_zero_df(self): - # GH#3590, modulo as ints - df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - - # this is technically wrong, as the integer portion is coerced to float - # ### - first = pd.Series([0, 0, 0, 0], dtype='float64') - second = pd.Series([np.nan, np.nan, np.nan, 0]) - expected = pd.DataFrame({'first': first, 'second': second}) - result = df % df - tm.assert_frame_equal(result, expected) - - def test_df_mod_zero_array(self): - # GH#3590, modulo as ints - df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - - # this is technically wrong, as the integer portion is coerced to float - # ### - first = pd.Series([0, 0, 0, 0], dtype='float64') - second = pd.Series([np.nan, np.nan, np.nan, 0]) - expected = pd.DataFrame({'first': first, 'second': second}) - - # numpy has a slightly different (wrong) treatment - with np.errstate(all='ignore'): - arr = df.values % df.values - result2 = pd.DataFrame(arr, index=df.index, - columns=df.columns, dtype='float64') - result2.iloc[0:3, 1] = np.nan - tm.assert_frame_equal(result2, expected) - - def test_df_mod_zero_int(self): - # GH#3590, modulo as ints - df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - - result = df % 0 - expected = pd.DataFrame(np.nan, index=df.index, columns=df.columns) - tm.assert_frame_equal(result, expected) - - # numpy has a slightly different (wrong) treatment - with np.errstate(all='ignore'): - arr = df.values.astype('float64') % 0 - result2 = pd.DataFrame(arr, index=df.index, columns=df.columns) - tm.assert_frame_equal(result2, expected) - - def test_df_mod_zero_series_does_not_commute(self): - # GH#3590, modulo as ints - # not commutative with series - df = pd.DataFrame(np.random.randn(10, 5)) - ser = df[0] - res = ser % df - res2 = df % ser - assert not res.fillna(0).equals(res2.fillna(0)) - - # ------------------------------------------------------------------ - # Division By Zero - - def test_df_div_zero_df(self): - # integer div, but deal with the 0's (GH#9144) - df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - result = df / df - - first = pd.Series([1.0, 1.0, 1.0, 1.0]) - second = pd.Series([np.nan, np.nan, np.nan, 1]) - expected = pd.DataFrame({'first': first, 'second': second}) - tm.assert_frame_equal(result, expected) - - def test_df_div_zero_array(self): - # integer div, but deal with the 0's (GH#9144) - df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - - first = pd.Series([1.0, 1.0, 1.0, 1.0]) - second = pd.Series([np.nan, np.nan, np.nan, 1]) - expected = pd.DataFrame({'first': first, 'second': second}) - - with np.errstate(all='ignore'): - arr = df.values.astype('float') / df.values - result = pd.DataFrame(arr, index=df.index, - columns=df.columns) - tm.assert_frame_equal(result, expected) - - def test_df_div_zero_int(self): - # integer div, but deal with the 0's (GH#9144) - df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) - - result = df / 0 - expected = pd.DataFrame(np.inf, index=df.index, columns=df.columns) - expected.iloc[0:3, 1] = np.nan - tm.assert_frame_equal(result, expected) - - # numpy has a slightly different (wrong) treatment - with np.errstate(all='ignore'): - arr = df.values.astype('float64') / 0 - result2 = pd.DataFrame(arr, index=df.index, - columns=df.columns) - tm.assert_frame_equal(result2, expected) - - def test_df_div_zero_series_does_not_commute(self): - # integer div, but deal with the 0's (GH#9144) - df = pd.DataFrame(np.random.randn(10, 5)) - ser = df[0] - res = ser / df - res2 = df / ser - assert not res.fillna(0).equals(res2.fillna(0)) - - class TestFrameArithmetic(object): @pytest.mark.xfail(reason='GH#7996 datetime64 units not converted to nano', diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index 01ff038c4dd1c..32edb2591b735 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -163,48 +163,6 @@ def test_divmod_series(self): for r, e in zip(result, expected): tm.assert_series_equal(r, e) - def test_div_zero(self, zero): - idx = self.create_index() - - expected = Index([np.nan, np.inf, np.inf, np.inf, np.inf], - dtype=np.float64) - result = idx / zero - tm.assert_index_equal(result, expected) - ser_compat = Series(idx).astype('i8') / np.array(zero).astype('i8') - tm.assert_series_equal(ser_compat, Series(result)) - - def test_floordiv_zero(self, zero): - idx = self.create_index() - expected = Index([np.nan, np.inf, np.inf, np.inf, np.inf], - dtype=np.float64) - - result = idx // zero - tm.assert_index_equal(result, expected) - ser_compat = Series(idx).astype('i8') // np.array(zero).astype('i8') - tm.assert_series_equal(ser_compat, Series(result)) - - def test_mod_zero(self, zero): - idx = self.create_index() - - expected = Index([np.nan, np.nan, np.nan, np.nan, np.nan], - dtype=np.float64) - result = idx % zero - tm.assert_index_equal(result, expected) - ser_compat = Series(idx).astype('i8') % np.array(zero).astype('i8') - tm.assert_series_equal(ser_compat, Series(result)) - - def test_divmod_zero(self, zero): - idx = self.create_index() - - exleft = Index([np.nan, np.inf, np.inf, np.inf, np.inf], - dtype=np.float64) - exright = Index([np.nan, np.nan, np.nan, np.nan, np.nan], - dtype=np.float64) - - result = divmod(idx, zero) - tm.assert_index_equal(result[0], exleft) - tm.assert_index_equal(result[1], exright) - def test_explicit_conversions(self): # GH 8608 diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index a5e75de2a267e..e60705075267f 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -7,11 +7,11 @@ import pandas as pd import pandas.util.testing as tm -from pandas import (DatetimeIndex, TimedeltaIndex, Float64Index, Int64Index, +from pandas import (DatetimeIndex, TimedeltaIndex, Int64Index, to_timedelta, timedelta_range, date_range, Series, Timestamp, Timedelta) -from pandas.errors import PerformanceWarning, NullFrequencyError +from pandas.errors import NullFrequencyError @pytest.fixture(params=[pd.offsets.Hour(2), timedelta(hours=2), @@ -124,147 +124,6 @@ def test_comparisons_nat(self): tm.assert_numpy_array_equal(result, expected) -class TestTimedeltaIndexMultiplicationDivision(object): - # __mul__, __rmul__, - # __div__, __rdiv__, __floordiv__, __rfloordiv__, - # __mod__, __rmod__, __divmod__, __rdivmod__ - - # ------------------------------------------------------------- - # Multiplication - # organized with scalar others first, then array-like - - def test_tdi_mul_int(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - result = idx * 1 - tm.assert_index_equal(result, idx) - - def test_tdi_rmul_int(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - result = 1 * idx - tm.assert_index_equal(result, idx) - - def test_tdi_mul_tdlike_scalar_raises(self, delta): - rng = timedelta_range('1 days', '10 days', name='foo') - with pytest.raises(TypeError): - rng * delta - - def test_tdi_mul_int_array_zerodim(self): - rng5 = np.arange(5, dtype='int64') - idx = TimedeltaIndex(rng5) - expected = TimedeltaIndex(rng5 * 5) - result = idx * np.array(5, dtype='int64') - tm.assert_index_equal(result, expected) - - def test_tdi_mul_int_array(self): - rng5 = np.arange(5, dtype='int64') - idx = TimedeltaIndex(rng5) - didx = TimedeltaIndex(rng5 ** 2) - - result = idx * rng5 - tm.assert_index_equal(result, didx) - - def test_tdi_mul_dti_raises(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - with pytest.raises(TypeError): - idx * idx - - def test_tdi_mul_too_short_raises(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - with pytest.raises(TypeError): - idx * TimedeltaIndex(np.arange(3)) - with pytest.raises(ValueError): - idx * np.array([1, 2]) - - def test_tdi_mul_int_series(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - didx = TimedeltaIndex(np.arange(5, dtype='int64') ** 2) - - result = idx * Series(np.arange(5, dtype='int64')) - - tm.assert_series_equal(result, Series(didx)) - - def test_tdi_mul_float_series(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - - rng5f = np.arange(5, dtype='float64') - result = idx * Series(rng5f + 0.1) - expected = Series(TimedeltaIndex(rng5f * (rng5f + 0.1))) - tm.assert_series_equal(result, expected) - - @pytest.mark.parametrize('other', [np.arange(1, 11), - pd.Int64Index(range(1, 11)), - pd.UInt64Index(range(1, 11)), - pd.Float64Index(range(1, 11)), - pd.RangeIndex(1, 11)]) - def test_tdi_rmul_arraylike(self, other): - tdi = TimedeltaIndex(['1 Day'] * 10) - expected = timedelta_range('1 days', '10 days') - - result = other * tdi - tm.assert_index_equal(result, expected) - commute = tdi * other - tm.assert_index_equal(commute, expected) - - # ------------------------------------------------------------- - # TimedeltaIndex.__div__ - - def test_tdi_div_int(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - result = idx / 1 - tm.assert_index_equal(result, idx) - - def test_tdi_div_tdlike_scalar(self, delta): - rng = timedelta_range('1 days', '10 days', name='foo') - expected = Int64Index((np.arange(10) + 1) * 12, name='foo') - - result = rng / delta - tm.assert_index_equal(result, expected, exact=False) - - def test_tdi_div_tdlike_scalar_with_nat(self, delta): - rng = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo') - expected = Float64Index([12, np.nan, 24], name='foo') - result = rng / delta - tm.assert_index_equal(result, expected) - - def test_tdi_div_nat_raises(self): - # don't allow division by NaT (make could in the future) - rng = timedelta_range('1 days', '10 days', name='foo') - with pytest.raises(TypeError): - rng / pd.NaT - - # ------------------------------------------------------------- - # TimedeltaIndex.__floordiv__ - - def test_tdi_floordiv_int(self): - idx = TimedeltaIndex(np.arange(5, dtype='int64')) - result = idx // 1 - tm.assert_index_equal(result, idx) - - def test_tdi_floordiv_tdlike_scalar(self, delta): - tdi = timedelta_range('1 days', '10 days', name='foo') - expected = Int64Index((np.arange(10) + 1) * 12, name='foo') - - result = tdi // delta - tm.assert_index_equal(result, expected, exact=False) - - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=10, seconds=7), - Timedelta('10m7s'), - Timedelta('10m7s').to_timedelta64()]) - def test_tdi_floordiv_timedelta_scalar(self, scalar_td): - # GH#19125 - tdi = TimedeltaIndex(['00:05:03', '00:05:03', pd.NaT], freq=None) - expected = pd.Index([2.0, 2.0, np.nan]) - - res = tdi.__rfloordiv__(scalar_td) - tm.assert_index_equal(res, expected) - - expected = pd.Index([0.0, 0.0, np.nan]) - - res = tdi // (scalar_td) - tm.assert_index_equal(res, expected) - - class TestTimedeltaIndexArithmetic(object): # Addition and Subtraction Operations @@ -324,122 +183,6 @@ def test_shift_no_freq(self): # ------------------------------------------------------------- - @pytest.mark.parametrize('names', [(None, None, None), - ('foo', 'bar', None), - ('foo', 'foo', 'foo')]) - def test_tdi_add_offset_index(self, names): - # GH#18849, GH#19744 - 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]) - - with tm.assert_produces_warning(PerformanceWarning): - res = tdi + other - tm.assert_index_equal(res, expected) - - with tm.assert_produces_warning(PerformanceWarning): - res2 = other + tdi - tm.assert_index_equal(res2, expected) - - def test_tdi_add_offset_array(self): - # GH#18849 - 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') - - with tm.assert_produces_warning(PerformanceWarning): - res = tdi + other - tm.assert_index_equal(res, expected) - - with tm.assert_produces_warning(PerformanceWarning): - res2 = other + tdi - tm.assert_index_equal(res2, expected) - - @pytest.mark.parametrize('names', [(None, None, None), - ('foo', 'bar', None), - ('foo', 'foo', 'foo')]) - def test_tdi_sub_offset_index(self, names): - # GH#18824, GH#19744 - 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]) - - with tm.assert_produces_warning(PerformanceWarning): - res = tdi - other - tm.assert_index_equal(res, expected) - - def test_tdi_sub_offset_array(self): - # GH#18824 - 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') - - with tm.assert_produces_warning(PerformanceWarning): - res = tdi - other - tm.assert_index_equal(res, expected) - - @pytest.mark.parametrize('names', [(None, None, None), - ('foo', 'bar', None), - ('foo', 'foo', 'foo')]) - def test_tdi_with_offset_series(self, names): - # GH#18849 - tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00'], - name=names[0]) - other = Series([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)], - name=names[1]) - - expected_add = Series([tdi[n] + other[n] for n in range(len(tdi))], - name=names[2]) - - with tm.assert_produces_warning(PerformanceWarning): - res = tdi + other - tm.assert_series_equal(res, expected_add) - - with tm.assert_produces_warning(PerformanceWarning): - res2 = other + tdi - tm.assert_series_equal(res2, expected_add) - - expected_sub = Series([tdi[n] - other[n] for n in range(len(tdi))], - name=names[2]) - - with tm.assert_produces_warning(PerformanceWarning): - res3 = tdi - other - tm.assert_series_equal(res3, expected_sub) - - @pytest.mark.parametrize('box', [np.array, pd.Index, pd.Series]) - def test_tdi_add_sub_anchored_offset_arraylike(self, box): - # GH#18824 - tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00']) - - anchored = box([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 - def test_ufunc_coercions(self): # normal ops are also tested in tseries/test_timedeltas.py idx = TimedeltaIndex(['2H', '4H', '6H', '8H', '10H'], @@ -546,14 +289,7 @@ def test_tdi_addsub_integer_array_no_freq(self, box): # ------------------------------------------------------------- # Binary operations TimedeltaIndex and timedelta-like - - def test_tdi_add_timedeltalike(self, delta): - # only test adding/sub offsets as + is now numeric - rng = timedelta_range('1 days', '10 days') - result = rng + delta - expected = timedelta_range('1 days 02:00:00', '10 days 02:00:00', - freq='D') - tm.assert_index_equal(result, expected) + # Note: add and sub are tested in tests.test_arithmetic def test_tdi_iadd_timedeltalike(self, delta): # only test adding/sub offsets as + is now numeric @@ -563,13 +299,6 @@ def test_tdi_iadd_timedeltalike(self, delta): rng += delta tm.assert_index_equal(rng, expected) - def test_tdi_sub_timedeltalike(self, delta): - # only test adding/sub offsets as - is now numeric - rng = timedelta_range('1 days', '10 days') - result = rng - delta - expected = timedelta_range('0 days 22:00:00', '9 days 22:00:00') - tm.assert_index_equal(result, expected) - def test_tdi_isub_timedeltalike(self, delta): # only test adding/sub offsets as - is now numeric rng = timedelta_range('1 days', '10 days') @@ -577,55 +306,6 @@ def test_tdi_isub_timedeltalike(self, delta): rng -= delta tm.assert_index_equal(rng, expected) - # ------------------------------------------------------------- - # __add__/__sub__ with ndarray[datetime64] and ndarray[timedelta64] - - def test_tdi_sub_dt64_array(self): - dti = pd.date_range('2016-01-01', periods=3) - tdi = dti - dti.shift(1) - dtarr = dti.values - - with pytest.raises(TypeError): - tdi - dtarr - - # TimedeltaIndex.__rsub__ - expected = pd.DatetimeIndex(dtarr) - tdi - result = dtarr - tdi - tm.assert_index_equal(result, expected) - - def test_tdi_add_dt64_array(self): - dti = pd.date_range('2016-01-01', periods=3) - tdi = dti - dti.shift(1) - dtarr = dti.values - - expected = pd.DatetimeIndex(dtarr) + tdi - result = tdi + dtarr - tm.assert_index_equal(result, expected) - result = dtarr + tdi - tm.assert_index_equal(result, expected) - - def test_tdi_add_td64_array(self): - dti = pd.date_range('2016-01-01', periods=3) - tdi = dti - dti.shift(1) - tdarr = tdi.values - - expected = 2 * tdi - result = tdi + tdarr - tm.assert_index_equal(result, expected) - result = tdarr + tdi - tm.assert_index_equal(result, expected) - - def test_tdi_sub_td64_array(self): - dti = pd.date_range('2016-01-01', periods=3) - tdi = dti - dti.shift(1) - tdarr = tdi.values - - expected = 0 * tdi - result = tdi - tdarr - tm.assert_index_equal(result, expected) - result = tdarr - tdi - tm.assert_index_equal(result, expected) - # ------------------------------------------------------------- def test_subtraction_ops(self): diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index c091df63fcfc7..1413e01000ec5 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -355,64 +355,6 @@ def test_div_equiv_binop(self): result = second / first tm.assert_series_equal(result, expected) - @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').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, - dtype='float64', name=None) - expected.iloc[0:3] = np.inf - - result = first / second - tm.assert_series_equal(result, expected) - assert not result.equals(second / first) - - def test_rdiv_zero_compat(self): - # GH#8674 - zero_array = np.array([0] * 5) - data = np.random.randn(5) - expected = Series([0.] * 5) - - result = zero_array / Series(data) - tm.assert_series_equal(result, expected) - - result = Series(zero_array) / data - tm.assert_series_equal(result, expected) - - result = Series(zero_array) / Series(data) - tm.assert_series_equal(result, expected) - - 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 - tm.assert_series_equal(result, expected) - - def test_rdiv_zero(self): - # GH#9144 - ser = Series([-1, 0, 1], name='first') - expected = Series([0.0, np.nan, 0.0], name='first') - - result = 0 / ser - tm.assert_series_equal(result, expected) - - def test_floordiv_div(self): - # GH#9144 - ser = Series([-1, 0, 1], name='first') - - result = ser // 0 - expected = Series([-np.inf, np.nan, np.inf], name='first') - tm.assert_series_equal(result, expected) - class TestSeriesArithmetic(object): # Standard, numeric, or otherwise not-Timestamp/Timedelta/Period dtypes diff --git a/pandas/tests/test_arithmetic.py b/pandas/tests/test_arithmetic.py index 8ee0bf9ec874a..0d62a5db581fe 100644 --- a/pandas/tests/test_arithmetic.py +++ b/pandas/tests/test_arithmetic.py @@ -10,10 +10,12 @@ import pandas as pd import pandas.util.testing as tm +from pandas.compat import long from pandas.core import ops -from pandas.errors import NullFrequencyError +from pandas.errors import NullFrequencyError, PerformanceWarning from pandas._libs.tslibs import IncompatibleFrequency from pandas import ( + timedelta_range, Timedelta, Timestamp, NaT, Series, TimedeltaIndex, DatetimeIndex) @@ -28,6 +30,49 @@ def tdser(): return Series(['59 Days', '59 Days', 'NaT'], dtype='timedelta64[ns]') +@pytest.fixture(params=[pd.offsets.Hour(2), timedelta(hours=2), + np.timedelta64(2, 'h'), Timedelta(hours=2)], + ids=lambda x: type(x).__name__) +def delta(request): + """ + Several ways of representing two hours + """ + return request.param + + +@pytest.fixture(params=[timedelta(minutes=5, seconds=4), + Timedelta('5m4s'), + Timedelta('5m4s').to_timedelta64()], + ids=lambda x: type(x).__name__) +def scalar_td(request): + """ + Several variants of Timedelta scalars representing 5 minutes and 4 seconds + """ + return request.param + + +@pytest.fixture(params=[pd.Index, Series, pd.DataFrame], + ids=lambda x: x.__name__) +def box(request): + """ + Several array-like containers that should have effectively identical + behavior with respect to arithmetic operations. + """ + return request.param + + +@pytest.fixture(params=[pd.Index, + 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 + + # ------------------------------------------------------------------ # Numeric dtypes Arithmetic with Timedelta Scalar @@ -58,7 +103,7 @@ def test_numeric_arr_mul_tdscalar(self, scalar_td, index, box): type(scalar_td) is timedelta and index.dtype == 'f8'): raise pytest.xfail(reason="Cannot multiply timedelta by float") - expected = pd.timedelta_range('1 days', '10 days') + expected = timedelta_range('1 days', '10 days') index = tm.box_expected(index, box) expected = tm.box_expected(expected, box) @@ -69,7 +114,6 @@ def test_numeric_arr_mul_tdscalar(self, scalar_td, index, box): commute = scalar_td * index tm.assert_equal(commute, expected) - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame]) @pytest.mark.parametrize('index', [ pd.Int64Index(range(1, 3)), pd.UInt64Index(range(1, 3)), @@ -109,8 +153,6 @@ class TestTimedeltaArraylikeAddSubOps(object): # ------------------------------------------------------------- # Invalid Operations - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame], - ids=lambda x: x.__name__) def test_td64arr_add_str_invalid(self, box): # GH#13624 tdi = TimedeltaIndex(['1 day', '2 days']) @@ -121,8 +163,6 @@ def test_td64arr_add_str_invalid(self, box): with pytest.raises(TypeError): 'a' + tdi - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame], - ids=lambda x: x.__name__) @pytest.mark.parametrize('other', [3.14, np.array([2.0, 3.0])]) @pytest.mark.parametrize('op', [operator.add, ops.radd, operator.sub, ops.rsub], @@ -186,8 +226,6 @@ def test_td64arr_sub_pi(self, box, tdi_freq, pi_freq): # ------------------------------------------------------------- # Binary operations td64 arraylike and datetime-like - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame], - ids=lambda x: x.__name__) def test_td64arr_sub_timestamp_raises(self, box): idx = TimedeltaIndex(['1 day', '2 day']) idx = tm.box_expected(idx, box) @@ -231,6 +269,73 @@ def test_td64_radd_timestamp(self, box): result = Timestamp('2011-01-01') + idx tm.assert_equal(result, expected) + @pytest.mark.parametrize('box', [ + pd.Index, + Series, + pytest.param(pd.DataFrame, + marks=pytest.mark.xfail(reason="Returns object dtype " + "instead of " + "datetime64[ns]", + strict=True)) + ], ids=lambda x: x.__name__) + def test_td64arr_add_sub_timestamp(self, box): + # GH#11925 + ts = Timestamp('2012-01-01') + # TODO: parametrize over types of datetime scalar? + + tdser = Series(timedelta_range('1 day', periods=3)) + expected = Series(pd.date_range('2012-01-02', periods=3)) + + tdser = tm.box_expected(tdser, box) + expected = tm.box_expected(expected, box) + + tm.assert_equal(ts + tdser, expected) + tm.assert_equal(tdser + ts, expected) + + expected2 = Series(pd.date_range('2011-12-31', + periods=3, freq='-1D')) + expected2 = tm.box_expected(expected2, box) + + tm.assert_equal(ts - tdser, expected2) + tm.assert_equal(ts + (-tdser), expected2) + + with pytest.raises(TypeError): + tdser - ts + + def test_tdi_sub_dt64_array(self, box_df_fail): + box = box_df_fail # DataFrame tries to broadcast incorrectly + + 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) + + with pytest.raises(TypeError): + tdi - dtarr + + # TimedeltaIndex.__rsub__ + result = dtarr - tdi + tm.assert_equal(result, expected) + + def test_tdi_add_dt64_array(self, box_df_fail): + box = box_df_fail # DataFrame tries to broadcast incorrectly + + 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) + + result = tdi + dtarr + tm.assert_equal(result, expected) + result = dtarr + tdi + tm.assert_equal(result, expected) + # ------------------------------------------------------------------ # Operations with int-like others @@ -280,8 +385,6 @@ def test_td64arr_sub_int_series_invalid(self, box, tdser): with pytest.raises(err): tdser - Series([2, 3, 4]) - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame], - ids=lambda x: x.__name__) @pytest.mark.xfail(reason='GH#19123 integer interpreted as nanoseconds', strict=True) def test_td64arr_rsub_int_series_invalid(self, box, tdser): @@ -293,7 +396,7 @@ def test_td64arr_rsub_int_series_invalid(self, box, tdser): pd.Index, Series, pytest.param(pd.DataFrame, - marks=pytest.mark.xfail(reason="Tries to broadcast " + marks=pytest.mark.xfail(reason="Attempts to broadcast " "incorrectly", strict=True, raises=ValueError)) ], ids=lambda x: x.__name__) @@ -326,8 +429,6 @@ def test_td64arr_add_intlike(self, box): with pytest.raises(err): ser - pd.Index(other) - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame], - ids=lambda x: x.__name__) @pytest.mark.parametrize('scalar', [1, 1.5, np.array(2)]) def test_td64arr_add_sub_numeric_scalar_invalid(self, box, scalar, tdser): @@ -387,43 +488,39 @@ def test_td64arr_add_sub_numeric_arr_invalid(self, box, vec, dtype, tdser): vector - tdser # ------------------------------------------------------------------ - # Operations with datetime-like others + # Operations with timedelta-like others - @pytest.mark.parametrize('box', [ - pd.Index, - Series, - pytest.param(pd.DataFrame, - marks=pytest.mark.xfail(reason="Returns object dtype " - "instead of " - "datetime64[ns]", - strict=True)) - ], ids=lambda x: x.__name__) - def test_td64arr_add_sub_timestamp(self, box): - # GH#11925 - ts = Timestamp('2012-01-01') - # TODO: parametrize over types of datetime scalar? + def test_td64arr_add_td64_array(self, box_df_fail): + box = box_df_fail # DataFrame tries to broadcast incorrectly - tdser = Series(pd.timedelta_range('1 day', periods=3)) - expected = Series(pd.date_range('2012-01-02', periods=3)) + dti = pd.date_range('2016-01-01', periods=3) + tdi = dti - dti.shift(1) + tdarr = tdi.values - tdser = tm.box_expected(tdser, box) + expected = 2 * tdi + tdi = tm.box_expected(tdi, box) expected = tm.box_expected(expected, box) - tm.assert_equal(ts + tdser, expected) - tm.assert_equal(tdser + ts, expected) + result = tdi + tdarr + tm.assert_equal(result, expected) + result = tdarr + tdi + tm.assert_equal(result, expected) - expected2 = Series(pd.date_range('2011-12-31', - periods=3, freq='-1D')) - expected2 = tm.box_expected(expected2, box) + def test_td64arr_sub_td64_array(self, box_df_fail): + box = box_df_fail # DataFrame tries to broadcast incorrectly - tm.assert_equal(ts - tdser, expected2) - tm.assert_equal(ts + (-tdser), expected2) + dti = pd.date_range('2016-01-01', periods=3) + tdi = dti - dti.shift(1) + tdarr = tdi.values - with pytest.raises(TypeError): - tdser - ts + expected = 0 * tdi + tdi = tm.box_expected(tdi, box) + expected = tm.box_expected(expected, box) - # ------------------------------------------------------------------ - # Operations with timedelta-like others (including DateOffsets) + result = tdi - tdarr + tm.assert_equal(result, expected) + result = tdarr - tdi + tm.assert_equal(result, expected) # TODO: parametrize over [add, sub, radd, rsub]? @pytest.mark.parametrize('box', [ @@ -481,8 +578,6 @@ def test_td64arr_add_sub_tdi(self, box, names): else: assert result.dtypes[0] == 'timedelta64[ns]' - @pytest.mark.parametrize('box', [pd.Index, Series, pd.DataFrame], - ids=lambda x: x.__name__) def test_td64arr_sub_NaT(self, box): # GH#18808 ser = Series([NaT, Timedelta('1s')]) @@ -491,14 +586,348 @@ def test_td64arr_sub_NaT(self, box): ser = tm.box_expected(ser, box) expected = tm.box_expected(expected, box) - res = ser - NaT + res = ser - pd.NaT + tm.assert_equal(res, expected) + + def test_td64arr_add_timedeltalike(self, delta, box): + # only test adding/sub offsets as + is now numeric + if box is pd.DataFrame and isinstance(delta, pd.DateOffset): + pytest.xfail(reason="Returns object dtype instead of m8[ns]") + + rng = timedelta_range('1 days', '10 days') + expected = timedelta_range('1 days 02:00:00', '10 days 02:00:00', + freq='D') + rng = tm.box_expected(rng, box) + expected = tm.box_expected(expected, box) + + result = rng + delta + tm.assert_equal(result, expected) + + def test_td64arr_sub_timedeltalike(self, delta, box): + # only test adding/sub offsets as - is now numeric + if box is pd.DataFrame and isinstance(delta, pd.DateOffset): + pytest.xfail(reason="Returns object dtype instead of m8[ns]") + + rng = timedelta_range('1 days', '10 days') + expected = timedelta_range('0 days 22:00:00', '9 days 22:00:00') + + rng = tm.box_expected(rng, box) + expected = tm.box_expected(expected, box) + + result = rng - delta + tm.assert_equal(result, expected) + + # ------------------------------------------------------------------ + # __add__/__sub__ with DateOffsets and arrays of DateOffsets + + @pytest.mark.parametrize('box', [ + pd.Index, + pytest.param(Series, + marks=pytest.mark.xfail(reason="Index fails to return " + "NotImplemented on " + "reverse op", + strict=True)), + pytest.param(pd.DataFrame, + marks=pytest.mark.xfail(reason="Tries to broadcast " + "incorrectly", + strict=True, raises=ValueError)) + ], ids=lambda x: x.__name__) + @pytest.mark.parametrize('names', [(None, None, None), + ('foo', 'bar', None), + ('foo', 'foo', 'foo')]) + def test_td64arr_add_offset_index(self, names, box): + # GH#18849, GH#19744 + 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]) + tdi = tm.box_expected(tdi, box) + expected = tm.box_expected(expected, box) + + with tm.assert_produces_warning(PerformanceWarning): + res = tdi + other + tm.assert_equal(res, expected) + + with tm.assert_produces_warning(PerformanceWarning): + res2 = other + tdi + tm.assert_equal(res2, expected) + + # TODO: combine with test_td64arr_add_offset_index by parametrizing + # over second box? + def test_td64arr_add_offset_array(self, box_df_fail): + # GH#18849 + box = box_df_fail # tries to broadcast incorrectly + 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) + + with tm.assert_produces_warning(PerformanceWarning): + res = tdi + other tm.assert_equal(res, expected) + with tm.assert_produces_warning(PerformanceWarning): + res2 = other + tdi + tm.assert_equal(res2, expected) + + @pytest.mark.parametrize('names', [(None, None, None), + ('foo', 'bar', None), + ('foo', 'foo', 'foo')]) + def test_td64arr_sub_offset_index(self, names, box_df_fail): + # GH#18824, GH#19744 + box = box_df_fail # tries to broadcast incorrectly + 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]) + + tdi = tm.box_expected(tdi, box) + expected = tm.box_expected(expected, box) + + with tm.assert_produces_warning(PerformanceWarning): + res = tdi - other + tm.assert_equal(res, expected) + + def test_td64arr_sub_offset_array(self, box_df_fail): + # GH#18824 + box = box_df_fail # tries to broadcast incorrectly + 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) + + with tm.assert_produces_warning(PerformanceWarning): + res = tdi - other + tm.assert_equal(res, expected) + + @pytest.mark.parametrize('box', [ + pd.Index, + pytest.param(Series, + marks=pytest.mark.xfail(reason="object dtype Series " + "fails to return " + "NotImplemented", + strict=True, raises=TypeError)), + pytest.param(pd.DataFrame, + marks=pytest.mark.xfail(reason="tries to broadcast " + "incorrectly", + strict=True, raises=ValueError)) + ], ids=lambda x: x.__name__) + @pytest.mark.parametrize('names', [(None, None, None), + ('foo', 'bar', None), + ('foo', 'foo', 'foo')]) + def test_td64arr_with_offset_series(self, names, box): + # GH#18849 + box2 = Series if box is pd.Index else box + + tdi = TimedeltaIndex(['1 days 00:00:00', '3 days 04:00:00'], + name=names[0]) + other = Series([pd.offsets.Hour(n=1), pd.offsets.Minute(n=-2)], + name=names[1]) + + 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) + + with tm.assert_produces_warning(PerformanceWarning): + res = tdi + other + tm.assert_equal(res, expected_add) + + with tm.assert_produces_warning(PerformanceWarning): + 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) + + with tm.assert_produces_warning(PerformanceWarning): + res3 = tdi - other + tm.assert_equal(res3, expected_sub) + + @pytest.mark.parametrize('obox', [np.array, pd.Index, pd.Series]) + def test_td64arr_addsub_anchored_offset_arraylike(self, obox, box_df_fail): + # GH#18824 + box = box_df_fail # DataFrame tries to broadcast incorrectly + 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 + class TestTimedeltaArraylikeMulDivOps(object): # Tests for timedelta64[ns] # __mul__, __rmul__, __div__, __rdiv__, __floordiv__, __rfloordiv__ + # ------------------------------------------------------------------ + # Multiplication + # organized with scalar others first, then array-like + + def test_td64arr_mul_int(self, box_df_fail): + box = box_df_fail # DataFrame op returns object instead of m8[ns] + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + idx = tm.box_expected(idx, box) + + result = idx * 1 + tm.assert_equal(result, idx) + + result = 1 * idx + tm.assert_equal(result, idx) + + def test_td64arr_mul_tdlike_scalar_raises(self, delta, box): + if box is pd.DataFrame and not isinstance(delta, pd.DateOffset): + pytest.xfail(reason="returns m8[ns] instead of raising") + + rng = timedelta_range('1 days', '10 days', name='foo') + rng = tm.box_expected(rng, box) + with pytest.raises(TypeError): + rng * delta + + def test_tdi_mul_int_array_zerodim(self, box_df_fail): + box = box_df_fail # DataFrame op returns object dtype + rng5 = np.arange(5, dtype='int64') + idx = TimedeltaIndex(rng5) + expected = TimedeltaIndex(rng5 * 5) + + idx = tm.box_expected(idx, box) + expected = tm.box_expected(expected, box) + + result = idx * np.array(5, dtype='int64') + tm.assert_equal(result, expected) + + def test_tdi_mul_int_array(self, box_df_fail): + box = box_df_fail # DataFrame tries to broadcast incorrectly + 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) + + result = idx * rng5 + tm.assert_equal(result, expected) + + def test_tdi_mul_int_series(self, box_df_fail): + box = box_df_fail # DataFrame tries to broadcast incorrectly + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + expected = TimedeltaIndex(np.arange(5, dtype='int64') ** 2) + + idx = tm.box_expected(idx, box) + + box2 = pd.Series if box is pd.Index else box + expected = tm.box_expected(expected, box2) + + 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 # DataFrame tries to broadcast incorrectly + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + idx = tm.box_expected(idx, box) + + 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) + + result = idx * Series(rng5f + 0.1) + tm.assert_equal(result, expected) + + # TODO: Put Series/DataFrame in others? + @pytest.mark.parametrize('other', [ + np.arange(1, 11), + pd.Int64Index(range(1, 11)), + pd.UInt64Index(range(1, 11)), + 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 + + tdi = TimedeltaIndex(['1 Day'] * 10) + expected = timedelta_range('1 days', '10 days') + + tdi = tm.box_expected(tdi, box) + expected = tm.box_expected(expected, box) + + result = other * tdi + tm.assert_equal(result, expected) + commute = tdi * other + tm.assert_equal(commute, expected) + + # ------------------------------------------------------------------ + # __div__ + + def test_td64arr_div_nat_invalid(self, box_df_fail): + # don't allow division by NaT (maybe could in the future) + box = box_df_fail # DataFrame returns all-NaT instead of raising + rng = timedelta_range('1 days', '10 days', name='foo') + rng = tm.box_expected(rng, box) + with pytest.raises(TypeError): + rng / pd.NaT + + def test_td64arr_div_int(self, box_df_fail): + box = box_df_fail # DataFrame returns object dtype instead of m8[ns] + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + idx = tm.box_expected(idx, box) + + result = idx / 1 + tm.assert_equal(result, idx) + + def test_tdi_div_tdlike_scalar(self, delta, box_df_fail): + box = box_df_fail # DataFrame op returns m8[ns] instead of float64 + rng = timedelta_range('1 days', '10 days', name='foo') + expected = pd.Float64Index((np.arange(10) + 1) * 12, name='foo') + + rng = tm.box_expected(rng, box) + expected = tm.box_expected(expected, box) + + result = rng / delta + tm.assert_equal(result, expected) + + def test_tdi_div_tdlike_scalar_with_nat(self, delta, box_df_fail): + box = box_df_fail # DataFrame op returns m8[ns] instead of float64 + rng = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo') + expected = pd.Float64Index([12, np.nan, 24], name='foo') + + rng = tm.box_expected(rng, box) + expected = tm.box_expected(expected, box) + + result = rng / delta + tm.assert_equal(result, expected) + # ------------------------------------------------------------------ # __floordiv__, __rfloordiv__ @@ -510,10 +939,6 @@ class TestTimedeltaArraylikeMulDivOps(object): "m8[ns] instead of f8", strict=True)) ], ids=lambda x: x.__name__) - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=5, seconds=4), - Timedelta('5m4s'), - Timedelta('5m4s').to_timedelta64()]) def test_td64arr_floordiv_tdscalar(self, box, scalar_td): # GH#18831 td1 = Series([timedelta(minutes=5, seconds=3)] * 3) @@ -534,10 +959,6 @@ def test_td64arr_floordiv_tdscalar(self, box, scalar_td): marks=pytest.mark.xfail(reason="Incorrectly casts to f8", strict=True)) ], ids=lambda x: x.__name__) - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=5, seconds=4), - Timedelta('5m4s'), - Timedelta('5m4s').to_timedelta64()]) def test_td64arr_rfloordiv_tdscalar(self, box, scalar_td): # GH#18831 td1 = Series([timedelta(minutes=5, seconds=3)] * 3) @@ -559,10 +980,6 @@ def test_td64arr_rfloordiv_tdscalar(self, box, scalar_td): "instead of f8", strict=True)) ], ids=lambda x: x.__name__) - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=5, seconds=4), - Timedelta('5m4s'), - Timedelta('5m4s').to_timedelta64()]) def test_td64arr_rfloordiv_tdscalar_explicit(self, box, scalar_td): # GH#18831 td1 = Series([timedelta(minutes=5, seconds=3)] * 3) @@ -578,8 +995,50 @@ def test_td64arr_rfloordiv_tdscalar_explicit(self, box, scalar_td): result = td1.__rfloordiv__(scalar_td) tm.assert_equal(result, expected) + def test_td64arr_floordiv_int(self, box_df_fail): + box = box_df_fail # DataFrame returns object dtype + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + idx = tm.box_expected(idx, box) + result = idx // 1 + tm.assert_equal(result, idx) + + def test_td64arr_floordiv_tdlike_scalar(self, delta, box_df_fail): + box = box_df_fail # DataFrame returns m8[ns] instead of int64 dtype + tdi = timedelta_range('1 days', '10 days', name='foo') + expected = pd.Int64Index((np.arange(10) + 1) * 12, name='foo') + + tdi = tm.box_expected(tdi, box) + expected = tm.box_expected(expected, box) + + result = tdi // delta + tm.assert_equal(result, expected) + + # TODO: Is this redundant with test_td64arr_floordiv_tdlike_scalar? + @pytest.mark.parametrize('scalar_td', [ + timedelta(minutes=10, seconds=7), + Timedelta('10m7s'), + Timedelta('10m7s').to_timedelta64() + ], ids=lambda x: type(x).__name__) + def test_td64arr_rfloordiv_tdlike_scalar(self, scalar_td, box_df_fail): + # GH#19125 + box = box_df_fail # DataFrame op returns m8[ns] instead of f8 dtype + tdi = TimedeltaIndex(['00:05:03', '00:05:03', pd.NaT], freq=None) + expected = pd.Index([2.0, 2.0, np.nan]) + + tdi = tm.box_expected(tdi, box) + expected = tm.box_expected(expected, box) + + res = tdi.__rfloordiv__(scalar_td) + tm.assert_equal(res, expected) + + expected = pd.Index([0.0, 0.0, np.nan]) + expected = tm.box_expected(expected, box) + + res = tdi // (scalar_td) + tm.assert_equal(res, expected) + # ------------------------------------------------------------------ - # Operations with timedelta-like others + # Operations with invalid others @pytest.mark.parametrize('box', [ pd.Index, @@ -590,10 +1049,6 @@ def test_td64arr_rfloordiv_tdscalar_explicit(self, box, scalar_td): "rmul OK", strict=True)) ], ids=lambda x: x.__name__) - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=5, seconds=4), - Timedelta('5m4s'), - Timedelta('5m4s').to_timedelta64()]) def test_td64arr_mul_tdscalar_invalid(self, box, scalar_td): td1 = Series([timedelta(minutes=5, seconds=3)] * 3) td1.iloc[2] = np.nan @@ -609,6 +1064,20 @@ def test_td64arr_mul_tdscalar_invalid(self, box, scalar_td): with tm.assert_raises_regex(TypeError, pattern): scalar_td * td1 + def test_td64arr_mul_too_short_raises(self, box): + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + idx = tm.box_expected(idx, box) + with pytest.raises(TypeError): + idx * idx[:3] + with pytest.raises(ValueError): + idx * np.array([1, 2]) + + def test_td64arr_mul_td64arr_raises(self, box): + idx = TimedeltaIndex(np.arange(5, dtype='int64')) + idx = tm.box_expected(idx, box) + with pytest.raises(TypeError): + idx * idx + # ------------------------------------------------------------------ # Operations with numeric others @@ -675,8 +1144,10 @@ def test_td64arr_div_numeric_scalar(self, box, two, tdser): 'float64', 'float32', 'float16']) @pytest.mark.parametrize('vector', [np.array([20, 30, 40]), pd.Index([20, 30, 40]), - Series([20, 30, 40])]) - def test_td64arr_mul_numeric_array(self, box, vector, dtype, 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, vector, dtype, tdser): # GH#4521 # divide/multiply by integers vector = vector.astype(dtype) @@ -689,7 +1160,7 @@ def test_td64arr_mul_numeric_array(self, box, vector, dtype, tdser): box = Series if (box is pd.Index and type(vector) is Series) else box expected = tm.box_expected(expected, box) - result = tdser * vector + result = op(vector, tdser) tm.assert_equal(result, expected) @pytest.mark.parametrize('box', [ @@ -707,35 +1178,6 @@ def test_td64arr_mul_numeric_array(self, box, vector, dtype, tdser): pd.Index([20, 30, 40]), Series([20, 30, 40])], ids=lambda x: type(x).__name__) - def test_td64arr_rmul_numeric_array(self, box, vector, dtype, tdser): - # GH#4521 - # divide/multiply by integers - vector = vector.astype(dtype) - - expected = Series(['1180 Days', '1770 Days', 'NaT'], - dtype='timedelta64[ns]') - - tdser = tm.box_expected(tdser, box) - box = Series if (box is pd.Index and type(vector) is Series) else box - expected = tm.box_expected(expected, box) - - result = 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']) - @pytest.mark.parametrize('vector', [np.array([20, 30, 40]), - pd.Index([20, 30, 40]), - Series([20, 30, 40])]) def test_td64arr_div_numeric_array(self, box, vector, dtype, tdser): # GH#4521 # divide/multiply by integers @@ -787,11 +1229,6 @@ def test_td64arr_mul_int_series(self, box, names): tm.assert_equal(result, expected) # TODO: Should we be parametrizing over types for `ser` too? - @pytest.mark.parametrize('box', [ - pd.Index, - Series, - pd.DataFrame - ], ids=lambda x: x.__name__) @pytest.mark.parametrize('names', [(None, None, None), ('Egon', 'Venkman', None), ('NCC1701D', 'NCC1701D', 'NCC1701D')]) @@ -829,10 +1266,6 @@ class TestTimedeltaArraylikeInvalidArithmeticOps(object): "instead of TypeError", strict=True)) ]) - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=5, seconds=4), - Timedelta('5m4s'), - Timedelta('5m4s').to_timedelta64()]) def test_td64arr_pow_invalid(self, scalar_td, box): td1 = Series([timedelta(minutes=5, seconds=3)] * 3) td1.iloc[2] = np.nan @@ -848,3 +1281,231 @@ def test_td64arr_pow_invalid(self, scalar_td, box): with tm.assert_raises_regex(TypeError, pattern): td1 ** scalar_td + + +# ------------------------------------------------------------------ + +@pytest.fixture(params=[pd.Float64Index(np.arange(5, dtype='float64')), + pd.Int64Index(np.arange(5, dtype='int64')), + pd.UInt64Index(np.arange(5, dtype='uint64'))], + ids=lambda x: type(x).__name__) +def idx(request): + return request.param + + +zeros = [box_cls([0] * 5, dtype=dtype) + for box_cls in [pd.Index, np.array] + for dtype in [np.int64, np.uint64, np.float64]] +zeros.extend([np.array(0, dtype=dtype) + for dtype in [np.int64, np.uint64, np.float64]]) +zeros.extend([0, 0.0, long(0)]) + + +@pytest.fixture(params=zeros) +def zero(request): + # For testing division by (or of) zero for Index with length 5, this + # gives several scalar-zeros and length-5 vector-zeros + return request.param + + +class TestDivisionByZero(object): + + def test_div_zero(self, zero, idx): + expected = pd.Index([np.nan, np.inf, np.inf, np.inf, np.inf], + dtype=np.float64) + result = idx / zero + tm.assert_index_equal(result, expected) + ser_compat = Series(idx).astype('i8') / np.array(zero).astype('i8') + tm.assert_series_equal(ser_compat, Series(result)) + + def test_floordiv_zero(self, zero, idx): + expected = pd.Index([np.nan, np.inf, np.inf, np.inf, np.inf], + dtype=np.float64) + + result = idx // zero + tm.assert_index_equal(result, expected) + ser_compat = Series(idx).astype('i8') // np.array(zero).astype('i8') + tm.assert_series_equal(ser_compat, Series(result)) + + def test_mod_zero(self, zero, idx): + expected = pd.Index([np.nan, np.nan, np.nan, np.nan, np.nan], + dtype=np.float64) + result = idx % zero + tm.assert_index_equal(result, expected) + ser_compat = Series(idx).astype('i8') % np.array(zero).astype('i8') + tm.assert_series_equal(ser_compat, Series(result)) + + def test_divmod_zero(self, zero, idx): + + exleft = pd.Index([np.nan, np.inf, np.inf, np.inf, np.inf], + dtype=np.float64) + exright = pd.Index([np.nan, np.nan, np.nan, np.nan, np.nan], + dtype=np.float64) + + result = divmod(idx, zero) + tm.assert_index_equal(result[0], exleft) + tm.assert_index_equal(result[1], exright) + + # ------------------------------------------------------------------ + + @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').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, + dtype='float64', name=None) + expected.iloc[0:3] = np.inf + + result = first / second + tm.assert_series_equal(result, expected) + assert not result.equals(second / first) + + def test_rdiv_zero_compat(self): + # GH#8674 + zero_array = np.array([0] * 5) + data = np.random.randn(5) + expected = Series([0.] * 5) + + result = zero_array / Series(data) + tm.assert_series_equal(result, expected) + + result = Series(zero_array) / data + tm.assert_series_equal(result, expected) + + result = Series(zero_array) / Series(data) + tm.assert_series_equal(result, expected) + + 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 + tm.assert_series_equal(result, expected) + + def test_rdiv_zero(self): + # GH#9144 + ser = Series([-1, 0, 1], name='first') + expected = Series([0.0, np.nan, 0.0], name='first') + + result = 0 / ser + tm.assert_series_equal(result, expected) + + def test_floordiv_div(self): + # GH#9144 + ser = Series([-1, 0, 1], name='first') + + result = ser // 0 + expected = Series([-np.inf, np.nan, np.inf], name='first') + tm.assert_series_equal(result, expected) + + def test_df_div_zero_df(self): + # integer div, but deal with the 0's (GH#9144) + df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) + result = df / df + + first = pd.Series([1.0, 1.0, 1.0, 1.0]) + second = pd.Series([np.nan, np.nan, np.nan, 1]) + expected = pd.DataFrame({'first': first, 'second': second}) + tm.assert_frame_equal(result, expected) + + def test_df_div_zero_array(self): + # integer div, but deal with the 0's (GH#9144) + df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) + + first = pd.Series([1.0, 1.0, 1.0, 1.0]) + second = pd.Series([np.nan, np.nan, np.nan, 1]) + expected = pd.DataFrame({'first': first, 'second': second}) + + with np.errstate(all='ignore'): + arr = df.values.astype('float') / df.values + result = pd.DataFrame(arr, index=df.index, + columns=df.columns) + tm.assert_frame_equal(result, expected) + + def test_df_div_zero_int(self): + # integer div, but deal with the 0's (GH#9144) + df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) + + result = df / 0 + expected = pd.DataFrame(np.inf, index=df.index, columns=df.columns) + expected.iloc[0:3, 1] = np.nan + tm.assert_frame_equal(result, expected) + + # numpy has a slightly different (wrong) treatment + with np.errstate(all='ignore'): + arr = df.values.astype('float64') / 0 + result2 = pd.DataFrame(arr, index=df.index, + columns=df.columns) + tm.assert_frame_equal(result2, expected) + + def test_df_div_zero_series_does_not_commute(self): + # integer div, but deal with the 0's (GH#9144) + df = pd.DataFrame(np.random.randn(10, 5)) + ser = df[0] + res = ser / df + res2 = df / ser + assert not res.fillna(0).equals(res2.fillna(0)) + + # ------------------------------------------------------------------ + # Mod By Zero + + def test_df_mod_zero_df(self): + # GH#3590, modulo as ints + df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) + + # this is technically wrong, as the integer portion is coerced to float + # ### + first = pd.Series([0, 0, 0, 0], dtype='float64') + second = pd.Series([np.nan, np.nan, np.nan, 0]) + expected = pd.DataFrame({'first': first, 'second': second}) + result = df % df + tm.assert_frame_equal(result, expected) + + def test_df_mod_zero_array(self): + # GH#3590, modulo as ints + df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) + + # this is technically wrong, as the integer portion is coerced to float + # ### + first = pd.Series([0, 0, 0, 0], dtype='float64') + second = pd.Series([np.nan, np.nan, np.nan, 0]) + expected = pd.DataFrame({'first': first, 'second': second}) + + # numpy has a slightly different (wrong) treatment + with np.errstate(all='ignore'): + arr = df.values % df.values + result2 = pd.DataFrame(arr, index=df.index, + columns=df.columns, dtype='float64') + result2.iloc[0:3, 1] = np.nan + tm.assert_frame_equal(result2, expected) + + def test_df_mod_zero_int(self): + # GH#3590, modulo as ints + df = pd.DataFrame({'first': [3, 4, 5, 8], 'second': [0, 0, 0, 3]}) + + result = df % 0 + expected = pd.DataFrame(np.nan, index=df.index, columns=df.columns) + tm.assert_frame_equal(result, expected) + + # numpy has a slightly different (wrong) treatment + with np.errstate(all='ignore'): + arr = df.values.astype('float64') % 0 + result2 = pd.DataFrame(arr, index=df.index, columns=df.columns) + tm.assert_frame_equal(result2, expected) + + def test_df_mod_zero_series_does_not_commute(self): + # GH#3590, modulo as ints + # not commutative with series + df = pd.DataFrame(np.random.randn(10, 5)) + ser = df[0] + res = ser % df + res2 = df % ser + assert not res.fillna(0).equals(res2.fillna(0))