From fecc906f6836298f7ccd612951d88cb2209963dd Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sat, 20 Jan 2018 22:29:26 -0800 Subject: [PATCH 01/16] update whatsnew --- doc/source/whatsnew/v0.23.0.txt | 2 +- pandas/core/indexes/base.py | 5 ++++- pandas/tests/indexes/timedeltas/test_arithmetic.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 86fc47dee09fc..101c79f6f3e69 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -427,7 +427,7 @@ Conversion - Bug in localization of a naive, datetime string in a ``Series`` constructor with a ``datetime64[ns, tz]`` dtype (:issue:`174151`) - :func:`Timestamp.replace` will now handle Daylight Savings transitions gracefully (:issue:`18319`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) - +- Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue:`????`) - - Bug in ``.astype()`` to non-ns timedelta units would hold the incorrect dtype (:issue:`19176`, :issue:`19223`, :issue:`12425`) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 6d0a415f5b420..7ec46500565be 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -16,7 +16,7 @@ from pandas.core.dtypes.generic import ( ABCSeries, ABCDataFrame, ABCMultiIndex, - ABCPeriodIndex, + ABCPeriodIndex, ABCTimedeltaIndex, ABCDateOffset) from pandas.core.dtypes.missing import isna, array_equivalent from pandas.core.dtypes.common import ( @@ -4026,6 +4026,9 @@ def _make_evaluate_binop(op, opstr, reversed=False, constructor=Index): def _evaluate_numeric_binop(self, other): if isinstance(other, (ABCSeries, ABCDataFrame)): return NotImplemented + elif isinstance(other, ABCTimedeltaIndex): + # Defer to subclass implementation + return NotImplemented other = self._validate_for_numeric_binop(other, op, opstr) diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index 44f48f3ea9833..b2dc696ab3045 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -161,7 +161,7 @@ def test_numeric_compat(self): # invalid pytest.raises(TypeError, lambda: idx * idx) - pytest.raises(ValueError, lambda: idx * self._holder(np.arange(3))) + pytest.raises(TypeError, lambda: idx * self._holder(np.arange(3))) pytest.raises(ValueError, lambda: idx * np.array([1, 2])) def test_ufunc_coercions(self): From 7e8d5ff6cffed59dcd3af2447358ef7dc7c1d33c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 12:59:37 -0800 Subject: [PATCH 02/16] add rmul test to timedeltas --- .../tests/indexes/timedeltas/test_arithmetic.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index b2dc696ab3045..8793e0e19c48c 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -338,6 +338,22 @@ def test_ops_compat(self): # don't allow division by NaT (make could in the future) pytest.raises(TypeError, lambda: rng / pd.NaT) + @pytest.mark.parametrize('other', [ + np.arange(1, 11), + pd.Int64Index(range(1, 11)), + pd.UInt64Index(range(1, 11)), + pd.Float64Index(range(1, 11)), + pytest.param(pd.RangeIndex(1, 11), + marks=pytest.mark.xfail(reason="RangeIndex needs to " + "defer to TimedeltaIndex " + "implementation"))]) + def test_tdi_rmul_arraylike(self, other): + tdi = TimedeltaIndex(['1 Day'] * 10) + expected = timedelta_range('1 days', '10 days') + + result = other * tdi + tm.assert_index_equal(result, expected) + def test_subtraction_ops(self): # with datetimes/timedelta and tdi/dti tdi = TimedeltaIndex(['1 days', pd.NaT, '2 days'], name='foo') From 1dd4ccfe4a366fa5561cbbcdcc36715dc0757013 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 13:16:11 -0800 Subject: [PATCH 03/16] handle Index * timedelta, with tests --- pandas/core/indexes/base.py | 18 ++++++++++++++++-- pandas/core/indexes/range.py | 5 ++++- pandas/tests/indexes/test_numeric.py | 19 ++++++++++++++++++- .../indexes/timedeltas/test_arithmetic.py | 18 ++++++++---------- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 5a24feb8022b5..ef18074a95427 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -5,7 +5,7 @@ import numpy as np from pandas._libs import (lib, index as libindex, tslib as libts, algos as libalgos, join as libjoin, - Timestamp) + Timestamp, Timedelta) from pandas._libs.lib import is_datetime_array from pandas.compat import range, u, set_function_name @@ -3864,7 +3864,21 @@ def dropna(self, how='any'): return self._shallow_copy() def _evaluate_with_timedelta_like(self, other, op, opstr, reversed=False): - raise TypeError("can only perform ops with timedelta like values") + # Timedelta knows how to operate with np.array, so dispatch to that + # operation and then wrap the results + other = Timedelta(other) + values = self.values + if reversed: + values, other = other, values + + with np.errstate(all='ignore'): + result = op(values, other) + + attrs = self._get_attributes_dict() + attrs = self._maybe_update_attributes(attrs) + if op == divmod: + return Index(result[0], **attrs), Index(result[1], **attrs) + return Index(result, **attrs) def _evaluate_with_datetime_like(self, other, op, opstr): raise TypeError("can only perform ops with datetime like values") diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index a82ee6b2b44af..5dd22dee4d456 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -8,7 +8,7 @@ is_integer, is_scalar, is_int64_dtype) -from pandas.core.dtypes.generic import ABCSeries +from pandas.core.dtypes.generic import ABCSeries, ABCTimedeltaIndex from pandas import compat from pandas.compat import lrange, range, get_range_parameters @@ -587,6 +587,9 @@ def _make_evaluate_binop(op, opstr, reversed=False, step=False): def _evaluate_numeric_binop(self, other): if isinstance(other, ABCSeries): return NotImplemented + elif isinstance(other, ABCTimedeltaIndex): + # Defer to TimedeltaIndex implementation + return NotImplemented other = self._validate_for_numeric_binop(other, op, opstr) attrs = self._get_attributes_dict() diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index 3de1c4c982654..3a8377c5f742e 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.lib import Timestamp +from pandas._libs.lib import Timestamp, Timedelta from pandas.tests.indexes.common import Base @@ -26,6 +26,23 @@ def full_like(array, value): return ret +class TestIndexArithmetic(object): + @pytest.mark.parametrize('index', [pd.Int64Index(range(1, 11)), + pd.UInt64Index(range(1, 11)), + pd.Float64Index(range(1, 11)), + pd.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_timdelta(self, scalar_td, index): + 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) + + class Numeric(Base): def test_numeric_compat(self): diff --git a/pandas/tests/indexes/timedeltas/test_arithmetic.py b/pandas/tests/indexes/timedeltas/test_arithmetic.py index 68a2e5dc72def..f0c57207977db 100644 --- a/pandas/tests/indexes/timedeltas/test_arithmetic.py +++ b/pandas/tests/indexes/timedeltas/test_arithmetic.py @@ -297,7 +297,7 @@ def test_dti_mul_dti_raises(self): def test_dti_mul_too_short_raises(self): idx = self._holder(np.arange(5, dtype='int64')) - with pytest.raises(ValueError): + with pytest.raises(TypeError): idx * self._holder(np.arange(3)) with pytest.raises(ValueError): idx * np.array([1, 2]) @@ -476,21 +476,19 @@ def test_ops_compat(self): # don't allow division by NaT (make could in the future) pytest.raises(TypeError, lambda: rng / pd.NaT) - @pytest.mark.parametrize('other', [ - np.arange(1, 11), - pd.Int64Index(range(1, 11)), - pd.UInt64Index(range(1, 11)), - pd.Float64Index(range(1, 11)), - pytest.param(pd.RangeIndex(1, 11), - marks=pytest.mark.xfail(reason="RangeIndex needs to " - "defer to TimedeltaIndex " - "implementation"))]) + @pytest.mark.parametrize('other', [np.arange(1, 11), + pd.Int64Index(range(1, 11)), + pd.UInt64Index(range(1, 11)), + pd.Float64Index(range(1, 11)), + pd.RangeIndex(1, 11)]) def test_tdi_rmul_arraylike(self, other): tdi = TimedeltaIndex(['1 Day'] * 10) expected = timedelta_range('1 days', '10 days') result = other * tdi tm.assert_index_equal(result, expected) + commute = tdi * other + tm.assert_index_equal(commute, expected) def test_subtraction_ops(self): # with datetimes/timedelta and tdi/dti From 71811c83822f4ddf5049abf1399b6980b2664276 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 13:29:38 -0800 Subject: [PATCH 04/16] whatsnew note --- doc/source/whatsnew/v0.23.0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index e2c0b641f7a64..7f8d8209d938a 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -429,6 +429,7 @@ Conversion - :func:`Timestamp.replace` will now handle Daylight Savings transitions gracefully (:issue:`18319`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) - Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue:`????`) +- Multiplication of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`????`) - - Bug in ``.astype()`` to non-ns timedelta units would hold the incorrect dtype (:issue:`19176`, :issue:`19223`, :issue:`12425`) From 4c2c74be6c800386133344e0f499dbec656b164e Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 13:38:30 -0800 Subject: [PATCH 05/16] division tests, fix typos --- doc/source/whatsnew/v0.23.0.txt | 2 +- pandas/tests/indexes/test_numeric.py | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 7f8d8209d938a..fc4b916ca9e10 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -429,7 +429,7 @@ Conversion - :func:`Timestamp.replace` will now handle Daylight Savings transitions gracefully (:issue:`18319`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) - Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue:`????`) -- Multiplication of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`????`) +- Multiplication and division of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`????`) - - Bug in ``.astype()`` to non-ns timedelta units would hold the incorrect dtype (:issue:`19176`, :issue:`19223`, :issue:`12425`) diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index 3a8377c5f742e..c2c204e403055 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -34,7 +34,7 @@ class TestIndexArithmetic(object): @pytest.mark.parametrize('scalar_td', [Timedelta(days=1), Timedelta(days=1).to_timedelta64(), Timedelta(days=1).to_pytimedelta()]) - def test_index_mul_timdelta(self, scalar_td, index): + def test_index_mul_timedelta(self, scalar_td, index): expected = pd.timedelta_range('1 days', '10 days') result = index * scalar_td @@ -42,6 +42,19 @@ def test_index_mul_timdelta(self, scalar_td, index): commute = scalar_td * index tm.assert_index_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)]) + @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(['NaT', '1 Day', '12 Hours']) + + result = scalar_td / index + tm.assert_index_equal(result, expected) + class Numeric(Base): From d45ba924bf7ce552769069e398c27449ee758cd7 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 13:43:17 -0800 Subject: [PATCH 06/16] Fix typo --- pandas/tests/indexes/test_numeric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index c2c204e403055..b4e8f44a6c320 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -50,7 +50,7 @@ def test_index_mul_timedelta(self, scalar_td, index): Timedelta(days=1).to_timedelta64(), Timedelta(days=1).to_pytimedelta()]) def test_index_rdiv_timedelta(self, scalar_td, index): - expected = pd.TimedeltaIndex(['NaT', '1 Day', '12 Hours']) + expected = pd.TimedeltaIndex(['1 Day', '12 Hours']) result = scalar_td / index tm.assert_index_equal(result, expected) From 4abd61ae9e086385d7f05bef226707f395fe8fe0 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 13:45:18 -0800 Subject: [PATCH 07/16] restore whitespace --- doc/source/whatsnew/v0.23.0.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index fc4b916ca9e10..9edc7f7f0bca2 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -430,6 +430,7 @@ Conversion - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) - Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue:`????`) - Multiplication and division of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`????`) +- - - Bug in ``.astype()`` to non-ns timedelta units would hold the incorrect dtype (:issue:`19176`, :issue:`19223`, :issue:`12425`) From 11424b0feca96e9938c6ba2f37ba68d2eead8bd8 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 14:30:13 -0800 Subject: [PATCH 08/16] fill in GH issue --- doc/source/whatsnew/v0.23.0.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 9edc7f7f0bca2..cefd169b4fe25 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -428,8 +428,8 @@ Conversion - Bug in localization of a naive, datetime string in a ``Series`` constructor with a ``datetime64[ns, tz]`` dtype (:issue:`174151`) - :func:`Timestamp.replace` will now handle Daylight Savings transitions gracefully (:issue:`18319`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) -- Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue:`????`) -- Multiplication and division of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`????`) +- Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue`19333`) +- Multiplication and division of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`19333`) - - From 1c974897d7dd12ab6357042edcdbaaf789ea5427 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 14:42:32 -0800 Subject: [PATCH 09/16] whitespace fixup from merge --- doc/source/whatsnew/v0.23.0.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index f71dc7a5cfa52..03a6b265cd13f 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -427,7 +427,6 @@ Timezones - Bug in comparing :class:`DatetimeIndex`, which failed to raise ``TypeError`` when attempting to compare timezone-aware and timezone-naive datetimelike objects (:issue:`18162`) - Bug in localization of a naive, datetime string in a ``Series`` constructor with a ``datetime64[ns, tz]`` dtype (:issue:`174151`) - :func:`Timestamp.replace` will now handle Daylight Savings transitions gracefully (:issue:`18319`) -- - Bug in tz-aware :class:`DatetimeIndex` where addition/subtraction with a :class:`TimedeltaIndex` or array with ``dtype='timedelta64[ns]'`` was incorrect (:issue:`17558`) - Bug in :func:`DatetimeIndex.insert` where inserting ``NaT`` into a timezone-aware index incorrectly raised (:issue:`16357`) From 23b7f4414cb75302d1587bf4020a890cc27cac89 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Sun, 21 Jan 2018 17:40:22 -0800 Subject: [PATCH 10/16] arrange whatsnew notes --- doc/source/whatsnew/v0.23.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index 03a6b265cd13f..357877f2ee469 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -417,6 +417,7 @@ Datetimelike - Bug in ``.astype()`` to non-ns timedelta units would hold the incorrect dtype (:issue:`19176`, :issue:`19223`, :issue:`12425`) - Bug in subtracting :class:`Series` from ``NaT`` incorrectly returning ``NaT`` (:issue:`19158`) - Bug in :func:`Series.truncate` which raises ``TypeError`` with a monotonic ``PeriodIndex`` (:issue:`17717`) +- Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue`19333`) Timezones ^^^^^^^^^ @@ -447,7 +448,6 @@ Numeric - Bug in the :class:`DataFrame` constructor in which data containing very large positive or very large negative numbers was causing ``OverflowError`` (:issue:`18584`) - Bug in :class:`Index` constructor with ``dtype='uint64'`` where int-like floats were not coerced to :class:`UInt64Index` (:issue:`18400`) - Bug in :class:`Index` multiplication and division methods where operating with a ``Series`` would return an ``Index`` object instead of a ``Series`` object (:issue:`19042`) -- Multiplication of :class:`TimedeltaIndex` by ``TimedeltaIndex`` will now raise ``TypeError`` instead of raising ``ValueError`` in cases of length mis-match (:issue`19333`) - Multiplication and division of numeric-dtyped :class:`Index` objects with timedelta-like scalars returns ``TimedeltaIndex`` instead of raising ``TypeError`` (:issue:`19333`) - From 6e04aba84d1bc01d03b2bd771b6246ba2e014f7f Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Mon, 22 Jan 2018 11:04:25 -0800 Subject: [PATCH 11/16] comment, requested edits --- pandas/tests/indexes/test_numeric.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index b4e8f44a6c320..f4464022f9f18 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -26,15 +26,17 @@ def full_like(array, value): return ret -class TestIndexArithmetic(object): - @pytest.mark.parametrize('index', [pd.Int64Index(range(1, 11)), - pd.UInt64Index(range(1, 11)), - pd.Float64Index(range(1, 11)), - pd.RangeIndex(1, 11)]) +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 @@ -42,10 +44,10 @@ def test_index_mul_timedelta(self, scalar_td, index): commute = scalar_td * index tm.assert_index_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)]) + @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()]) @@ -55,6 +57,9 @@ def test_index_rdiv_timedelta(self, scalar_td, index): result = scalar_td / index tm.assert_index_equal(result, expected) + with pytest.raises(TypeError): + index / scalar_td + class Numeric(Base): From 0ed9276b00cdf234f366a87e73966f7bf231e550 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Wed, 24 Jan 2018 22:15:15 -0800 Subject: [PATCH 12/16] xfail RangeIndex test --- pandas/tests/indexes/test_numeric.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index f4464022f9f18..caca80f56aa4b 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -28,10 +28,13 @@ def full_like(array, value): 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('index', [ + Int64Index(range(1, 11)), + UInt64Index(range(1, 11)), + Float64Index(range(1, 11)), + pytest.param(RangeIndex(1, 11), + marks=pytest.mark.xfail('GH#19333 Unexplained failure ' + 'only in CircleCI'))]) @pytest.mark.parametrize('scalar_td', [Timedelta(days=1), Timedelta(days=1).to_timedelta64(), Timedelta(days=1).to_pytimedelta()]) From a04f97036a36c67e875579274279cc7279c3944c Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Thu, 25 Jan 2018 08:55:45 -0800 Subject: [PATCH 13/16] fix pytest xfail problem --- pandas/tests/indexes/test_numeric.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index caca80f56aa4b..e9d4369aee4b6 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -33,8 +33,9 @@ class TestIndexArithmeticWithTimedeltaScalar(object): UInt64Index(range(1, 11)), Float64Index(range(1, 11)), pytest.param(RangeIndex(1, 11), - marks=pytest.mark.xfail('GH#19333 Unexplained failure ' - 'only in CircleCI'))]) + marks=pytest.mark.xfail(reason='GH#19333 Unexplained ' + 'failure only in ' + 'CircleCI'))]) @pytest.mark.parametrize('scalar_td', [Timedelta(days=1), Timedelta(days=1).to_timedelta64(), Timedelta(days=1).to_pytimedelta()]) From a46acb96e2f57a90bd431f1919f4f31c24b01e65 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 13 Feb 2018 12:37:39 -0800 Subject: [PATCH 14/16] remove xfail --- pandas/tests/indexes/test_numeric.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pandas/tests/indexes/test_numeric.py b/pandas/tests/indexes/test_numeric.py index 92028891046ac..f37bd15f80838 100644 --- a/pandas/tests/indexes/test_numeric.py +++ b/pandas/tests/indexes/test_numeric.py @@ -32,10 +32,7 @@ class TestIndexArithmeticWithTimedeltaScalar(object): Int64Index(range(1, 11)), UInt64Index(range(1, 11)), Float64Index(range(1, 11)), - pytest.param(RangeIndex(1, 11), - marks=pytest.mark.xfail(reason='GH#19333 Unexplained ' - 'failure only in ' - 'CircleCI'))]) + RangeIndex(1, 11)]) @pytest.mark.parametrize('scalar_td', [Timedelta(days=1), Timedelta(days=1).to_timedelta64(), Timedelta(days=1).to_pytimedelta()]) From 080c06c640c0ad1ea176f5b89d1f769145b4b834 Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 20 Feb 2018 18:17:37 -0800 Subject: [PATCH 15/16] troubleshoot RangeIndex test failure --- pandas/core/indexes/range.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 5747fa1dfd0cb..44127af079cc4 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -1,5 +1,6 @@ from sys import getsizeof import operator +from datetime import timedelta import numpy as np from pandas._libs import index as libindex @@ -590,6 +591,10 @@ def _evaluate_numeric_binop(self, other): elif isinstance(other, ABCTimedeltaIndex): # Defer to TimedeltaIndex implementation return NotImplemented + elif isinstance(other, (timedelta, np.timedelta64)): + # GH#19333 is_integer evaluated True on timedelta64, + # so we need to catch these explicitly + return op(self._int64index, other) other = self._validate_for_numeric_binop(other, op, opstr) attrs = self._get_attributes_dict() From 22d155e16cc213205685e14053afa98d196755ff Mon Sep 17 00:00:00 2001 From: Brock Mendel Date: Tue, 20 Feb 2018 18:36:32 -0800 Subject: [PATCH 16/16] fixup reversed op --- pandas/core/indexes/range.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/core/indexes/range.py b/pandas/core/indexes/range.py index 44127af079cc4..0ac415ee0b701 100644 --- a/pandas/core/indexes/range.py +++ b/pandas/core/indexes/range.py @@ -594,6 +594,8 @@ def _evaluate_numeric_binop(self, other): elif isinstance(other, (timedelta, np.timedelta64)): # GH#19333 is_integer evaluated True on timedelta64, # so we need to catch these explicitly + if reversed: + return op(other, self._int64index) return op(self._int64index, other) other = self._validate_for_numeric_binop(other, op, opstr)