Repository: pandas-dev/pandas

Issue:
Comparison between TimedeltaIndex/PeriodIndex and 0-dim ndarray raises
```python
In [17]: a = pd.timedelta_range('2H', periods=4)

In [18]: a <= np.array(a.to_numpy()[0])
```
```pytb
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-18-e196011a0cff> in <module>
----> 1 a <= np.array(a.to_numpy()[0])

~/sandbox/pandas/pandas/core/indexes/datetimelike.py in wrapper(self, other)
    116                 other = other._values
    117
--> 118             result = op(self._data, maybe_unwrap_index(other))
    119             return result
    120

~/sandbox/pandas/pandas/core/arrays/timedeltas.py in wrapper(self, other)
     78
     79         elif not is_list_like(other):
---> 80             return ops.invalid_comparison(self, other, op)
     81
     82         elif len(other) != len(self):

~/sandbox/pandas/pandas/core/ops.py in invalid_comparison(left, right, op)
   1196     else:
   1197         raise TypeError("Invalid comparison between dtype={dtype} and {typ}"
-> 1198                         .format(dtype=left.dtype, typ=type(right).__name__))
   1199     return res_values
   1200

TypeError: Invalid comparison between dtype=timedelta64[ns] and ndarray
```

Works for DatetimeIndex.

Raises a different error for PeriodIndex

```python
In [24]: a = pd.period_range('2000', periods=4)

In [25]: a <= np.array(a.to_numpy()[0])
```
```pytb
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-e196011a0cff> in <module>
----> 1 a <= np.array(a.to_numpy()[0])

~/sandbox/pandas/pandas/core/indexes/datetimelike.py in wrapper(self, other)
    116                 other = other._values
    117
--> 118             result = op(self._data, maybe_unwrap_index(other))
    119             return result
    120

~/sandbox/pandas/pandas/core/arrays/period.py in wrapper(self, other)
     76             result.fill(nat_result)
     77         else:
---> 78             other = Period(other, freq=self.freq)
     79             result = op(other.ordinal)
     80

~/sandbox/pandas/pandas/_libs/tslibs/period.pyx in pandas._libs.tslibs.period.Period.__new__()
   2448                 ordinal = converted.ordinal
   2449
-> 2450         elif is_null_datetimelike(value) or value in nat_strings:
   2451             ordinal = NPY_NAT
   2452

TypeError: unhashable type: 'numpy.ndarray'
```

@jbrockmendel would you be interested in looking at this?


Discussion:
will take a look

Resolving patch:
diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py
--- 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
--- 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
 


Test patch:
diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py
--- 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
--- 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
--- 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)])