Repository: pandas-dev/pandas

Issue:
Index.__add__ OK but __sub__ not?
Curious about why `Index.__add__` tries does the natural thing but `Index.__sub__` raises `TypeError`.

```
    def __add__(self, other):
        return Index(np.array(self) + other)

    def __radd__(self, other):
        return Index(other + np.array(self))

    __iadd__ = __add__

    def __sub__(self, other):
        raise TypeError("cannot perform __sub__ with this index type: "
                        "{typ}".format(typ=type(self)))
```


Discussion:
this is why comments are important!

add is concat

eg 
Index(list(‘abc’)) + ‘d’

sub doesn’t make sense
That seems pretty string-specific.  Shouldn't the object-dtype try to broadcast the scalar operation?

```
pd.Index([1, 2, 3], dtype='o') + 1
pd.Index([1, 2, 3], dtype='o') - 1
```

The motivation here is in https://github.com/pandas-dev/pandas/blob/master/pandas/core/indexes/datetimes.py#L933

where `_add_offset_array` can use `self.astype('O') + np.array(other)` but `_sub_offset_array` has to use `self.__class__(self.astype('O').values - np.array(other))`

Resolving patch:
diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py
--- a/pandas/core/indexes/base.py
+++ b/pandas/core/indexes/base.py
@@ -2630,8 +2630,10 @@ def __iadd__(self, other):
         return self + other
 
     def __sub__(self, other):
-        raise TypeError("cannot perform __sub__ with this index type: "
-                        "{typ}".format(typ=type(self).__name__))
+        return Index(np.array(self) - other)
+
+    def __rsub__(self, other):
+        return Index(other - np.array(self))
 
     def __and__(self, other):
         return self.intersection(other)


Test patch:
diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py
--- a/pandas/tests/indexes/test_base.py
+++ b/pandas/tests/indexes/test_base.py
@@ -3,7 +3,7 @@
 import pytest
 
 from datetime import datetime, timedelta
-
+from decimal import Decimal
 from collections import defaultdict
 
 import pandas.util.testing as tm
@@ -864,13 +864,47 @@ def test_add(self):
         expected = Index(['1a', '1b', '1c'])
         tm.assert_index_equal('1' + index, expected)
 
-    def test_sub(self):
+    def test_sub_fail(self):
         index = self.strIndex
         pytest.raises(TypeError, lambda: index - 'a')
         pytest.raises(TypeError, lambda: index - index)
         pytest.raises(TypeError, lambda: index - index.tolist())
         pytest.raises(TypeError, lambda: index.tolist() - index)
 
+    def test_sub_object(self):
+        # GH#19369
+        index = pd.Index([Decimal(1), Decimal(2)])
+        expected = pd.Index([Decimal(0), Decimal(1)])
+
+        result = index - Decimal(1)
+        tm.assert_index_equal(result, expected)
+
+        result = index - pd.Index([Decimal(1), Decimal(1)])
+        tm.assert_index_equal(result, expected)
+
+        with pytest.raises(TypeError):
+            index - 'foo'
+
+        with pytest.raises(TypeError):
+            index - np.array([2, 'foo'])
+
+    def test_rsub_object(self):
+        # GH#19369
+        index = pd.Index([Decimal(1), Decimal(2)])
+        expected = pd.Index([Decimal(1), Decimal(0)])
+
+        result = Decimal(2) - index
+        tm.assert_index_equal(result, expected)
+
+        result = np.array([Decimal(2), Decimal(2)]) - index
+        tm.assert_index_equal(result, expected)
+
+        with pytest.raises(TypeError):
+            'foo' - index
+
+        with pytest.raises(TypeError):
+            np.array([True, pd.Timestamp.now()]) - index
+
     def test_map_identity_mapping(self):
         # GH 12766
         # TODO: replace with fixture