diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 82c43811c0444..f892dcdd364b9 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -479,6 +479,7 @@ Indexing - Bug in :meth:`DataFrame.copy` _item_cache not invalidated after copy causes post-copy value updates to not be reflected (:issue:`31784`) - Bug in `Series.__getitem__` with an integer key and a :class:`MultiIndex` with leading integer level failing to raise ``KeyError`` if the key is not present in the first level (:issue:`33355`) - Bug in :meth:`DataFrame.iloc` when slicing a single column-:class:`DataFrame`` with ``ExtensionDtype`` (e.g. ``df.iloc[:, :1]``) returning an invalid result (:issue:`32957`) +- Bug in :meth:`DatetimeIndex.insert` and :meth:`TimedeltaIndex.insert` causing index ``freq`` to be lost when setting an element into an empty :class:`Series` (:issue:33573`) Missing ^^^^^^^ diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index c15680a47d216..b83b64c144681 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -941,6 +941,10 @@ def insert(self, loc, item): freq = self.freq elif (loc == len(self)) and item - self.freq == self[-1]: freq = self.freq + elif self.freq is not None: + # Adding a single item to an empty index may preserve freq + if self.freq.is_on_offset(item): + freq = self.freq item = item.asm8 try: diff --git a/pandas/tests/indexes/datetimes/test_insert.py b/pandas/tests/indexes/datetimes/test_insert.py index 4abb4f0006444..034e1c6a4e1b0 100644 --- a/pandas/tests/indexes/datetimes/test_insert.py +++ b/pandas/tests/indexes/datetimes/test_insert.py @@ -24,6 +24,20 @@ def test_insert_invalid_na(self, tz): with pytest.raises(TypeError, match="incompatible label"): idx.insert(0, np.timedelta64("NaT")) + def test_insert_empty_preserves_freq(self, tz_naive_fixture): + # GH#33573 + tz = tz_naive_fixture + dti = DatetimeIndex([], tz=tz, freq="D") + item = Timestamp("2017-04-05").tz_localize(tz) + + result = dti.insert(0, item) + assert result.freq == dti.freq + + # But not when we insert an item that doesnt conform to freq + dti = DatetimeIndex([], tz=tz, freq="W-THU") + result = dti.insert(0, item) + assert result.freq is None + def test_insert(self): idx = DatetimeIndex(["2000-01-04", "2000-01-01", "2000-01-02"], name="idx") diff --git a/pandas/tests/indexes/timedeltas/test_insert.py b/pandas/tests/indexes/timedeltas/test_insert.py index b214e009db869..e65c871428bab 100644 --- a/pandas/tests/indexes/timedeltas/test_insert.py +++ b/pandas/tests/indexes/timedeltas/test_insert.py @@ -93,9 +93,15 @@ def test_insert_dont_cast_strings(self): def test_insert_empty(self): # Corner case inserting with length zero doesnt raise IndexError + # GH#33573 for freq preservation idx = timedelta_range("1 Day", periods=3) td = idx[0] - idx[:0].insert(0, td) - idx[:0].insert(1, td) - idx[:0].insert(-1, td) + result = idx[:0].insert(0, td) + assert result.freq == "D" + + result = idx[:0].insert(1, td) + assert result.freq == "D" + + result = idx[:0].insert(-1, td) + assert result.freq == "D" diff --git a/pandas/tests/series/indexing/test_indexing.py b/pandas/tests/series/indexing/test_indexing.py index 522ed4df96ad2..900374824eb25 100644 --- a/pandas/tests/series/indexing/test_indexing.py +++ b/pandas/tests/series/indexing/test_indexing.py @@ -284,6 +284,8 @@ def test_setitem(datetime_series, string_series): expected = string_series.append(app) tm.assert_series_equal(s, expected) + +def test_setitem_empty_series(): # Test for issue #10193 key = pd.Timestamp("2012-01-01") series = pd.Series(dtype=object) @@ -291,10 +293,12 @@ def test_setitem(datetime_series, string_series): expected = pd.Series(47, [key]) tm.assert_series_equal(series, expected) + # GH#33573 our index should retain its freq series = pd.Series([], pd.DatetimeIndex([], freq="D"), dtype=object) series[key] = 47 expected = pd.Series(47, pd.DatetimeIndex([key], freq="D")) tm.assert_series_equal(series, expected) + assert series.index.freq == expected.index.freq def test_setitem_dtypes():