From 8a3eaef8b6f30f65cbc00bcdbaa340c26d59f23d Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 30 Sep 2020 15:24:42 +0100 Subject: [PATCH 1/3] TYP: some more static definitions of methods for DatetimeIndex --- pandas/core/indexes/datetimes.py | 71 +++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 016544d823ae3..3b93f06da831a 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1,6 +1,7 @@ from datetime import date, datetime, time, timedelta, tzinfo +import functools import operator -from typing import Optional +from typing import TYPE_CHECKING, Callable, Optional, cast import warnings import numpy as np @@ -8,9 +9,9 @@ from pandas._libs import NaT, Period, Timestamp, index as libindex, lib from pandas._libs.tslibs import Resolution, ints_to_pydatetime, parsing, to_offset from pandas._libs.tslibs.offsets import prefix_mapping -from pandas._typing import DtypeObj, Label +from pandas._typing import DtypeObj, F, Label from pandas.errors import InvalidIndexError -from pandas.util._decorators import cache_readonly, doc +from pandas.util._decorators import cache_readonly from pandas.core.dtypes.common import ( DT64NS_DTYPE, @@ -21,6 +22,7 @@ is_integer, is_scalar, ) +from pandas.core.dtypes.generic import ABCDataFrame from pandas.core.dtypes.missing import is_valid_nat_for_dtype from pandas.core.arrays.datetimes import DatetimeArray, tz_to_dtype @@ -30,6 +32,29 @@ from pandas.core.indexes.extension import inherit_names from pandas.core.tools.times import to_time +if TYPE_CHECKING: + from pandas import DataFrame, PeriodIndex, TimedeltaIndex + + +def dispatch_to_array_and_wrap(delegate) -> Callable[[F], F]: + def decorator(func: F) -> F: + name = func.__name__ + + @functools.wraps(func) + def method_wrapper(self, *args, **kwargs): + + result = getattr(self._data, name)(*args, **kwargs) + if isinstance(result, type(self._data)): + return type(self)._simple_new(result, name=self.name) + elif isinstance(result, ABCDataFrame): + return result.set_index(self) + return Index(result, name=self.name) + + method_wrapper.__doc__ = getattr(delegate, name).__doc__ + return cast(F, method_wrapper) + + return decorator + def _new_DatetimeIndex(cls, d): """ @@ -64,8 +89,7 @@ def _new_DatetimeIndex(cls, d): @inherit_names( - ["to_perioddelta", "to_julian_date", "strftime", "isocalendar"] - + DatetimeArray._field_ops + DatetimeArray._field_ops + [ method for method in DatetimeArray._datetimelike_methods @@ -185,6 +209,8 @@ class DatetimeIndex(DatetimeTimedeltaMixin): ceil to_period to_perioddelta + to_julian_date + isocalendar to_pydatetime to_series to_frame @@ -220,24 +246,37 @@ class DatetimeIndex(DatetimeTimedeltaMixin): tz: Optional[tzinfo] # -------------------------------------------------------------------- - # methods that dispatch to array and wrap result in DatetimeIndex + # methods that dispatch to DatetimeArray and wrap result + + @dispatch_to_array_and_wrap(DatetimeArray) + def strftime(self, date_format) -> Index: + pass - @doc(DatetimeArray.tz_convert) + @dispatch_to_array_and_wrap(DatetimeArray) def tz_convert(self, tz) -> "DatetimeIndex": - arr = self._data.tz_convert(tz) - return type(self)._simple_new(arr, name=self.name) + pass - @doc(DatetimeArray.tz_localize) + @dispatch_to_array_and_wrap(DatetimeArray) def tz_localize( self, tz, ambiguous="raise", nonexistent="raise" ) -> "DatetimeIndex": - arr = self._data.tz_localize(tz, ambiguous, nonexistent) - return type(self)._simple_new(arr, name=self.name) + pass + + @dispatch_to_array_and_wrap(DatetimeArray) + def to_period(self, freq=None) -> "PeriodIndex": + pass + + @dispatch_to_array_and_wrap(DatetimeArray) + def to_perioddelta(self, freq) -> "TimedeltaIndex": + pass + + @dispatch_to_array_and_wrap(DatetimeArray) + def to_julian_date(self) -> Index: + pass - @doc(DatetimeArray.to_period) - def to_period(self, freq=None) -> "DatetimeIndex": - arr = self._data.to_period(freq) - return type(self)._simple_new(arr, name=self.name) + @dispatch_to_array_and_wrap(DatetimeArray) + def isocalendar(self) -> "DataFrame": + pass # -------------------------------------------------------------------- # Constructors From ca5ee61205e7f3cde7df77bd812779ea4c04bf24 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 30 Sep 2020 19:13:35 +0100 Subject: [PATCH 2/3] reduce the layers of wrapping by one --- pandas/core/indexes/datetimes.py | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 3b93f06da831a..3b9505d308413 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1,7 +1,7 @@ from datetime import date, datetime, time, timedelta, tzinfo import functools import operator -from typing import TYPE_CHECKING, Callable, Optional, cast +from typing import TYPE_CHECKING, Optional, cast import warnings import numpy as np @@ -11,7 +11,7 @@ from pandas._libs.tslibs.offsets import prefix_mapping from pandas._typing import DtypeObj, F, Label from pandas.errors import InvalidIndexError -from pandas.util._decorators import cache_readonly +from pandas.util._decorators import cache_readonly, doc from pandas.core.dtypes.common import ( DT64NS_DTYPE, @@ -36,24 +36,20 @@ from pandas import DataFrame, PeriodIndex, TimedeltaIndex -def dispatch_to_array_and_wrap(delegate) -> Callable[[F], F]: - def decorator(func: F) -> F: - name = func.__name__ +def dispatch_to_array_and_wrap(func: F) -> F: + name = func.__name__ - @functools.wraps(func) - def method_wrapper(self, *args, **kwargs): + @functools.wraps(func) + def method_wrapper(self, *args, **kwargs): - result = getattr(self._data, name)(*args, **kwargs) - if isinstance(result, type(self._data)): - return type(self)._simple_new(result, name=self.name) - elif isinstance(result, ABCDataFrame): - return result.set_index(self) - return Index(result, name=self.name) + result = getattr(self._data, name)(*args, **kwargs) + if isinstance(result, type(self._data)): + return type(self)._simple_new(result, name=self.name) + elif isinstance(result, ABCDataFrame): + return result.set_index(self) + return Index(result, name=self.name) - method_wrapper.__doc__ = getattr(delegate, name).__doc__ - return cast(F, method_wrapper) - - return decorator + return cast(F, method_wrapper) def _new_DatetimeIndex(cls, d): @@ -209,8 +205,6 @@ class DatetimeIndex(DatetimeTimedeltaMixin): ceil to_period to_perioddelta - to_julian_date - isocalendar to_pydatetime to_series to_frame @@ -248,33 +242,40 @@ class DatetimeIndex(DatetimeTimedeltaMixin): # -------------------------------------------------------------------- # methods that dispatch to DatetimeArray and wrap result - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.strftime) + @dispatch_to_array_and_wrap def strftime(self, date_format) -> Index: pass - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.tz_convert) + @dispatch_to_array_and_wrap def tz_convert(self, tz) -> "DatetimeIndex": pass - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.tz_localize) + @dispatch_to_array_and_wrap def tz_localize( self, tz, ambiguous="raise", nonexistent="raise" ) -> "DatetimeIndex": pass - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.to_period) + @dispatch_to_array_and_wrap def to_period(self, freq=None) -> "PeriodIndex": pass - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.to_perioddelta) + @dispatch_to_array_and_wrap def to_perioddelta(self, freq) -> "TimedeltaIndex": pass - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.to_julian_date) + @dispatch_to_array_and_wrap def to_julian_date(self) -> Index: pass - @dispatch_to_array_and_wrap(DatetimeArray) + @doc(DatetimeArray.isocalendar) + @dispatch_to_array_and_wrap def isocalendar(self) -> "DataFrame": pass From bdef044df1584b6081145e2abf3419f889cdcaae Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Wed, 30 Sep 2020 19:53:27 +0100 Subject: [PATCH 3/3] remove dispatch_to_array_and_wrap --- pandas/core/indexes/datetimes.py | 60 +++++++++++++------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 3b9505d308413..da78f8ff5d603 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1,7 +1,6 @@ from datetime import date, datetime, time, timedelta, tzinfo -import functools import operator -from typing import TYPE_CHECKING, Optional, cast +from typing import TYPE_CHECKING, Optional import warnings import numpy as np @@ -9,7 +8,7 @@ from pandas._libs import NaT, Period, Timestamp, index as libindex, lib from pandas._libs.tslibs import Resolution, ints_to_pydatetime, parsing, to_offset from pandas._libs.tslibs.offsets import prefix_mapping -from pandas._typing import DtypeObj, F, Label +from pandas._typing import DtypeObj, Label from pandas.errors import InvalidIndexError from pandas.util._decorators import cache_readonly, doc @@ -22,7 +21,6 @@ is_integer, is_scalar, ) -from pandas.core.dtypes.generic import ABCDataFrame from pandas.core.dtypes.missing import is_valid_nat_for_dtype from pandas.core.arrays.datetimes import DatetimeArray, tz_to_dtype @@ -33,23 +31,7 @@ from pandas.core.tools.times import to_time if TYPE_CHECKING: - from pandas import DataFrame, PeriodIndex, TimedeltaIndex - - -def dispatch_to_array_and_wrap(func: F) -> F: - name = func.__name__ - - @functools.wraps(func) - def method_wrapper(self, *args, **kwargs): - - result = getattr(self._data, name)(*args, **kwargs) - if isinstance(result, type(self._data)): - return type(self)._simple_new(result, name=self.name) - elif isinstance(result, ABCDataFrame): - return result.set_index(self) - return Index(result, name=self.name) - - return cast(F, method_wrapper) + from pandas import DataFrame, Float64Index, PeriodIndex, TimedeltaIndex def _new_DatetimeIndex(cls, d): @@ -243,41 +225,47 @@ class DatetimeIndex(DatetimeTimedeltaMixin): # methods that dispatch to DatetimeArray and wrap result @doc(DatetimeArray.strftime) - @dispatch_to_array_and_wrap def strftime(self, date_format) -> Index: - pass + arr = self._data.strftime(date_format) + return Index(arr, name=self.name) @doc(DatetimeArray.tz_convert) - @dispatch_to_array_and_wrap def tz_convert(self, tz) -> "DatetimeIndex": - pass + arr = self._data.tz_convert(tz) + return type(self)._simple_new(arr, name=self.name) @doc(DatetimeArray.tz_localize) - @dispatch_to_array_and_wrap def tz_localize( self, tz, ambiguous="raise", nonexistent="raise" ) -> "DatetimeIndex": - pass + arr = self._data.tz_localize(tz, ambiguous, nonexistent) + return type(self)._simple_new(arr, name=self.name) @doc(DatetimeArray.to_period) - @dispatch_to_array_and_wrap def to_period(self, freq=None) -> "PeriodIndex": - pass + from pandas.core.indexes.api import PeriodIndex + + arr = self._data.to_period(freq) + return PeriodIndex._simple_new(arr, name=self.name) @doc(DatetimeArray.to_perioddelta) - @dispatch_to_array_and_wrap def to_perioddelta(self, freq) -> "TimedeltaIndex": - pass + from pandas.core.indexes.api import TimedeltaIndex + + arr = self._data.to_perioddelta(freq) + return TimedeltaIndex._simple_new(arr, name=self.name) @doc(DatetimeArray.to_julian_date) - @dispatch_to_array_and_wrap - def to_julian_date(self) -> Index: - pass + def to_julian_date(self) -> "Float64Index": + from pandas.core.indexes.api import Float64Index + + arr = self._data.to_julian_date() + return Float64Index._simple_new(arr, name=self.name) @doc(DatetimeArray.isocalendar) - @dispatch_to_array_and_wrap def isocalendar(self) -> "DataFrame": - pass + df = self._data.isocalendar() + return df.set_index(self) # -------------------------------------------------------------------- # Constructors