Time series (astropy_timeseries)

Introduction

Many different areas of astrophysics have to deal with 1D time series data, either sampling a continuous variable at fixed times or counting some events binned into time windows. The astropy_timeseries package therefore provides classes to represent and manipulate time series

The time series classes presented below are QTable sub-classes that have special columns to represent times using the Time class. Therefore, much of the functionality described in Data Tables (astropy.table) applies here.

Getting Started

In this section, we take a quick look at how to read in a time series, access the data, and carry out some basic analysis. For more details about creating and using time series, see the full documentation in Using timeseries.

The simplest time series class is TimeSeries - it represents a time series as a collection of values at specific points in time. If you are interested in representing time series as measurements in discrete time bins, you will likely be interested in the BinnedTimeSeries sub-class which we show in Using timeseries).

To start off, we retrieve a FITS file containing a Kepler light curve for a source:

>>> from astropy.utils.data import get_pkg_data_filename
>>> filename = get_pkg_data_filename('timeseries/kplr010666592-2009131110544_slc.fits')  

We can then use the TimeSeries class to read in this file:

>>> from astropy_timeseries import TimeSeries
>>> ts = TimeSeries.read(filename, format='kepler.fits')  

Time series are specialized kinds of Table objects:

>>> ts  
<TimeSeries length=14280>
    time             timecorr   ...   pos_corr1      pos_corr2
                        d       ...     pixels         pixels
   object            float32    ...    float32        float32
----------------------- ------------- ... -------------- --------------
2009-05-02T00:41:40.338  6.630610e-04 ...  1.5822421e-03 -1.4463664e-03
2009-05-02T00:42:39.187  6.630857e-04 ...  1.5743829e-03 -1.4540013e-03
2009-05-02T00:43:38.045  6.631103e-04 ...  1.5665225e-03 -1.4616371e-03
              ...           ... ...            ...            ...
2009-05-11T18:05:14.479  1.014664e-03 ...  3.5986886e-03  3.1407392e-03
2009-05-11T18:06:13.328  1.014689e-03 ...  3.5967610e-03  3.1329831e-03
2009-05-11T18:07:12.186  1.014713e-03 ...  3.5948332e-03  3.1252259e-03

In the same way as for Table, the various columns and rows can be accessed and sliced using index notation:

>>> ts['sap_flux']  
<Quantity [1027045.06, 1027184.44, 1027076.25, ..., 1025451.56, 1025468.5 ,
           1025930.9 ] electron / s>

>>> ts['time', 'sap_flux']  
<TimeSeries length=14280>
          time             sap_flux
                         electron / s
         object            float32
----------------------- --------------
2009-05-02T00:41:40.338  1.0270451e+06
2009-05-02T00:42:39.187  1.0271844e+06
2009-05-02T00:43:38.045  1.0270762e+06
                    ...            ...
2009-05-11T18:05:14.479  1.0254516e+06
2009-05-11T18:06:13.328  1.0254685e+06
2009-05-11T18:07:12.186  1.0259309e+06

>>> ts[0:4]  
<TimeSeries length=4>
          time             timecorr   ...   pos_corr1      pos_corr2
                              d       ...     pixels         pixels
         object            float32    ...    float32        float32
----------------------- ------------- ... -------------- --------------
2009-05-02T00:41:40.338  6.630610e-04 ...  1.5822421e-03 -1.4463664e-03
2009-05-02T00:42:39.187  6.630857e-04 ...  1.5743829e-03 -1.4540013e-03
2009-05-02T00:43:38.045  6.631103e-04 ...  1.5665225e-03 -1.4616371e-03
2009-05-02T00:44:36.894  6.631350e-04 ...  1.5586632e-03 -1.4692718e-03

As seen in the example above, TimeSeries objects have a time column, which is always the first column. This column can also be accessed using the .time attribute:

>>> ts.time  
<Time object: scale='tcb' format='isot' value=['2009-05-02T00:41:40.338' '2009-05-02T00:42:39.187'
 '2009-05-02T00:43:38.045' ... '2009-05-11T18:05:14.479'
 '2009-05-11T18:06:13.328' '2009-05-11T18:07:12.186']>

and is always a Time object (see Times and Dates), which therefore supports the ability to convert to different time scales and formats:

>>> ts.time.mjd  
array([54953.0289391 , 54953.02962023, 54953.03030145, ...,
       54962.7536398 , 54962.75432093, 54962.75500215])

>>> ts.time.unix  
array([1.24122482e+09, 1.24122488e+09, 1.24122494e+09, ...,
       1.24206503e+09, 1.24206509e+09, 1.24206515e+09])

Let’s use what we’ve seen so far to make a plot

import matplotlib.pyplot as plt
plt.plot(ts.time.jd, ts['sap_flux'], 'k.', markersize=1)
plt.xlabel('Barycentric Julian Date')
plt.ylabel('SAP Flux (e-/s)')

