diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 2b68ddf3d8918..5ec56018d7107 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -407,6 +407,9 @@ or ``matplotlib.Axes.plot``. See :ref:`plotting.formatters` for more. - Removed the previously deprecated :meth:`DataFrame.get_ftype_counts`, :meth:`Series.get_ftype_counts` (:issue:`18243`) - Removed the previously deprecated :meth:`Index.get_duplicated`, use ``idx[idx.duplicated()].unique()`` instead (:issue:`20239`) - Removed the previously deprecated :meth:`Series.clip_upper`, :meth:`Series.clip_lower`, :meth:`DataFrame.clip_upper`, :meth:`DataFrame.clip_lower` (:issue:`24203`) +- Removed the ability to alter :attr:`DatetimeIndex.freq`, :attr:`TimedeltaIndex.freq`, or :attr:`PeriodIndex.freq` (:issue:`20772`) +- Removed the previously deprecated :attr:`DatetimeIndex.offset` (:issue:`20730`) +- Removed the previously deprecated :meth:`DatetimeIndex.asobject`, :meth:`TimedeltaIndex.asobject`, :meth:`PeriodIndex.asobject`, use ``astype(object)`` instead (:issue:`29801`) - Removed previously deprecated "order" argument from :func:`factorize` (:issue:`19751`) - Removed previously deprecated "v" argument from :meth:`FrozenNDarray.searchsorted`, use "value" instead (:issue:`22672`) - :func:`read_stata` and :meth:`DataFrame.to_stata` no longer supports the "encoding" argument (:issue:`21400`) diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index e420cf0cb0d78..b41227871ae03 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -3,7 +3,6 @@ """ import operator from typing import Set -import warnings import numpy as np @@ -104,11 +103,6 @@ def freq(self): """ return self._data.freq - @freq.setter - def freq(self, value): - # validation is handled by _data setter - self._data.freq = value - @property def freqstr(self): """ @@ -332,23 +326,6 @@ def take(self, indices, axis=0, allow_fill=True, fill_value=None, **kwargs): _na_value = NaT """The expected NA value to use with this index.""" - @property - def asobject(self): - """ - Return object Index which contains boxed values. - - .. deprecated:: 0.23.0 - Use ``astype(object)`` instead. - - *this is an internal non-public method* - """ - warnings.warn( - "'asobject' is deprecated. Use 'astype(object)' instead", - FutureWarning, - stacklevel=2, - ) - return self.astype(object) - def _convert_tolerance(self, tolerance, target): tolerance = np.asarray(to_timedelta(tolerance).to_numpy()) @@ -612,7 +589,8 @@ def intersection(self, other, sort=False): result = Index.intersection(self, other, sort=sort) if isinstance(result, type(self)): if result.freq is None: - result.freq = to_offset(result.inferred_freq) + # TODO: find a less code-smelly way to set this + result._data._freq = to_offset(result.inferred_freq) return result elif ( @@ -626,7 +604,9 @@ def intersection(self, other, sort=False): # Invalidate the freq of `result`, which may not be correct at # this point, depending on the values. - result.freq = None + + # TODO: find a less code-smelly way to set this + result._data._freq = None if hasattr(self, "tz"): result = self._shallow_copy( result._values, name=result.name, tz=result.tz, freq=None @@ -634,7 +614,8 @@ def intersection(self, other, sort=False): else: result = self._shallow_copy(result._values, name=result.name, freq=None) if result.freq is None: - result.freq = to_offset(result.inferred_freq) + # TODO: find a less code-smelly way to set this + result._data._freq = to_offset(result.inferred_freq) return result # to make our life easier, "sort" the two ranges diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index b6891bc7e2b59..ab9f57ff9ac69 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -467,7 +467,7 @@ def _convert_for_op(self, value): @Appender(Index.difference.__doc__) def difference(self, other, sort=None): new_idx = super().difference(other, sort=sort) - new_idx.freq = None + new_idx._data._freq = None return new_idx # -------------------------------------------------------------------- @@ -522,7 +522,7 @@ def _union(self, other, sort): if result.freq is None and ( this.freq is not None or other.freq is not None ): - result.freq = to_offset(result.inferred_freq) + result._data._freq = to_offset(result.inferred_freq) return result def union_many(self, others): @@ -1208,7 +1208,7 @@ def offset(self, value): ) ) warnings.warn(msg, FutureWarning, stacklevel=2) - self.freq = value + self._data.freq = value def __getitem__(self, key): result = self._data.__getitem__(key) diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index cae1380e930f1..cdd0e600c888d 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -313,21 +313,6 @@ def values(self): def freq(self) -> DateOffset: return self._data.freq - @freq.setter - def freq(self, value): - value = Period._maybe_convert_freq(value) - # TODO: When this deprecation is enforced, PeriodIndex.freq can - # be removed entirely, and we'll just inherit. - msg = ( - "Setting {cls}.freq has been deprecated and will be " - "removed in a future version; use {cls}.asfreq instead. " - "The {cls}.freq setter is not guaranteed to work." - ) - warnings.warn(msg.format(cls=type(self).__name__), FutureWarning, stacklevel=2) - # PeriodArray._freq isn't actually mutable. We set the private _freq - # here, but people shouldn't be doing this anyway. - self._data._freq = value - def _shallow_copy(self, values=None, **kwargs): # TODO: simplify, figure out type of values if values is None: diff --git a/pandas/core/indexes/timedeltas.py b/pandas/core/indexes/timedeltas.py index 1fd824235c2be..7a7720f730312 100644 --- a/pandas/core/indexes/timedeltas.py +++ b/pandas/core/indexes/timedeltas.py @@ -356,7 +356,8 @@ def _union(self, other, sort): result = Index._union(this, other, sort=sort) if isinstance(result, TimedeltaIndex): if result.freq is None: - result.freq = to_offset(result.inferred_freq) + # TODO: find a less code-smelly way to set this + result._data._freq = to_offset(result.inferred_freq) return result def join(self, other, how="left", level=None, return_indexers=False, sort=False): @@ -409,7 +410,8 @@ def intersection(self, other, sort=False): @Appender(Index.difference.__doc__) def difference(self, other, sort=None): new_idx = super().difference(other, sort=sort) - new_idx.freq = None + # TODO: find a less code-smelly way to set this + new_idx._data._freq = None return new_idx def _wrap_joined_index(self, joined, other): diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 25731c4e1c54c..2433e3f52b4a9 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -1080,7 +1080,8 @@ def _downsample(self, how, **kwargs): if not len(ax): # reset to the new freq obj = obj.copy() - obj.index.freq = self.freq + # TODO: find a less code-smelly way to set this + obj.index._data._freq = self.freq return obj # do we have a regular frequency diff --git a/pandas/tests/arrays/categorical/test_constructors.py b/pandas/tests/arrays/categorical/test_constructors.py index 6eb26d26e14bd..59017a1442cb4 100644 --- a/pandas/tests/arrays/categorical/test_constructors.py +++ b/pandas/tests/arrays/categorical/test_constructors.py @@ -311,7 +311,7 @@ def test_constructor_with_datetimelike(self, dtl): c = Categorical(s) expected = type(dtl)(s) - expected.freq = None + expected._data.freq = None tm.assert_index_equal(c.categories, expected) tm.assert_numpy_array_equal(c.codes, np.arange(5, dtype="int8")) @@ -322,7 +322,7 @@ def test_constructor_with_datetimelike(self, dtl): c = Categorical(s2) expected = type(dtl)(s2.dropna()) - expected.freq = None + expected._data.freq = None tm.assert_index_equal(c.categories, expected) diff --git a/pandas/tests/indexes/datetimelike.py b/pandas/tests/indexes/datetimelike.py index f7cded9f44918..e6e38ce9921f5 100644 --- a/pandas/tests/indexes/datetimelike.py +++ b/pandas/tests/indexes/datetimelike.py @@ -81,7 +81,7 @@ def test_map_dictlike(self, mapper): # don't compare the freqs if isinstance(expected, pd.DatetimeIndex): - expected.freq = None + expected._data.freq = None result = index.map(mapper(expected, index)) tm.assert_index_equal(result, expected) @@ -95,10 +95,3 @@ def test_map_dictlike(self, mapper): expected = pd.Index([np.nan] * len(index)) result = index.map(mapper([], [])) tm.assert_index_equal(result, expected) - - def test_asobject_deprecated(self): - # GH18572 - d = self.create_index() - with tm.assert_produces_warning(FutureWarning): - i = d.asobject - assert isinstance(i, pd.Index) diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index ba7e3c9d38861..f95137cd1bf88 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -798,7 +798,7 @@ def test_daterange_bug_456(self): # GH #456 rng1 = bdate_range("12/5/2011", "12/5/2011") rng2 = bdate_range("12/2/2011", "12/5/2011") - rng2.freq = BDay() + rng2._data.freq = BDay() # TODO: shouldnt this already be set? result = rng1.union(rng2) assert isinstance(result, DatetimeIndex) @@ -855,7 +855,7 @@ def test_daterange_bug_456(self): # GH #456 rng1 = bdate_range("12/5/2011", "12/5/2011", freq="C") rng2 = bdate_range("12/2/2011", "12/5/2011", freq="C") - rng2.freq = CDay() + rng2._data.freq = CDay() # TODO: shouldnt this already be set? result = rng1.union(rng2) assert isinstance(result, DatetimeIndex) diff --git a/pandas/tests/indexes/datetimes/test_ops.py b/pandas/tests/indexes/datetimes/test_ops.py index 2944767ba4c02..c9c5963e5590c 100644 --- a/pandas/tests/indexes/datetimes/test_ops.py +++ b/pandas/tests/indexes/datetimes/test_ops.py @@ -413,12 +413,12 @@ def test_freq_setter(self, values, freq, tz): idx = DatetimeIndex(values, tz=tz) # can set to an offset, converting from string if necessary - idx.freq = freq + idx._data.freq = freq assert idx.freq == freq assert isinstance(idx.freq, ABCDateOffset) # can reset to None - idx.freq = None + idx._data.freq = None assert idx.freq is None def test_freq_setter_errors(self): @@ -431,11 +431,11 @@ def test_freq_setter_errors(self): "passed frequency 5D" ) with pytest.raises(ValueError, match=msg): - idx.freq = "5D" + idx._data.freq = "5D" # setting with non-freq string with pytest.raises(ValueError, match="Invalid frequency"): - idx.freq = "foo" + idx._data.freq = "foo" def test_offset_deprecated(self): # GH 20716 diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py index 67fc70c17d7bc..3fb39b2081d83 100644 --- a/pandas/tests/indexes/datetimes/test_setops.py +++ b/pandas/tests/indexes/datetimes/test_setops.py @@ -157,7 +157,7 @@ def test_union_bug_4564(self, sort): def test_union_freq_both_none(self, sort): # GH11086 expected = bdate_range("20150101", periods=10) - expected.freq = None + expected._data.freq = None result = expected.union(expected, sort=sort) tm.assert_index_equal(result, expected) diff --git a/pandas/tests/indexes/period/test_ops.py b/pandas/tests/indexes/period/test_ops.py index 96042f4dbaba2..6690a8207eb58 100644 --- a/pandas/tests/indexes/period/test_ops.py +++ b/pandas/tests/indexes/period/test_ops.py @@ -343,5 +343,5 @@ def test_freq_setter_deprecated(self): idx.freq # warning for setter - with tm.assert_produces_warning(FutureWarning): + with pytest.raises(AttributeError, match="can't set attribute"): idx.freq = pd.offsets.Day() diff --git a/pandas/tests/indexes/timedeltas/test_ops.py b/pandas/tests/indexes/timedeltas/test_ops.py index 54ed5058b5253..df448f4332d38 100644 --- a/pandas/tests/indexes/timedeltas/test_ops.py +++ b/pandas/tests/indexes/timedeltas/test_ops.py @@ -286,12 +286,12 @@ def test_freq_setter(self, values, freq): idx = TimedeltaIndex(values) # can set to an offset, converting from string if necessary - idx.freq = freq + idx._data.freq = freq assert idx.freq == freq assert isinstance(idx.freq, ABCDateOffset) # can reset to None - idx.freq = None + idx._data.freq = None assert idx.freq is None def test_freq_setter_errors(self): @@ -304,13 +304,13 @@ def test_freq_setter_errors(self): "passed frequency 5D" ) with pytest.raises(ValueError, match=msg): - idx.freq = "5D" + idx._data.freq = "5D" # setting with a non-fixed frequency msg = r"<2 \* BusinessDays> is a non-fixed frequency" with pytest.raises(ValueError, match=msg): - idx.freq = "2B" + idx._data.freq = "2B" # setting with non-freq string with pytest.raises(ValueError, match="Invalid frequency"): - idx.freq = "foo" + idx._data.freq = "foo" diff --git a/pandas/tests/reshape/test_concat.py b/pandas/tests/reshape/test_concat.py index 323b3126c2461..795bbabdfad50 100644 --- a/pandas/tests/reshape/test_concat.py +++ b/pandas/tests/reshape/test_concat.py @@ -2774,5 +2774,5 @@ def test_concat_datetimeindex_freq(): # Non-monotonic index result result = pd.concat([expected[50:], expected[:50]]) expected = pd.DataFrame(data[50:] + data[:50], index=dr[50:].append(dr[:50])) - expected.index.freq = None + expected.index._data.freq = None tm.assert_frame_equal(result, expected)