diff --git a/bigframes/core/compile/scalar_op_compiler.py b/bigframes/core/compile/scalar_op_compiler.py index 8243627a91..2b9208137b 100644 --- a/bigframes/core/compile/scalar_op_compiler.py +++ b/bigframes/core/compile/scalar_op_compiler.py @@ -676,6 +676,13 @@ def dayofweek_op_impl(x: ibis_types.Value): ) +@scalar_op_compiler.register_unary_op(ops.dayofyear_op) +def dayofyear_op_impl(x: ibis_types.Value): + return ( + typing.cast(ibis_types.TimestampValue, x).day_of_year().cast(ibis_dtypes.int64) + ) + + @scalar_op_compiler.register_unary_op(ops.hour_op) def hour_op_impl(x: ibis_types.Value): return typing.cast(ibis_types.TimestampValue, x).hour().cast(ibis_dtypes.int64) diff --git a/bigframes/operations/__init__.py b/bigframes/operations/__init__.py index 74ff5c0f98..c8ccaf2a25 100644 --- a/bigframes/operations/__init__.py +++ b/bigframes/operations/__init__.py @@ -42,6 +42,7 @@ date_diff_op, day_op, dayofweek_op, + dayofyear_op, month_op, quarter_op, year_op, @@ -261,6 +262,7 @@ "month_op", "year_op", "dayofweek_op", + "dayofyear_op", "quarter_op", # Time ops "hour_op", diff --git a/bigframes/operations/date_ops.py b/bigframes/operations/date_ops.py index 32d8eec118..9bcdddb8df 100644 --- a/bigframes/operations/date_ops.py +++ b/bigframes/operations/date_ops.py @@ -39,6 +39,11 @@ type_signature=op_typing.DATELIKE_ACCESSOR, ) +dayofyear_op = base_ops.create_unary_op( + name="dayofyear", + type_signature=op_typing.DATELIKE_ACCESSOR, +) + quarter_op = base_ops.create_unary_op( name="quarter", type_signature=op_typing.DATELIKE_ACCESSOR, diff --git a/bigframes/operations/datetimes.py b/bigframes/operations/datetimes.py index 7d25ac3622..032bd50390 100644 --- a/bigframes/operations/datetimes.py +++ b/bigframes/operations/datetimes.py @@ -43,6 +43,10 @@ def day(self) -> series.Series: def dayofweek(self) -> series.Series: return self._apply_unary_op(ops.dayofweek_op) + @property + def dayofyear(self) -> series.Series: + return self._apply_unary_op(ops.dayofyear_op) + @property def date(self) -> series.Series: return self._apply_unary_op(ops.date_op) diff --git a/tests/system/small/operations/test_datetimes.py b/tests/system/small/operations/test_datetimes.py index 0463124309..705439fd96 100644 --- a/tests/system/small/operations/test_datetimes.py +++ b/tests/system/small/operations/test_datetimes.py @@ -81,6 +81,20 @@ def test_dt_dayofweek(scalars_dfs, col_name): assert_series_equal(pd_result, bf_result, check_dtype=False) +@pytest.mark.parametrize( + ("col_name",), + DATE_COLUMNS, +) +def test_dt_dayofyear(scalars_dfs, col_name): + pytest.importorskip("pandas", minversion="2.0.0") + scalars_df, scalars_pandas_df = scalars_dfs + bf_series: bigframes.series.Series = scalars_df[col_name] + bf_result = bf_series.dt.dayofyear.to_pandas() + pd_result = scalars_pandas_df[col_name].dt.dayofyear + + assert_series_equal(pd_result, bf_result, check_dtype=False) + + @pytest.mark.parametrize( ("col_name",), DATETIME_COL_NAMES, diff --git a/third_party/bigframes_vendored/pandas/core/indexes/accessor.py b/third_party/bigframes_vendored/pandas/core/indexes/accessor.py index f34612cb11..a3c0d59e46 100644 --- a/third_party/bigframes_vendored/pandas/core/indexes/accessor.py +++ b/third_party/bigframes_vendored/pandas/core/indexes/accessor.py @@ -66,6 +66,35 @@ def dayofweek(self): raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) + @property + def dayofyear(self): + """The ordinal day of the year. + + **Examples:** + + >>> import pandas as pd + >>> import bigframes.pandas as bpd + >>> bpd.options.display.progress_bar = None + >>> s = bpd.Series( + ... pd.date_range('2016-12-28', '2017-01-03', freq='D').to_series() + ... ) + >>> s.dt.dayofyear + 2016-12-28 00:00:00 363 + 2016-12-29 00:00:00 364 + 2016-12-30 00:00:00 365 + 2016-12-31 00:00:00 366 + 2017-01-01 00:00:00 1 + 2017-01-02 00:00:00 2 + 2017-01-03 00:00:00 3 + dtype: Int64 + dtype: Int64 + + Returns: + Series: Containing integers indicating the day number. + """ + + raise NotImplementedError(constants.ABSTRACT_METHOD_ERROR_MESSAGE) + @property def date(self): """Returns a Series with the date part of Timestamps without time and