Repository: pandas-dev/pandas
Pull request: BUG: appending a Timedelta to Series incorrectly casts to integer
- [x] closes #22717
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
Timedelta interpreted as int upon first insertion into Series
#### Code Sample, a copy-pastable example if possible

```python
>>> s = pd.Series(['x'])
>>> s['td'] = pd.Timedelta('9 days')
>>> s
0                   x
td    777600000000000
dtype: object

# However, *if the key already exists*, it works as expected:
>>> s['td_2'] = 0
>>> s['td_2'] = pd.Timedelta('9 days')
>>> s
0                    x
td     777600000000000
td_2   9 days 00:00:00
dtype: object
```
#### Problem description

Timedeltas should remain Timedeltas when inserted into object Series, particularly because they are nice to work with. :sweat_smile: 

#### Expected Output

```python
>>> s = pd.Series(['x'])
>>> s['td'] = pd.Timedelta('9 days')
>>> s
0                    x
td     9 days 00:00:00
dtype: object

```

#### Output of ``pd.show_versions()``

<details>

pandas 0.23.3

</details>

Hints from issue discussion:
xref #13829

Commit message: use concat_compat

Patch to review:
diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst
index 4908bf6495d61..b84dad51997a1 100644
--- a/doc/source/whatsnew/v0.25.0.rst
+++ b/doc/source/whatsnew/v0.25.0.rst
@@ -1051,6 +1051,7 @@ Indexing
 - Bug in :class:`Categorical` and  :class:`CategoricalIndex` with :class:`Interval` values when using the ``in`` operator (``__contains``) with objects that are not comparable to the values in the ``Interval`` (:issue:`23705`)
 - Bug in :meth:`DataFrame.loc` and :meth:`DataFrame.iloc` on a :class:`DataFrame` with a single timezone-aware datetime64[ns] column incorrectly returning a scalar instead of a :class:`Series` (:issue:`27110`)
 - Bug in :class:`CategoricalIndex` and :class:`Categorical` incorrectly raising ``ValueError`` instead of ``TypeError`` when a list is passed using the ``in`` operator (``__contains__``) (:issue:`21729`)
+- Bug in setting a new value in a :class:`Series` with a :class:`Timedelta` object incorrectly casting the value to an integer (:issue:`22717`)
 -
 
 Missing
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
index 612a857897a0c..5f0473e43382b 100755
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -20,6 +20,7 @@
     is_sequence,
     is_sparse,
 )
+from pandas.core.dtypes.concat import _concat_compat
 from pandas.core.dtypes.generic import ABCDataFrame, ABCSeries
 from pandas.core.dtypes.missing import _infer_fill_value, isna
 
@@ -429,11 +430,9 @@ def _setitem_with_indexer(self, indexer, value):
                     # this preserves dtype of the value
                     new_values = Series([value])._values
                     if len(self.obj._values):
-                        try:
-                            new_values = np.concatenate([self.obj._values, new_values])
-                        except TypeError:
-                            as_obj = self.obj.astype(object)
-                            new_values = np.concatenate([as_obj, new_values])
+                        # GH#22717 np.concatenate incorrect casts
+                        #  timedelta64 to integer
+                        new_values = _concat_compat([self.obj._values, new_values])
                     self.obj._data = self.obj._constructor(
                         new_values, index=new_index, name=self.obj.name
                     )._data
diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py
index c8342c54e9b5d..93c279cd36e62 100644
--- a/pandas/tests/series/indexing/test_indexing.py
+++ b/pandas/tests/series/indexing/test_indexing.py
@@ -654,6 +654,31 @@ def test_timedelta_assignment():
     tm.assert_series_equal(s, expected)
 
 
+@pytest.mark.parametrize(
+    "td",
+    [
+        pd.Timedelta("9 days"),
+        pd.Timedelta("9 days").to_timedelta64(),
+        pd.Timedelta("9 days").to_pytimedelta(),
+    ],
+)
+def test_append_timedelta_does_not_cast(td):
+    # GH#22717 inserting a Timedelta should _not_ cast to int64
+    expected = pd.Series(["x", td], index=[0, "td"], dtype=object)
+
+    # FIXME: these should either _both_ cast the object to Timedelta
+    #  or both retain type(td)
+    ser = pd.Series(["x"])
+    ser["td"] = td
+    tm.assert_series_equal(ser, expected)
+    assert isinstance(ser["td"], pd.Timedelta)
+
+    ser = pd.Series(["x"])
+    ser.loc["td"] = pd.Timedelta("9 days")
+    tm.assert_series_equal(ser, expected)
+    assert isinstance(ser["td"], pd.Timedelta)
+
+
 def test_underlying_data_conversion():
     # GH 4080
     df = DataFrame({c: [1, 2, 3] for c in ["a", "b", "c"]})


Code review comments:
diff_hunk: @@ -429,11 +430,9 @@ def _setitem_with_indexer(self, indexer, value):
                     # this preserves dtype of the value
                     new_values = Series([value])._values
                     if len(self.obj._values):
-                        try:
-                            new_values = np.concatenate([self.obj._values, new_values])
-                        except TypeError:
-                            as_obj = self.obj.astype(object)
-                            new_values = np.concatenate([as_obj, new_values])
+                        # GH#22717 np.concatenate incorrect casts
path: pandas/core/indexing.py
text: @user1:
I don't think this comment here is useful (pls remove in next pass)