Repository: pandas-dev/pandas

Issue:
Series[timedelta64] +/- TimedeltaIndex inherits name from TimedeltaIndex
Opening mainly for an issue to reference in upcoming PR.

This behavior is not present in 0.22.0, is present in master as of 04beec77e5a713dd

```
tdi = pd.TimedeltaIndex(['1day', '2days', '3days'])
ser = pd.Series(tdi)
tdi.name = 'foo'
ser.name = 'bar'

>>> (tdi + ser).name
'foo'
>>> (ser + tdi).name
'foo'
>>> (ser - tdi).name
'foo'
>>> (tdi - ser).name
'foo'
```

These names should all be coming back as `None`.


Discussion:

Resolving patch:
diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py
--- a/pandas/core/indexes/timedeltas.py
+++ b/pandas/core/indexes/timedeltas.py
@@ -371,6 +371,9 @@ def _add_delta(self, delta):
         return result
 
     def _evaluate_with_timedelta_like(self, other, op, opstr):
+        if isinstance(other, ABCSeries):
+            # GH#19042
+            return NotImplemented
 
         # allow division by a timedelta
         if opstr in ['__div__', '__truediv__', '__floordiv__']:
diff --git a/pandas/core/ops.py b/pandas/core/ops.py
--- a/pandas/core/ops.py
+++ b/pandas/core/ops.py
@@ -597,7 +597,15 @@ def _align_method_SERIES(left, right, align_asobject=False):
 
 
 def _construct_result(left, result, index, name, dtype):
-    return left._constructor(result, index=index, name=name, dtype=dtype)
+    """
+    If the raw op result has a non-None name (e.g. it is an Index object) and
+    the name argument is None, then passing name to the constructor will
+    not be enough; we still need to override the name attribute.
+    """
+    out = left._constructor(result, index=index, dtype=dtype)
+
+    out.name = name
+    return out
 
 
 def _construct_divmod_result(left, result, index, name, dtype):
@@ -687,21 +695,10 @@ def wrapper(left, right, name=name, na_op=na_op):
                     not isinstance(lvalues, ABCDatetimeIndex)):
                 lvalues = lvalues.values
 
-        if isinstance(right, (ABCSeries, pd.Index)):
-            # `left` is always a Series object
-            res_name = _maybe_match_name(left, right)
-        else:
-            res_name = left.name
-
         result = wrap_results(safe_na_op(lvalues, rvalues))
         res_name = _get_series_op_result_name(left, right)
-        return construct_result(
-            left,
-            result,
-            index=left.index,
-            name=res_name,
-            dtype=dtype,
-        )
+        return construct_result(left, result,
+                                index=left.index, name=res_name, dtype=dtype)
 
     return wrapper
 


Test patch:
diff --git a/pandas/tests/series/test_operators.py b/pandas/tests/series/test_operators.py
--- a/pandas/tests/series/test_operators.py
+++ b/pandas/tests/series/test_operators.py
@@ -1045,6 +1045,74 @@ def test_timedelta_floordiv(self, scalar_td):
         expected = Series([0, 0, np.nan])
         tm.assert_series_equal(result, expected)
 
+    @pytest.mark.parametrize('names', [(None, None, None),
+                                       ('Egon', 'Venkman', None),
+                                       ('NCC1701D', 'NCC1701D', 'NCC1701D')])
+    def test_td64_series_with_tdi(self, names):
+        # GH#17250 make sure result dtype is correct
+        # GH#19043 make sure names are propogated correctly
+        tdi = pd.TimedeltaIndex(['0 days', '1 day'], name=names[0])
+        ser = Series([Timedelta(hours=3), Timedelta(hours=4)], name=names[1])
+        expected = Series([Timedelta(hours=3), Timedelta(days=1, hours=4)],
+                          name=names[2])
+
+        result = tdi + ser
+        tm.assert_series_equal(result, expected)
+        assert result.dtype == 'timedelta64[ns]'
+
+        result = ser + tdi
+        tm.assert_series_equal(result, expected)
+        assert result.dtype == 'timedelta64[ns]'
+
+        expected = Series([Timedelta(hours=-3), Timedelta(days=1, hours=-4)],
+                          name=names[2])
+
+        result = tdi - ser
+        tm.assert_series_equal(result, expected)
+        assert result.dtype == 'timedelta64[ns]'
+
+        result = ser - tdi
+        tm.assert_series_equal(result, -expected)
+        assert result.dtype == 'timedelta64[ns]'
+
+    @pytest.mark.parametrize('names', [(None, None, None),
+                                       ('Egon', 'Venkman', None),
+                                       ('NCC1701D', 'NCC1701D', 'NCC1701D')])
+    def test_tdi_mul_int_series(self, names):
+        # GH#19042
+        tdi = pd.TimedeltaIndex(['0days', '1day', '2days', '3days', '4days'],
+                                name=names[0])
+        ser = Series([0, 1, 2, 3, 4], dtype=np.int64, name=names[1])
+
+        expected = Series(['0days', '1day', '4days', '9days', '16days'],
+                          dtype='timedelta64[ns]',
+                          name=names[2])
+
+        result = ser * tdi
+        tm.assert_series_equal(result, expected)
+
+        # The direct operation tdi * ser still needs to be fixed.
+        result = ser.__rmul__(tdi)
+        tm.assert_series_equal(result, expected)
+
+    @pytest.mark.parametrize('names', [(None, None, None),
+                                       ('Egon', 'Venkman', None),
+                                       ('NCC1701D', 'NCC1701D', 'NCC1701D')])
+    def test_float_series_rdiv_tdi(self, names):
+        # GH#19042
+        # TODO: the direct operation TimedeltaIndex / Series still
+        # needs to be fixed.
+        tdi = pd.TimedeltaIndex(['0days', '1day', '2days', '3days', '4days'],
+                                name=names[0])
+        ser = Series([1.5, 3, 4.5, 6, 7.5], dtype=np.float64, name=names[1])
+
+        expected = Series([tdi[n] / ser[n] for n in range(len(ser))],
+                          dtype='timedelta64[ns]',
+                          name=names[2])
+
+        result = ser.__rdiv__(tdi)
+        tm.assert_series_equal(result, expected)
+
 
 class TestDatetimeSeriesArithmetic(object):
     @pytest.mark.parametrize(