From d30c4a0696d5fbdc3c7ce36a9b9b19224a557e09 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 29 Jul 2018 09:04:57 -0700 Subject: [PATCH] [POC] implement test_arithmetic.py (#22033) --- pandas/tests/indexes/test_numeric.py | 38 +------- pandas/tests/series/test_arithmetic.py | 19 ---- pandas/tests/test_arithmetic.py | 119 +++++++++++++++++++++++++ pandas/util/testing.py | 44 +++++++++ 4 files changed, 164 insertions(+), 56 deletions(-) create mode 100644 pandas/tests/test_arithmetic.py diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index a323e2487e356..71b2774a92612 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -13,7 +13,7 @@ import pandas.util.testing as tm import pandas as pd -from pandas._libs.tslibs import Timestamp, Timedelta +from pandas._libs.tslibs import Timestamp from pandas.tests.indexes.common import Base @@ -26,42 +26,6 @@ def full_like(array, value): return ret -class TestIndexArithmeticWithTimedeltaScalar(object): - - @pytest.mark.parametrize('index', [ - Int64Index(range(1, 11)), - UInt64Index(range(1, 11)), - Float64Index(range(1, 11)), - RangeIndex(1, 11)]) - @pytest.mark.parametrize('scalar_td', [Timedelta(days=1), - Timedelta(days=1).to_timedelta64(), - Timedelta(days=1).to_pytimedelta()]) - def test_index_mul_timedelta(self, scalar_td, index): - # GH#19333 - expected = pd.timedelta_range('1 days', '10 days') - - result = index * scalar_td - tm.assert_index_equal(result, expected) - commute = scalar_td * index - tm.assert_index_equal(commute, expected) - - @pytest.mark.parametrize('index', [Int64Index(range(1, 3)), - UInt64Index(range(1, 3)), - Float64Index(range(1, 3)), - RangeIndex(1, 3)]) - @pytest.mark.parametrize('scalar_td', [Timedelta(days=1), - Timedelta(days=1).to_timedelta64(), - Timedelta(days=1).to_pytimedelta()]) - def test_index_rdiv_timedelta(self, scalar_td, index): - expected = pd.TimedeltaIndex(['1 Day', '12 Hours']) - - result = scalar_td / index - tm.assert_index_equal(result, expected) - - with pytest.raises(TypeError): - index / scalar_td - - class Numeric(Base): def test_can_hold_identifiers(self): diff --git a/pandas/tests/series/test_arithmetic.py b/pandas/tests/series/test_arithmetic.py index bf2308cd8c097..2571498ca802c 100644 --- a/pandas/tests/series/test_arithmetic.py +++ b/pandas/tests/series/test_arithmetic.py @@ -891,22 +891,3 @@ def test_td64series_mul_timedeltalike_invalid(self, scalar_td): td1 * scalar_td with tm.assert_raises_regex(TypeError, pattern): scalar_td * td1 - - -class TestTimedeltaSeriesInvalidArithmeticOps(object): - @pytest.mark.parametrize('scalar_td', [ - timedelta(minutes=5, seconds=4), - Timedelta('5m4s'), - Timedelta('5m4s').to_timedelta64()]) - def test_td64series_pow_invalid(self, scalar_td): - td1 = Series([timedelta(minutes=5, seconds=3)] * 3) - td1.iloc[2] = np.nan - - # check that we are getting a TypeError - # with 'operate' (from core/ops.py) for the ops that are not - # defined - pattern = 'operate|unsupported|cannot|not supported' - with tm.assert_raises_regex(TypeError, pattern): - scalar_td ** td1 - with tm.assert_raises_regex(TypeError, pattern): - td1 ** scalar_td diff --git a/pandas/tests/test_arithmetic.py b/pandas/tests/test_arithmetic.py new file mode 100644 index 0000000000000..f15b629f15ae3 --- /dev/null +++ b/pandas/tests/test_arithmetic.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Arithmetc tests for DataFrame/Series/Index/Array classes that should +# behave identically. +from datetime import timedelta + +import pytest +import numpy as np + +import pandas as pd +import pandas.util.testing as tm + +from pandas import Timedelta + + +# ------------------------------------------------------------------ +# Numeric dtypes Arithmetic with Timedelta Scalar + +class TestNumericArraylikeArithmeticWithTimedeltaScalar(object): + + @pytest.mark.parametrize('box', [ + pd.Index, + pd.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_index_mul_timedelta(self, scalar_td, index, box): + # GH#19333 + + if (box is pd.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('box', [pd.Index, pd.Series, pd.DataFrame]) + @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_index_rdiv_timedelta(self, scalar_td, index, box): + + if box is pd.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 = pd.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 + + +class TestTimedeltaArraylikeInvalidArithmeticOps(object): + + @pytest.mark.parametrize('box', [ + pd.Index, + pd.Series, + pytest.param(pd.DataFrame, + marks=pytest.mark.xfail(reason="raises ValueError " + "instead of TypeError", + strict=True)) + ]) + @pytest.mark.parametrize('scalar_td', [ + timedelta(minutes=5, seconds=4), + Timedelta('5m4s'), + Timedelta('5m4s').to_timedelta64()]) + def test_td64series_pow_invalid(self, scalar_td, box): + td1 = pd.Series([timedelta(minutes=5, seconds=3)] * 3) + td1.iloc[2] = np.nan + + td1 = tm.box_expected(td1, box) + + # check that we are getting a TypeError + # with 'operate' (from core/ops.py) for the ops that are not + # defined + pattern = 'operate|unsupported|cannot|not supported' + with tm.assert_raises_regex(TypeError, pattern): + scalar_td ** td1 + + with tm.assert_raises_regex(TypeError, pattern): + td1 ** scalar_td diff --git a/pandas/util/testing.py b/pandas/util/testing.py index 9697c991122dd..6dffbcb0b4f01 100644 --- a/pandas/util/testing.py +++ b/pandas/util/testing.py @@ -1478,6 +1478,50 @@ def assert_panel_equal(left, right, assert item in left, msg +def assert_equal(left, right, **kwargs): + """ + Wrapper for tm.assert_*_equal to dispatch to the appropriate test function. + + Parameters + ---------- + left : Index, Series, or DataFrame + right : Index, Series, or DataFrame + **kwargs + """ + if isinstance(left, pd.Index): + assert_index_equal(left, right, **kwargs) + elif isinstance(left, pd.Series): + assert_series_equal(left, right, **kwargs) + elif isinstance(left, pd.DataFrame): + assert_frame_equal(left, right, **kwargs) + else: + raise NotImplementedError(type(left)) + + +def box_expected(expected, box_cls): + """ + Helper function to wrap the expected output of a test in a given box_class. + + Parameters + ---------- + expected : np.ndarray, Index, Series + box_cls : {Index, Series, DataFrame} + + Returns + ------- + subclass of box_cls + """ + if box_cls is pd.Index: + expected = pd.Index(expected) + elif box_cls is pd.Series: + expected = pd.Series(expected) + elif box_cls is pd.DataFrame: + expected = pd.Series(expected).to_frame() + else: + raise NotImplementedError(box_cls) + return expected + + # ----------------------------------------------------------------------------- # Sparse