From 42fe86a0a6e314d66094216a77587686ba226e05 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 8 Aug 2018 14:10:41 -0700 Subject: [PATCH 1/3] implement test_numeric --- pandas/tests/arithmetic/conftest.py | 36 +++ pandas/tests/arithmetic/test_numeric.py | 288 +++++++++++++++++++++++ pandas/tests/test_arithmetic.py | 300 ------------------------ 3 files changed, 324 insertions(+), 300 deletions(-) create mode 100644 pandas/tests/arithmetic/test_numeric.py diff --git a/pandas/tests/arithmetic/conftest.py b/pandas/tests/arithmetic/conftest.py index 1e25d4a5224ee..844472b8bcf0d 100644 --- a/pandas/tests/arithmetic/conftest.py +++ b/pandas/tests/arithmetic/conftest.py @@ -4,6 +4,8 @@ import numpy as np import pandas as pd +from pandas.compat import long + @pytest.fixture(params=[1, np.array(1, dtype=np.int64)]) def one(request): @@ -11,6 +13,40 @@ def one(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 + + +@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 + + +@pytest.fixture(params=[pd.Timedelta('5m4s').to_pytimedelta(), + pd.Timedelta('5m4s'), + pd.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, pd.Series, pd.DataFrame], diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py new file mode 100644 index 0000000000000..93341f5349b1e --- /dev/null +++ b/pandas/tests/arithmetic/test_numeric.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# Arithmetc tests for DataFrame/Series/Index/Array classes that should +# behave identically. +# Specifically for numeric dtypes +from datetime import timedelta + +import pytest +import numpy as np + +import pandas as pd +import pandas.util.testing as tm +from pandas import Timedelta, Series, TimedeltaIndex + + +# ------------------------------------------------------------------ +# Numeric dtypes Arithmetic with Timedelta Scalar + +class TestNumericArraylikeArithmeticWithTimedeltaScalar(object): + + @pytest.mark.parametrize('box', [ + pd.Index, + Series, + pytest.param(pd.DataFrame, + marks=pytest.mark.xfail(reason="block.eval incorrect", + strict=True)) + ]) + @pytest.mark.parametrize('index', [ + 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__) + @pytest.mark.parametrize('scalar_td', [ + Timedelta(days=1), + Timedelta(days=1).to_timedelta64(), + Timedelta(days=1).to_pytimedelta()], + ids=lambda x: type(x).__name__) + def test_numeric_arr_mul_tdscalar(self, scalar_td, index, box): + # GH#19333 + + if (box is Series and + 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') + + index = tm.box_expected(index, box) + expected = tm.box_expected(expected, box) + + result = index * scalar_td + tm.assert_equal(result, expected) + + commute = scalar_td * index + tm.assert_equal(commute, expected) + + @pytest.mark.parametrize('index', [ + pd.Int64Index(range(1, 3)), + pd.UInt64Index(range(1, 3)), + pd.Float64Index(range(1, 3)), + pd.RangeIndex(1, 3)], + ids=lambda x: type(x).__name__) + @pytest.mark.parametrize('scalar_td', [ + Timedelta(days=1), + Timedelta(days=1).to_timedelta64(), + Timedelta(days=1).to_pytimedelta()], + ids=lambda x: type(x).__name__) + def test_numeric_arr_rdiv_tdscalar(self, scalar_td, index, box): + + if box is Series and type(scalar_td) is timedelta: + raise pytest.xfail(reason="TODO: Figure out why this case fails") + if box is pd.DataFrame and isinstance(scalar_td, timedelta): + raise pytest.xfail(reason="TODO: Figure out why this case fails") + + expected = TimedeltaIndex(['1 Day', '12 Hours']) + + index = tm.box_expected(index, box) + expected = tm.box_expected(expected, box) + + result = scalar_td / index + tm.assert_equal(result, expected) + + with pytest.raises(TypeError): + index / scalar_td + + +# ------------------------------------------------------------------ + +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)) diff --git a/pandas/tests/test_arithmetic.py b/pandas/tests/test_arithmetic.py index 0d62a5db581fe..4f6d3cf270e9f 100644 --- a/pandas/tests/test_arithmetic.py +++ b/pandas/tests/test_arithmetic.py @@ -10,7 +10,6 @@ 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, PerformanceWarning from pandas._libs.tslibs import IncompatibleFrequency @@ -73,77 +72,6 @@ def box_df_fail(request): return request.param -# ------------------------------------------------------------------ -# Numeric dtypes Arithmetic with Timedelta Scalar - -class TestNumericArraylikeArithmeticWithTimedeltaScalar(object): - - @pytest.mark.parametrize('box', [ - pd.Index, - Series, - pytest.param(pd.DataFrame, - marks=pytest.mark.xfail(reason="block.eval incorrect", - strict=True)) - ]) - @pytest.mark.parametrize('index', [ - 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__) - @pytest.mark.parametrize('scalar_td', [ - Timedelta(days=1), - Timedelta(days=1).to_timedelta64(), - Timedelta(days=1).to_pytimedelta()], - ids=lambda x: type(x).__name__) - def test_numeric_arr_mul_tdscalar(self, scalar_td, index, box): - # GH#19333 - - if (box is Series and - type(scalar_td) is timedelta and index.dtype == 'f8'): - raise pytest.xfail(reason="Cannot multiply timedelta by float") - - expected = timedelta_range('1 days', '10 days') - - index = tm.box_expected(index, box) - expected = tm.box_expected(expected, box) - - result = index * scalar_td - tm.assert_equal(result, expected) - - commute = scalar_td * index - tm.assert_equal(commute, expected) - - @pytest.mark.parametrize('index', [ - pd.Int64Index(range(1, 3)), - pd.UInt64Index(range(1, 3)), - pd.Float64Index(range(1, 3)), - pd.RangeIndex(1, 3)], - ids=lambda x: type(x).__name__) - @pytest.mark.parametrize('scalar_td', [ - Timedelta(days=1), - Timedelta(days=1).to_timedelta64(), - Timedelta(days=1).to_pytimedelta()], - ids=lambda x: type(x).__name__) - def test_numeric_arr_rdiv_tdscalar(self, scalar_td, index, box): - - if box is Series and type(scalar_td) is timedelta: - raise pytest.xfail(reason="TODO: Figure out why this case fails") - if box is pd.DataFrame and isinstance(scalar_td, timedelta): - raise pytest.xfail(reason="TODO: Figure out why this case fails") - - expected = TimedeltaIndex(['1 Day', '12 Hours']) - - index = tm.box_expected(index, box) - expected = tm.box_expected(expected, box) - - result = scalar_td / index - tm.assert_equal(result, expected) - - with pytest.raises(TypeError): - index / scalar_td - - # ------------------------------------------------------------------ # Timedelta64[ns] dtype Arithmetic Operations @@ -1281,231 +1209,3 @@ 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)) From 6a5b6aaff2ee627871ba96b9f24d22699a4ed78b Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 8 Aug 2018 14:38:14 -0700 Subject: [PATCH 2/3] implement test_object --- pandas/tests/arithmetic/test_numeric.py | 102 ++++++++++++++++++++++++ pandas/tests/arithmetic/test_object.py | 73 +++++++++++++++++ pandas/tests/frame/test_arithmetic.py | 37 --------- pandas/tests/series/test_arithmetic.py | 87 +------------------- 4 files changed, 176 insertions(+), 123 deletions(-) create mode 100644 pandas/tests/arithmetic/test_object.py diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index 93341f5349b1e..fe0086096cbd9 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -3,6 +3,7 @@ # behave identically. # Specifically for numeric dtypes from datetime import timedelta +from decimal import Decimal import pytest import numpy as np @@ -12,6 +13,10 @@ from pandas import Timedelta, Series, TimedeltaIndex +# ------------------------------------------------------------------ +# Comparisons + + # ------------------------------------------------------------------ # Numeric dtypes Arithmetic with Timedelta Scalar @@ -84,6 +89,7 @@ def test_numeric_arr_rdiv_tdscalar(self, scalar_td, index, box): # ------------------------------------------------------------------ +# Arithmetic class TestDivisionByZero(object): @@ -286,3 +292,99 @@ def test_df_mod_zero_series_does_not_commute(self): res = ser % df res2 = df % ser assert not res.fillna(0).equals(res2.fillna(0)) + + +class TestDivision(object): + # __div__, __rdiv__, __floordiv__, __rfloordiv__ + # for non-timestamp/timedelta/period dtypes + + @pytest.mark.parametrize('box', [ + pytest.param(pd.Index, + marks=pytest.mark.xfail(reason="Index.__div__ always " + "raises", + raises=TypeError, strict=True)), + pd.Series, + pd.DataFrame + ], ids=lambda x: x.__name__) + def test_divide_decimal(self, box): + # resolves issue GH#9787 + ser = Series([Decimal(10)]) + expected = Series([Decimal(5)]) + + ser = tm.box_expected(ser, box) + expected = tm.box_expected(expected, box) + + result = ser / Decimal(2) + + tm.assert_equal(result, expected) + + result = ser // Decimal(2) + tm.assert_equal(result, expected) + + def test_div_equiv_binop(self): + # Test Series.div as well as Series.__div__ + # float/integer issue + # GH#7785 + first = Series([1, 0], name='first') + second = Series([-0.01, -0.02], name='second') + expected = Series([-0.01, -np.inf]) + + result = second.div(first) + tm.assert_series_equal(result, expected, check_names=False) + + result = second / first + tm.assert_series_equal(result, expected) + + +class TestAdditionSubtraction(object): + # __add__, __sub__, __radd__, __rsub__, __iadd__, __isub__ + # for non-timestamp/timedelta/period dtypes + pass + + +class TestObjectDtypeEquivalence(object): + # Tests that arithmetic operations match operations executed elementwise + + @pytest.mark.parametrize('dtype', [None, object]) + def test_series_with_dtype_radd_nan(self, dtype): + ser = pd.Series([1, 2, 3], dtype=dtype) + expected = pd.Series([np.nan, np.nan, np.nan], dtype=dtype) + + result = np.nan + ser + tm.assert_series_equal(result, expected) + + result = ser + np.nan + tm.assert_series_equal(result, expected) + + @pytest.mark.parametrize('dtype', [None, object]) + def test_series_with_dtype_radd_int(self, dtype): + ser = pd.Series([1, 2, 3], dtype=dtype) + expected = pd.Series([2, 3, 4], dtype=dtype) + + result = 1 + ser + tm.assert_series_equal(result, expected) + + result = ser + 1 + tm.assert_series_equal(result, expected) + + @pytest.mark.parametrize('dtype', [None, object]) + def test_df_with_dtype_radd_nan(self, dtype): + df = pd.DataFrame([1, 2, 3], dtype=dtype) + expected = pd.DataFrame([np.nan, np.nan, np.nan], dtype=dtype) + + result = np.nan + df + tm.assert_frame_equal(result, expected) + + result = df + np.nan + tm.assert_frame_equal(result, expected) + + @pytest.mark.parametrize('dtype', [None, object]) + def test_df_with_dtype_radd_int(self, dtype): + df = pd.DataFrame([1, 2, 3], dtype=dtype) + expected = pd.DataFrame([2, 3, 4], dtype=dtype) + + result = 1 + df + tm.assert_frame_equal(result, expected) + + result = df + 1 + tm.assert_frame_equal(result, expected) diff --git a/pandas/tests/arithmetic/test_object.py b/pandas/tests/arithmetic/test_object.py new file mode 100644 index 0000000000000..059feb07f0974 --- /dev/null +++ b/pandas/tests/arithmetic/test_object.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Arithmetc tests for DataFrame/Series/Index/Array classes that should +# behave identically. +# Specifically for object dtype + +import pytest +import numpy as np + +import pandas as pd +import pandas.util.testing as tm + +from pandas import Series, Timestamp + + +# ------------------------------------------------------------------ +# Comparisons + + +# ------------------------------------------------------------------ +# Arithmetic + +class TestArithmetic(object): + def test_df_radd_str(self): + df = pd.DataFrame(['x', np.nan, 'x']) + + expected = pd.DataFrame(['ax', np.nan, 'ax']) + result = 'a' + df + tm.assert_frame_equal(result, expected) + + expected = pd.DataFrame(['xa', np.nan, 'xa']) + result = df + 'a' + tm.assert_frame_equal(result, expected) + + def test_series_radd_str(self): + ser = pd.Series(['x', np.nan, 'x']) + tm.assert_series_equal('a' + ser, pd.Series(['ax', np.nan, 'ax'])) + tm.assert_series_equal(ser + 'a', pd.Series(['xa', np.nan, 'xa'])) + + @pytest.mark.parametrize('data', [ + [1, 2, 3], + [1.1, 2.2, 3.3], + [pd.Timestamp('2011-01-01'), pd.Timestamp('2011-01-02'), pd.NaT], + ['x', 'y', 1]]) + @pytest.mark.parametrize('dtype', [None, object]) + def test_df_radd_str_invalid(self, dtype, data): + df = pd.DataFrame(data, dtype=dtype) + with pytest.raises(TypeError): + 'foo_' + df + + @pytest.mark.parametrize('data', [ + [1, 2, 3], + [1.1, 2.2, 3.3], + [Timestamp('2011-01-01'), Timestamp('2011-01-02'), pd.NaT], + ['x', 'y', 1]]) + @pytest.mark.parametrize('dtype', [None, object]) + def test_series_radd_str_invalid(self, dtype, data): + ser = Series(data, dtype=dtype) + with pytest.raises(TypeError): + 'foo_' + ser + + # TODO: parametrize, better name + def test_object_ser_add_invalid(self): + # invalid ops + obj_ser = tm.makeObjectSeries() + obj_ser.name = 'objects' + with pytest.raises(Exception): + obj_ser + 1 + with pytest.raises(Exception): + obj_ser + np.array(1, dtype=np.int64) + with pytest.raises(Exception): + obj_ser - 1 + with pytest.raises(Exception): + obj_ser - np.array(1, dtype=np.int64) diff --git a/pandas/tests/frame/test_arithmetic.py b/pandas/tests/frame/test_arithmetic.py index 20c547382ad57..1c677cebd3bb0 100644 --- a/pandas/tests/frame/test_arithmetic.py +++ b/pandas/tests/frame/test_arithmetic.py @@ -78,40 +78,3 @@ def test_df_add_flex_filled_mixed_dtypes(self): dtype='datetime64[ns]'), 'B': ser * 2}) tm.assert_frame_equal(result, expected) - - -class TestFrameArithmetic(object): - - @pytest.mark.parametrize('data', [ - [1, 2, 3], - [1.1, 2.2, 3.3], - [pd.Timestamp('2011-01-01'), pd.Timestamp('2011-01-02'), pd.NaT], - ['x', 'y', 1]]) - @pytest.mark.parametrize('dtype', [None, object]) - def test_df_radd_str_invalid(self, dtype, data): - df = pd.DataFrame(data, dtype=dtype) - with pytest.raises(TypeError): - 'foo_' + df - - @pytest.mark.parametrize('dtype', [None, object]) - def test_df_with_dtype_radd_int(self, dtype): - df = pd.DataFrame([1, 2, 3], dtype=dtype) - expected = pd.DataFrame([2, 3, 4], dtype=dtype) - result = 1 + df - tm.assert_frame_equal(result, expected) - result = df + 1 - tm.assert_frame_equal(result, expected) - - @pytest.mark.parametrize('dtype', [None, object]) - def test_df_with_dtype_radd_nan(self, dtype): - df = pd.DataFrame([1, 2, 3], dtype=dtype) - expected = pd.DataFrame([np.nan, np.nan, np.nan], dtype=dtype) - result = np.nan + df - tm.assert_frame_equal(result, expected) - result = df + np.nan - tm.assert_frame_equal(result, expected) - - def test_df_radd_str(self): - df = pd.DataFrame(['x', np.nan, 'x']) - tm.assert_frame_equal('a' + df, pd.DataFrame(['ax', np.nan, 'ax'])) - tm.assert_frame_equal(df + 'a', pd.DataFrame(['xa', np.nan, 'xa'])) diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index 9ec9cbd8e1b89..15b30ec2c774a 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -1,12 +1,11 @@ # -*- coding: utf-8 -*- from datetime import timedelta import operator -from decimal import Decimal import numpy as np import pytest -from pandas import Series, Timestamp +from pandas import Series import pandas as pd import pandas.util.testing as tm @@ -90,92 +89,8 @@ def test_compare_timedelta_series(self): # ------------------------------------------------------------------ # Arithmetic -class TestSeriesDivision(object): - # __div__, __rdiv__, __floordiv__, __rfloordiv__ - # for non-timestamp/timedelta/period dtypes - - def test_divide_decimal(self): - # resolves issue GH#9787 - expected = Series([Decimal(5)]) - - ser = Series([Decimal(10)]) - result = ser / Decimal(2) - - tm.assert_series_equal(result, expected) - - ser = Series([Decimal(10)]) - result = ser // Decimal(2) - - tm.assert_series_equal(result, expected) - - def test_div_equiv_binop(self): - # Test Series.div as well as Series.__div__ - # float/integer issue - # GH#7785 - first = Series([1, 0], name='first') - second = Series([-0.01, -0.02], name='second') - expected = Series([-0.01, -np.inf]) - - result = second.div(first) - tm.assert_series_equal(result, expected, check_names=False) - - result = second / first - tm.assert_series_equal(result, expected) - - class TestSeriesArithmetic(object): # Standard, numeric, or otherwise not-Timestamp/Timedelta/Period dtypes - @pytest.mark.parametrize('data', [ - [1, 2, 3], - [1.1, 2.2, 3.3], - [Timestamp('2011-01-01'), Timestamp('2011-01-02'), pd.NaT], - ['x', 'y', 1]]) - @pytest.mark.parametrize('dtype', [None, object]) - def test_series_radd_str_invalid(self, dtype, data): - ser = Series(data, dtype=dtype) - with pytest.raises(TypeError): - 'foo_' + ser - - # TODO: parametrize, better name - def test_object_ser_add_invalid(self): - # invalid ops - obj_ser = tm.makeObjectSeries() - obj_ser.name = 'objects' - with pytest.raises(Exception): - obj_ser + 1 - with pytest.raises(Exception): - obj_ser + np.array(1, dtype=np.int64) - with pytest.raises(Exception): - obj_ser - 1 - with pytest.raises(Exception): - obj_ser - np.array(1, dtype=np.int64) - - @pytest.mark.parametrize('dtype', [None, object]) - def test_series_with_dtype_radd_nan(self, dtype): - ser = pd.Series([1, 2, 3], dtype=dtype) - expected = pd.Series([np.nan, np.nan, np.nan], dtype=dtype) - - result = np.nan + ser - tm.assert_series_equal(result, expected) - - result = ser + np.nan - tm.assert_series_equal(result, expected) - - @pytest.mark.parametrize('dtype', [None, object]) - def test_series_with_dtype_radd_int(self, dtype): - ser = pd.Series([1, 2, 3], dtype=dtype) - expected = pd.Series([2, 3, 4], dtype=dtype) - - result = 1 + ser - tm.assert_series_equal(result, expected) - - result = ser + 1 - tm.assert_series_equal(result, expected) - - def test_series_radd_str(self): - ser = pd.Series(['x', np.nan, 'x']) - tm.assert_series_equal('a' + ser, pd.Series(['ax', np.nan, 'ax'])) - tm.assert_series_equal(ser + 'a', pd.Series(['xa', np.nan, 'xa'])) @pytest.mark.parametrize('dtype', [None, object]) def test_series_with_dtype_radd_timedelta(self, dtype): From d255a6b2b8dde6ea2876a745ef40ff97fabbcf53 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 8 Aug 2018 15:30:53 -0700 Subject: [PATCH 3/3] move more tests --- pandas/tests/arithmetic/test_datetime64.py | 351 ++++++++++++- pandas/tests/arithmetic/test_numeric.py | 150 +++++- pandas/tests/arithmetic/test_object.py | 38 ++ pandas/tests/indexes/test_numeric.py | 147 +----- .../indexes/timedeltas/test_arithmetic.py | 163 +----- pandas/tests/series/test_operators.py | 467 ------------------ pandas/tests/test_arithmetic.py | 250 ++++++++++ 7 files changed, 789 insertions(+), 777 deletions(-) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index b09d3c3183803..77e8da6c44af2 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -8,6 +8,7 @@ import numpy as np import pytest +import pytz import pandas as pd import pandas.util.testing as tm @@ -21,7 +22,7 @@ from pandas.core import ops from pandas import ( - Timestamp, Timedelta, Period, Series, date_range, + Timestamp, Timedelta, Period, Series, date_range, NaT, DatetimeIndex, TimedeltaIndex) @@ -63,6 +64,47 @@ def test_tz_aware_scalar_comparison(self, timestamps): class TestDatetime64SeriesComparison(object): + @pytest.mark.parametrize('data', [ + [Timestamp('2011-01-01'), NaT, Timestamp('2011-01-03')], + [Timedelta('1 days'), NaT, Timedelta('3 days')], + [Period('2011-01', freq='M'), NaT, Period('2011-03', freq='M')] + ]) + @pytest.mark.parametrize('dtype', [None, object]) + def test_nat_comparisons_scalar(self, dtype, data): + left = Series(data, dtype=dtype) + + expected = Series([False, False, False]) + tm.assert_series_equal(left == NaT, expected) + tm.assert_series_equal(NaT == left, expected) + + expected = Series([True, True, True]) + tm.assert_series_equal(left != NaT, expected) + tm.assert_series_equal(NaT != left, expected) + + expected = Series([False, False, False]) + tm.assert_series_equal(left < NaT, expected) + tm.assert_series_equal(NaT > left, expected) + tm.assert_series_equal(left <= NaT, expected) + tm.assert_series_equal(NaT >= left, expected) + + tm.assert_series_equal(left > NaT, expected) + tm.assert_series_equal(NaT < left, expected) + tm.assert_series_equal(left >= NaT, expected) + tm.assert_series_equal(NaT <= left, expected) + + def test_series_comparison_scalars(self): + series = Series(date_range('1/1/2000', periods=10)) + + val = datetime(2000, 1, 4) + result = series > val + expected = Series([x > val for x in series]) + tm.assert_series_equal(result, expected) + + val = series[5] + result = series > val + expected = Series([x > val for x in series]) + tm.assert_series_equal(result, expected) + def test_dt64_ser_cmp_date_warning(self): # https://github.com/pandas-dev/pandas/issues/21359 # Remove this test and enble invalid test below @@ -585,6 +627,7 @@ def test_df_sub_datetime64_not_ns(self): class TestTimestampSeriesArithmetic(object): + def test_timestamp_sub_series(self): ser = pd.Series(pd.date_range('2014-03-17', periods=2, freq='D', tz='US/Eastern')) @@ -602,6 +645,312 @@ def test_dt64ser_sub_datetime_dtype(self): result = pd.to_timedelta(np.abs(ser - dt)) assert result.dtype == 'timedelta64[ns]' + # ------------------------------------------------------------- + # TODO: This next block of tests came from tests.series.test_operators, + # needs to be de-duplicated and parametrized over `box` classes + + @pytest.mark.parametrize( + 'box, assert_func', + [(Series, tm.assert_series_equal), + (pd.Index, tm.assert_index_equal)]) + def test_sub_datetime64_not_ns(self, box, assert_func): + # GH#7996 + dt64 = np.datetime64('2013-01-01') + assert dt64.dtype == 'datetime64[D]' + + obj = box(date_range('20130101', periods=3)) + res = obj - dt64 + expected = box([Timedelta(days=0), Timedelta(days=1), + Timedelta(days=2)]) + assert_func(res, expected) + + res = dt64 - obj + assert_func(res, -expected) + + def test_sub_single_tz(self): + # GH12290 + s1 = Series([pd.Timestamp('2016-02-10', tz='America/Sao_Paulo')]) + s2 = Series([pd.Timestamp('2016-02-08', tz='America/Sao_Paulo')]) + result = s1 - s2 + expected = Series([Timedelta('2days')]) + tm.assert_series_equal(result, expected) + result = s2 - s1 + expected = Series([Timedelta('-2days')]) + tm.assert_series_equal(result, expected) + + def test_dt64tz_series_sub_dtitz(self): + # GH#19071 subtracting tzaware DatetimeIndex from tzaware Series + # (with same tz) raises, fixed by #19024 + dti = pd.date_range('1999-09-30', periods=10, tz='US/Pacific') + ser = pd.Series(dti) + expected = pd.Series(pd.TimedeltaIndex(['0days'] * 10)) + + res = dti - ser + tm.assert_series_equal(res, expected) + res = ser - dti + tm.assert_series_equal(res, expected) + + def test_sub_datetime_compat(self): + # see gh-14088 + s = Series([datetime(2016, 8, 23, 12, tzinfo=pytz.utc), pd.NaT]) + dt = datetime(2016, 8, 22, 12, tzinfo=pytz.utc) + exp = Series([Timedelta('1 days'), pd.NaT]) + tm.assert_series_equal(s - dt, exp) + tm.assert_series_equal(s - Timestamp(dt), exp) + + def test_dt64_series_addsub_timedelta(self): + # scalar timedeltas/np.timedelta64 objects + # operate with np.timedelta64 correctly + s = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) + + result = s + np.timedelta64(1, 's') + result2 = np.timedelta64(1, 's') + s + expected = Series([Timestamp('20130101 9:01:01'), + Timestamp('20130101 9:02:01')]) + tm.assert_series_equal(result, expected) + tm.assert_series_equal(result2, expected) + + result = s + np.timedelta64(5, 'ms') + result2 = np.timedelta64(5, 'ms') + s + expected = Series([Timestamp('20130101 9:01:00.005'), + Timestamp('20130101 9:02:00.005')]) + tm.assert_series_equal(result, expected) + tm.assert_series_equal(result2, expected) + + def test_dt64_series_add_tick_DateOffset(self): + # GH 4532 + # operate with pd.offsets + ser = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) + expected = Series([Timestamp('20130101 9:01:05'), + Timestamp('20130101 9:02:05')]) + + result = ser + pd.offsets.Second(5) + tm.assert_series_equal(result, expected) + + result2 = pd.offsets.Second(5) + ser + tm.assert_series_equal(result2, expected) + + def test_dt64_series_sub_tick_DateOffset(self): + # GH 4532 + # operate with pd.offsets + ser = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) + expected = Series([Timestamp('20130101 9:00:55'), + Timestamp('20130101 9:01:55')]) + + result = ser - pd.offsets.Second(5) + tm.assert_series_equal(result, expected) + + result2 = -pd.offsets.Second(5) + ser + tm.assert_series_equal(result2, expected) + + with pytest.raises(TypeError): + pd.offsets.Second(5) - ser + + @pytest.mark.parametrize('cls_name', ['Day', 'Hour', 'Minute', 'Second', + 'Milli', 'Micro', 'Nano']) + def test_dt64_series_add_tick_DateOffset_smoke(self, cls_name): + # GH 4532 + # smoke tests for valid DateOffsets + ser = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) + + offset_cls = getattr(pd.offsets, cls_name) + ser + offset_cls(5) + offset_cls(5) + ser + + def test_dt64_series_add_mixed_tick_DateOffset(self): + # GH 4532 + # operate with pd.offsets + s = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) + + result = s + pd.offsets.Milli(5) + result2 = pd.offsets.Milli(5) + s + expected = Series([Timestamp('20130101 9:01:00.005'), + Timestamp('20130101 9:02:00.005')]) + tm.assert_series_equal(result, expected) + tm.assert_series_equal(result2, expected) + + result = s + pd.offsets.Minute(5) + pd.offsets.Milli(5) + expected = Series([Timestamp('20130101 9:06:00.005'), + Timestamp('20130101 9:07:00.005')]) + tm.assert_series_equal(result, expected) + + def test_dt64_series_sub_NaT(self): + # GH#18808 + dti = pd.DatetimeIndex([pd.NaT, pd.Timestamp('19900315')]) + ser = pd.Series(dti) + res = ser - pd.NaT + expected = pd.Series([pd.NaT, pd.NaT], dtype='timedelta64[ns]') + tm.assert_series_equal(res, expected) + + dti_tz = dti.tz_localize('Asia/Tokyo') + ser_tz = pd.Series(dti_tz) + res = ser_tz - pd.NaT + expected = pd.Series([pd.NaT, pd.NaT], dtype='timedelta64[ns]') + tm.assert_series_equal(res, expected) + + def test_dt64_series_arith_overflow(self): + # GH#12534, fixed by #19024 + dt = pd.Timestamp('1700-01-31') + td = pd.Timedelta('20000 Days') + dti = pd.date_range('1949-09-30', freq='100Y', periods=4) + ser = pd.Series(dti) + with pytest.raises(OverflowError): + ser - dt + with pytest.raises(OverflowError): + dt - ser + with pytest.raises(OverflowError): + ser + td + with pytest.raises(OverflowError): + td + ser + + ser.iloc[-1] = pd.NaT + expected = pd.Series(['2004-10-03', '2104-10-04', '2204-10-04', 'NaT'], + dtype='datetime64[ns]') + res = ser + td + tm.assert_series_equal(res, expected) + res = td + ser + tm.assert_series_equal(res, expected) + + ser.iloc[1:] = pd.NaT + expected = pd.Series(['91279 Days', 'NaT', 'NaT', 'NaT'], + dtype='timedelta64[ns]') + res = ser - dt + tm.assert_series_equal(res, expected) + res = dt - ser + tm.assert_series_equal(res, -expected) + + def test_datetime64_ops_nat(self): + # GH 11349 + datetime_series = Series([NaT, Timestamp('19900315')]) + nat_series_dtype_timestamp = Series([NaT, NaT], dtype='datetime64[ns]') + single_nat_dtype_datetime = Series([NaT], dtype='datetime64[ns]') + + # subtraction + tm.assert_series_equal(-NaT + datetime_series, + nat_series_dtype_timestamp) + with pytest.raises(TypeError): + -single_nat_dtype_datetime + datetime_series + + tm.assert_series_equal(-NaT + nat_series_dtype_timestamp, + nat_series_dtype_timestamp) + with pytest.raises(TypeError): + -single_nat_dtype_datetime + nat_series_dtype_timestamp + + # addition + tm.assert_series_equal(nat_series_dtype_timestamp + NaT, + nat_series_dtype_timestamp) + tm.assert_series_equal(NaT + nat_series_dtype_timestamp, + nat_series_dtype_timestamp) + + tm.assert_series_equal(nat_series_dtype_timestamp + NaT, + nat_series_dtype_timestamp) + tm.assert_series_equal(NaT + nat_series_dtype_timestamp, + nat_series_dtype_timestamp) + + # ------------------------------------------------------------- + # Invalid Operations + # TODO: this block also needs to be de-duplicated and parametrized + + @pytest.mark.parametrize('dt64_series', [ + Series([Timestamp('19900315'), Timestamp('19900315')]), + Series([pd.NaT, Timestamp('19900315')]), + Series([pd.NaT, pd.NaT], dtype='datetime64[ns]')]) + @pytest.mark.parametrize('one', [1, 1.0, np.array(1)]) + def test_dt64_mul_div_numeric_invalid(self, one, dt64_series): + # multiplication + with pytest.raises(TypeError): + dt64_series * one + with pytest.raises(TypeError): + one * dt64_series + + # division + with pytest.raises(TypeError): + dt64_series / one + with pytest.raises(TypeError): + one / dt64_series + + @pytest.mark.parametrize('op', ['__add__', '__radd__', + '__sub__', '__rsub__']) + @pytest.mark.parametrize('tz', [None, 'Asia/Tokyo']) + def test_dt64_series_add_intlike(self, tz, op): + # GH#19123 + dti = pd.DatetimeIndex(['2016-01-02', '2016-02-03', 'NaT'], tz=tz) + ser = Series(dti) + + other = Series([20, 30, 40], dtype='uint8') + + pytest.raises(TypeError, getattr(ser, op), 1) + + pytest.raises(TypeError, getattr(ser, op), other) + + pytest.raises(TypeError, getattr(ser, op), other.values) + + pytest.raises(TypeError, getattr(ser, op), pd.Index(other)) + + # ------------------------------------------------------------- + # Timezone-Centric Tests + + def test_operators_datetimelike_with_timezones(self): + tz = 'US/Eastern' + dt1 = Series(date_range('2000-01-01 09:00:00', periods=5, + tz=tz), name='foo') + dt2 = dt1.copy() + dt2.iloc[2] = np.nan + + td1 = Series(pd.timedelta_range('1 days 1 min', periods=5, freq='H')) + td2 = td1.copy() + td2.iloc[1] = np.nan + + result = dt1 + td1[0] + exp = (dt1.dt.tz_localize(None) + td1[0]).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + result = dt2 + td2[0] + exp = (dt2.dt.tz_localize(None) + td2[0]).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + # odd numpy behavior with scalar timedeltas + result = td1[0] + dt1 + exp = (dt1.dt.tz_localize(None) + td1[0]).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + result = td2[0] + dt2 + exp = (dt2.dt.tz_localize(None) + td2[0]).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + result = dt1 - td1[0] + exp = (dt1.dt.tz_localize(None) - td1[0]).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + with pytest.raises(TypeError): + td1[0] - dt1 + + result = dt2 - td2[0] + exp = (dt2.dt.tz_localize(None) - td2[0]).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + with pytest.raises(TypeError): + td2[0] - dt2 + + result = dt1 + td1 + exp = (dt1.dt.tz_localize(None) + td1).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + result = dt2 + td2 + exp = (dt2.dt.tz_localize(None) + td2).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + result = dt1 - td1 + exp = (dt1.dt.tz_localize(None) - td1).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + result = dt2 - td2 + exp = (dt2.dt.tz_localize(None) - td2).dt.tz_localize(tz) + tm.assert_series_equal(result, exp) + + with pytest.raises(TypeError): + td1 - dt1 + with pytest.raises(TypeError): + td2 - dt2 + class TestDatetimeIndexArithmetic(object): diff --git a/pandas/tests/arithmetic/test_numeric.py b/pandas/tests/arithmetic/test_numeric.py index fe0086096cbd9..44aaba2885bf7 100644 --- a/pandas/tests/arithmetic/test_numeric.py +++ b/pandas/tests/arithmetic/test_numeric.py @@ -10,18 +10,51 @@ import pandas as pd import pandas.util.testing as tm -from pandas import Timedelta, Series, TimedeltaIndex + +from pandas.compat import PY3 +from pandas import Timedelta, Series, Index, TimedeltaIndex + + +@pytest.fixture(params=[pd.Float64Index(np.arange(5, dtype='float64')), + pd.UInt64Index(np.arange(5, dtype='uint64')), + pd.Int64Index(np.arange(5, dtype='int64')), + pd.RangeIndex(5)], + ids=lambda x: type(x).__name__) +def idx(request): + return request.param # ------------------------------------------------------------------ # Comparisons +class TestNumericComparisons(object): + def test_operator_series_comparison_zerorank(self): + # GH#13006 + result = np.float64(0) > pd.Series([1, 2, 3]) + expected = 0.0 > pd.Series([1, 2, 3]) + tm.assert_series_equal(result, expected) + result = pd.Series([1, 2, 3]) < np.float64(0) + expected = pd.Series([1, 2, 3]) < 0.0 + tm.assert_series_equal(result, expected) + result = np.array([0, 1, 2])[0] > pd.Series([0, 1, 2]) + expected = 0.0 > pd.Series([1, 2, 3]) + tm.assert_series_equal(result, expected) + # ------------------------------------------------------------------ # Numeric dtypes Arithmetic with Timedelta Scalar class TestNumericArraylikeArithmeticWithTimedeltaScalar(object): + # TODO: de-duplicate with test_numeric_arr_mul_tdscalar + def test_ops_series(self): + # regression test for G#H8813 + td = Timedelta('1 day') + other = pd.Series([1, 2]) + expected = pd.Series(pd.to_timedelta(['1 day', '2 days'])) + tm.assert_series_equal(expected, td * other) + tm.assert_series_equal(expected, other * td) + @pytest.mark.parametrize('box', [ pd.Index, Series, @@ -294,8 +327,8 @@ def test_df_mod_zero_series_does_not_commute(self): assert not res.fillna(0).equals(res2.fillna(0)) -class TestDivision(object): - # __div__, __rdiv__, __floordiv__, __rfloordiv__ +class TestMultiplicationDivision(object): + # __mul__, __rmul__, __div__, __rdiv__, __floordiv__, __rfloordiv__ # for non-timestamp/timedelta/period dtypes @pytest.mark.parametrize('box', [ @@ -335,6 +368,117 @@ def test_div_equiv_binop(self): result = second / first tm.assert_series_equal(result, expected) + def test_div_int(self, idx): + # truediv under PY3 + result = idx / 1 + expected = idx + if PY3: + expected = expected.astype('float64') + tm.assert_index_equal(result, expected) + + result = idx / 2 + if PY3: + expected = expected.astype('float64') + expected = Index(idx.values / 2) + tm.assert_index_equal(result, expected) + + def test_mul_int(self, idx): + result = idx * 1 + tm.assert_index_equal(result, idx) + + def test_rmul_int(self, idx): + result = 1 * idx + tm.assert_index_equal(result, idx) + + def test_floordiv_int(self, idx): + result = idx // 1 + tm.assert_index_equal(result, idx) + + def test_mul_int_array(self, idx): + didx = idx * idx + + result = idx * np.array(5, dtype='int64') + tm.assert_index_equal(result, idx * 5) + + arr_dtype = 'uint64' if isinstance(idx, pd.UInt64Index) else 'int64' + result = idx * np.arange(5, dtype=arr_dtype) + tm.assert_index_equal(result, didx) + + def test_mul_int_series(self, idx): + didx = idx * idx + + arr_dtype = 'uint64' if isinstance(idx, pd.UInt64Index) else 'int64' + result = idx * Series(np.arange(5, dtype=arr_dtype)) + tm.assert_series_equal(result, Series(didx)) + + def test_mul_float_series(self, idx): + rng5 = np.arange(5, dtype='float64') + + result = idx * Series(rng5 + 0.1) + expected = Series(rng5 * (rng5 + 0.1)) + tm.assert_series_equal(result, expected) + + def test_mul_index(self, idx): + # in general not true for RangeIndex + if not isinstance(idx, pd.RangeIndex): + result = idx * idx + tm.assert_index_equal(result, idx ** 2) + + def test_mul_datelike_raises(self, idx): + with pytest.raises(TypeError): + idx * pd.date_range('20130101', periods=5) + + def test_mul_size_mismatch_raises(self, idx): + with pytest.raises(ValueError): + idx * idx[0:3] + with pytest.raises(ValueError): + idx * np.array([1, 2]) + + def test_pow_float(self, idx): + # test power calculations both ways, GH#14973 + expected = pd.Float64Index(idx.values**2.0) + result = idx**2.0 + tm.assert_index_equal(result, expected) + + def test_rpow_float(self, idx): + # test power calculations both ways, GH#14973 + expected = pd.Float64Index(2.0**idx.values) + result = 2.0**idx + tm.assert_index_equal(result, expected) + + def test_modulo(self, idx): + # GH#9244 + expected = Index(idx.values % 2) + tm.assert_index_equal(idx % 2, expected) + + def test_divmod(self, idx): + result = divmod(idx, 2) + with np.errstate(all='ignore'): + div, mod = divmod(idx.values, 2) + expected = Index(div), Index(mod) + for r, e in zip(result, expected): + tm.assert_index_equal(r, e) + + other = np.ones(idx.values.shape, dtype=idx.values.dtype) * 2 + result = divmod(idx, other) + with np.errstate(all='ignore'): + div, mod = divmod(idx.values, other) + expected = Index(div), Index(mod) + for r, e in zip(result, expected): + tm.assert_index_equal(r, e) + + @pytest.mark.xfail(reason='GH#19252 Series has no __rdivmod__', + strict=True) + def test_divmod_series(self, idx): + other = np.ones(idx.values.shape, dtype=idx.values.dtype) * 2 + result = divmod(idx, Series(other)) + with np.errstate(all='ignore'): + div, mod = divmod(idx.values, other) + expected = Series(div), Series(mod) + + for r, e in zip(result, expected): + tm.assert_series_equal(r, e) + class TestAdditionSubtraction(object): # __add__, __sub__, __radd__, __rsub__, __iadd__, __isub__ diff --git a/pandas/tests/arithmetic/test_object.py b/pandas/tests/arithmetic/test_object.py index 059feb07f0974..e27ba6af39b90 100644 --- a/pandas/tests/arithmetic/test_object.py +++ b/pandas/tests/arithmetic/test_object.py @@ -15,6 +15,44 @@ # ------------------------------------------------------------------ # Comparisons +class TestObjectComparisons(object): + + def test_object_comparisons(self): + ser = Series(['a', 'b', np.nan, 'c', 'a']) + + result = ser == 'a' + expected = Series([True, False, False, False, True]) + tm.assert_series_equal(result, expected) + + result = ser < 'a' + expected = Series([False, False, False, False, False]) + tm.assert_series_equal(result, expected) + + result = ser != 'a' + expected = -(ser == 'a') + tm.assert_series_equal(result, expected) + + @pytest.mark.parametrize('dtype', [None, object]) + def test_more_na_comparisons(self, dtype): + left = Series(['a', np.nan, 'c'], dtype=dtype) + right = Series(['a', np.nan, 'd'], dtype=dtype) + + result = left == right + expected = Series([True, False, False]) + tm.assert_series_equal(result, expected) + + result = left != right + expected = Series([False, True, True]) + tm.assert_series_equal(result, expected) + + result = left == np.nan + expected = Series([False, False, False]) + tm.assert_series_equal(result, expected) + + result = left != np.nan + expected = Series([True, True, True]) + tm.assert_series_equal(result, expected) + # ------------------------------------------------------------------ # Arithmetic diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index 32edb2591b735..b6d61187a3fc3 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -3,12 +3,12 @@ import pytest from datetime import datetime -from pandas.compat import range, PY3 +from pandas.compat import range import numpy as np -from pandas import (date_range, Series, Index, Float64Index, - Int64Index, UInt64Index, RangeIndex) +from pandas import (Series, Index, Float64Index, + Int64Index, UInt64Index) import pandas.util.testing as tm @@ -18,14 +18,6 @@ from pandas.tests.indexes.common import Base -def full_like(array, value): - """Compatibility for numpy<1.8.0 - """ - ret = np.empty(array.shape, dtype=np.array(value).dtype) - ret.fill(value) - return ret - - class Numeric(Base): def test_can_hold_identifiers(self): @@ -36,133 +28,6 @@ def test_can_hold_identifiers(self): def test_numeric_compat(self): pass # override Base method - def test_mul_int(self): - idx = self.create_index() - result = idx * 1 - tm.assert_index_equal(result, idx) - - def test_rmul_int(self): - idx = self.create_index() - - result = 1 * idx - tm.assert_index_equal(result, idx) - - def test_div_int(self): - idx = self.create_index() - - # truediv under PY3 - result = idx / 1 - expected = idx - if PY3: - expected = expected.astype('float64') - tm.assert_index_equal(result, expected) - - result = idx / 2 - if PY3: - expected = expected.astype('float64') - expected = Index(idx.values / 2) - tm.assert_index_equal(result, expected) - - def test_floordiv_int(self): - idx = self.create_index() - - result = idx // 1 - tm.assert_index_equal(result, idx) - - def test_mul_int_array(self): - idx = self.create_index() - didx = idx * idx - - result = idx * np.array(5, dtype='int64') - tm.assert_index_equal(result, idx * 5) - - arr_dtype = 'uint64' if isinstance(idx, UInt64Index) else 'int64' - result = idx * np.arange(5, dtype=arr_dtype) - tm.assert_index_equal(result, didx) - - def test_mul_int_series(self): - idx = self.create_index() - didx = idx * idx - - arr_dtype = 'uint64' if isinstance(idx, UInt64Index) else 'int64' - result = idx * Series(np.arange(5, dtype=arr_dtype)) - tm.assert_series_equal(result, Series(didx)) - - def test_mul_float_series(self): - idx = self.create_index() - rng5 = np.arange(5, dtype='float64') - - result = idx * Series(rng5 + 0.1) - expected = Series(rng5 * (rng5 + 0.1)) - tm.assert_series_equal(result, expected) - - def test_mul_index(self): - idx = self.create_index() - - # in general not true for RangeIndex - if not isinstance(idx, RangeIndex): - result = idx * idx - tm.assert_index_equal(result, idx ** 2) - - def test_mul_datelike_raises(self): - idx = self.create_index() - with pytest.raises(TypeError): - idx * date_range('20130101', periods=5) - - def test_mul_size_mismatch_raises(self): - idx = self.create_index() - - with pytest.raises(ValueError): - idx * idx[0:3] - with pytest.raises(ValueError): - idx * np.array([1, 2]) - - def test_divmod(self): - idx = self.create_index() - - result = divmod(idx, 2) - with np.errstate(all='ignore'): - div, mod = divmod(idx.values, 2) - expected = Index(div), Index(mod) - for r, e in zip(result, expected): - tm.assert_index_equal(r, e) - - result = divmod(idx, full_like(idx.values, 2)) - with np.errstate(all='ignore'): - div, mod = divmod(idx.values, full_like(idx.values, 2)) - expected = Index(div), Index(mod) - for r, e in zip(result, expected): - tm.assert_index_equal(r, e) - - def test_pow_float(self): - # test power calculations both ways, GH 14973 - idx = self.create_index() - - expected = pd.Float64Index(idx.values**2.0) - result = idx**2.0 - tm.assert_index_equal(result, expected) - - def test_rpow_float(self): - # test power calculations both ways, GH 14973 - idx = self.create_index() - - expected = pd.Float64Index(2.0**idx.values) - result = 2.0**idx - tm.assert_index_equal(result, expected) - - @pytest.mark.xfail(reason='GH#19252 Series has no __rdivmod__', - strict=True) - def test_divmod_series(self): - idx = self.create_index() - - result = divmod(idx, Series(full_like(idx.values, 2))) - with np.errstate(all='ignore'): - div, mod = divmod(idx.values, full_like(idx.values, 2)) - expected = Series(div), Series(mod) - - for r, e in zip(result, expected): - tm.assert_series_equal(r, e) - def test_explicit_conversions(self): # GH 8608 @@ -218,12 +83,6 @@ def test_index_groupby(self): ex_keys[1]: idx[[1, 4]]} tm.assert_dict_equal(idx.groupby(to_groupby), expected) - def test_modulo(self): - # GH 9244 - index = self.create_index() - expected = Index(index.values % 2) - tm.assert_index_equal(index % 2, expected) - @pytest.mark.parametrize('klass', [list, tuple, np.array, Series]) def test_where(self, klass): i = self.create_index() diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index e60705075267f..21d895fa59021 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -8,7 +8,7 @@ import pandas as pd import pandas.util.testing as tm from pandas import (DatetimeIndex, TimedeltaIndex, Int64Index, - to_timedelta, timedelta_range, date_range, + timedelta_range, date_range, Series, Timestamp, Timedelta) from pandas.errors import NullFrequencyError @@ -27,103 +27,6 @@ def freq(request): return request.param -class TestTimedeltaIndexComparisons(object): - def test_tdi_cmp_str_invalid(self): - # GH 13624 - tdi = TimedeltaIndex(['1 day', '2 days']) - - for left, right in [(tdi, 'a'), ('a', tdi)]: - with pytest.raises(TypeError): - left > right - - with pytest.raises(TypeError): - left == right - - with pytest.raises(TypeError): - left != right - - def test_comparisons_coverage(self): - rng = timedelta_range('1 days', periods=10) - - result = rng < rng[3] - exp = np.array([True, True, True] + [False] * 7) - tm.assert_numpy_array_equal(result, exp) - - # raise TypeError for now - pytest.raises(TypeError, rng.__lt__, rng[3].value) - - result = rng == list(rng) - exp = rng == rng - tm.assert_numpy_array_equal(result, exp) - - def test_comp_nat(self): - left = pd.TimedeltaIndex([pd.Timedelta('1 days'), pd.NaT, - pd.Timedelta('3 days')]) - right = pd.TimedeltaIndex([pd.NaT, pd.NaT, pd.Timedelta('3 days')]) - - for lhs, rhs in [(left, right), - (left.astype(object), right.astype(object))]: - result = rhs == lhs - expected = np.array([False, False, True]) - tm.assert_numpy_array_equal(result, expected) - - result = rhs != lhs - expected = np.array([True, True, False]) - tm.assert_numpy_array_equal(result, expected) - - expected = np.array([False, False, False]) - tm.assert_numpy_array_equal(lhs == pd.NaT, expected) - tm.assert_numpy_array_equal(pd.NaT == rhs, expected) - - expected = np.array([True, True, True]) - tm.assert_numpy_array_equal(lhs != pd.NaT, expected) - tm.assert_numpy_array_equal(pd.NaT != lhs, expected) - - expected = np.array([False, False, False]) - tm.assert_numpy_array_equal(lhs < pd.NaT, expected) - tm.assert_numpy_array_equal(pd.NaT > lhs, expected) - - def test_comparisons_nat(self): - tdidx1 = pd.TimedeltaIndex(['1 day', pd.NaT, '1 day 00:00:01', pd.NaT, - '1 day 00:00:01', '5 day 00:00:03']) - tdidx2 = pd.TimedeltaIndex(['2 day', '2 day', pd.NaT, pd.NaT, - '1 day 00:00:02', '5 days 00:00:03']) - tdarr = np.array([np.timedelta64(2, 'D'), - np.timedelta64(2, 'D'), np.timedelta64('nat'), - np.timedelta64('nat'), - np.timedelta64(1, 'D') + np.timedelta64(2, 's'), - np.timedelta64(5, 'D') + np.timedelta64(3, 's')]) - - cases = [(tdidx1, tdidx2), (tdidx1, tdarr)] - - # Check pd.NaT is handles as the same as np.nan - for idx1, idx2 in cases: - - result = idx1 < idx2 - expected = np.array([True, False, False, False, True, False]) - tm.assert_numpy_array_equal(result, expected) - - result = idx2 > idx1 - expected = np.array([True, False, False, False, True, False]) - tm.assert_numpy_array_equal(result, expected) - - result = idx1 <= idx2 - expected = np.array([True, False, False, False, True, True]) - tm.assert_numpy_array_equal(result, expected) - - result = idx2 >= idx1 - expected = np.array([True, False, False, False, True, True]) - tm.assert_numpy_array_equal(result, expected) - - result = idx1 == idx2 - expected = np.array([False, False, False, False, False, True]) - tm.assert_numpy_array_equal(result, expected) - - result = idx1 != idx2 - expected = np.array([True, True, True, True, True, False]) - tm.assert_numpy_array_equal(result, expected) - - class TestTimedeltaIndexArithmetic(object): # Addition and Subtraction Operations @@ -527,14 +430,6 @@ def test_ops_ndarray(self): if LooseVersion(np.__version__) >= LooseVersion('1.8'): tm.assert_numpy_array_equal(other - td, expected) - def test_ops_series(self): - # regression test for GH8813 - td = Timedelta('1 day') - other = pd.Series([1, 2]) - expected = pd.Series(pd.to_timedelta(['1 day', '2 days'])) - tm.assert_series_equal(expected, td * other) - tm.assert_series_equal(expected, other * td) - def test_ops_series_object(self): # GH 13043 s = pd.Series([pd.Timestamp('2015-01-01', tz='US/Eastern'), @@ -656,62 +551,6 @@ def test_timedelta_ops_with_missing_values(self): actual = df1 - pd.NaT tm.assert_frame_equal(actual, dfn) - def test_add_overflow(self): - # see gh-14068 - msg = "too (big|large) to convert" - with tm.assert_raises_regex(OverflowError, msg): - to_timedelta(106580, 'D') + Timestamp('2000') - with tm.assert_raises_regex(OverflowError, msg): - Timestamp('2000') + to_timedelta(106580, 'D') - - _NaT = int(pd.NaT) + 1 - msg = "Overflow in int64 addition" - with tm.assert_raises_regex(OverflowError, msg): - to_timedelta([106580], 'D') + Timestamp('2000') - with tm.assert_raises_regex(OverflowError, msg): - Timestamp('2000') + to_timedelta([106580], 'D') - with tm.assert_raises_regex(OverflowError, msg): - to_timedelta([_NaT]) - Timedelta('1 days') - with tm.assert_raises_regex(OverflowError, msg): - to_timedelta(['5 days', _NaT]) - Timedelta('1 days') - with tm.assert_raises_regex(OverflowError, msg): - (to_timedelta([_NaT, '5 days', '1 hours']) - - to_timedelta(['7 seconds', _NaT, '4 hours'])) - - # These should not overflow! - exp = TimedeltaIndex([pd.NaT]) - result = to_timedelta([pd.NaT]) - Timedelta('1 days') - tm.assert_index_equal(result, exp) - - exp = TimedeltaIndex(['4 days', pd.NaT]) - result = to_timedelta(['5 days', pd.NaT]) - Timedelta('1 days') - tm.assert_index_equal(result, exp) - - exp = TimedeltaIndex([pd.NaT, pd.NaT, '5 hours']) - result = (to_timedelta([pd.NaT, '5 days', '1 hours']) + - to_timedelta(['7 seconds', pd.NaT, '4 hours'])) - tm.assert_index_equal(result, exp) - - def test_timedeltaindex_add_timestamp_nat_masking(self): - # GH17991 checking for overflow-masking with NaT - tdinat = pd.to_timedelta(['24658 days 11:15:00', 'NaT']) - - tsneg = Timestamp('1950-01-01') - ts_neg_variants = [tsneg, - tsneg.to_pydatetime(), - tsneg.to_datetime64().astype('datetime64[ns]'), - tsneg.to_datetime64().astype('datetime64[D]')] - - tspos = Timestamp('1980-01-01') - ts_pos_variants = [tspos, - tspos.to_pydatetime(), - tspos.to_datetime64().astype('datetime64[ns]'), - tspos.to_datetime64().astype('datetime64[D]')] - - for variant in ts_neg_variants + ts_pos_variants: - res = tdinat + variant - assert res[1] is pd.NaT - def test_tdi_ops_attributes(self): rng = timedelta_range('2 days', periods=5, freq='2D', name='x') diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py index df52b4cabc77c..c5d8d93bd768b 100644 --- a/pandas/tests/series/test_operators.py +++ b/pandas/tests/series/test_operators.py @@ -2,7 +2,6 @@ # pylint: disable-msg=E1101,W0612 import pytest -import pytz from collections import Iterable from datetime import datetime, timedelta @@ -19,7 +18,6 @@ from pandas.core.indexes.timedeltas import Timedelta import pandas.core.nanops as nanops -from pandas.errors import PerformanceWarning from pandas.compat import range, zip from pandas import compat from pandas.util.testing import (assert_series_equal, assert_almost_equal, @@ -30,19 +28,6 @@ class TestSeriesComparisons(object): - def test_series_comparison_scalars(self): - series = Series(date_range('1/1/2000', periods=10)) - - val = datetime(2000, 1, 4) - result = series > val - expected = Series([x > val for x in series]) - tm.assert_series_equal(result, expected) - - val = series[5] - result = series > val - expected = Series([x > val for x in series]) - tm.assert_series_equal(result, expected) - def test_comparisons(self): left = np.random.randn(10) right = np.random.randn(10) @@ -63,33 +48,6 @@ def test_comparisons(self): assert_series_equal(s == s2, exp) assert_series_equal(s2 == s, exp) - def test_operator_series_comparison_zerorank(self): - # GH 13006 - result = np.float64(0) > pd.Series([1, 2, 3]) - expected = 0.0 > pd.Series([1, 2, 3]) - tm.assert_series_equal(result, expected) - result = pd.Series([1, 2, 3]) < np.float64(0) - expected = pd.Series([1, 2, 3]) < 0.0 - tm.assert_series_equal(result, expected) - result = np.array([0, 1, 2])[0] > pd.Series([0, 1, 2]) - expected = 0.0 > pd.Series([1, 2, 3]) - tm.assert_series_equal(result, expected) - - def test_object_comparisons(self): - s = Series(['a', 'b', np.nan, 'c', 'a']) - - result = s == 'a' - expected = Series([True, False, False, False, True]) - assert_series_equal(result, expected) - - result = s < 'a' - expected = Series([False, False, False, False, False]) - assert_series_equal(result, expected) - - result = s != 'a' - expected = -(s == 'a') - assert_series_equal(result, expected) - def test_categorical_comparisons(self): # GH 8938 # allow equality comparisons @@ -284,27 +242,6 @@ def test_unequal_categorical_comparison_raises_type_error(self): tm.assert_series_equal(cat == "d", Series([False, False, False])) tm.assert_series_equal(cat != "d", Series([True, True, True])) - @pytest.mark.parametrize('dtype', [None, object]) - def test_more_na_comparisons(self, dtype): - left = Series(['a', np.nan, 'c'], dtype=dtype) - right = Series(['a', np.nan, 'd'], dtype=dtype) - - result = left == right - expected = Series([True, False, False]) - assert_series_equal(result, expected) - - result = left != right - expected = Series([False, True, True]) - assert_series_equal(result, expected) - - result = left == np.nan - expected = Series([False, False, False]) - assert_series_equal(result, expected) - - result = left != np.nan - expected = Series([True, True, True]) - assert_series_equal(result, expected) - @pytest.mark.parametrize('pair', [ ([pd.Timestamp('2011-01-01'), NaT, pd.Timestamp('2011-01-03')], [NaT, NaT, pd.Timestamp('2011-01-03')]), @@ -345,34 +282,6 @@ def test_nat_comparisons(self, dtype, box, reverse, pair): expected = Series([False, False, True]) assert_series_equal(left <= right, expected) - @pytest.mark.parametrize('data', [ - [pd.Timestamp('2011-01-01'), NaT, pd.Timestamp('2011-01-03')], - [pd.Timedelta('1 days'), NaT, pd.Timedelta('3 days')], - [pd.Period('2011-01', freq='M'), NaT, pd.Period('2011-03', freq='M')] - ]) - @pytest.mark.parametrize('dtype', [None, object]) - def test_nat_comparisons_scalar(self, dtype, data): - left = Series(data, dtype=dtype) - - expected = Series([False, False, False]) - assert_series_equal(left == pd.NaT, expected) - assert_series_equal(pd.NaT == left, expected) - - expected = Series([True, True, True]) - assert_series_equal(left != pd.NaT, expected) - assert_series_equal(pd.NaT != left, expected) - - expected = Series([False, False, False]) - assert_series_equal(left < pd.NaT, expected) - assert_series_equal(pd.NaT > left, expected) - assert_series_equal(left <= pd.NaT, expected) - assert_series_equal(pd.NaT >= left, expected) - - assert_series_equal(left > pd.NaT, expected) - assert_series_equal(pd.NaT < left, expected) - assert_series_equal(left >= pd.NaT, expected) - assert_series_equal(pd.NaT <= left, expected) - def test_comparison_different_length(self): a = Series(['a', 'b', 'c']) b = Series(['b', 'a']) @@ -591,73 +500,6 @@ def test_comp_ops_df_compat(self): class TestTimedeltaSeriesArithmetic(object): - def test_timedelta64_operations_with_DateOffset(self): - # GH 10699 - td = Series([timedelta(minutes=5, seconds=3)] * 3) - result = td + pd.offsets.Minute(1) - expected = Series([timedelta(minutes=6, seconds=3)] * 3) - assert_series_equal(result, expected) - - result = td - pd.offsets.Minute(1) - expected = Series([timedelta(minutes=4, seconds=3)] * 3) - assert_series_equal(result, expected) - - with tm.assert_produces_warning(PerformanceWarning): - result = td + Series([pd.offsets.Minute(1), pd.offsets.Second(3), - pd.offsets.Hour(2)]) - expected = Series([timedelta(minutes=6, seconds=3), timedelta( - minutes=5, seconds=6), timedelta(hours=2, minutes=5, seconds=3)]) - assert_series_equal(result, expected) - - result = td + pd.offsets.Minute(1) + pd.offsets.Second(12) - expected = Series([timedelta(minutes=6, seconds=15)] * 3) - assert_series_equal(result, expected) - - # valid DateOffsets - for do in ['Hour', 'Minute', 'Second', 'Day', 'Micro', 'Milli', - 'Nano']: - op = getattr(pd.offsets, do) - td + op(5) - op(5) + td - td - op(5) - op(5) - td - - def test_timedelta64_operations_with_timedeltas(self): - # td operate with td - td1 = Series([timedelta(minutes=5, seconds=3)] * 3) - td2 = timedelta(minutes=5, seconds=4) - result = td1 - td2 - expected = (Series([timedelta(seconds=0)] * 3) - - Series([timedelta(seconds=1)] * 3)) - assert result.dtype == 'm8[ns]' - assert_series_equal(result, expected) - - result2 = td2 - td1 - expected = (Series([timedelta(seconds=1)] * 3) - - Series([timedelta(seconds=0)] * 3)) - assert_series_equal(result2, expected) - - # roundtrip - assert_series_equal(result + td2, td1) - - # Now again, using pd.to_timedelta, which should build - # a Series or a scalar, depending on input. - td1 = Series(pd.to_timedelta(['00:05:03'] * 3)) - td2 = pd.to_timedelta('00:05:04') - result = td1 - td2 - expected = (Series([timedelta(seconds=0)] * 3) - - Series([timedelta(seconds=1)] * 3)) - assert result.dtype == 'm8[ns]' - assert_series_equal(result, expected) - - result2 = td2 - td1 - expected = (Series([timedelta(seconds=1)] * 3) - - Series([timedelta(seconds=0)] * 3)) - assert_series_equal(result2, expected) - - # roundtrip - assert_series_equal(result + td2, td1) - def test_operators_timedelta64(self): # series ops v1 = date_range('2012-1-1', periods=3, freq='D') @@ -807,40 +649,8 @@ def test_timedelta64_ops_nat(self): assert_series_equal(timedelta_series / nan, nat_series_dtype_timedelta) - @pytest.mark.parametrize('scalar_td', [timedelta(minutes=5, seconds=4), - Timedelta(minutes=5, seconds=4), - Timedelta('5m4s').to_timedelta64()]) - def test_operators_timedelta64_with_timedelta(self, scalar_td): - # smoke tests - td1 = Series([timedelta(minutes=5, seconds=3)] * 3) - td1.iloc[2] = np.nan - - td1 + scalar_td - scalar_td + td1 - td1 - scalar_td - scalar_td - td1 - td1 / scalar_td - scalar_td / td1 - class TestDatetimeSeriesArithmetic(object): - @pytest.mark.parametrize( - 'box, assert_func', - [(Series, tm.assert_series_equal), - (pd.Index, tm.assert_index_equal)]) - def test_sub_datetime64_not_ns(self, box, assert_func): - # GH#7996 - dt64 = np.datetime64('2013-01-01') - assert dt64.dtype == 'datetime64[D]' - - obj = box(date_range('20130101', periods=3)) - res = obj - dt64 - expected = box([Timedelta(days=0), Timedelta(days=1), - Timedelta(days=2)]) - assert_func(res, expected) - - res = dt64 - obj - assert_func(res, -expected) def test_operators_datetimelike_invalid(self, all_arithmetic_operators): # these are all TypeEror ops @@ -913,283 +723,6 @@ def test_operators_datetimelike(self): td1 + dt1 dt1 + td1 - def test_operators_datetimelike_with_timezones(self): - - tz = 'US/Eastern' - dt1 = Series(date_range('2000-01-01 09:00:00', periods=5, - tz=tz), name='foo') - dt2 = dt1.copy() - dt2.iloc[2] = np.nan - - td1 = Series(timedelta_range('1 days 1 min', periods=5, freq='H')) - td2 = td1.copy() - td2.iloc[1] = np.nan - - result = dt1 + td1[0] - exp = (dt1.dt.tz_localize(None) + td1[0]).dt.tz_localize(tz) - assert_series_equal(result, exp) - - result = dt2 + td2[0] - exp = (dt2.dt.tz_localize(None) + td2[0]).dt.tz_localize(tz) - assert_series_equal(result, exp) - - # odd numpy behavior with scalar timedeltas - result = td1[0] + dt1 - exp = (dt1.dt.tz_localize(None) + td1[0]).dt.tz_localize(tz) - assert_series_equal(result, exp) - - result = td2[0] + dt2 - exp = (dt2.dt.tz_localize(None) + td2[0]).dt.tz_localize(tz) - assert_series_equal(result, exp) - - result = dt1 - td1[0] - exp = (dt1.dt.tz_localize(None) - td1[0]).dt.tz_localize(tz) - assert_series_equal(result, exp) - with pytest.raises(TypeError): - td1[0] - dt1 - - result = dt2 - td2[0] - exp = (dt2.dt.tz_localize(None) - td2[0]).dt.tz_localize(tz) - assert_series_equal(result, exp) - with pytest.raises(TypeError): - td2[0] - dt2 - - result = dt1 + td1 - exp = (dt1.dt.tz_localize(None) + td1).dt.tz_localize(tz) - assert_series_equal(result, exp) - - result = dt2 + td2 - exp = (dt2.dt.tz_localize(None) + td2).dt.tz_localize(tz) - assert_series_equal(result, exp) - - result = dt1 - td1 - exp = (dt1.dt.tz_localize(None) - td1).dt.tz_localize(tz) - assert_series_equal(result, exp) - - result = dt2 - td2 - exp = (dt2.dt.tz_localize(None) - td2).dt.tz_localize(tz) - assert_series_equal(result, exp) - - with pytest.raises(TypeError): - td1 - dt1 - with pytest.raises(TypeError): - td2 - dt2 - - def test_sub_single_tz(self): - # GH12290 - s1 = Series([pd.Timestamp('2016-02-10', tz='America/Sao_Paulo')]) - s2 = Series([pd.Timestamp('2016-02-08', tz='America/Sao_Paulo')]) - result = s1 - s2 - expected = Series([Timedelta('2days')]) - assert_series_equal(result, expected) - result = s2 - s1 - expected = Series([Timedelta('-2days')]) - assert_series_equal(result, expected) - - def test_dt64tz_series_sub_dtitz(self): - # GH#19071 subtracting tzaware DatetimeIndex from tzaware Series - # (with same tz) raises, fixed by #19024 - dti = pd.date_range('1999-09-30', periods=10, tz='US/Pacific') - ser = pd.Series(dti) - expected = pd.Series(pd.TimedeltaIndex(['0days'] * 10)) - - res = dti - ser - tm.assert_series_equal(res, expected) - res = ser - dti - tm.assert_series_equal(res, expected) - - def test_sub_datetime_compat(self): - # see gh-14088 - s = Series([datetime(2016, 8, 23, 12, tzinfo=pytz.utc), pd.NaT]) - dt = datetime(2016, 8, 22, 12, tzinfo=pytz.utc) - exp = Series([Timedelta('1 days'), pd.NaT]) - assert_series_equal(s - dt, exp) - assert_series_equal(s - Timestamp(dt), exp) - - def test_dt64_series_with_timedelta(self): - # scalar timedeltas/np.timedelta64 objects - # operate with np.timedelta64 correctly - s = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) - - result = s + np.timedelta64(1, 's') - result2 = np.timedelta64(1, 's') + s - expected = Series([Timestamp('20130101 9:01:01'), - Timestamp('20130101 9:02:01')]) - assert_series_equal(result, expected) - assert_series_equal(result2, expected) - - result = s + np.timedelta64(5, 'ms') - result2 = np.timedelta64(5, 'ms') + s - expected = Series([Timestamp('20130101 9:01:00.005'), - Timestamp('20130101 9:02:00.005')]) - assert_series_equal(result, expected) - assert_series_equal(result2, expected) - - def test_dt64_series_add_tick_DateOffset(self): - # GH 4532 - # operate with pd.offsets - ser = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) - expected = Series([Timestamp('20130101 9:01:05'), - Timestamp('20130101 9:02:05')]) - - result = ser + pd.offsets.Second(5) - assert_series_equal(result, expected) - - result2 = pd.offsets.Second(5) + ser - assert_series_equal(result2, expected) - - def test_dt64_series_sub_tick_DateOffset(self): - # GH 4532 - # operate with pd.offsets - ser = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) - expected = Series([Timestamp('20130101 9:00:55'), - Timestamp('20130101 9:01:55')]) - - result = ser - pd.offsets.Second(5) - assert_series_equal(result, expected) - - result2 = -pd.offsets.Second(5) + ser - assert_series_equal(result2, expected) - - with pytest.raises(TypeError): - pd.offsets.Second(5) - ser - - @pytest.mark.parametrize('cls_name', ['Day', 'Hour', 'Minute', 'Second', - 'Milli', 'Micro', 'Nano']) - def test_dt64_series_with_tick_DateOffset_smoke(self, cls_name): - # GH 4532 - # smoke tests for valid DateOffsets - ser = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) - - offset_cls = getattr(pd.offsets, cls_name) - ser + offset_cls(5) - offset_cls(5) + ser - - def test_dt64_series_add_mixed_tick_DateOffset(self): - # GH 4532 - # operate with pd.offsets - s = Series([Timestamp('20130101 9:01'), Timestamp('20130101 9:02')]) - - result = s + pd.offsets.Milli(5) - result2 = pd.offsets.Milli(5) + s - expected = Series([Timestamp('20130101 9:01:00.005'), - Timestamp('20130101 9:02:00.005')]) - assert_series_equal(result, expected) - assert_series_equal(result2, expected) - - result = s + pd.offsets.Minute(5) + pd.offsets.Milli(5) - expected = Series([Timestamp('20130101 9:06:00.005'), - Timestamp('20130101 9:07:00.005')]) - assert_series_equal(result, expected) - - def test_dt64_series_sub_NaT(self): - # GH#18808 - dti = pd.DatetimeIndex([pd.NaT, pd.Timestamp('19900315')]) - ser = pd.Series(dti) - res = ser - pd.NaT - expected = pd.Series([pd.NaT, pd.NaT], dtype='timedelta64[ns]') - tm.assert_series_equal(res, expected) - - dti_tz = dti.tz_localize('Asia/Tokyo') - ser_tz = pd.Series(dti_tz) - res = ser_tz - pd.NaT - expected = pd.Series([pd.NaT, pd.NaT], dtype='timedelta64[ns]') - tm.assert_series_equal(res, expected) - - def test_datetime64_ops_nat(self): - # GH 11349 - datetime_series = Series([NaT, Timestamp('19900315')]) - nat_series_dtype_timestamp = Series([NaT, NaT], dtype='datetime64[ns]') - single_nat_dtype_datetime = Series([NaT], dtype='datetime64[ns]') - - # subtraction - assert_series_equal(-NaT + datetime_series, nat_series_dtype_timestamp) - with pytest.raises(TypeError): - -single_nat_dtype_datetime + datetime_series - - assert_series_equal(-NaT + nat_series_dtype_timestamp, - nat_series_dtype_timestamp) - with pytest.raises(TypeError): - -single_nat_dtype_datetime + nat_series_dtype_timestamp - - # addition - assert_series_equal(nat_series_dtype_timestamp + NaT, - nat_series_dtype_timestamp) - assert_series_equal(NaT + nat_series_dtype_timestamp, - nat_series_dtype_timestamp) - - assert_series_equal(nat_series_dtype_timestamp + NaT, - nat_series_dtype_timestamp) - assert_series_equal(NaT + nat_series_dtype_timestamp, - nat_series_dtype_timestamp) - - @pytest.mark.parametrize('dt64_series', [ - Series([Timestamp('19900315'), Timestamp('19900315')]), - Series([NaT, Timestamp('19900315')]), - Series([NaT, NaT], dtype='datetime64[ns]')]) - @pytest.mark.parametrize('one', [1, 1.0, np.array(1)]) - def test_dt64_mul_div_numeric_invalid(self, one, dt64_series): - # multiplication - with pytest.raises(TypeError): - dt64_series * one - with pytest.raises(TypeError): - one * dt64_series - - # division - with pytest.raises(TypeError): - dt64_series / one - with pytest.raises(TypeError): - one / dt64_series - - def test_dt64_series_arith_overflow(self): - # GH#12534, fixed by #19024 - dt = pd.Timestamp('1700-01-31') - td = pd.Timedelta('20000 Days') - dti = pd.date_range('1949-09-30', freq='100Y', periods=4) - ser = pd.Series(dti) - with pytest.raises(OverflowError): - ser - dt - with pytest.raises(OverflowError): - dt - ser - with pytest.raises(OverflowError): - ser + td - with pytest.raises(OverflowError): - td + ser - - ser.iloc[-1] = pd.NaT - expected = pd.Series(['2004-10-03', '2104-10-04', '2204-10-04', 'NaT'], - dtype='datetime64[ns]') - res = ser + td - tm.assert_series_equal(res, expected) - res = td + ser - tm.assert_series_equal(res, expected) - - ser.iloc[1:] = pd.NaT - expected = pd.Series(['91279 Days', 'NaT', 'NaT', 'NaT'], - dtype='timedelta64[ns]') - res = ser - dt - tm.assert_series_equal(res, expected) - res = dt - ser - tm.assert_series_equal(res, -expected) - - @pytest.mark.parametrize('op', ['__add__', '__radd__', - '__sub__', '__rsub__']) - @pytest.mark.parametrize('tz', [None, 'Asia/Tokyo']) - def test_dt64_series_add_intlike(self, tz, op): - # GH#19123 - dti = pd.DatetimeIndex(['2016-01-02', '2016-02-03', 'NaT'], tz=tz) - ser = Series(dti) - - other = Series([20, 30, 40], dtype='uint8') - - pytest.raises(TypeError, getattr(ser, op), 1) - - pytest.raises(TypeError, getattr(ser, op), other) - - pytest.raises(TypeError, getattr(ser, op), other.values) - - pytest.raises(TypeError, getattr(ser, op), pd.Index(other)) - class TestSeriesOperators(TestData): @pytest.mark.parametrize( diff --git a/pandas/tests/test_arithmetic.py b/pandas/tests/test_arithmetic.py index 4f6d3cf270e9f..c4bc9c71927fb 100644 --- a/pandas/tests/test_arithmetic.py +++ b/pandas/tests/test_arithmetic.py @@ -72,9 +72,173 @@ def box_df_fail(request): return request.param +# ------------------------------------------------------------------ +# Timedelta64[ns] dtype Comparisons + +class TestTimedelta64ArrayComparisons(object): + # TODO: All of these need to be parametrized over box + + def test_tdi_cmp_str_invalid(self): + # GH#13624 + tdi = TimedeltaIndex(['1 day', '2 days']) + + for left, right in [(tdi, 'a'), ('a', tdi)]: + with pytest.raises(TypeError): + left > right + + with pytest.raises(TypeError): + left == right + + with pytest.raises(TypeError): + left != right + + def test_comp_nat(self): + left = pd.TimedeltaIndex([pd.Timedelta('1 days'), pd.NaT, + pd.Timedelta('3 days')]) + right = pd.TimedeltaIndex([pd.NaT, pd.NaT, pd.Timedelta('3 days')]) + + for lhs, rhs in [(left, right), + (left.astype(object), right.astype(object))]: + result = rhs == lhs + expected = np.array([False, False, True]) + tm.assert_numpy_array_equal(result, expected) + + result = rhs != lhs + expected = np.array([True, True, False]) + tm.assert_numpy_array_equal(result, expected) + + expected = np.array([False, False, False]) + tm.assert_numpy_array_equal(lhs == pd.NaT, expected) + tm.assert_numpy_array_equal(pd.NaT == rhs, expected) + + expected = np.array([True, True, True]) + tm.assert_numpy_array_equal(lhs != pd.NaT, expected) + tm.assert_numpy_array_equal(pd.NaT != lhs, expected) + + expected = np.array([False, False, False]) + tm.assert_numpy_array_equal(lhs < pd.NaT, expected) + tm.assert_numpy_array_equal(pd.NaT > lhs, expected) + + def test_comparisons_nat(self): + tdidx1 = pd.TimedeltaIndex(['1 day', pd.NaT, '1 day 00:00:01', pd.NaT, + '1 day 00:00:01', '5 day 00:00:03']) + tdidx2 = pd.TimedeltaIndex(['2 day', '2 day', pd.NaT, pd.NaT, + '1 day 00:00:02', '5 days 00:00:03']) + tdarr = np.array([np.timedelta64(2, 'D'), + np.timedelta64(2, 'D'), np.timedelta64('nat'), + np.timedelta64('nat'), + np.timedelta64(1, 'D') + np.timedelta64(2, 's'), + np.timedelta64(5, 'D') + np.timedelta64(3, 's')]) + + cases = [(tdidx1, tdidx2), (tdidx1, tdarr)] + + # Check pd.NaT is handles as the same as np.nan + for idx1, idx2 in cases: + + result = idx1 < idx2 + expected = np.array([True, False, False, False, True, False]) + tm.assert_numpy_array_equal(result, expected) + + result = idx2 > idx1 + expected = np.array([True, False, False, False, True, False]) + tm.assert_numpy_array_equal(result, expected) + + result = idx1 <= idx2 + expected = np.array([True, False, False, False, True, True]) + tm.assert_numpy_array_equal(result, expected) + + result = idx2 >= idx1 + expected = np.array([True, False, False, False, True, True]) + tm.assert_numpy_array_equal(result, expected) + + result = idx1 == idx2 + expected = np.array([False, False, False, False, False, True]) + tm.assert_numpy_array_equal(result, expected) + + result = idx1 != idx2 + expected = np.array([True, True, True, True, True, False]) + tm.assert_numpy_array_equal(result, expected) + + # TODO: better name + def test_comparisons_coverage(self): + rng = timedelta_range('1 days', periods=10) + + result = rng < rng[3] + expected = np.array([True, True, True] + [False] * 7) + tm.assert_numpy_array_equal(result, expected) + + # raise TypeError for now + with pytest.raises(TypeError): + rng < rng[3].value + + result = rng == list(rng) + exp = rng == rng + tm.assert_numpy_array_equal(result, exp) + + # ------------------------------------------------------------------ # Timedelta64[ns] dtype Arithmetic Operations +class TestAddSubNaTMasking(object): + # TODO: parametrize over boxes + + def test_tdi_add_timestamp_nat_masking(self): + # GH#17991 checking for overflow-masking with NaT + tdinat = pd.to_timedelta(['24658 days 11:15:00', 'NaT']) + + tsneg = Timestamp('1950-01-01') + ts_neg_variants = [tsneg, + tsneg.to_pydatetime(), + tsneg.to_datetime64().astype('datetime64[ns]'), + tsneg.to_datetime64().astype('datetime64[D]')] + + tspos = Timestamp('1980-01-01') + ts_pos_variants = [tspos, + tspos.to_pydatetime(), + tspos.to_datetime64().astype('datetime64[ns]'), + tspos.to_datetime64().astype('datetime64[D]')] + + for variant in ts_neg_variants + ts_pos_variants: + res = tdinat + variant + assert res[1] is pd.NaT + + def test_tdi_add_overflow(self): + # See GH#14068 + msg = "too (big|large) to convert" + with tm.assert_raises_regex(OverflowError, msg): + pd.to_timedelta(106580, 'D') + Timestamp('2000') + with tm.assert_raises_regex(OverflowError, msg): + Timestamp('2000') + pd.to_timedelta(106580, 'D') + + _NaT = int(pd.NaT) + 1 + msg = "Overflow in int64 addition" + with tm.assert_raises_regex(OverflowError, msg): + pd.to_timedelta([106580], 'D') + Timestamp('2000') + with tm.assert_raises_regex(OverflowError, msg): + Timestamp('2000') + pd.to_timedelta([106580], 'D') + with tm.assert_raises_regex(OverflowError, msg): + pd.to_timedelta([_NaT]) - Timedelta('1 days') + with tm.assert_raises_regex(OverflowError, msg): + pd.to_timedelta(['5 days', _NaT]) - Timedelta('1 days') + with tm.assert_raises_regex(OverflowError, msg): + (pd.to_timedelta([_NaT, '5 days', '1 hours']) - + pd.to_timedelta(['7 seconds', _NaT, '4 hours'])) + + # These should not overflow! + exp = TimedeltaIndex([pd.NaT]) + result = pd.to_timedelta([pd.NaT]) - Timedelta('1 days') + tm.assert_index_equal(result, exp) + + exp = TimedeltaIndex(['4 days', pd.NaT]) + result = pd.to_timedelta(['5 days', pd.NaT]) - Timedelta('1 days') + tm.assert_index_equal(result, exp) + + exp = TimedeltaIndex([pd.NaT, pd.NaT, '5 hours']) + result = (pd.to_timedelta([pd.NaT, '5 days', '1 hours']) + + pd.to_timedelta(['7 seconds', pd.NaT, '4 hours'])) + tm.assert_index_equal(result, exp) + + class TestTimedeltaArraylikeAddSubOps(object): # Tests for timedelta64[ns] __add__, __sub__, __radd__, __rsub__ @@ -418,6 +582,59 @@ def test_td64arr_add_sub_numeric_arr_invalid(self, box, vec, dtype, tdser): # ------------------------------------------------------------------ # Operations with timedelta-like others + # TODO: this was taken from tests.series.test_ops; de-duplicate + @pytest.mark.parametrize('scalar_td', [timedelta(minutes=5, seconds=4), + Timedelta(minutes=5, seconds=4), + Timedelta('5m4s').to_timedelta64()]) + def test_operators_timedelta64_with_timedelta(self, scalar_td): + # smoke tests + td1 = Series([timedelta(minutes=5, seconds=3)] * 3) + td1.iloc[2] = np.nan + + td1 + scalar_td + scalar_td + td1 + td1 - scalar_td + scalar_td - td1 + td1 / scalar_td + scalar_td / td1 + + # TODO: this was taken from tests.series.test_ops; de-duplicate + def test_timedelta64_operations_with_timedeltas(self): + # td operate with td + td1 = Series([timedelta(minutes=5, seconds=3)] * 3) + td2 = timedelta(minutes=5, seconds=4) + result = td1 - td2 + expected = (Series([timedelta(seconds=0)] * 3) - + Series([timedelta(seconds=1)] * 3)) + assert result.dtype == 'm8[ns]' + tm.assert_series_equal(result, expected) + + result2 = td2 - td1 + expected = (Series([timedelta(seconds=1)] * 3) - + Series([timedelta(seconds=0)] * 3)) + tm.assert_series_equal(result2, expected) + + # roundtrip + tm.assert_series_equal(result + td2, td1) + + # Now again, using pd.to_timedelta, which should build + # a Series or a scalar, depending on input. + td1 = Series(pd.to_timedelta(['00:05:03'] * 3)) + td2 = pd.to_timedelta('00:05:04') + result = td1 - td2 + expected = (Series([timedelta(seconds=0)] * 3) - + Series([timedelta(seconds=1)] * 3)) + assert result.dtype == 'm8[ns]' + tm.assert_series_equal(result, expected) + + result2 = td2 - td1 + expected = (Series([timedelta(seconds=1)] * 3) - + Series([timedelta(seconds=0)] * 3)) + tm.assert_series_equal(result2, expected) + + # roundtrip + tm.assert_series_equal(result + td2, td1) + def test_td64arr_add_td64_array(self, box_df_fail): box = box_df_fail # DataFrame tries to broadcast incorrectly @@ -548,6 +765,39 @@ def test_td64arr_sub_timedeltalike(self, delta, box): # ------------------------------------------------------------------ # __add__/__sub__ with DateOffsets and arrays of DateOffsets + # TODO: this was taken from tests.series.test_operators; de-duplicate + def test_timedelta64_operations_with_DateOffset(self): + # GH#10699 + td = Series([timedelta(minutes=5, seconds=3)] * 3) + result = td + pd.offsets.Minute(1) + expected = Series([timedelta(minutes=6, seconds=3)] * 3) + tm.assert_series_equal(result, expected) + + result = td - pd.offsets.Minute(1) + expected = Series([timedelta(minutes=4, seconds=3)] * 3) + tm.assert_series_equal(result, expected) + + with tm.assert_produces_warning(PerformanceWarning): + result = td + Series([pd.offsets.Minute(1), pd.offsets.Second(3), + pd.offsets.Hour(2)]) + expected = Series([timedelta(minutes=6, seconds=3), + timedelta(minutes=5, seconds=6), + timedelta(hours=2, minutes=5, seconds=3)]) + tm.assert_series_equal(result, expected) + + result = td + pd.offsets.Minute(1) + pd.offsets.Second(12) + expected = Series([timedelta(minutes=6, seconds=15)] * 3) + tm.assert_series_equal(result, expected) + + # valid DateOffsets + for do in ['Hour', 'Minute', 'Second', 'Day', 'Micro', 'Milli', + 'Nano']: + op = getattr(pd.offsets, do) + td + op(5) + op(5) + td + td - op(5) + op(5) - td + @pytest.mark.parametrize('box', [ pd.Index, pytest.param(Series,