Skip to content

Commit

Permalink
Add missing DataArray.dt.total_seconds() method
Browse files Browse the repository at this point in the history
  • Loading branch information
maresb committed Nov 9, 2023
1 parent feba698 commit 48478f1
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ Datetimelike properties
DataArray.dt.seconds
DataArray.dt.microseconds
DataArray.dt.nanoseconds
DataArray.dt.total_seconds

**Timedelta methods**:

Expand Down
2 changes: 2 additions & 0 deletions doc/whats-new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ New Features

- Use `opt_einsum <https://optimized-einsum.readthedocs.io/en/stable/>`_ for :py:func:`xarray.dot` by default if installed.
By `Deepak Cherian <https://github.com/dcherian>`_. (:issue:`7764`, :pull:`8373`).
- Add ``DataArray.dt.total_seconds()`` method to match the Pandas API. (:pull:`8435`).
By `Ben Mares <https://github.com/maresb>`_.

Breaking changes
~~~~~~~~~~~~~~~~
Expand Down
14 changes: 14 additions & 0 deletions xarray/core/accessor_dt.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ def _access_through_series(values, name):
if name == "season":
months = values_as_series.dt.month.values
field_values = _season_from_months(months)
elif name == "total_seconds":
field_values = values_as_series.dt.total_seconds().values
elif name == "isocalendar":
# special NaT-handling can be removed when
# https://github.com/pandas-dev/pandas/issues/54657 is resolved
Expand Down Expand Up @@ -574,6 +576,13 @@ class TimedeltaAccessor(TimeAccessor[T_DataArray]):
43200, 64800])
Coordinates:
* time (time) timedelta64[ns] 1 days 00:00:00 ... 5 days 18:00:00
>>> ts.dt.total_seconds()
<xarray.DataArray 'total_seconds' (time: 20)>
array([ 86400., 108000., 129600., 151200., 172800., 194400., 216000.,
237600., 259200., 280800., 302400., 324000., 345600., 367200.,
388800., 410400., 432000., 453600., 475200., 496800.])
Coordinates:
* time (time) timedelta64[ns] 1 days 00:00:00 ... 5 days 18:00:00
"""

@property
Expand All @@ -596,6 +605,11 @@ def nanoseconds(self) -> T_DataArray:
"""Number of nanoseconds (>= 0 and less than 1 microsecond) for each element"""
return self._date_field("nanoseconds", np.int64)

# Not defined as a property in order to match the Pandas API
def total_seconds(self) -> T_DataArray:
"""Total duration of each element expressed in seconds."""
return self._date_field("total_seconds", np.float64)


class CombinedDatetimelikeAccessor(
DatetimeAccessor[T_DataArray], TimedeltaAccessor[T_DataArray]
Expand Down
14 changes: 14 additions & 0 deletions xarray/tests/test_accessor_dt.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import xarray as xr
from xarray.tests import (
assert_allclose,
assert_array_equal,
assert_chunks_equal,
assert_equal,
Expand Down Expand Up @@ -100,6 +101,19 @@ def test_field_access(self, field) -> None:
assert expected.dtype == actual.dtype
assert_identical(expected, actual)

def test_total_seconds(self) -> None:
# Subtract a value in the middle of the range to ensure that some values
# are negative
delta = self.data.time - np.datetime64("2000-01-03")
actual = delta.dt.total_seconds()
expected = xr.DataArray(
np.arange(-48, 52, dtype=np.float64) * 3600,
name="total_seconds",
coords=[self.data.time],
)
# This works with assert_identical when pandas is >=1.5.0.
assert_allclose(expected, actual)

@pytest.mark.parametrize(
"field, pandas_field",
[
Expand Down

0 comments on commit 48478f1

Please sign in to comment.