From 4007381e48a0a591f7b8c1fa744b4fd40606a2e6 Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Tue, 14 Apr 2020 22:13:51 +0200 Subject: [PATCH 01/10] Deprecate week and weekofyear in Series.dt/DatetimeIndex --- pandas/core/arrays/datetimes.py | 34 ++++++++++++++++----- pandas/core/indexes/accessors.py | 26 ++++++++++++++++ pandas/tests/indexes/datetimes/test_misc.py | 8 +++++ pandas/tests/series/test_datetime_values.py | 8 +++++ 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index d34bba68da49c..a0b1684ed3115 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1298,6 +1298,32 @@ def isocalendar(self): iso_calendar_df.iloc[self._isnan] = None return iso_calendar_df + @property + def weekofyear(self): + """ + The week ordinal of the year. + + .. deprecated:: 1.1.0 + + weekofyear and week have been deprecated. + Please use DatetimeIndex.isocalendar().week instead. + """ + import pandas as pd + import warnings + + warnings.warn( + "weekofyear and week have been deprecated, please use " + "DatetimeIndex.isocalendar().week instead, which returns " + "a Series. To exactly reproduce the behavior of week and " + "weekofyear and return an Index, you may call " + "pd.Int64Index(idx.isocalendar().week)", + FutureWarning, + stacklevel=3, + ) + return pd.Int64Index(self.isocalendar().week) + + week = weekofyear + year = _field_accessor( "year", "Y", @@ -1482,14 +1508,6 @@ def isocalendar(self): dtype: int64 """, ) - weekofyear = _field_accessor( - "weekofyear", - "woy", - """ - The week ordinal of the year. - """, - ) - week = weekofyear _dayofweek_doc = """ The day of the week with Monday=0, Sunday=6. diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 90c9f4562515a..dd010da4d4990 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -250,6 +250,32 @@ def isocalendar(self): """ return self._get_values().isocalendar().set_index(self._parent.index) + @property + def weekofyear(self): + """ + The week ordinal of the year. + + .. deprecated:: 1.1.0 + + Series.dt.weekofyear and Series.dt.week have been deprecated. + Please use Series.dt.isocalendar().week instead. + """ + import warnings + + warnings.warn( + "Series.dt.weekofyear and Series.dt.week have been deprecated. " + "Please use Series.dt.isocalendar().week instead.", + FutureWarning, + stacklevel=2, + ) + week_series = self.isocalendar().week + week_series.name = self.name + if week_series.hasnans: + return week_series.astype("float64") + return week_series.astype("int64") + + week = weekofyear + @delegate_names( delegate=TimedeltaArray, accessors=TimedeltaArray._datetimelike_ops, typ="property" diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py index 42a72125ba411..f7466c0c05168 100644 --- a/pandas/tests/indexes/datetimes/test_misc.py +++ b/pandas/tests/indexes/datetimes/test_misc.py @@ -383,6 +383,14 @@ def test_iter_readonly(): list(dti) +def test_week_and_weekofyear_are_deprecated(): + idx = pd.date_range(start="2019-12-29", freq="D", periods=4) + with tm.assert_produces_warning(FutureWarning): + idx.week + with tm.assert_produces_warning(FutureWarning): + idx.weekofyear + + def test_isocalendar_returns_correct_values_close_to_new_year_with_tz(): # GH 6538: Check that DatetimeIndex and its TimeStamp elements # return the same weekofyear accessor close to new year w/ tz diff --git a/pandas/tests/series/test_datetime_values.py b/pandas/tests/series/test_datetime_values.py index e903e850ec36c..dfeb63c747544 100644 --- a/pandas/tests/series/test_datetime_values.py +++ b/pandas/tests/series/test_datetime_values.py @@ -684,3 +684,11 @@ def test_isocalendar(self, input_series, expected_output): expected_output, columns=["year", "week", "day"], dtype="UInt32" ) tm.assert_frame_equal(result, expected_frame) + + +def test_week_and_weekofyear_are_deprecated(): + series = pd.to_datetime(pd.Series(["2020-01-01"])) + with tm.assert_produces_warning(FutureWarning): + series.dt.week + with tm.assert_produces_warning(FutureWarning): + series.dt.weekofyear From 38efb17cbd9af0badd5373cfdce04c35b4b176b6 Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Tue, 14 Apr 2020 23:38:12 +0200 Subject: [PATCH 02/10] Add What's New for deprecations --- doc/source/whatsnew/v1.1.0.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 07849702c646d..18a3f7c6c2562 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -406,6 +406,9 @@ Deprecations arguments (:issue:`27573`). - :func:`pandas.api.types.is_categorical` is deprecated and will be removed in a future version; use `:func:pandas.api.types.is_categorical_dtype` instead (:issue:`33385`) +- :meth:`Series.dt.week` and `Series.dt.weekofyear` are deprecated and will be removed in a future version, use :meth:`Series.dt.isocalendar().week` instead (:issue:`TBD`) +- :meth:`DatetimeIndex.week` and `DatetimeIndex.weekofyear` are deprecated and will be removed in a future version, use :meth:`DatetimeIndex.isocalendar().week` instead (:issue:`TBD`) +- :meth:`DatetimeArray.week` and `DatetimeArray.weekofyear` are deprecated and will be removed in a future version, use :meth:`DatetimeArray.isocalendar().week` instead (:issue:`TBD`) .. --------------------------------------------------------------------------- From f5b42b8585aa2afaab42ec86dc93fb51550cb53a Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Wed, 15 Apr 2020 21:49:48 +0200 Subject: [PATCH 03/10] Ignore week/weekofyear attributes in tests --- pandas/tests/arrays/test_datetimelike.py | 3 +++ pandas/tests/indexes/datetimes/test_misc.py | 3 +++ pandas/tests/indexes/datetimes/test_scalar_compat.py | 2 -- pandas/tests/scalar/test_nat.py | 6 ++++++ pandas/tests/series/test_api.py | 3 +++ pandas/tests/series/test_datetime_values.py | 6 ++++-- 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 5b703cfe8fae5..5b109f7faf363 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -469,6 +469,9 @@ def test_bool_properties(self, datetime_index, propname): @pytest.mark.parametrize("propname", pd.DatetimeIndex._field_ops) def test_int_properties(self, datetime_index, propname): + if propname in ["week", "weekofyear"]: + # GH#33595 Deprecate week and weekofyear + return dti = datetime_index arr = DatetimeArray(dti) diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py index f7466c0c05168..7d25cc7eb7bcd 100644 --- a/pandas/tests/indexes/datetimes/test_misc.py +++ b/pandas/tests/indexes/datetimes/test_misc.py @@ -205,6 +205,9 @@ def test_datetimeindex_accessors(self): # non boolean accessors -> return Index for accessor in DatetimeIndex._field_ops: + if accessor in ["week", "weekofyear"]: + # GH#33595 Deprecate week and weekofyear + continue res = getattr(dti, accessor) assert len(res) == 365 assert isinstance(res, Index) diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 21ee8649172da..653b9692a17ef 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -40,8 +40,6 @@ def test_dti_date_out_of_range(self, data): [ "dayofweek", "dayofyear", - "week", - "weekofyear", "quarter", "days_in_month", "is_month_start", diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index 0e5414a8b4d2d..f1a5574404817 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -66,6 +66,9 @@ def test_nat_vector_field_access(): # on NaT/Timestamp for compat with datetime if field == "weekday": continue + if field in ["week", "weekofyear"]: + # GH#33595 Deprecate week and weekofyear + continue result = getattr(idx, field) expected = Index([getattr(x, field) for x in idx]) @@ -78,6 +81,9 @@ def test_nat_vector_field_access(): # on NaT/Timestamp for compat with datetime if field == "weekday": continue + if field in ["week", "weekofyear"]: + # GH#33595 Deprecate week and weekofyear + continue result = getattr(ser.dt, field) expected = [getattr(x, field) for x in idx] diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index a6430b4525d4a..042841bb4e019 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -719,6 +719,9 @@ def test_dt_accessor_api_for_categorical(self): tm.assert_equal(res, exp) for attr in attr_names: + if attr in ["week", "weekofyear"]: + # GH#33595 Deprecate week and weekofyear + continue res = getattr(c.dt, attr) exp = getattr(s.dt, attr) diff --git a/pandas/tests/series/test_datetime_values.py b/pandas/tests/series/test_datetime_values.py index dfeb63c747544..802e8b6353722 100644 --- a/pandas/tests/series/test_datetime_values.py +++ b/pandas/tests/series/test_datetime_values.py @@ -89,7 +89,8 @@ def compare(s, name): for s in cases: for prop in ok_for_dt: # we test freq below - if prop != "freq": + # we ignore week and weekofyear because they are deprecated + if prop not in ["freq", "week", "weekofyear"]: compare(s, prop) for prop in ok_for_dt_methods: @@ -122,7 +123,8 @@ def compare(s, name): for prop in ok_for_dt: # we test freq below - if prop != "freq": + # we ignore week and weekofyear because they are deprecated + if prop not in ["freq", "week", "weekofyear"]: compare(s, prop) for prop in ok_for_dt_methods: From 22c96f533c0095eb132c537ea69ec1dcb98614af Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Thu, 16 Apr 2020 21:33:34 +0200 Subject: [PATCH 04/10] Remove week/weekofyear attributes from tests --- pandas/tests/groupby/transform/test_transform.py | 4 +++- pandas/tests/indexes/datetimes/test_misc.py | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pandas/tests/groupby/transform/test_transform.py b/pandas/tests/groupby/transform/test_transform.py index e1042bf35acc4..a7a12f2df20f3 100644 --- a/pandas/tests/groupby/transform/test_transform.py +++ b/pandas/tests/groupby/transform/test_transform.py @@ -1022,7 +1022,9 @@ def test_groupby_transform_with_datetimes(func, values): dates = pd.date_range("1/1/2011", periods=10, freq="D") stocks = pd.DataFrame({"price": np.arange(10.0)}, index=dates) - stocks["week_id"] = pd.to_datetime(stocks.index).week + stocks["week_id"] = ( + pd.to_datetime(stocks.index).isocalendar().set_index(stocks.index).week + ) result = stocks.groupby(stocks["week_id"])["price"].transform(func) diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py index 7d25cc7eb7bcd..c64d6590a2f63 100644 --- a/pandas/tests/indexes/datetimes/test_misc.py +++ b/pandas/tests/indexes/datetimes/test_misc.py @@ -156,8 +156,8 @@ def test_datetimeindex_accessors(self): assert dti.dayofyear[0] == 1 assert dti.dayofyear[120] == 121 - assert dti.weekofyear[0] == 1 - assert dti.weekofyear[120] == 18 + assert dti.isocalendar().week[0] == 1 + assert dti.isocalendar().week[120] == 18 assert dti.quarter[0] == 1 assert dti.quarter[120] == 2 @@ -192,7 +192,7 @@ def test_datetimeindex_accessors(self): assert len(dti.microsecond) == 365 assert len(dti.dayofweek) == 365 assert len(dti.dayofyear) == 365 - assert len(dti.weekofyear) == 365 + assert len(dti.isocalendar()) == 365 assert len(dti.quarter) == 365 assert len(dti.is_month_start) == 365 assert len(dti.is_month_end) == 365 @@ -288,7 +288,7 @@ def test_datetimeindex_accessors(self): dates = ["2013/12/29", "2013/12/30", "2013/12/31"] dates = DatetimeIndex(dates, tz="Europe/Brussels") expected = [52, 1, 1] - assert dates.weekofyear.tolist() == expected + assert dates.isocalendar().week.tolist() == expected assert [d.weekofyear for d in dates] == expected # GH 12806 From 9688dd0170729f00637b417a72fdfefcee9c8dd3 Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Thu, 16 Apr 2020 21:35:05 +0200 Subject: [PATCH 05/10] Add isocalendar test to replace removed week/weekofyear tests --- pandas/tests/indexes/datetimes/test_scalar_compat.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 653b9692a17ef..e5d1277aed9cd 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -57,6 +57,12 @@ def test_dti_timestamp_fields(self, field): result = getattr(Timestamp(idx[-1]), field) assert result == expected + def test_dti_timestamp_isocalendar_fields(self): + idx = tm.makeDateIndex(100) + expected = tuple(idx.isocalendar().iloc[-1].to_list()) + result = idx[-1].isocalendar() + assert result == expected + def test_dti_timestamp_freq_fields(self): # extra fields from DatetimeIndex like quarter and week idx = tm.makeDateIndex(100) From cd702ebb49359e686fde495f8eaecf9b17220a54 Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Thu, 16 Apr 2020 22:03:25 +0200 Subject: [PATCH 06/10] Add PR issue number --- doc/source/whatsnew/v1.1.0.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 18a3f7c6c2562..892bbfc102258 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -406,9 +406,9 @@ Deprecations arguments (:issue:`27573`). - :func:`pandas.api.types.is_categorical` is deprecated and will be removed in a future version; use `:func:pandas.api.types.is_categorical_dtype` instead (:issue:`33385`) -- :meth:`Series.dt.week` and `Series.dt.weekofyear` are deprecated and will be removed in a future version, use :meth:`Series.dt.isocalendar().week` instead (:issue:`TBD`) -- :meth:`DatetimeIndex.week` and `DatetimeIndex.weekofyear` are deprecated and will be removed in a future version, use :meth:`DatetimeIndex.isocalendar().week` instead (:issue:`TBD`) -- :meth:`DatetimeArray.week` and `DatetimeArray.weekofyear` are deprecated and will be removed in a future version, use :meth:`DatetimeArray.isocalendar().week` instead (:issue:`TBD`) +- :meth:`Series.dt.week` and `Series.dt.weekofyear` are deprecated and will be removed in a future version, use :meth:`Series.dt.isocalendar().week` instead (:issue:`33595`) +- :meth:`DatetimeIndex.week` and `DatetimeIndex.weekofyear` are deprecated and will be removed in a future version, use :meth:`DatetimeIndex.isocalendar().week` instead (:issue:`33595`) +- :meth:`DatetimeArray.week` and `DatetimeArray.weekofyear` are deprecated and will be removed in a future version, use :meth:`DatetimeArray.isocalendar().week` instead (:issue:`33595`) .. --------------------------------------------------------------------------- From b4eb746f5819b68f6bc2f80bf27d9d46de0277a5 Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Fri, 17 Apr 2020 07:37:31 +0200 Subject: [PATCH 07/10] Import warnings at top of module --- pandas/core/arrays/datetimes.py | 1 - pandas/core/indexes/accessors.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index a0b1684ed3115..e98b7936243fe 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1309,7 +1309,6 @@ def weekofyear(self): Please use DatetimeIndex.isocalendar().week instead. """ import pandas as pd - import warnings warnings.warn( "weekofyear and week have been deprecated, please use " diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index dd010da4d4990..314cfdaa87ec0 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -2,6 +2,7 @@ datetimelike delegation """ from typing import TYPE_CHECKING +import warnings import numpy as np @@ -260,8 +261,6 @@ def weekofyear(self): Series.dt.weekofyear and Series.dt.week have been deprecated. Please use Series.dt.isocalendar().week instead. """ - import warnings - warnings.warn( "Series.dt.weekofyear and Series.dt.week have been deprecated. " "Please use Series.dt.isocalendar().week instead.", From 56874ec9440e23ccdabe54cd5ee109d688dcc19f Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Fri, 17 Apr 2020 07:41:09 +0200 Subject: [PATCH 08/10] Tag deprecation tests with PR --- pandas/tests/indexes/datetimes/test_misc.py | 1 + pandas/tests/series/test_datetime_values.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pandas/tests/indexes/datetimes/test_misc.py b/pandas/tests/indexes/datetimes/test_misc.py index c64d6590a2f63..b9373328eb87f 100644 --- a/pandas/tests/indexes/datetimes/test_misc.py +++ b/pandas/tests/indexes/datetimes/test_misc.py @@ -387,6 +387,7 @@ def test_iter_readonly(): def test_week_and_weekofyear_are_deprecated(): + # GH#33595 Deprecate week and weekofyear idx = pd.date_range(start="2019-12-29", freq="D", periods=4) with tm.assert_produces_warning(FutureWarning): idx.week diff --git a/pandas/tests/series/test_datetime_values.py b/pandas/tests/series/test_datetime_values.py index 802e8b6353722..8d4f3873e9599 100644 --- a/pandas/tests/series/test_datetime_values.py +++ b/pandas/tests/series/test_datetime_values.py @@ -689,6 +689,7 @@ def test_isocalendar(self, input_series, expected_output): def test_week_and_weekofyear_are_deprecated(): + # GH#33595 Deprecate week and weekofyear series = pd.to_datetime(pd.Series(["2020-01-01"])) with tm.assert_produces_warning(FutureWarning): series.dt.week From 0bda2e81499235ce536b0303125e8d78b541aa8a Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Fri, 17 Apr 2020 07:42:56 +0200 Subject: [PATCH 09/10] Refactor test to improve readability --- pandas/tests/groupby/transform/test_transform.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/groupby/transform/test_transform.py b/pandas/tests/groupby/transform/test_transform.py index a7a12f2df20f3..7997247ca0307 100644 --- a/pandas/tests/groupby/transform/test_transform.py +++ b/pandas/tests/groupby/transform/test_transform.py @@ -1022,9 +1022,7 @@ def test_groupby_transform_with_datetimes(func, values): dates = pd.date_range("1/1/2011", periods=10, freq="D") stocks = pd.DataFrame({"price": np.arange(10.0)}, index=dates) - stocks["week_id"] = ( - pd.to_datetime(stocks.index).isocalendar().set_index(stocks.index).week - ) + stocks["week_id"] = dates.isocalendar().set_index(dates).week result = stocks.groupby(stocks["week_id"])["price"].transform(func) From 658506d40fff3fea81b54e69a4cec66ea1ed0282 Mon Sep 17 00:00:00 2001 From: Michael Marino Date: Tue, 21 Apr 2020 18:09:43 +0200 Subject: [PATCH 10/10] Return ndarray from DatetimeArray.week - This is then wrapped by DatetimeIndex.week --- pandas/core/arrays/datetimes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index e98b7936243fe..0f17df4ff74a7 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1308,8 +1308,6 @@ def weekofyear(self): weekofyear and week have been deprecated. Please use DatetimeIndex.isocalendar().week instead. """ - import pandas as pd - warnings.warn( "weekofyear and week have been deprecated, please use " "DatetimeIndex.isocalendar().week instead, which returns " @@ -1319,7 +1317,10 @@ def weekofyear(self): FutureWarning, stacklevel=3, ) - return pd.Int64Index(self.isocalendar().week) + week_series = self.isocalendar().week + if week_series.hasnans: + return week_series.to_numpy(dtype="float64", na_value=np.nan) + return week_series.to_numpy(dtype="int64") week = weekofyear