From 853fed43a1d78bdbc12ee9a699788a7a79cd2931 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Thu, 6 Apr 2023 15:24:32 -0400 Subject: [PATCH 01/22] components function for datetime data type --- pandas/core/indexes/accessors.py | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 76666d62bc5c0..002e1caa282f8 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -371,6 +371,40 @@ def isocalendar(self) -> DataFrame: """ return self._get_values().isocalendar().set_index(self._parent.index) + @property + def components(self): + """ + Return a Dataframe of the components of the DateTime. + + Returns + ------- + DataFrame + + Examples + -------- + >>> s = pd.Series(pd.date_range('20180310', periods=2, freq='ns')) + >>> s + 0 2018-03-10 00:00:00.000000000 + 1 2018-03-10 00:00:00.000000001 + 2 2018-03-10 00:00:00.000000002 + 3 2018-03-10 00:00:00.000000003 + 4 2018-03-10 00:00:00.000000004 + + dtype: datetime64[ns] + >>> s.dt.components + year month day hour minute second microsecond nanosecond + 0 0 0 0 0 0 0 0 0 + 1 0 0 0 0 0 0 0 1 + 2 0 0 0 0 0 0 0 2 + 3 0 0 0 0 0 0 0 3 + 4 0 0 0 0 0 0 0 4 + """ + return ( + self._get_values() + .components.set_index(self._parent.index) + .__finalize__(self._parent) + ) + @delegate_names( delegate=TimedeltaArray, accessors=TimedeltaArray._datetimelike_ops, typ="property" From 637b83bf8eef9184492caf0b43d930fd2eddf77c Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Thu, 6 Apr 2023 15:37:16 -0400 Subject: [PATCH 02/22] test case - not final --- pandas/tests/indexes/datetimes/test_scalar_compat.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index f07a9dce5f6ae..5e2fd4776a5b6 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -160,6 +160,16 @@ def test_round(self, tz_naive_fixture): ts = "2016-10-17 12:00:00.001501031" DatetimeIndex([ts]).round("1010ns") + def test_components(self): + s = pd.Series(pd.date_range('20180310', periods=2, freq='ns')) + s.dt.to_pydatetime() + + result = s.dt.components + + assert not result.iloc[0].isna().all() + assert result.iloc[1].isna().all() + + def test_no_rounding_occurs(self, tz_naive_fixture): # GH 21262 tz = tz_naive_fixture @@ -345,3 +355,5 @@ def test_second(self): r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2) + + From eec29ee53b1636d23ae491f0f21ebe6d8c23cb76 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Tue, 11 Apr 2023 17:13:39 -0400 Subject: [PATCH 03/22] datetime components --- pandas/_libs/tslibs/timestamps.pyx | 163 +++++++++++++++++- pandas/core/arrays/datetimes.py | 46 +++++ pandas/core/indexes/datetimes.py | 2 + .../indexes/datetimes/test_scalar_compat.py | 8 +- 4 files changed, 216 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index fd89d0e795ecc..5e23fd69d0d77 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -8,6 +8,7 @@ shadows the python class, where we do any heavy lifting. """ import warnings +import collections cimport cython @@ -128,6 +129,22 @@ from pandas._libs.tslibs.tzconversion cimport ( _zero_time = dt_time(0, 0) _no_input = object() +#components named tuple +Components = collections.namedtuple( + "Components", + [ + "year", + "month", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", + ] +) + # ---------------------------------------------------------------------- @@ -227,7 +244,6 @@ class MinMaxReso: # ---------------------------------------------------------------------- cdef class _Timestamp(ABCTimestamp): - # higher than np.ndarray and np.matrix __array_priority__ = 100 dayofweek = _Timestamp.day_of_week @@ -269,6 +285,121 @@ cdef class _Timestamp(ABCTimestamp): """ return npy_unit_to_abbrev(self._creso) + @property + cdef year(self) -> int: + """ + Returns the year of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._y + + @property + cdef month(self) -> int: + """ + Returns the month of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._month + + @property + cdef day(self) -> int: + """ + Returns the day of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._d + + + @property + cdef hour(self) -> int: + """ + Returns the hour of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._h + + + @property + cdef minute(self) -> int: + """ + Returns the minute of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._m + + + @property + cdef second(self) -> int: + """ + Returns the second of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._s + + + @property + cdef millisecond(self) -> int: + """ + Returns the millisecond of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._ms + + + @property + cdef microsecond(self) -> int: + """ + Returns the microsecond of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._us + + + @property + cdef nanosecond(self) -> int: + """ + Returns the nanosecond of the timestamp. + + Returns + ------- + int + + self._ensure_components() + return self._ns + + # ----------------------------------------------------------------- # Constructors @@ -568,6 +699,36 @@ cdef class _Timestamp(ABCTimestamp): return type(self)(other) - self return NotImplemented + cdef _ensure_components(_Timestamp self): + """ + compute the components + """ + + if self._is_populated: + return + + cdef: + npy_datetimestruct dts + + pandas_datetime_to_datetimestruct(self._value, self._creso, &dts) + self._y = dts.year + self._month = dts.month + self._d = dts.day + self._h = dts.hour + self._m = dts.minute + self._s = dts.sec + self._ms = dts.millisecond + self._us = dts.microsecond + self._ns = dts.nanosecond + + self._is_populated = 1 + + @property + def components(self): + self._ensure_components() + return Components(self._y, self._month, self._d, self._h, self._m, self._s, self._ms, self._us, self._ns) + + # ----------------------------------------------------------------- cdef int64_t _maybe_convert_value_to_local(self): diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index dcb1c0965cc5b..bf0fa4dfceef6 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -141,6 +141,10 @@ def f(self): result = fields.get_date_name_field(values, field, reso=self._creso) result = self._maybe_mask_results(result, fill_value=None) + if self._hasna: + result = fields.get_date_field(values, field, reso=self._creso) + result = self._maybe_mask_results(result, fill_value=None, convert="float64") + else: result = fields.get_date_field(values, field, reso=self._creso) result = self._maybe_mask_results( @@ -1193,6 +1197,47 @@ def to_period(self, freq=None) -> PeriodArray: return PeriodArray._from_datetime64(self._ndarray, freq, tz=self.tz) + @property + def components(self) -> DataFrame: + """ + Return a DataFrame of the individual resolution components of the Datetime. + + The components (year, month, day, hour, minute, second, microsecond, + nanosecond) are returned as columns in a DataFrame. + + Returns + ------- + DataFrame + """ + from pandas import DataFrame + + columns = [ + "year", + "month", + "day", + "hour", + "minute", + "second", + "micorsecond", + "nanosecond", + ] + hasnans = self._hasna + if hasnans: + + def f(x): + if isna(x): + return [np.nan] * len(columns) + return x.components + + else: + def f(x): + return x.components + + result = DataFrame([f(x) for x in self], columns=columns) + if not hasnans: + result = result.astype("int64") + return result + # ----------------------------------------------------------------- # Properties - Vectorized Timestamp Properties/Methods @@ -1903,6 +1948,7 @@ def isocalendar(self) -> DataFrame: dtype: bool """, ) + def to_julian_date(self) -> npt.NDArray[np.float64]: """ diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 3cf84804e0ae1..f070f24b49573 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -115,6 +115,7 @@ def _new_DatetimeIndex(cls, d): @inherit_names(["is_normalized"], DatetimeArray, cache=True) @inherit_names( [ + "components", "tz", "tzinfo", "dtype", @@ -212,6 +213,7 @@ class DatetimeIndex(DatetimeTimedeltaMixin): is_year_end is_leap_year inferred_freq + components Methods ------- diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 5e2fd4776a5b6..33b6de451d45e 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -14,6 +14,7 @@ import pandas as pd from pandas import ( + Series, DatetimeIndex, Timestamp, date_range, @@ -161,9 +162,12 @@ def test_round(self, tz_naive_fixture): DatetimeIndex([ts]).round("1010ns") def test_components(self): - s = pd.Series(pd.date_range('20180310', periods=2, freq='ns')) - s.dt.to_pydatetime() + rng = date_range("1 days, 10:11:12", periods=2, freq="ns") + rng.components + s = Series(rng) + s[1] = np.nan + result = s.dt.components assert not result.iloc[0].isna().all() From 62fa43a60e9603a0715241171157f9ec5a118e4d Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Tue, 11 Apr 2023 17:43:23 -0400 Subject: [PATCH 04/22] fixes --- pandas/_libs/tslibs/timestamps.pxd | 1 + pandas/_libs/tslibs/timestamps.pyx | 36 ++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pxd b/pandas/_libs/tslibs/timestamps.pxd index 26018cd904249..40085fb32ee4a 100644 --- a/pandas/_libs/tslibs/timestamps.pxd +++ b/pandas/_libs/tslibs/timestamps.pxd @@ -34,3 +34,4 @@ cdef class _Timestamp(ABCTimestamp): int op) except -1 cdef bint _compare_mismatched_resos(_Timestamp self, _Timestamp other, int op) cdef _Timestamp _as_creso(_Timestamp self, NPY_DATETIMEUNIT creso, bint round_ok=*) + cdef _ensure_components(_Timestamp self) \ No newline at end of file diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 5e23fd69d0d77..349a399685892 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -286,7 +286,7 @@ cdef class _Timestamp(ABCTimestamp): return npy_unit_to_abbrev(self._creso) @property - cdef year(self) -> int: + def year(self) -> int: # TODO(cython3): make cdef property """ Returns the year of the timestamp. @@ -294,11 +294,13 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._y @property - cdef month(self) -> int: + def month(self) -> int: # TODO(cython3): make cdef property """ Returns the month of the timestamp. @@ -306,11 +308,13 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._month @property - cdef day(self) -> int: + def day(self) -> int: # TODO(cython3): make cdef property """ Returns the day of the timestamp. @@ -318,12 +322,14 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._d @property - cdef hour(self) -> int: + def hour(self) -> int: # TODO(cython3): make cdef property """ Returns the hour of the timestamp. @@ -331,12 +337,14 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._h @property - cdef minute(self) -> int: + def minute(self) -> int: # TODO(cython3): make cdef property """ Returns the minute of the timestamp. @@ -344,12 +352,14 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._m @property - cdef second(self) -> int: + def second(self) -> int: # TODO(cython3): make cdef property """ Returns the second of the timestamp. @@ -357,12 +367,14 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._s @property - cdef millisecond(self) -> int: + def millisecond(self) -> int: # TODO(cython3): make cdef property """ Returns the millisecond of the timestamp. @@ -370,12 +382,14 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._ms @property - cdef microsecond(self) -> int: + def microsecond(self) -> int: # TODO(cython3): make cdef property """ Returns the microsecond of the timestamp. @@ -383,12 +397,14 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._us @property - cdef nanosecond(self) -> int: + def nanosecond(self) -> int: # TODO(cython3): make cdef property """ Returns the nanosecond of the timestamp. @@ -396,6 +412,8 @@ cdef class _Timestamp(ABCTimestamp): ------- int + """ + self._ensure_components() return self._ns From 46c48023449a3069b60123fb977c6f29b7cf45bc Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Wed, 12 Apr 2023 15:25:11 -0400 Subject: [PATCH 05/22] more fixes --- pandas/_libs/tslibs/timestamps.pxd | 1 + pandas/_libs/tslibs/timestamps.pyx | 133 ----------------------------- 2 files changed, 1 insertion(+), 133 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pxd b/pandas/_libs/tslibs/timestamps.pxd index 40085fb32ee4a..708ec20a34e46 100644 --- a/pandas/_libs/tslibs/timestamps.pxd +++ b/pandas/_libs/tslibs/timestamps.pxd @@ -23,6 +23,7 @@ cdef class _Timestamp(ABCTimestamp): cdef readonly: int64_t _value, nanosecond, year NPY_DATETIMEUNIT _creso + bint _is_populated cdef bint _get_start_end_field(self, str field, freq) cdef _get_date_name_field(self, str field, object locale) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 349a399685892..6c0896f762577 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -285,139 +285,6 @@ cdef class _Timestamp(ABCTimestamp): """ return npy_unit_to_abbrev(self._creso) - @property - def year(self) -> int: # TODO(cython3): make cdef property - """ - Returns the year of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._y - - @property - def month(self) -> int: # TODO(cython3): make cdef property - """ - Returns the month of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._month - - @property - def day(self) -> int: # TODO(cython3): make cdef property - """ - Returns the day of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._d - - - @property - def hour(self) -> int: # TODO(cython3): make cdef property - """ - Returns the hour of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._h - - - @property - def minute(self) -> int: # TODO(cython3): make cdef property - """ - Returns the minute of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._m - - - @property - def second(self) -> int: # TODO(cython3): make cdef property - """ - Returns the second of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._s - - - @property - def millisecond(self) -> int: # TODO(cython3): make cdef property - """ - Returns the millisecond of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._ms - - - @property - def microsecond(self) -> int: # TODO(cython3): make cdef property - """ - Returns the microsecond of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._us - - - @property - def nanosecond(self) -> int: # TODO(cython3): make cdef property - """ - Returns the nanosecond of the timestamp. - - Returns - ------- - int - - """ - - self._ensure_components() - return self._ns - - # ----------------------------------------------------------------- # Constructors From 5da1cacb2a67a6d5483fc9b73b18e9e60b21ab81 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Wed, 12 Apr 2023 15:46:09 -0400 Subject: [PATCH 06/22] more fixes --- pandas/_libs/tslibs/timestamps.pxd | 3 ++- pandas/core/arrays/datetimes.py | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pxd b/pandas/_libs/tslibs/timestamps.pxd index 708ec20a34e46..0d2fdecd8e511 100644 --- a/pandas/_libs/tslibs/timestamps.pxd +++ b/pandas/_libs/tslibs/timestamps.pxd @@ -23,7 +23,8 @@ cdef class _Timestamp(ABCTimestamp): cdef readonly: int64_t _value, nanosecond, year NPY_DATETIMEUNIT _creso - bint _is_populated + bint _is_populated #are my components populated + int64_t _y, _month, _d, _h, _m, _s, _ms, _us, _ns cdef bint _get_start_end_field(self, str field, freq) cdef _get_date_name_field(self, str field, object locale) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index bf0fa4dfceef6..f5aefd7c40746 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -141,10 +141,6 @@ def f(self): result = fields.get_date_name_field(values, field, reso=self._creso) result = self._maybe_mask_results(result, fill_value=None) - if self._hasna: - result = fields.get_date_field(values, field, reso=self._creso) - result = self._maybe_mask_results(result, fill_value=None, convert="float64") - else: result = fields.get_date_field(values, field, reso=self._creso) result = self._maybe_mask_results( From 21f2cc138d254ce7b3bef0fa2a67a00a022e12d3 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Wed, 12 Apr 2023 16:12:41 -0400 Subject: [PATCH 07/22] fixing some errors from pre-commit failure --- pandas/_libs/tslibs/timestamps.pxd | 4 ++-- pandas/_libs/tslibs/timestamps.pyx | 10 +++++----- pandas/core/indexes/accessors.py | 21 ++++++++++++++------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pxd b/pandas/_libs/tslibs/timestamps.pxd index 0d2fdecd8e511..d0ab5a27a52ca 100644 --- a/pandas/_libs/tslibs/timestamps.pxd +++ b/pandas/_libs/tslibs/timestamps.pxd @@ -23,7 +23,7 @@ cdef class _Timestamp(ABCTimestamp): cdef readonly: int64_t _value, nanosecond, year NPY_DATETIMEUNIT _creso - bint _is_populated #are my components populated + bint _is_populated # are my components populated int64_t _y, _month, _d, _h, _m, _s, _ms, _us, _ns cdef bint _get_start_end_field(self, str field, freq) @@ -36,4 +36,4 @@ cdef class _Timestamp(ABCTimestamp): int op) except -1 cdef bint _compare_mismatched_resos(_Timestamp self, _Timestamp other, int op) cdef _Timestamp _as_creso(_Timestamp self, NPY_DATETIMEUNIT creso, bint round_ok=*) - cdef _ensure_components(_Timestamp self) \ No newline at end of file + cdef _ensure_components(_Timestamp self) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 6c0896f762577..7a8f886f3778c 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -129,7 +129,7 @@ from pandas._libs.tslibs.tzconversion cimport ( _zero_time = dt_time(0, 0) _no_input = object() -#components named tuple +# components named tuple Components = collections.namedtuple( "Components", [ @@ -591,10 +591,10 @@ cdef class _Timestamp(ABCTimestamp): if self._is_populated: return - + cdef: npy_datetimestruct dts - + pandas_datetime_to_datetimestruct(self._value, self._creso, &dts) self._y = dts.year self._month = dts.month @@ -611,8 +611,8 @@ cdef class _Timestamp(ABCTimestamp): @property def components(self): self._ensure_components() - return Components(self._y, self._month, self._d, self._h, self._m, self._s, self._ms, self._us, self._ns) - + return Components(self._y, self._month, self._d, + self._h, self._m, self._s, self._ms, self._us, self._ns) # ----------------------------------------------------------------- diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 0e4ee5dc9b999..389011f4bf19f 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -413,13 +413,20 @@ def components(self): dtype: datetime64[ns] >>> s.dt.components - year month day hour minute second microsecond nanosecond - 0 0 0 0 0 0 0 0 0 - 1 0 0 0 0 0 0 0 1 - 2 0 0 0 0 0 0 0 2 - 3 0 0 0 0 0 0 0 3 - 4 0 0 0 0 0 0 0 4 - """ + year month day hour minute + 0 0 0 0 0 0 + 1 0 0 0 0 0 + 2 0 0 0 0 0 + 3 0 0 0 0 0 + 4 0 0 0 0 0 + + second microsecond nanosecond + 0 0 0 + 0 0 1 + 0 0 2 + 0 0 3 + 0 0 4 + """ return ( self._get_values() .components.set_index(self._parent.index) From abb5eaa9d2aa11d31c30870dfe173f926ed8f99c Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Thu, 13 Apr 2023 10:06:30 -0400 Subject: [PATCH 08/22] pre-commit check fixes --- pandas/_libs/tslibs/timestamps.pyx | 6 ++++-- pandas/core/indexes/accessors.py | 1 + pandas/tests/indexes/datetimes/test_scalar_compat.py | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 7a8f886f3778c..7aae045e7a667 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -611,8 +611,10 @@ cdef class _Timestamp(ABCTimestamp): @property def components(self): self._ensure_components() - return Components(self._y, self._month, self._d, - self._h, self._m, self._s, self._ms, self._us, self._ns) + return Components( + self._y, self._month, self._d, + self._h, self._m, self._s, self._ms, self._us, self._ns + ) # ----------------------------------------------------------------- diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 389011f4bf19f..3f0d16ee19960 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -426,6 +426,7 @@ def components(self): 0 0 2 0 0 3 0 0 4 + """ return ( self._get_values() diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 33b6de451d45e..20ac23c38570d 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -14,8 +14,8 @@ import pandas as pd from pandas import ( - Series, DatetimeIndex, + Series, Timestamp, date_range, ) @@ -165,6 +165,7 @@ def test_components(self): rng = date_range("1 days, 10:11:12", periods=2, freq="ns") rng.components + s = Series(rng) s[1] = np.nan From 2ace0804a05d302ea3041c790cddb924b34c736a Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Thu, 13 Apr 2023 15:15:45 -0400 Subject: [PATCH 09/22] ran pre-commit --- pandas/_libs/tslibs/timestamps.pyx | 2 +- pandas/core/arrays/datetimes.py | 2 +- pandas/core/indexes/accessors.py | 6 +++--- pandas/tests/indexes/datetimes/test_scalar_compat.py | 6 +----- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 7aae045e7a667..59a7737ca377b 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -7,8 +7,8 @@ construction requirements, we need to do object instantiation in python shadows the python class, where we do any heavy lifting. """ -import warnings import collections +import warnings cimport cython diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index ed7839352995a..1c336d8db7a7d 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1225,6 +1225,7 @@ def f(x): return x.components else: + def f(x): return x.components @@ -1943,7 +1944,6 @@ def isocalendar(self) -> DataFrame: dtype: bool """, ) - def to_julian_date(self) -> npt.NDArray[np.float64]: """ diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 40857f13c1ef3..2f08e797f7ab3 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -391,7 +391,7 @@ def isocalendar(self) -> DataFrame: """ return self._get_values().isocalendar().set_index(self._parent.index) - @property + @property def components(self): """ Return a Dataframe of the components of the DateTime. @@ -425,8 +425,8 @@ def components(self): 0 0 2 0 0 3 0 0 4 - - """ + + """ return ( self._get_values() .components.set_index(self._parent.index) diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 20ac23c38570d..782d030b03643 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -164,8 +164,7 @@ def test_round(self, tz_naive_fixture): def test_components(self): rng = date_range("1 days, 10:11:12", periods=2, freq="ns") rng.components - - + s = Series(rng) s[1] = np.nan @@ -174,7 +173,6 @@ def test_components(self): assert not result.iloc[0].isna().all() assert result.iloc[1].isna().all() - def test_no_rounding_occurs(self, tz_naive_fixture): # GH 21262 tz = tz_naive_fixture @@ -360,5 +358,3 @@ def test_second(self): r2 = dr.to_julian_date() assert isinstance(r2, pd.Index) and r2.dtype == np.float64 tm.assert_index_equal(r1, r2) - - From ba44e0e161dc1d4753e8784cbadbc09c8e6fb64d Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Thu, 13 Apr 2023 16:00:53 -0400 Subject: [PATCH 10/22] removing test for now --- .../tests/indexes/datetimes/test_scalar_compat.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pandas/tests/indexes/datetimes/test_scalar_compat.py b/pandas/tests/indexes/datetimes/test_scalar_compat.py index 782d030b03643..f07a9dce5f6ae 100644 --- a/pandas/tests/indexes/datetimes/test_scalar_compat.py +++ b/pandas/tests/indexes/datetimes/test_scalar_compat.py @@ -15,7 +15,6 @@ import pandas as pd from pandas import ( DatetimeIndex, - Series, Timestamp, date_range, ) @@ -161,18 +160,6 @@ def test_round(self, tz_naive_fixture): ts = "2016-10-17 12:00:00.001501031" DatetimeIndex([ts]).round("1010ns") - def test_components(self): - rng = date_range("1 days, 10:11:12", periods=2, freq="ns") - rng.components - - s = Series(rng) - s[1] = np.nan - - result = s.dt.components - - assert not result.iloc[0].isna().all() - assert result.iloc[1].isna().all() - def test_no_rounding_occurs(self, tz_naive_fixture): # GH 21262 tz = tz_naive_fixture From a882650597e666d891fe3f940424f5622ce918cf Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Fri, 14 Apr 2023 09:55:50 -0400 Subject: [PATCH 11/22] test cases --- .../tests/scalar/timestamp/test_timestamp.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index afb4dd7422114..52cfe6e5d8e06 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -94,6 +94,29 @@ def test_fields(self, attr, expected, tz): assert isinstance(result, int) assert result == expected + #components + tzstr = "dateutil/usr/share/zoneinfo/America/Chicago" + ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, second=32, microsecond=3, nanosecond=7, tz=tzstr) + dt = ts.to_pydatetime + assert dt.components.year == 2013 + assert dt.components.month == 11 + assert dt.components.day == 3 + assert dt.components.hour == 1 + assert dt.components.minute == 0 + assert dt.components.second == 32 + assert dt.components.microsecond == 3 + assert dt.components.nanosecond == 7 + + tzstr = "dateutil/usr/share/zoneinfo/America/Detroit" + ts = Timestamp(year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr) + dt = ts.to_pydatetime + assert dt.components.year == 2023 + assert dt.components.month == 4 + assert dt.components.day == 14 + assert dt.components.hour == 9 + assert dt.components.minute == 53 + + @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_millisecond_raises(self, tz): ts = Timestamp("2014-12-31 23:59:00", tz=tz) From 20f3805faf8d705b69c1312a90a86c6fa967e46b Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Fri, 14 Apr 2023 09:59:36 -0400 Subject: [PATCH 12/22] pre-commit run --- .../tests/scalar/timestamp/test_timestamp.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 52cfe6e5d8e06..814812cb51b50 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -94,9 +94,20 @@ def test_fields(self, attr, expected, tz): assert isinstance(result, int) assert result == expected - #components + # components tzstr = "dateutil/usr/share/zoneinfo/America/Chicago" - ts = Timestamp(year=2013, month=11, day=3, hour=1, minute=0, fold=1, second=32, microsecond=3, nanosecond=7, tz=tzstr) + ts = Timestamp( + year=2013, + month=11, + day=3, + hour=1, + minute=0, + fold=1, + second=32, + microsecond=3, + nanosecond=7, + tz=tzstr, + ) dt = ts.to_pydatetime assert dt.components.year == 2013 assert dt.components.month == 11 @@ -114,8 +125,7 @@ def test_fields(self, attr, expected, tz): assert dt.components.month == 4 assert dt.components.day == 14 assert dt.components.hour == 9 - assert dt.components.minute == 53 - + assert dt.components.minute == 53 @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_millisecond_raises(self, tz): From 896e38812dd7f59ff7d12b94101318457da43d8e Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Fri, 14 Apr 2023 10:42:54 -0400 Subject: [PATCH 13/22] whatsnew --- doc/source/whatsnew/v2.0.1.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v2.0.1.rst b/doc/source/whatsnew/v2.0.1.rst index 838fd4fa28442..90f501e09cc23 100644 --- a/doc/source/whatsnew/v2.0.1.rst +++ b/doc/source/whatsnew/v2.0.1.rst @@ -43,6 +43,7 @@ Other - :class:`DataFrame` created from empty dicts had :attr:`~DataFrame.columns` of dtype ``object``. It is now a :class:`RangeIndex` (:issue:`52404`) - :class:`Series` created from empty dicts had :attr:`~Series.index` of dtype ``object``. It is now a :class:`RangeIndex` (:issue:`52404`) - Implemented :meth:`Series.str.split` and :meth:`Series.str.rsplit` for :class:`ArrowDtype` with ``pyarrow.string`` (:issue:`52401`) +- Implemented :meth:`_Timestamp._ensure_components` and :meth:`_Timestamp.components` timestamps.pyx and :meth:`Series.dt.components` in datetimes.py .. --------------------------------------------------------------------------- .. _whatsnew_201.contributors: From f325edf5fa3c7a9c495260c7d435bd839663a9a7 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Fri, 14 Apr 2023 17:03:40 -0400 Subject: [PATCH 14/22] invoke function --- pandas/tests/scalar/timestamp/test_timestamp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 814812cb51b50..77d90fff8963b 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -108,7 +108,7 @@ def test_fields(self, attr, expected, tz): nanosecond=7, tz=tzstr, ) - dt = ts.to_pydatetime + dt = ts.to_pydatetime() assert dt.components.year == 2013 assert dt.components.month == 11 assert dt.components.day == 3 From e88f5e71dd2400ab0c34e3aa5dcdbaf92c51d693 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Sat, 15 Apr 2023 22:12:59 -0400 Subject: [PATCH 15/22] making some more fixes --- pandas/core/arrays/datetimes.py | 43 +------------------ pandas/core/indexes/accessors.py | 42 ------------------ pandas/core/indexes/datetimes.py | 2 - .../tests/scalar/timestamp/test_timestamp.py | 29 ++++++------- 4 files changed, 15 insertions(+), 101 deletions(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 1c336d8db7a7d..46365e93743f8 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1191,48 +1191,7 @@ def to_period(self, freq=None) -> PeriodArray: freq = res return PeriodArray._from_datetime64(self._ndarray, freq, tz=self.tz) - - @property - def components(self) -> DataFrame: - """ - Return a DataFrame of the individual resolution components of the Datetime. - - The components (year, month, day, hour, minute, second, microsecond, - nanosecond) are returned as columns in a DataFrame. - - Returns - ------- - DataFrame - """ - from pandas import DataFrame - - columns = [ - "year", - "month", - "day", - "hour", - "minute", - "second", - "micorsecond", - "nanosecond", - ] - hasnans = self._hasna - if hasnans: - - def f(x): - if isna(x): - return [np.nan] * len(columns) - return x.components - - else: - - def f(x): - return x.components - - result = DataFrame([f(x) for x in self], columns=columns) - if not hasnans: - result = result.astype("int64") - return result + # ----------------------------------------------------------------- # Properties - Vectorized Timestamp Properties/Methods diff --git a/pandas/core/indexes/accessors.py b/pandas/core/indexes/accessors.py index 2f08e797f7ab3..4f529b71c867f 100644 --- a/pandas/core/indexes/accessors.py +++ b/pandas/core/indexes/accessors.py @@ -391,48 +391,6 @@ def isocalendar(self) -> DataFrame: """ return self._get_values().isocalendar().set_index(self._parent.index) - @property - def components(self): - """ - Return a Dataframe of the components of the DateTime. - - Returns - ------- - DataFrame - - Examples - -------- - >>> s = pd.Series(pd.date_range('20180310', periods=2, freq='ns')) - >>> s - 0 2018-03-10 00:00:00.000000000 - 1 2018-03-10 00:00:00.000000001 - 2 2018-03-10 00:00:00.000000002 - 3 2018-03-10 00:00:00.000000003 - 4 2018-03-10 00:00:00.000000004 - - dtype: datetime64[ns] - >>> s.dt.components - year month day hour minute - 0 0 0 0 0 0 - 1 0 0 0 0 0 - 2 0 0 0 0 0 - 3 0 0 0 0 0 - 4 0 0 0 0 0 - - second microsecond nanosecond - 0 0 0 - 0 0 1 - 0 0 2 - 0 0 3 - 0 0 4 - - """ - return ( - self._get_values() - .components.set_index(self._parent.index) - .__finalize__(self._parent) - ) - @delegate_names( delegate=TimedeltaArray, accessors=TimedeltaArray._datetimelike_ops, typ="property" diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index a7a3b3a7819ee..7de1972de5e4a 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -116,7 +116,6 @@ def _new_DatetimeIndex(cls, d): @inherit_names(["is_normalized"], DatetimeArray, cache=True) @inherit_names( [ - "components", "tz", "tzinfo", "dtype", @@ -220,7 +219,6 @@ class DatetimeIndex(DatetimeTimedeltaMixin): is_year_end is_leap_year inferred_freq - components Methods ------- diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 77d90fff8963b..126938567dd7b 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -108,24 +108,23 @@ def test_fields(self, attr, expected, tz): nanosecond=7, tz=tzstr, ) - dt = ts.to_pydatetime() - assert dt.components.year == 2013 - assert dt.components.month == 11 - assert dt.components.day == 3 - assert dt.components.hour == 1 - assert dt.components.minute == 0 - assert dt.components.second == 32 - assert dt.components.microsecond == 3 - assert dt.components.nanosecond == 7 + + assert ts.components.year == 2013 + assert ts.components.month == 11 + assert ts.components.day == 3 + assert ts.components.hour == 1 + assert ts.components.minute == 0 + assert ts.components.second == 32 + assert ts.components.microsecond == 3 + assert ts.components.nanosecond == 7 tzstr = "dateutil/usr/share/zoneinfo/America/Detroit" ts = Timestamp(year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr) - dt = ts.to_pydatetime - assert dt.components.year == 2023 - assert dt.components.month == 4 - assert dt.components.day == 14 - assert dt.components.hour == 9 - assert dt.components.minute == 53 + assert ts.components.year == 2023 + assert ts.components.month == 4 + assert ts.components.day == 14 + assert ts.components.hour == 9 + assert ts.components.minute == 53 @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_millisecond_raises(self, tz): From b4d52884f068f698f98b598afa931197e743184e Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Sat, 15 Apr 2023 22:15:32 -0400 Subject: [PATCH 16/22] pre-commit --- pandas/core/arrays/datetimes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index 46365e93743f8..12245a144ec2a 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -1191,7 +1191,6 @@ def to_period(self, freq=None) -> PeriodArray: freq = res return PeriodArray._from_datetime64(self._ndarray, freq, tz=self.tz) - # ----------------------------------------------------------------- # Properties - Vectorized Timestamp Properties/Methods From b64176d714cf5b4a084aabf225f0ae7826a3f4ab Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Sat, 15 Apr 2023 22:28:43 -0400 Subject: [PATCH 17/22] datetimes --- pandas/core/indexes/datetimes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 7de1972de5e4a..a7a3b3a7819ee 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -116,6 +116,7 @@ def _new_DatetimeIndex(cls, d): @inherit_names(["is_normalized"], DatetimeArray, cache=True) @inherit_names( [ + "components", "tz", "tzinfo", "dtype", @@ -219,6 +220,7 @@ class DatetimeIndex(DatetimeTimedeltaMixin): is_year_end is_leap_year inferred_freq + components Methods ------- From e966b5e57d8a0b8929ca874cef85aef6cf7b9edc Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Mon, 17 Apr 2023 20:39:06 -0400 Subject: [PATCH 18/22] deleting from datetimes.py --- pandas/core/indexes/datetimes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index a7a3b3a7819ee..7de1972de5e4a 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -116,7 +116,6 @@ def _new_DatetimeIndex(cls, d): @inherit_names(["is_normalized"], DatetimeArray, cache=True) @inherit_names( [ - "components", "tz", "tzinfo", "dtype", @@ -220,7 +219,6 @@ class DatetimeIndex(DatetimeTimedeltaMixin): is_year_end is_leap_year inferred_freq - components Methods ------- From 78c5ac8d650ad7bda7d79c0efa4c2f49576016fa Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Tue, 18 Apr 2023 12:16:02 -0400 Subject: [PATCH 19/22] tests passed locally --- pandas/_libs/tslibs/timestamps.pyx | 10 +++--- pandas/tests/scalar/test_nat.py | 2 +- .../tests/scalar/timestamp/test_timestamp.py | 33 ++++++++++--------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pyx b/pandas/_libs/tslibs/timestamps.pyx index 59a7737ca377b..0576274cff3fa 100644 --- a/pandas/_libs/tslibs/timestamps.pyx +++ b/pandas/_libs/tslibs/timestamps.pyx @@ -139,7 +139,6 @@ Components = collections.namedtuple( "hour", "minute", "second", - "millisecond", "microsecond", "nanosecond", ] @@ -600,11 +599,10 @@ cdef class _Timestamp(ABCTimestamp): self._month = dts.month self._d = dts.day self._h = dts.hour - self._m = dts.minute + self._m = dts.min self._s = dts.sec - self._ms = dts.millisecond - self._us = dts.microsecond - self._ns = dts.nanosecond + self._us = dts.us + self._ns = dts.ps // 1000 self._is_populated = 1 @@ -613,7 +611,7 @@ cdef class _Timestamp(ABCTimestamp): self._ensure_components() return Components( self._y, self._month, self._d, - self._h, self._m, self._s, self._ms, self._us, self._ns + self._h, self._m, self._s, self._us, self._ns ) # ----------------------------------------------------------------- diff --git a/pandas/tests/scalar/test_nat.py b/pandas/tests/scalar/test_nat.py index c13ea4eeb9e0d..0accbbf84f3f3 100644 --- a/pandas/tests/scalar/test_nat.py +++ b/pandas/tests/scalar/test_nat.py @@ -174,7 +174,7 @@ def test_nat_iso_format(get_nat): @pytest.mark.parametrize( "klass,expected", [ - (Timestamp, ["normalize", "to_julian_date", "to_period", "unit"]), + (Timestamp, ["components", "normalize", "to_julian_date", "to_period", "unit"]), ( Timedelta, [ diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 126938567dd7b..e6b67eac3e9d5 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -107,24 +107,25 @@ def test_fields(self, attr, expected, tz): microsecond=3, nanosecond=7, tz=tzstr, - ) - - assert ts.components.year == 2013 - assert ts.components.month == 11 - assert ts.components.day == 3 - assert ts.components.hour == 1 - assert ts.components.minute == 0 - assert ts.components.second == 32 - assert ts.components.microsecond == 3 - assert ts.components.nanosecond == 7 + ).components + + assert ts.year == 2013 + print(ts.year) + assert ts.month == 11 + assert ts.day == 3 + assert ts.hour == 1 + assert ts.minute == 0 + assert ts.second == 32 + assert ts.microsecond == 3 + assert ts.nanosecond == 7 tzstr = "dateutil/usr/share/zoneinfo/America/Detroit" - ts = Timestamp(year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr) - assert ts.components.year == 2023 - assert ts.components.month == 4 - assert ts.components.day == 14 - assert ts.components.hour == 9 - assert ts.components.minute == 53 + ts = Timestamp(year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr).components + assert ts.year == 2023 + assert ts.month == 4 + assert ts.day == 14 + assert ts.hour == 9 + assert ts.minute == 53 @pytest.mark.parametrize("tz", [None, "US/Eastern"]) def test_millisecond_raises(self, tz): From 22ef157016fd4d4d4c9fae15b98ea60fcc5d2786 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Tue, 18 Apr 2023 12:17:48 -0400 Subject: [PATCH 20/22] fix --- pandas/_libs/tslibs/timestamps.pxd | 2 +- pandas/tests/scalar/timestamp/test_timestamp.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/_libs/tslibs/timestamps.pxd b/pandas/_libs/tslibs/timestamps.pxd index d0ab5a27a52ca..9a885f8fcc437 100644 --- a/pandas/_libs/tslibs/timestamps.pxd +++ b/pandas/_libs/tslibs/timestamps.pxd @@ -24,7 +24,7 @@ cdef class _Timestamp(ABCTimestamp): int64_t _value, nanosecond, year NPY_DATETIMEUNIT _creso bint _is_populated # are my components populated - int64_t _y, _month, _d, _h, _m, _s, _ms, _us, _ns + int64_t _y, _month, _d, _h, _m, _s, _us, _ns cdef bint _get_start_end_field(self, str field, freq) cdef _get_date_name_field(self, str field, object locale) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index e6b67eac3e9d5..92c752b6bf015 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -110,7 +110,6 @@ def test_fields(self, attr, expected, tz): ).components assert ts.year == 2013 - print(ts.year) assert ts.month == 11 assert ts.day == 3 assert ts.hour == 1 From c00d5a3e03d308e0b13a43fe9606a6525317ebeb Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Tue, 18 Apr 2023 12:23:43 -0400 Subject: [PATCH 21/22] pre-commit --- pandas/tests/scalar/timestamp/test_timestamp.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/tests/scalar/timestamp/test_timestamp.py b/pandas/tests/scalar/timestamp/test_timestamp.py index 92c752b6bf015..40520be9dcea0 100644 --- a/pandas/tests/scalar/timestamp/test_timestamp.py +++ b/pandas/tests/scalar/timestamp/test_timestamp.py @@ -119,7 +119,9 @@ def test_fields(self, attr, expected, tz): assert ts.nanosecond == 7 tzstr = "dateutil/usr/share/zoneinfo/America/Detroit" - ts = Timestamp(year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr).components + ts = Timestamp( + year=2023, month=4, day=14, hour=9, minute=53, fold=1, tz=tzstr + ).components assert ts.year == 2023 assert ts.month == 4 assert ts.day == 14 From eab110fbfd877a78042d0f041112ee6c16890383 Mon Sep 17 00:00:00 2001 From: JosieGarba Date: Tue, 18 Apr 2023 19:13:10 -0400 Subject: [PATCH 22/22] jg/52131 --- pandas/core/generic.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 582a043a8a78a..77ebcb4b3fed9 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9145,9 +9145,19 @@ def last(self, offset) -> Self: offset = to_offset(offset) - start_date = self.index[-1] - offset - start = self.index.searchsorted(start_date, side="right") - return self.iloc[start:] + if not isinstance(offset, Tick) and offset.is_on_offset(self.index[-1]): + # GH#29623 if first value is end of period, remove offset with n = 1 + # before adding the real offset + start_date = start = self.index[-1] - offset.base - offset + else: + start_date = start = self.index[-1] - offset + + # Tick-like, e.g. 3 weeks + if isinstance(offset, Tick) and start_date in self.index: + start = self.index.searchsorted(start_date, side="right") + return self.iloc[:start] + + return self.loc[:start] @final def rank(