def test_lomb_scargle_period_folding(): """Tests for features derived from fitting a Lomb-Scargle periodic model and period-folding the data by the estimated period. """ frequencies = np.hstack((WAVE_FREQS[0], np.zeros(len(WAVE_FREQS)-1))) amplitudes = np.zeros((len(WAVE_FREQS),4)) amplitudes[0,:] = [8,4,2,1] phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) # Folding is numerically unstable so we need to use the exact fitted frequency freq_est = all_lomb['freq1_freq'] # Fold by 1*period fold1ed_times = (times-times[0]) % (1./freq_est) sort_indices = np.argsort(fold1ed_times) fold1ed_times = fold1ed_times[sort_indices] fold1ed_values = values[sort_indices] # Fold by 2*period fold2ed_times = (times-times[0]) % (2./freq_est) sort_indices = np.argsort(fold2ed_times) fold2ed_times = fold2ed_times[sort_indices] fold2ed_values = values[sort_indices] npt.assert_allclose(np.sum(np.diff(fold2ed_values)**2) / np.sum(np.diff(values)**2), all_lomb['p2p_scatter_2praw']) npt.assert_allclose(np.sum(np.diff(values)**2) / ((len(values) - 1) * np.var(values)), all_lomb['p2p_ssqr_diff_over_var']) npt.assert_allclose(np.median(np.abs(np.diff(values))) / np.median(np.abs(values-np.median(values))), all_lomb['p2p_scatter_over_mad']) npt.assert_allclose(np.median(np.abs(np.diff(fold1ed_values))) / np.median(np.abs(values-np.median(values))), all_lomb['p2p_scatter_pfold_over_mad'])
def test_lomb_scargle_irregular_single_freq(): """Test Lomb-Scargle model features on irregularly-sampled periodic data with one frequency/multiple harmonics. More difficult than regularly-sampled case, so we allow parameter estimates to be slightly noisy. """ frequencies = np.hstack((WAVE_FREQS[0], np.zeros(len(WAVE_FREQS)-1))) amplitudes = np.zeros((len(WAVE_FREQS),4)) amplitudes[0,:] = [8,4,2,1] phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) # Only test the first (true) frequency; the rest correspond to noise npt.assert_allclose(all_lomb['freq1_freq'], frequencies[0], rtol=1e-2) # Only test first frequency here; noise gives non-zero amplitudes for residuals for j in range(amplitudes.shape[1]): npt.assert_allclose(amplitudes[0,j], all_lomb['freq1_amplitude{}'.format(j+1)], rtol=5e-2, atol=5e-2) if j >= 1: npt.assert_allclose(phase*j*(-1**j), all_lomb['freq1_rel_phase{}'.format(j+1)], rtol=1e-1, atol=1e-1) npt.assert_array_less(10., all_lomb['freq1_signif']) # Only one frequency, so this should explain basically all the variance npt.assert_allclose(0., all_lomb['freq_varrat'], atol=5e-3) npt.assert_allclose(-np.mean(values), all_lomb['freq_y_offset'], rtol=5e-2)
def test_lomb_scargle_irregular_multi_freq(): """Test Lomb-Scargle model features on irregularly-sampled periodic data with multiple frequencies, each with a single harmonic. More difficult than regularly-sampled case, so we allow parameter estimates to be slightly noisy. """ frequencies = WAVE_FREQS amplitudes = np.zeros((len(frequencies),4)) amplitudes[:,0] = [4,2,1] phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) for i, frequency in enumerate(frequencies): npt.assert_allclose(frequency, all_lomb['freq{}_freq'.format(i+1)], rtol=1e-2) for (i,j), amplitude in np.ndenumerate(amplitudes): npt.assert_allclose(amplitude, all_lomb['freq{}_amplitude{}'.format(i+1,j+1)], rtol=1e-1, atol=1e-1) for i in [2,3]: npt.assert_allclose(amplitudes[i-1,0] / amplitudes[0,0], all_lomb['freq_amplitude_ratio_{}1'.format(i)], atol=2e-2) npt.assert_allclose(frequencies[i-1] / frequencies[0], all_lomb['freq_frequency_ratio_{}1'.format(i)], atol=5e-2) npt.assert_array_less(10., all_lomb['freq1_signif'])
def test_lomb_scargle_irregular_single_freq(): """Test Lomb-Scargle model features on irregularly-sampled periodic data with one frequency/multiple harmonics. More difficult than regularly-sampled case, so we allow parameter estimates to be slightly noisy. """ frequencies = np.hstack((WAVE_FREQS[0], np.zeros(len(WAVE_FREQS) - 1))) amplitudes = np.zeros((len(WAVE_FREQS), 4)) amplitudes[0, :] = [8, 4, 2, 1] phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) # Only test the first (true) frequency; the rest correspond to noise npt.assert_allclose(all_lomb['freq1_freq'], frequencies[0], rtol=1e-2) # Only test first frequency here; noise gives non-zero amplitudes for residuals for j in range(amplitudes.shape[1]): npt.assert_allclose(amplitudes[0, j], all_lomb['freq1_amplitude{}'.format(j + 1)], rtol=5e-2, atol=5e-2) if j >= 1: npt.assert_allclose(phase * j * (-1**j), all_lomb['freq1_rel_phase{}'.format(j + 1)], rtol=1e-1, atol=1e-1) npt.assert_array_less(10., all_lomb['freq1_signif']) # Only one frequency, so this should explain basically all the variance npt.assert_allclose(0., all_lomb['freq_varrat'], atol=5e-3) npt.assert_allclose(-np.mean(values), all_lomb['freq_y_offset'], rtol=5e-2)
def test_lomb_scargle_irregular_multi_freq(): """Test Lomb-Scargle model features on irregularly-sampled periodic data with multiple frequencies, each with a single harmonic. More difficult than regularly-sampled case, so we allow parameter estimates to be slightly noisy. """ frequencies = WAVE_FREQS amplitudes = np.zeros((len(frequencies), 4)) amplitudes[:, 0] = [4, 2, 1] phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) for i, frequency in enumerate(frequencies): npt.assert_allclose(frequency, all_lomb['freq{}_freq'.format(i + 1)], rtol=1e-2) for (i, j), amplitude in np.ndenumerate(amplitudes): npt.assert_allclose(amplitude, all_lomb['freq{}_amplitude{}'.format(i + 1, j + 1)], rtol=1e-1, atol=1e-1) for i in [2, 3]: npt.assert_allclose(amplitudes[i - 1, 0] / amplitudes[0, 0], all_lomb['freq_amplitude_ratio_{}1'.format(i)], atol=2e-2) npt.assert_allclose(frequencies[i - 1] / frequencies[0], all_lomb['freq_frequency_ratio_{}1'.format(i)], atol=5e-2) npt.assert_array_less(10., all_lomb['freq1_signif'])
def test_lomb_scargle_fast_irregular(): """Test gatspy's fast Lomb-Scargle period estimate on irregularly-sampled periodic data. Note: this model fits only a single sinusoid with no additional harmonics, so we use only 1 frequency and 1 amplitude to generate test data. """ frequencies = np.array([4]) amplitudes = np.array([[1]]) phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) f = generate_features(times, values, errors, ['period_fast']) npt.assert_allclose(f['period_fast'], 1. / frequencies[0], rtol=3e-2)
def test_lomb_scargle_linear_trend(): frequencies = np.hstack((WAVE_FREQS[0], np.zeros(len(WAVE_FREQS)-1))) amplitudes = np.zeros((len(WAVE_FREQS),4)) amplitudes[0,:] = [8,4,2,1] phase = 0.1 slope = 0.5 # Estimated trend should be almost exact for noiseless data times, values, errors = regular_periodic(frequencies, amplitudes, phase) values += slope * times all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) npt.assert_allclose(slope, all_lomb['linear_trend'], rtol=1e-3) # Should still be close to true trend when noise is present times, values, errors = irregular_periodic(frequencies, amplitudes, phase) values += slope * times values += np.random.normal(scale=1e-3, size=len(times)) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) npt.assert_allclose(slope, all_lomb['linear_trend'], rtol=1e-1)
def test_lomb_scargle_linear_trend(): frequencies = np.hstack((WAVE_FREQS[0], np.zeros(len(WAVE_FREQS) - 1))) amplitudes = np.zeros((len(WAVE_FREQS), 4)) amplitudes[0, :] = [8, 4, 2, 1] phase = 0.1 slope = 0.5 # Estimated trend should be almost exact for noiseless data times, values, errors = regular_periodic(frequencies, amplitudes, phase) values += slope * times all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) npt.assert_allclose(slope, all_lomb['linear_trend'], rtol=1e-3) # Should still be close to true trend when noise is present times, values, errors = irregular_periodic(frequencies, amplitudes, phase) values += slope * times values += np.random.normal(scale=1e-3, size=len(times)) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) npt.assert_allclose(slope, all_lomb['linear_trend'], rtol=1e-1)
def test_lomb_scargle_period_folding(): """Tests for features derived from fitting a Lomb-Scargle periodic model and period-folding the data by the estimated period. """ frequencies = np.hstack((WAVE_FREQS[0], np.zeros(len(WAVE_FREQS) - 1))) amplitudes = np.zeros((len(WAVE_FREQS), 4)) amplitudes[0, :] = [8, 4, 2, 1] phase = 0.1 times, values, errors = irregular_periodic(frequencies, amplitudes, phase) all_lomb = generate_features(times, values, errors, LOMB_SCARGLE_FEATS) # Folding is numerically unstable so we need to use the exact fitted frequency freq_est = all_lomb['freq1_freq'] # Fold by 1*period fold1ed_times = (times - times[0]) % (1. / freq_est) sort_indices = np.argsort(fold1ed_times) fold1ed_times = fold1ed_times[sort_indices] fold1ed_values = values[sort_indices] # Fold by 2*period fold2ed_times = (times - times[0]) % (2. / freq_est) sort_indices = np.argsort(fold2ed_times) fold2ed_times = fold2ed_times[sort_indices] fold2ed_values = values[sort_indices] npt.assert_allclose( np.sum(np.diff(fold2ed_values)**2) / np.sum(np.diff(values)**2), all_lomb['p2p_scatter_2praw']) npt.assert_allclose( np.sum(np.diff(values)**2) / ((len(values) - 1) * np.var(values)), all_lomb['p2p_ssqr_diff_over_var']) npt.assert_allclose( np.median(np.abs(np.diff(values))) / np.median(np.abs(values - np.median(values))), all_lomb['p2p_scatter_over_mad']) npt.assert_allclose( np.median(np.abs(np.diff(fold1ed_values))) / np.median(np.abs(values - np.median(values))), all_lomb['p2p_scatter_pfold_over_mad'])