Repository: pandas-dev/pandas

Issue:
Add .dt.total_seconds() method for timedelta64 Series
Background: I am trying to build a horizonal bar graph that shows me when various builds on a server started and ended, so that I can see visually which builds overlapped in time. But the idea that a horizontal bar graph's bar-width would be a timedelta seems completely foreign to all of the plotting libraries that I have tried today (matplotlib, bokeh, ggplot), so I want to punt and instead reduce my timedelta64's to a floating-point number of days. I can then fudge the axes to display dates anyway.

Problem: there is no easy way to move from a timedelta64 to the standard Python idea of an ordinal date. I would have to extract all of the components manually (days, seconds, fractions of a second) and then multiply them each by an appropriate factor and then add them back together.

The Python `timedelta` class has an alternative that I like very much: a `total_seconds()` method that expresses then entire value of the `timedelta` as a single scalar number of seconds.

This is not a perfect solution, since the factors it uses internally neglect the fact that a day might have included a leap second, but it works pretty well for non-astronomical use.

Could a `Series.dt.total_seconds()` method be added that returns the floating point value `days * 86400 + seconds + microseconds / 1e6`? It would be a great convenience for moving from date-land to scalar-land before plotting. Thanks!



Discussion:
see here: http://pandas.pydata.org/pandas-docs/stable/timedeltas.html#frequency-conversion

canoncially this is just an astype or division operation

```
In [3]: s = Series(pd.timedelta_range('1day',periods=5,freq='1s'))

In [4]: s
Out[4]: 
0   1 days 00:00:00
1   1 days 00:00:01
2   1 days 00:00:02
3   1 days 00:00:03
4   1 days 00:00:04
dtype: timedelta64[ns]

In [5]: s.astype('timedelta64[s]')
Out[5]: 
0    86400
1    86401
2    86402
3    86403
4    86404
dtype: float64

In [12]: s/np.timedelta64(1,'s')
Out[12]: 
0    86400
1    86401
2    86402
3    86403
4    86404
dtype: float64

In [6]: s.iloc[0].total_seconds()
Out[6]: 86400.0
```

I suppose could add a `Series.dt.total_seconds` to make `TimedeltaIndex` consistent with the method on `Timedelta`

+1 on a `Series.dt.total_seconds()`


Resolving patch:
diff --git a/pandas/tseries/common.py b/pandas/tseries/common.py
--- 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
--- 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


Test patch:
diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py
--- 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/tests/test_timedeltas.py b/pandas/tseries/tests/test_timedeltas.py
--- 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