def test_empty_initialization(): ts = TimeSeries() ts['time'] = Time([1, 2, 3], format='mjd')
def test_read(): timeseries = TimeSeries.read(CSV_FILE, time_column='Date', format='csv') assert timeseries.colnames == ['time', 'A', 'B', 'C', 'D', 'E', 'F', 'G'] assert len(timeseries) == 11 assert timeseries['time'].format == 'iso' assert timeseries['A'].sum() == 266.5
def test_read_time_missing(): with pytest.raises(ValueError) as exc: TimeSeries.read(CSV_FILE, format='csv') assert exc.value.args[ 0] == '``time_column`` should be provided since the default Table readers are being used.'
def test_read_time_wrong(): with pytest.raises(ValueError) as exc: TimeSeries.read(CSV_FILE, time_column='abc', format='csv') assert exc.value.args[ 0] == "Time column 'abc' not found in the input data."
def test_initialization_missing_time_delta(): with pytest.raises(TypeError) as exc: TimeSeries(time_start=datetime(2018, 7, 1, 10, 10, 10), data=[[10, 2, 3], [4, 5, 6]], names=['a', 'b']) assert exc.value.args[0] == "'time' is scalar, so 'time_delta' is required"
def test_binning_arg_invalid(): ts = TimeSeries(time=INPUT_TIME, data=[[1, 2, 3, 4, 5]], names=['a']) with pytest.raises(TypeError, match=r"With single 'time_bin_start' either 'n_bins', " "'time_bin_size' or time_bin_end' must be provided"): aggregate_downsample(ts)
def test_initialization_invalid_time_delta(): with pytest.raises(TypeError) as exc: TimeSeries(time_start=datetime(2018, 7, 1, 10, 10, 10), time_delta=[1, 4, 3], data=[[10, 2, 3], [4, 5, 6]], names=['a', 'b']) assert exc.value.args[0] == "'time_delta' should be a Quantity or a TimeDelta"
def test_initialize_only_data(): with pytest.raises(TypeError) as exc: TimeSeries(data=[[10, 2, 3], [4, 5, 6]], names=['a', 'b']) assert exc.value.args[ 0] == "Either 'time' or 'time_start' should be specified"
def test_initialization_with_data(): ts = TimeSeries(time=INPUT_TIME, data=[[10, 2, 3], [4, 5, 6]], names=['a', 'b']) assert_equal(ts.time.isot, INPUT_TIME.isot) assert_equal(ts['a'], [10, 2, 3]) assert_equal(ts['b'], [4, 5, 6])
def test_initialization_invalid_time_and_time_start(): with pytest.raises(TypeError) as exc: TimeSeries(time=INPUT_TIME, time_start=datetime(2018, 7, 1, 10, 10, 10), data=[[10, 2, 3], [4, 5, 6]], names=['a', 'b']) assert exc.value.args[0] == "Cannot specify both 'time' and 'time_start'"
def test_empty_initialization(): ts = TimeSeries() ts['time'] = Time([50001, 50002, 50003], format='mjd')
def test_fold_invalid_options(): times = Time([1, 2, 3, 8, 9, 12], format='unix') ts = TimeSeries(time=times) ts['flux'] = [1, 4, 4, 3, 2, 3] with pytest.raises(u.UnitsError, match='period should be a Quantity in units of time'): ts.fold(period=3.2) with pytest.raises(u.UnitsError, match='period should be a Quantity in units of time'): ts.fold(period=3.2 * u.m) with pytest.raises(u.UnitsError, match='epoch_phase should be a Quantity in units of ' 'time when normalize_phase=False'): ts.fold(period=3.2 * u.s, epoch_phase=0.2) with pytest.raises(u.UnitsError, match='epoch_phase should be a dimensionless Quantity ' 'or a float when normalize_phase=True'): ts.fold(period=3.2 * u.s, epoch_phase=0.2 * u.s, normalize_phase=True) with pytest.raises(u.UnitsError, match='wrap_phase should be a Quantity in units of ' 'time when normalize_phase=False'): ts.fold(period=3.2 * u.s, wrap_phase=0.2) with pytest.raises(u.UnitsError, match='wrap_phase should be dimensionless when ' 'normalize_phase=True'): ts.fold(period=3.2 * u.s, wrap_phase=0.2 * u.s, normalize_phase=True) with pytest.raises(ValueError, match='wrap_phase should be between 0 and the period'): ts.fold(period=3.2 * u.s, wrap_phase=-0.1 * u.s) with pytest.raises(ValueError, match='wrap_phase should be between 0 and the period'): ts.fold(period=3.2 * u.s, wrap_phase=-4.2 * u.s) with pytest.raises(ValueError, match='wrap_phase should be between 0 and 1'): ts.fold(period=3.2 * u.s, wrap_phase=-0.1, normalize_phase=True) with pytest.raises(ValueError, match='wrap_phase should be between 0 and 1'): ts.fold(period=3.2 * u.s, wrap_phase=2.2, normalize_phase=True)
def test_fold(): times = Time([1, 2, 3, 8, 9, 12], format='unix') ts = TimeSeries(time=times) ts['flux'] = [1, 4, 4, 3, 2, 3] # Try without epoch time, as it should default to the first time and # wrapping at half the period. tsf = ts.fold(period=3.2 * u.s) assert isinstance(tsf.time, TimeDelta) assert_allclose(tsf.time.sec, [0, 1, -1.2, 0.6, -1.6, 1.4], rtol=1e-6) # Try with epoch time tsf = ts.fold(period=3.2 * u.s, epoch_time=Time(1.6, format='unix')) assert isinstance(tsf.time, TimeDelta) assert_allclose(tsf.time.sec, [-0.6, 0.4, 1.4, 0.0, 1.0, 0.8], rtol=1e-6, atol=1e-6) # Now with wrap_phase set to the full period tsf = ts.fold(period=3.2 * u.s, wrap_phase=3.2 * u.s) assert isinstance(tsf.time, TimeDelta) assert_allclose(tsf.time.sec, [0, 1, 2, 0.6, 1.6, 1.4], rtol=1e-6) # Now set epoch_phase to be 1/4 of the way through the phase tsf = ts.fold(period=3.2 * u.s, epoch_phase=0.8 * u.s) assert isinstance(tsf.time, TimeDelta) assert_allclose(tsf.time.sec, [0.8, -1.4, -0.4, 1.4, -0.8, -1.0], rtol=1e-6) # And combining epoch_phase and wrap_phase tsf = ts.fold(period=3.2 * u.s, epoch_phase=0.8 * u.s, wrap_phase=3.2 * u.s) assert isinstance(tsf.time, TimeDelta) assert_allclose(tsf.time.sec, [0.8, 1.8, 2.8, 1.4, 2.4, 2.2], rtol=1e-6) # Now repeat the above tests but with normalization applied # Try without epoch time, as it should default to the first time and # wrapping at half the period. tsf = ts.fold(period=3.2 * u.s, normalize_phase=True) assert isinstance(tsf.time, Quantity) assert_allclose(tsf.time.to_value(u.one), [0, 1/3.2, -1.2/3.2, 0.6/3.2, -1.6/3.2, 1.4/3.2], rtol=1e-6) # Try with epoch time tsf = ts.fold(period=3.2 * u.s, epoch_time=Time(1.6, format='unix'), normalize_phase=True) assert isinstance(tsf.time, Quantity) assert_allclose(tsf.time.to_value(u.one), [-0.6/3.2, 0.4/3.2, 1.4/3.2, 0.0/3.2, 1.0/3.2, 0.8/3.2], rtol=1e-6, atol=1e-6) # Now with wrap_phase set to the full period tsf = ts.fold(period=3.2 * u.s, wrap_phase=1, normalize_phase=True) assert isinstance(tsf.time, Quantity) assert_allclose(tsf.time.to_value(u.one), [0, 1/3.2, 2/3.2, 0.6/3.2, 1.6/3.2, 1.4/3.2], rtol=1e-6) # Now set epoch_phase to be 1/4 of the way through the phase tsf = ts.fold(period=3.2 * u.s, epoch_phase=0.25, normalize_phase=True) assert isinstance(tsf.time, Quantity) assert_allclose(tsf.time.to_value(u.one), [0.8/3.2, -1.4/3.2, -0.4/3.2, 1.4/3.2, -0.8/3.2, -1.0/3.2], rtol=1e-6) # And combining epoch_phase and wrap_phase tsf = ts.fold(period=3.2 * u.s, epoch_phase=0.25, wrap_phase=1, normalize_phase=True) assert isinstance(tsf.time, Quantity) assert_allclose(tsf.time.to_value(u.one), [0.8/3.2, 1.8/3.2, 2.8/3.2, 1.4/3.2, 2.4/3.2, 2.2/3.2], rtol=1e-6)
def test_required_columns(): # Test the machinery that makes sure that the required columns are present ts = TimeSeries(time=INPUT_TIME, data=[[10, 2, 3], [4, 5, 6]], names=['a', 'b']) # In the examples below, the operation (e.g. remove_column) is actually # carried out before the checks are made, so we need to use copy() so that # we don't change the main version of the time series. # Make sure copy works fine ts.copy() with pytest.raises(ValueError) as exc: ts.copy().add_column(Column([3, 4, 5], name='c'), index=0) assert exc.value.args[0] == ("TimeSeries object is invalid - expected " "'time' as the first column but found 'c'") with pytest.raises(ValueError) as exc: ts.copy().add_columns( [Column([3, 4, 5], name='d'), Column([3, 4, 5], name='e')], indexes=[0, 1]) assert exc.value.args[0] == ("TimeSeries object is invalid - expected " "'time' as the first column but found 'd'") with pytest.raises(ValueError) as exc: ts.copy().keep_columns(['a', 'b']) assert exc.value.args[0] == ("TimeSeries object is invalid - expected " "'time' as the first column but found 'a'") with pytest.raises(ValueError) as exc: ts.copy().remove_column('time') assert exc.value.args[0] == ("TimeSeries object is invalid - expected " "'time' as the first column but found 'a'") with pytest.raises(ValueError) as exc: ts.copy().remove_columns(['time', 'a']) assert exc.value.args[0] == ("TimeSeries object is invalid - expected " "'time' as the first column but found 'b'") with pytest.raises(ValueError) as exc: ts.copy().rename_column('time', 'banana') assert exc.value.args[0] == ( "TimeSeries object is invalid - expected " "'time' as the first column but found 'banana'")
def kepler_fits_reader(filename): """ This serves as the FITS reader for KEPLER or TESS files within astropy-timeseries. This function should generally not be called directly, and instead this time series reader should be accessed with the :meth:`~astropy.timeseries.TimeSeries.read` method:: >>> from astropy.timeseries import TimeSeries >>> ts = TimeSeries.read('kplr33122.fits', format='kepler.fits') # doctest: +SKIP Parameters ---------- filename : `str` or `pathlib.Path` File to load. Returns ------- ts : `~astropy.timeseries.TimeSeries` Data converted into a TimeSeries. """ hdulist = fits.open(filename) # Get the lightcurve HDU telescope = hdulist[0].header['telescop'].lower() if telescope == 'tess': hdu = hdulist['LIGHTCURVE'] elif telescope == 'kepler': hdu = hdulist[1] else: raise NotImplementedError( "{} is not implemented, only KEPLER or TESS are " "supported through this reader".format( hdulist[0].header['telescop'])) if hdu.header['EXTVER'] > 1: raise NotImplementedError("Support for {} v{} files not yet " "implemented".format(hdu.header['TELESCOP'], hdu.header['EXTVER'])) # Check time scale if hdu.header['TIMESYS'] != 'TDB': raise NotImplementedError("Support for {} time scale not yet " "implemented in {} reader".format( hdu.header['TIMESYS'], hdu.header['TELESCOP'])) tab = Table.read(hdu, format='fits') # Some KEPLER files have a T column instead of TIME. if "T" in tab.colnames: tab.rename_column("T", "TIME") for colname in tab.colnames: # Fix units if tab[colname].unit == 'e-/s': tab[colname].unit = 'electron/s' if tab[colname].unit == 'pixels': tab[colname].unit = 'pixel' # Rename columns to lowercase tab.rename_column(colname, colname.lower()) # Filter out NaN rows nans = np.isnan(tab['time'].data) if np.any(nans): warnings.warn(f'Ignoring {np.sum(nans)} rows with NaN times') tab = tab[~nans] # Time column is dependent on source and we correct it here reference_date = Time(hdu.header['BJDREFI'], hdu.header['BJDREFF'], scale=hdu.header['TIMESYS'].lower(), format='jd') time = reference_date + TimeDelta(tab['time'].data) time.format = 'isot' # Remove original time column tab.remove_column('time') return TimeSeries(time=time, data=tab)
def test_initialize_only_time(): ts = TimeSeries(time=INPUT_TIME) assert ts['time'] is ts.time # NOTE: the object in the table is a copy assert_equal(ts.time.isot, INPUT_TIME.isot)
def setup_method(self, method): self.series = TimeSeries(time=INPUT_TIME, data=PLAIN_TABLE) self.time_attr = 'time'
def test_initialization_with_table(): ts = TimeSeries(time=INPUT_TIME, data=PLAIN_TABLE) assert ts.colnames == ['time', 'a', 'b', 'c']
def test_initialization_invalid_both_time_and_time_delta(): with pytest.raises(TypeError) as exc: TimeSeries(time=INPUT_TIME, time_delta=TimeDelta(3, format='sec')) assert exc.value.args[0] == ("'time_delta' should not be specified since " "'time' is an array")
def test_initialization_length_mismatch(): with pytest.raises(ValueError) as exc: TimeSeries(time=INPUT_TIME, data=[[10, 2], [4, 5]], names=['a', 'b']) assert exc.value.args[0] == "Length of 'time' (3) should match data length (2)"
def test_downsample(): ts = TimeSeries(time=INPUT_TIME, data=[[1, 2, 3, 4, 5]], names=['a']) ts_units = TimeSeries(time=INPUT_TIME, data=[[1, 2, 3, 4, 5] * u.count], names=['a']) # Avoid precision problems with floating-point comparisons on 32bit if sys.maxsize > 2**32: # 64 bit time_bin_incr = 1 * u.s time_bin_start = None else: # 32 bit time_bin_incr = (1 - 1e-6) * u.s time_bin_start = ts.time[0] - 1 * u.ns down_1 = aggregate_downsample(ts, time_bin_size=time_bin_incr, time_bin_start=time_bin_start) u.isclose(down_1.time_bin_size, [1, 1, 1, 1, 1] * time_bin_incr) assert_equal( down_1.time_bin_start.isot, Time([ '2016-03-22T12:30:31.000', '2016-03-22T12:30:32.000', '2016-03-22T12:30:33.000', '2016-03-22T12:30:34.000', '2016-03-22T12:30:35.000' ])) assert_equal(down_1["a"].data.data, np.array([1, 2, 3, 4, 5])) down_2 = aggregate_downsample(ts, time_bin_size=2 * time_bin_incr, time_bin_start=time_bin_start) u.isclose(down_2.time_bin_size, [2, 2, 2] * time_bin_incr) assert_equal( down_2.time_bin_start.isot, Time([ '2016-03-22T12:30:31.000', '2016-03-22T12:30:33.000', '2016-03-22T12:30:35.000' ])) assert_equal(down_2["a"].data.data, np.array([1, 3, 5])) down_3 = aggregate_downsample(ts, time_bin_size=3 * time_bin_incr, time_bin_start=time_bin_start) u.isclose(down_3.time_bin_size, [3, 3] * time_bin_incr) assert_equal(down_3.time_bin_start.isot, Time(['2016-03-22T12:30:31.000', '2016-03-22T12:30:34.000'])) assert_equal(down_3["a"].data.data, np.array([2, 4])) down_4 = aggregate_downsample(ts, time_bin_size=4 * time_bin_incr, time_bin_start=time_bin_start) u.isclose(down_4.time_bin_size, [4, 4] * time_bin_incr) assert_equal(down_4.time_bin_start.isot, Time(['2016-03-22T12:30:31.000', '2016-03-22T12:30:35.000'])) assert_equal(down_4["a"].data.data, np.array([2, 5])) down_units = aggregate_downsample(ts_units, time_bin_size=4 * time_bin_incr, time_bin_start=time_bin_start) u.isclose(down_units.time_bin_size, [4, 4] * time_bin_incr) assert_equal(down_units.time_bin_start.isot, Time(['2016-03-22T12:30:31.000', '2016-03-22T12:30:35.000'])) assert down_units["a"].unit.name == 'ct' assert_equal(down_units["a"].data, np.array([2.5, 5.0])) # Contiguous bins with uneven bin sizes: `time_bin_size` is an array down_uneven_bins = aggregate_downsample(ts, time_bin_size=[2, 1, 1] * time_bin_incr, time_bin_start=time_bin_start) u.isclose(down_uneven_bins.time_bin_size, [2, 1, 1] * time_bin_incr) assert_equal( down_uneven_bins.time_bin_start.isot, Time([ '2016-03-22T12:30:31.000', '2016-03-22T12:30:33.000', '2016-03-22T12:30:34.000' ])) assert_equal(down_uneven_bins["a"].data.data, np.array([1, 3, 4])) # Uncontiguous bins with even bin sizes: `time_bin_start` and `time_bin_end` are both arrays down_time_array = aggregate_downsample(ts, time_bin_start=Time([ '2016-03-22T12:30:31.000', '2016-03-22T12:30:34.000' ]), time_bin_end=Time([ '2016-03-22T12:30:32.000', '2016-03-22T12:30:35.000' ])) u.isclose(down_time_array.time_bin_size, [1, 1] * u.second) assert_equal(down_time_array.time_bin_start.isot, Time(['2016-03-22T12:30:31.000', '2016-03-22T12:30:34.000'])) assert_equal(down_time_array["a"].data.data, np.array([1, 4])) # Overlapping bins with pytest.warns(AstropyUserWarning, match="Overlapping bins should be avoided since they " "can lead to double-counting of data during binning."): down_overlap_bins = aggregate_downsample(ts, time_bin_start=Time([ '2016-03-22T12:30:31.000', '2016-03-22T12:30:33.000' ]), time_bin_end=Time([ '2016-03-22T12:30:34', '2016-03-22T12:30:36.000' ])) assert_equal(down_overlap_bins["a"].data, np.array([2, 5]))