(png, svg, pdf)

../_images/index-2.png

It looks like there are a few transits! Let’s use BoxLeastSquares to estimate the period, using a box with a duration of 0.2 days:

>>> import numpy as np
>>> from astropy import units as u
>>> from astropy.stats import BoxLeastSquares
>>> keep = ~np.isnan(ts['sap_flux'])  
>>> periodogram = BoxLeastSquares(ts.time.jd[keep] * u.day,
...                               ts['sap_flux'][keep]).autopower(0.2 * u.day)  
>>> period = periodogram.period[np.argmax(periodogram.power)]  
>>> period  
<Quantity 2.20551724 d>

We can now fold the time series using the period we’ve found above using the fold() method:

>>> ts_folded = ts.fold(period=period, midpoint_epoch='2009-05-02T07:41:40')  

Let’s take a look at the folded time series:

plt.plot(ts_folded.time.jd, ts_folded['sap_flux'], 'k.', markersize=1)
plt.xlabel('Time (days)')
plt.ylabel('SAP Flux (e-/s)')

(png, svg, pdf)

../_images/index-6.png

Using the Astrostatistics Tools (astropy.stats) module, we can normalize the flux by sigma-clipping the data to determine the baseline flux:

>>> from astropy.stats import sigma_clipped_stats
>>> mean, median, stddev = sigma_clipped_stats(ts_folded['sap_flux'])  
>>> ts_folded['sap_flux_norm'] = ts_folded['sap_flux'] / median  

and we can downsample the time series by binning the points into bins of equal time - this returns a BinnedTimeSeries:

>>> from astropy_timeseries import simple_downsample
>>> ts_binned = simple_downsample(ts_folded, 0.03 * u.day)  
>>> ts_binned  
<BinnedTimeSeries length=74>
  time_bin_start      time_bin_size    ...   pos_corr2    sap_flux_norm
                            s          ...
       object            float64       ...    float32        float32
------------------- ------------------ ... -------------- -------------
-1.1024637743446593             2592.0 ...  -0.0007693809     1.0000684
-1.0724637743446592 2591.9999999999905 ...  0.00038625093     1.0000265
-1.0424637743446594 2592.0000000000095 ...  0.00031359284     1.0000247
-1.0124637743446594             2592.0 ...  0.00017846933     1.0000288
-0.9824637743446593             2592.0 ...  -6.542908e-05    0.99999636
-0.9524637743446593             2592.0 ... -2.9231509e-05     1.0000253
-0.9224637743446593             2592.0 ...   9.763808e-05     1.0000322
-0.8924637743446593             2592.0 ...  0.00012931376     1.0000411
-0.8624637743446593 2592.0000000000023 ...  -0.0011244675     1.0000389
                ...                ... ...            ...           ...
 0.8175362256553407 2591.9999999999977 ...  -0.0006246633     1.0000035
 0.8475362256553407 2592.0000000000014 ...  -0.0006362205     1.0000261
 0.8775362256553407  2592.000000000019 ... -0.00089895696     1.0000074
  0.907536225655341 2591.9999999999814 ...  -0.0008507269     1.0000023
 0.9375362256553407   2592.00000000002 ...  -0.0009239185     1.0000565
 0.9675362256553409  2591.999999999981 ... -0.00020750743     1.0000125
 0.9975362256553407 2592.0000000000095 ... -0.00055085105     1.0000396
 1.0275362256553409 2591.9999999999905 ...  -0.0005766208     1.0000231
 1.0575362256553407             2592.0 ... -0.00023155387     1.0000265
 1.0875362256553407             2592.0 ...  1.5008554e-06    0.99995804

Let’s take a look at the final result:

plt.plot(ts_folded.time.jd, ts_folded['sap_flux_norm'], 'k.', markersize=1)
plt.plot(ts_binned.time_bin_start.jd, ts_binned['sap_flux_norm'], 'r-', drawstyle='steps-post')
plt.xlabel('Time (days)')
plt.ylabel('Normalized flux')

(png, svg, pdf)

../_images/index-10.png

It looks like there might be a hint of a secondary transit!

To learn more about the capabilities in the astropy_timeseries module, you can find links to the full documentation in the next section.

Reference/API

astropy_timeseries Package

This subpackage contains classes and functions for work with time series data sets.

Functions

simple_downsample(time_series, time_bin_size) Downsample a time series by binning values into bins with a fixed size,
test(**kwargs) Run the tests for the package.

Classes

BaseTimeSeries([data, masked, names, dtype, …])
BinnedTimeSeries([data, time_bin_start, …])
TimeSeries([data, time, time_delta, n_samples])

Class Inheritance Diagram

Inheritance diagram of astropy_timeseries.core.BaseTimeSeries, astropy_timeseries.binned.BinnedTimeSeries, astropy_timeseries.sampled.TimeSeries