diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 1890d7b31bed2..5150532c8ce7f 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -539,6 +539,7 @@ Datetimelike - Bug in adding :class:`DateOffset` with nonzero month to :class:`DatetimeIndex` would raise ``ValueError`` (:issue:`26258`) - Bug in :func:`to_datetime` which raises unhandled ``OverflowError`` when called with mix of invalid dates and ``NaN`` values with ``format='%Y%m%d'`` and ``error='coerce'`` (:issue:`25512`) - Bug in :func:`to_datetime` which raises ``TypeError`` for ``format='%Y%m%d'`` when called for invalid integer dates with length >= 6 digits with ``errors='ignore'`` +- Bug when comparing a :class:`PeriodIndex` against a zero-dimensional numpy array (:issue:`26689`) Timedelta ^^^^^^^^^ @@ -546,6 +547,7 @@ Timedelta - Bug in :func:`TimedeltaIndex.intersection` where for non-monotonic indices in some cases an empty ``Index`` was returned when in fact an intersection existed (:issue:`25913`) - Bug with comparisons between :class:`Timedelta` and ``NaT`` raising ``TypeError`` (:issue:`26039`) - Bug when adding or subtracting a :class:`BusinessHour` to a :class:`Timestamp` with the resulting time landing in a following or prior day respectively (:issue:`26381`) +- Bug when comparing a :class:`TimedeltaIndex` against a zero-dimensional numpy array (:issue:`26689`) Timezones ^^^^^^^^^ diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 8a6640e11ad74..a6e282462af68 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -4,6 +4,7 @@ import numpy as np +from pandas._libs import lib from pandas._libs.tslibs import ( NaT, NaTType, frequencies as libfrequencies, iNaT, period as libperiod) from pandas._libs.tslibs.fields import isleapyear_arr @@ -51,6 +52,7 @@ def _period_array_cmp(cls, op): def wrapper(self, other): op = getattr(self.asi8, opname) + other = lib.item_from_zerodim(other) if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)): return NotImplemented diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 58d9e4085a612..3ba6829b4ac28 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -62,6 +62,7 @@ def _td_array_cmp(cls, op): nat_result = opname == '__ne__' def wrapper(self, other): + other = lib.item_from_zerodim(other) if isinstance(other, (ABCDataFrame, ABCSeries, ABCIndexClass)): return NotImplemented diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 13adae279c989..afd29852fea7e 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -37,6 +37,7 @@ def assert_all(obj): # ------------------------------------------------------------------ # Comparisons + class TestDatetime64DataFrameComparison: @pytest.mark.parametrize('timestamps', [ [pd.Timestamp('2012-01-01 13:00:00+00:00')] * 2, @@ -338,6 +339,17 @@ def test_comparison_tzawareness_compat(self, op): class TestDatetimeIndexComparisons: + # TODO: parametrize over box + def test_compare_zerodim(self, tz_naive_fixture): + # Test comparison with zero-dimensional array is unboxed + tz = tz_naive_fixture + dti = date_range('20130101', periods=3, tz=tz) + + other = np.array(dti.to_numpy()[0]) + result = dti <= other + expected = np.array([True, False, False]) + tm.assert_numpy_array_equal(result, expected) + # TODO: moved from tests.indexes.test_base; parametrize and de-duplicate @pytest.mark.parametrize("op", [ operator.eq, operator.ne, operator.gt, operator.lt, diff --git a/pandas/tests/arithmetic/test_period.py b/pandas/tests/arithmetic/test_period.py index 935f6817dcb4c..e254312e39724 100644 --- a/pandas/tests/arithmetic/test_period.py +++ b/pandas/tests/arithmetic/test_period.py @@ -22,6 +22,16 @@ class TestPeriodIndexComparisons: + # TODO: parameterize over boxes + def test_compare_zerodim(self): + # GH#26689 make sure we unbox zero-dimensional arrays + pi = pd.period_range('2000', periods=4) + other = np.array(pi.to_numpy()[0]) + + result = pi <= other + expected = np.array([True, False, False, False]) + tm.assert_numpy_array_equal(result, expected) + @pytest.mark.parametrize("other", ["2017", 2017]) def test_eq(self, other): idx = PeriodIndex(['2017', '2017', '2018'], freq="D") diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index c214a880345ae..56f47324967a1 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -33,6 +33,19 @@ def get_upcast_box(box, vector): class TestTimedelta64ArrayComparisons: # TODO: All of these need to be parametrized over box + def test_compare_timedelta64_zerodim(self): + # GH#26689 should unbox when comparing with zerodim array + tdi = pd.timedelta_range('2H', periods=4) + other = np.array(tdi.to_numpy()[0]) + + res = tdi <= other + expected = np.array([True, False, False, False]) + tm.assert_numpy_array_equal(res, expected) + + with pytest.raises(TypeError): + # zero-dim of wrong dtype should still raise + tdi >= np.array(4) + def test_compare_timedelta_series(self): # regresssion test for GH#5963 s = pd.Series([timedelta(days=1), timedelta(days=2)])