diff --git a/doc/time-series.rst b/doc/time-series.rst index c225c246a8c..7f5389d3ae1 100644 --- a/doc/time-series.rst +++ b/doc/time-series.rst @@ -263,14 +263,16 @@ For data indexed by a :py:class:`~xarray.CFTimeIndex` xarray currently supports: da.sel(time=slice('0001-05', '0002-02')) - Access of basic datetime components via the ``dt`` accessor (in this case - just "year", "month", "day", "hour", "minute", "second", "microsecond", and - "season"): + just "year", "month", "day", "hour", "minute", "second", "microsecond", + "season", "dayofyear", and "dayofweek"): .. ipython:: python da.time.dt.year da.time.dt.month da.time.dt.season + da.time.dt.dayofyear + da.time.dt.dayofweek - Group-by operations based on datetime accessor attributes (e.g. by month of the year): diff --git a/doc/whats-new.rst b/doc/whats-new.rst index e4d204497ef..0fb8013d30f 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -41,6 +41,9 @@ Enhancements - :py:class:`CFTimeIndex` uses slicing for string indexing when possible (like :py:class:`pandas.DatetimeIndex`), which avoids unnecessary copies. By `Stephan Hoyer `_ +- Like :py:class:`pandas.DatetimeIndex`, :py:class:`CFTimeIndex` now supports + "dayofyear" and "dayofweek" accessors (:issue:`2597`). By `Spencer Clark + `_. Bug fixes ~~~~~~~~~ diff --git a/xarray/coding/cftimeindex.py b/xarray/coding/cftimeindex.py index 71fa7f2fa6b..98954e9af0c 100644 --- a/xarray/coding/cftimeindex.py +++ b/xarray/coding/cftimeindex.py @@ -207,6 +207,9 @@ class CFTimeIndex(pd.Index): second = _field_accessor('second', 'The seconds of the datetime') microsecond = _field_accessor('microsecond', 'The microseconds of the datetime') + dayofyear = _field_accessor('dayofyr', + 'The ordinal day of year of the datetime') + dayofweek = _field_accessor('dayofwk', 'The day of week of the datetime') date_type = property(get_date_type) def __new__(cls, data, name=None): diff --git a/xarray/tests/test_accessors.py b/xarray/tests/test_accessors.py index 38038fc8f65..37b9c272e6e 100644 --- a/xarray/tests/test_accessors.py +++ b/xarray/tests/test_accessors.py @@ -158,7 +158,8 @@ def times_3d(times): @pytest.mark.skipif(not has_cftime, reason='cftime not installed') -@pytest.mark.parametrize('field', ['year', 'month', 'day', 'hour']) +@pytest.mark.parametrize('field', ['year', 'month', 'day', 'hour', + 'dayofyear', 'dayofweek']) def test_field_access(data, field): result = getattr(data.time.dt, field) expected = xr.DataArray( @@ -170,7 +171,8 @@ def test_field_access(data, field): @pytest.mark.skipif(not has_dask, reason='dask not installed') @pytest.mark.skipif(not has_cftime, reason='cftime not installed') -@pytest.mark.parametrize('field', ['year', 'month', 'day', 'hour']) +@pytest.mark.parametrize('field', ['year', 'month', 'day', 'hour', + 'dayofyear', 'dayofweek']) def test_dask_field_access_1d(data, field): import dask.array as da @@ -186,7 +188,8 @@ def test_dask_field_access_1d(data, field): @pytest.mark.skipif(not has_dask, reason='dask not installed') @pytest.mark.skipif(not has_cftime, reason='cftime not installed') -@pytest.mark.parametrize('field', ['year', 'month', 'day', 'hour']) +@pytest.mark.parametrize('field', ['year', 'month', 'day', 'hour', 'dayofyear', + 'dayofweek']) def test_dask_field_access(times_3d, data, field): import dask.array as da diff --git a/xarray/tests/test_cftimeindex.py b/xarray/tests/test_cftimeindex.py index b9006774c30..ea41115937b 100644 --- a/xarray/tests/test_cftimeindex.py +++ b/xarray/tests/test_cftimeindex.py @@ -175,6 +175,20 @@ def test_cftimeindex_field_accessors(index, field, expected): assert_array_equal(result, expected) +@pytest.mark.skipif(not has_cftime, reason='cftime not installed') +def test_cftimeindex_dayofyear_accessor(index): + result = index.dayofyear + expected = [date.dayofyr for date in index] + assert_array_equal(result, expected) + + +@pytest.mark.skipif(not has_cftime, reason='cftime not installed') +def test_cftimeindex_dayofweek_accessor(index): + result = index.dayofweek + expected = [date.dayofwk for date in index] + assert_array_equal(result, expected) + + @pytest.mark.skipif(not has_cftime, reason='cftime not installed') @pytest.mark.parametrize(('string', 'date_args', 'reso'), [ ('1999', (1999, 1, 1), 'year'),