Skip to content

Commit 3830b3b

Browse files
committed
Add isocalendar property to DatetimeArray
- Add corresponding access properties in accessors - This calculates the year, week, and day components according to the ISO 8601 calendar and returns them in a data frame. - It is analogous to Timestamp.isocalendar and datetime.date.isocalendar
1 parent d48df99 commit 3830b3b

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

pandas/core/arrays/datetimes.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class DatetimeArray(dtl.DatetimeLikeArrayMixin, dtl.TimelikeOps, dtl.DatelikeOps
182182
"microsecond",
183183
"nanosecond",
184184
]
185-
_other_ops = ["date", "time", "timetz"]
185+
_other_ops = ["date", "time", "timetz", "isocalendar"]
186186
_datetimelike_ops = _field_ops + _object_ops + _bool_ops + _other_ops
187187
_datetimelike_methods = [
188188
"to_period",
@@ -1234,6 +1234,48 @@ def date(self):
12341234

12351235
return tslib.ints_to_pydatetime(timestamps, box="date")
12361236

1237+
@property
1238+
def isocalendar(self):
1239+
"""
1240+
Returns a DataFrame with the year, week, and day calculated according to
1241+
the ISO 8601 standard.
1242+
1243+
.. versionadded:: 1.1.0
1244+
1245+
Returns
1246+
-------
1247+
DataFrame
1248+
with columns year, week and day
1249+
1250+
See Also
1251+
--------
1252+
Timestamp.isocalendar
1253+
datetime.date.isocalendar
1254+
1255+
Examples
1256+
--------
1257+
>>> idx = pd.date_range(start='2019-12-29', freq='D', periods=4)
1258+
>>> idx.isocalendar
1259+
year week day
1260+
0 2019 52 7
1261+
1 2020 1 1
1262+
2 2020 1 2
1263+
3 2020 1 3
1264+
>>> idx.isocalendar.week
1265+
0 52
1266+
1 1
1267+
2 1
1268+
3 1
1269+
Name: week, dtype: int32
1270+
"""
1271+
from pandas import DataFrame
1272+
1273+
sarray = fields.build_isocalendar_sarray(self.asi8)
1274+
iso_calendar_array = self._maybe_mask_results(
1275+
sarray, fill_value=np.nan, convert=[(n, "<f8") for n in sarray.dtype.names]
1276+
)
1277+
return DataFrame(iso_calendar_array)
1278+
12371279
year = _field_accessor(
12381280
"year",
12391281
"Y",

pandas/core/indexes/accessors.py

+34
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,40 @@ def to_pydatetime(self) -> np.ndarray:
219219
def freq(self):
220220
return self._get_values().inferred_freq
221221

222+
@property
223+
def isocalendar(self):
224+
"""
225+
Returns a DataFrame with the year, week, and day calculated according to
226+
the ISO 8601 standard.
227+
228+
.. versionadded:: 1.1.0
229+
230+
Returns
231+
-------
232+
DataFrame
233+
with columns year, week and day
234+
235+
See Also
236+
--------
237+
Timestamp.isocalendar
238+
datetime.date.isocalendar
239+
240+
Examples
241+
--------
242+
>>> pd.Series(["2020-01-01"], dtype="datetime64[D]").dt.isocalendar
243+
year week day
244+
0 2020 1 3
245+
>>> ser = pd.Series(["2010-01-01", pd.NaT], dtype="datetime64[D]")
246+
>>> ser.dt.isocalendar
247+
year week day
248+
0 2009.0 53.0 5.0
249+
1 NaN NaN NaN
250+
>>> pd.Series(["2019-12-31"], dtype="datetime64[D]").dt.isocalendar.week
251+
0 1
252+
Name: week, dtype: int32
253+
"""
254+
return self._get_values().isocalendar.set_index(self._parent.index)
255+
222256

223257
@delegate_names(
224258
delegate=TimedeltaArray, accessors=TimedeltaArray._datetimelike_ops, typ="property"

pandas/core/indexes/datetimes.py

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def _new_DatetimeIndex(cls, d):
8989
"date",
9090
"time",
9191
"timetz",
92+
"isocalendar",
9293
]
9394
+ DatetimeArray._bool_ops,
9495
DatetimeArray,

pandas/tests/series/test_datetime_values.py

+20
Original file line numberDiff line numberDiff line change
@@ -665,3 +665,23 @@ def test_setitem_with_different_tz(self):
665665
dtype=object,
666666
)
667667
tm.assert_series_equal(ser, expected)
668+
669+
@pytest.mark.parametrize(
670+
"input_series, expected_output, expected_type",
671+
[
672+
[["2020-01-01"], [[2020, 1, 3]], "int32"],
673+
[[pd.NaT], [[np.NaN, np.NaN, np.NaN]], "float64"],
674+
[["2019-12-31", "2019-12-29"], [[2020, 1, 2], [2019, 52, 7]], "int32"],
675+
[
676+
["2010-01-01", pd.NaT],
677+
[[2009, 53, 5], [np.NaN, np.NaN, np.NaN]],
678+
"float64",
679+
],
680+
],
681+
)
682+
def test_isocalendar(self, input_series, expected_output, expected_type):
683+
ser = pd.Series(input_series, dtype="datetime64[D]")
684+
expected_frame = pd.DataFrame(
685+
expected_output, columns=["year", "week", "day"]
686+
).astype(expected_type)
687+
tm.assert_frame_equal(ser.dt.isocalendar, expected_frame)

0 commit comments

Comments
 (0)