diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 30a34282889f8..0b50f18e0e290 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1176,10 +1176,7 @@ def _add_timedeltalike_scalar(self, other): # adding a scalar preserves freq new_freq = self.freq - if new_freq is not None: - # fastpath that doesnt require inference - return type(self)(new_values, dtype=self.dtype, freq=new_freq) - return type(self)(new_values, dtype=self.dtype)._with_freq("infer") + return type(self)(new_values, dtype=self.dtype, freq=new_freq) def _add_timedelta_arraylike(self, other): """ @@ -1209,7 +1206,7 @@ def _add_timedelta_arraylike(self, other): mask = (self._isnan) | (other._isnan) new_values[mask] = iNaT - return type(self)(new_values, dtype=self.dtype)._with_freq("infer") + return type(self)(new_values, dtype=self.dtype) def _add_nat(self): """ diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 2ccc0ff2fa31d..1b897d04b4d35 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -698,7 +698,7 @@ def _add_offset(self, offset): # GH#30336 _from_sequence won't be able to infer self.tz return type(self)._from_sequence(result).tz_localize(self.tz) - return type(self)._from_sequence(result)._with_freq("infer") + return type(self)._from_sequence(result) def _sub_datetimelike_scalar(self, other): # subtract a datetime from myself, yielding a ndarray[timedelta64[ns]] diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 25333b3a08dce..d15bec764275e 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -625,6 +625,11 @@ def _set_freq(self, freq): # GH#29843 self._data._with_freq(freq) + def _with_freq(self, freq): + index = self.copy(deep=False) + index._set_freq(freq) + return index + def _shallow_copy(self, values=None, name: Label = lib.no_default): name = self.name if name is lib.no_default else name cache = self._cache.copy() if values is None else {} diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 765b948f13e96..7fafebd0a64f3 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -57,7 +57,6 @@ "std", "median", "_format_native_types", - "freq", ], TimedeltaArray, ) diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index 56c5647d865d3..a8e9ad9ff7cc9 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -865,7 +865,7 @@ def test_dt64arr_add_sub_td64ndarray(self, tz_naive_fixture, box_with_array): tdi = pd.TimedeltaIndex(["-1 Day", "-1 Day", "-1 Day"]) tdarr = tdi.values - expected = pd.date_range("2015-12-31", periods=3, tz=tz) + expected = pd.date_range("2015-12-31", "2016-01-02", periods=3, tz=tz) dtarr = tm.box_expected(dti, box_with_array) expected = tm.box_expected(expected, box_with_array) @@ -875,7 +875,7 @@ def test_dt64arr_add_sub_td64ndarray(self, tz_naive_fixture, box_with_array): result = tdarr + dtarr tm.assert_equal(result, expected) - expected = pd.date_range("2016-01-02", periods=3, tz=tz) + expected = pd.date_range("2016-01-02", "2016-01-04", periods=3, tz=tz) expected = tm.box_expected(expected, box_with_array) result = dtarr - tdarr @@ -1385,13 +1385,13 @@ def test_dt64arr_add_sub_DateOffset(self, box_with_array): s = tm.box_expected(s, box_with_array) result = s + pd.DateOffset(years=1) result2 = pd.DateOffset(years=1) + s - exp = date_range("2001-01-01", "2001-01-31", name="a") + exp = date_range("2001-01-01", "2001-01-31", name="a")._with_freq(None) exp = tm.box_expected(exp, box_with_array) tm.assert_equal(result, exp) tm.assert_equal(result2, exp) result = s - pd.DateOffset(years=1) - exp = date_range("1999-01-01", "1999-01-31", name="a") + exp = date_range("1999-01-01", "1999-01-31", name="a")._with_freq(None) exp = tm.box_expected(exp, box_with_array) tm.assert_equal(result, exp) @@ -1553,7 +1553,7 @@ def test_dti_add_sub_nonzero_mth_offset( mth = getattr(date, op) result = mth(offset) - expected = pd.DatetimeIndex(exp, tz=tz, freq=exp_freq) + expected = pd.DatetimeIndex(exp, tz=tz) expected = tm.box_expected(expected, box_with_array, False) tm.assert_equal(result, expected) @@ -2344,29 +2344,29 @@ def test_ufunc_coercions(self): assert result.freq == "2D" exp = date_range("2010-12-31", periods=3, freq="2D", name="x") + for result in [idx - delta, np.subtract(idx, delta)]: assert isinstance(result, DatetimeIndex) tm.assert_index_equal(result, exp) assert result.freq == "2D" + # When adding/subtracting an ndarray (which has no .freq), the result + # does not infer freq + idx = idx._with_freq(None) delta = np.array( [np.timedelta64(1, "D"), np.timedelta64(2, "D"), np.timedelta64(3, "D")] ) - exp = DatetimeIndex( - ["2011-01-02", "2011-01-05", "2011-01-08"], freq="3D", name="x" - ) + exp = DatetimeIndex(["2011-01-02", "2011-01-05", "2011-01-08"], name="x") + for result in [idx + delta, np.add(idx, delta)]: - assert isinstance(result, DatetimeIndex) tm.assert_index_equal(result, exp) - assert result.freq == "3D" + assert result.freq == exp.freq - exp = DatetimeIndex( - ["2010-12-31", "2011-01-01", "2011-01-02"], freq="D", name="x" - ) + exp = DatetimeIndex(["2010-12-31", "2011-01-01", "2011-01-02"], name="x") for result in [idx - delta, np.subtract(idx, delta)]: assert isinstance(result, DatetimeIndex) tm.assert_index_equal(result, exp) - assert result.freq == "D" + assert result.freq == exp.freq @pytest.mark.parametrize( "names", [("foo", None, None), ("baz", "bar", None), ("bar", "bar", "bar")] diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index beb16c9549cc4..e2b1a4499e197 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -487,6 +487,7 @@ def test_timedelta(self, freq): shifted = index + timedelta(1) back = shifted + timedelta(-1) + back = back._with_freq("infer") tm.assert_index_equal(index, back) if freq == "D": diff --git a/pandas/tests/indexes/timedeltas/test_shift.py b/pandas/tests/indexes/timedeltas/test_shift.py index c02aa71d97aac..1282bd510ec17 100644 --- a/pandas/tests/indexes/timedeltas/test_shift.py +++ b/pandas/tests/indexes/timedeltas/test_shift.py @@ -38,7 +38,8 @@ def test_tdi_shift_minutes(self): def test_tdi_shift_int(self): # GH#8083 - trange = pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) + tdi = pd.to_timedelta(range(5), unit="d") + trange = tdi._with_freq("infer") + pd.offsets.Hour(1) result = trange.shift(1) expected = TimedeltaIndex( [ @@ -54,7 +55,8 @@ def test_tdi_shift_int(self): def test_tdi_shift_nonstandard_freq(self): # GH#8083 - trange = pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) + tdi = pd.to_timedelta(range(5), unit="d") + trange = tdi._with_freq("infer") + pd.offsets.Hour(1) result = trange.shift(3, freq="2D 1s") expected = TimedeltaIndex( [ diff --git a/pandas/tests/indexes/timedeltas/test_timedelta.py b/pandas/tests/indexes/timedeltas/test_timedelta.py index 129bdef870a14..f724badd51da8 100644 --- a/pandas/tests/indexes/timedeltas/test_timedelta.py +++ b/pandas/tests/indexes/timedeltas/test_timedelta.py @@ -30,7 +30,9 @@ def indices(self): return tm.makeTimedeltaIndex(10) def create_index(self) -> TimedeltaIndex: - return pd.to_timedelta(range(5), unit="d") + pd.offsets.Hour(1) + index = pd.to_timedelta(range(5), unit="d")._with_freq("infer") + assert index.freq == "D" + return index + pd.offsets.Hour(1) def test_numeric_compat(self): # Dummy method to override super's version; this test is now done