From a16526913a7810514b54d2c176da8c7d7d2a4ca7 Mon Sep 17 00:00:00 2001 From: Samuel Denny Date: Sun, 30 Aug 2015 13:35:14 +0100 Subject: [PATCH] ENH: Added Series.dt.total_seconds() (#10817) --- doc/source/whatsnew/v0.17.0.txt | 4 ++++ pandas/tests/test_series.py | 6 ++++- pandas/tseries/common.py | 2 +- pandas/tseries/tdi.py | 4 ++++ pandas/tseries/tests/test_timedeltas.py | 31 +++++++++++++++++++++++++ pandas/tslib.pyx | 12 +++++++++- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index 3e81a923a114c..b7feec3895f97 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -176,6 +176,10 @@ Other enhancements - ``pandas.tseries.offsets`` larger than the ``Day`` offset can now be used with with ``Series`` for addition/subtraction (:issue:`10699`). See the :ref:`Documentation ` for more details. +- ``pd.Series`` of type ``timedelta64`` has new method ``.dt.total_seconds()`` returning the duration of the timedelta in seconds (:issue: `10817`) + +- ``pd.Timedelta.total_seconds()`` now returns Timedelta duration to ns precision (previously microsecond precision) (:issue: `10939`) + - ``.as_blocks`` will now take a ``copy`` optional argument to return a copy of the data, default is to copy (no change in behavior from prior versions), (:issue:`9607`) - ``regex`` argument to ``DataFrame.filter`` now handles numeric column names instead of raising ``ValueError`` (:issue:`10384`). diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index 34ea674fe10c0..86eafdf7ca2c8 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -89,7 +89,7 @@ def test_dt_namespace_accessor(self): 'is_quarter_end', 'is_year_start', 'is_year_end', 'tz'] ok_for_dt_methods = ['to_period','to_pydatetime','tz_localize','tz_convert', 'normalize', 'strftime'] ok_for_td = ['days','seconds','microseconds','nanoseconds'] - ok_for_td_methods = ['components','to_pytimedelta'] + ok_for_td_methods = ['components','to_pytimedelta','total_seconds'] def get_expected(s, name): result = getattr(Index(s.values),prop) @@ -157,6 +157,10 @@ def compare(s, name): result = s.dt.to_pytimedelta() self.assertIsInstance(result,np.ndarray) self.assertTrue(result.dtype == object) + + result = s.dt.total_seconds() + self.assertIsInstance(result,pd.Series) + self.assertTrue(result.dtype == 'float64') freq_result = s.dt.freq self.assertEqual(freq_result, TimedeltaIndex(s.values, freq='infer').freq) diff --git a/pandas/tseries/common.py b/pandas/tseries/common.py index a4d5939d386ae..9a282bec2e9e4 100644 --- a/pandas/tseries/common.py +++ b/pandas/tseries/common.py @@ -161,7 +161,7 @@ def components(self): accessors=TimedeltaIndex._datetimelike_ops, typ='property') TimedeltaProperties._add_delegate_accessors(delegate=TimedeltaIndex, - accessors=["to_pytimedelta"], + accessors=["to_pytimedelta", "total_seconds"], typ='method') class PeriodProperties(Properties): diff --git a/pandas/tseries/tdi.py b/pandas/tseries/tdi.py index b0c9d8852f8c9..984f2a1cec706 100644 --- a/pandas/tseries/tdi.py +++ b/pandas/tseries/tdi.py @@ -391,6 +391,10 @@ def f(x): result = result.astype('int64') return result + def total_seconds(self): + """ Total duration of each element expressed in seconds. """ + return self._maybe_mask_results(1e-9*self.asi8) + def to_pytimedelta(self): """ Return TimedeltaIndex as object ndarray of datetime.timedelta objects diff --git a/pandas/tseries/tests/test_timedeltas.py b/pandas/tseries/tests/test_timedeltas.py index 4870fbd55f33e..eef0894bdd349 100644 --- a/pandas/tseries/tests/test_timedeltas.py +++ b/pandas/tseries/tests/test_timedeltas.py @@ -19,6 +19,7 @@ assert_almost_equal, assert_index_equal, ensure_clean) +from numpy.testing import assert_allclose from pandas.tseries.offsets import Day, Second, Hour import pandas.util.testing as tm from numpy.random import rand, randn @@ -945,6 +946,36 @@ def test_fields(self): tm.assert_series_equal(s.dt.days,Series([1,np.nan],index=[0,1])) tm.assert_series_equal(s.dt.seconds,Series([10*3600+11*60+12,np.nan],index=[0,1])) + def test_total_seconds(self): + # GH 10939 + # test index + rng = timedelta_range('1 days, 10:11:12.100123456', periods=2, freq='s') + expt = [1*86400+10*3600+11*60+12+100123456./1e9,1*86400+10*3600+11*60+13+100123456./1e9] + assert_allclose(rng.total_seconds(), expt, atol=1e-10, rtol=0) + + # test Series + s = Series(rng) + s_expt = Series(expt,index=[0,1]) + tm.assert_series_equal(s.dt.total_seconds(),s_expt) + + # with nat + s[1] = np.nan + s_expt = Series([1*86400+10*3600+11*60+12+100123456./1e9,np.nan],index=[0,1]) + tm.assert_series_equal(s.dt.total_seconds(),s_expt) + + # with both nat + s = Series([np.nan,np.nan], dtype='timedelta64[ns]') + tm.assert_series_equal(s.dt.total_seconds(),Series([np.nan,np.nan],index=[0,1])) + + def test_total_seconds_scalar(self): + # GH 10939 + rng = Timedelta('1 days, 10:11:12.100123456') + expt = 1*86400+10*3600+11*60+12+100123456./1e9 + assert_allclose(rng.total_seconds(), expt, atol=1e-10, rtol=0) + + rng = Timedelta(np.nan) + self.assertTrue(np.isnan(rng.total_seconds())) + def test_components(self): rng = timedelta_range('1 days, 10:11:12', periods=2, freq='s') rng.components diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 369993b4c54d1..226cfc843b3cf 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -642,6 +642,10 @@ class NaTType(_NaT): def __reduce__(self): return (__nat_unpickle, (None, )) + + def total_seconds(self): + # GH 10939 + return np.nan fields = ['year', 'quarter', 'month', 'day', 'hour', @@ -673,7 +677,7 @@ def _make_nan_func(func_name): _nat_methods = ['date', 'now', 'replace', 'to_datetime', 'today'] -_nan_methods = ['weekday', 'isoweekday'] +_nan_methods = ['weekday', 'isoweekday', 'total_seconds'] _implemented_methods = ['to_datetime64'] _implemented_methods.extend(_nat_methods) @@ -2412,6 +2416,12 @@ class Timedelta(_Timedelta): """ self._ensure_components() return self._ns + + def total_seconds(self): + """ + Total duration of timedelta in seconds (to ns precision) + """ + return 1e-9*self.value def __setstate__(self, state): (value) = state