def get_timing_lombscargle(n, ofac, hfac): x, y, dy = generate_random_signal(n) Nf = int(floor(0.5 * len(x) * ofac * hfac)) df = 1. / (ofac * (max(x) - min(x))) f0 = df model = LombScargleFast(silence_warnings=True) model.fit(x, y, dy) t0 = time() model.score_frequency_grid(f0, df, Nf) dt = time() - t0 return dt
def FlarePer(time, minper=0.1, maxper=30.0, nper=20000): ''' Look for periodicity in the flare occurrence times. Could be due to: a) mis-identified periodic things (e.g. heartbeat stars) b) crazy binary star flaring things c) flares on a rotating star d) bugs in code e) aliens ''' # use energy = 1 for flare times. # This will create something like the window function energy = np.ones_like(time) # Use Jake Vanderplas faster version! pgram = LombScargleFast(fit_offset=False) pgram.optimizer.set(period_range=(minper, maxper)) pgram = pgram.fit(time, energy - np.nanmedian(energy)) df = (1. / minper - 1. / maxper) / nper f0 = 1. / maxper pwr = pgram.score_frequency_grid(f0, df, nper) freq = f0 + df * np.arange(nper) per = 1. / freq pk = per[np.argmax(pwr)] # peak period pp = np.max(pwr) # peak period power return pk, pp
def Periodogram(self, madVar=True): """ This function computes the power spectrum from the timeseries ONLY. """ dtav = np.mean(np.diff(self.time_fix)) # mean value of time differences (s) dtmed = np.median(np.diff(self.time_fix)) # median value of time differences (s) if dtmed == 0: dtmed = dtav # compute periodogram from regular frequency values fmin = 0 # minimum frequency N = len(self.time_fix) # n-points df = 1./(dtmed*N) # bin width (1/Tobs) (in Hz) model = LombScargleFast().fit(self.time_fix, self.flux_fix, np.ones(N)) power = model.score_frequency_grid(fmin, df, N/2) # signal-to-noise ratio, (1) eqn 9 freqs = fmin + df * np.arange(N/2) # the periodogram was computed over these freqs (Hz) # the variance of the flux if madVar: var = mad_std(self.flux_fix)**2 else: var = np.std(self.flux_fix)**2 # convert to PSD, see (1) eqn 1, 8, 9 & (2) power /= np.sum(power) # make the power sum to unity (dimensionless) power *= var # Parseval's theorem. time-series units: ppm. variance units: ppm^2 power /= df * 1e6 # convert from ppm^2 to ppm^2 muHz^-1 if len(freqs) < len(power): power = power[0:len(freqs)] if len(freqs) > len(power): freqs = freqs[0:len(power)] self.freq = freqs * 1e6 # muHz self.power = power # ppm^2 muHz^-1 self.bin_width = df * 1e6 # muHz
def fast_find_spectrum(self, f_samps, mag_low, mag_high, raw=False): """ Finds the Lomb-Scargle periodogram of the concentration time series, frequency units are in hz. This method uses the Gatspy package, which has a fast fft based periodogram calculator. f_samps -- int; number of frequency samples to be used in the fitting, they are spaced logarithmically mag_low -- float; mag_high -- float; the sampled frequency range is [10^mag_low, 10^mag_high] raw -- Boolean; if true, self.c_raw is used, otherwise self.c is used NB the frequency grid is linear (logarithmic in self.find_spectrum()) in this method, so many more points should be used. """ assert mag_low < mag_high if raw: abs_time = self.t_raw gas_conc = self.c_raw else: abs_time = self.t gas_conc = self.c rel_time = np.array([]) #the date/datetime objects should be in increasing order, but it should #work all the same t0 = EPOCH for t in abs_time: delta_t = t - t0 rel_time = np.append(rel_time, delta_t.total_seconds()) fmin = 10**mag_low fmax = 10**mag_high df = (fmax - fmin) / f_samps #NB uses same x and t in the above section model = LombScargleFast().fit(rel_time, gas_conc) rel_power = model.score_frequency_grid(fmin, df, f_samps) freqs = fmin + df * np.arange(f_samps) abs_amplitude = gas_conc.std() * np.sqrt(2 * rel_power) self.rel_spectrum = np.array([freqs, rel_power]) self.spectrum = np.array([freqs, abs_amplitude]) return None
def fAnalysis(self, rr_samples): """ Frequency analysis to calc self.lf, self.hf, returns the LF/HF-ratio and also calculates the spectrum as pairs of (self.f_hr_axis,self.f_hr). The input arrary is in sample points where R peaks have been detected. """ # discrete timestamps self.hr_discrete = self._intervals(rr_samples) / 1000 # hr positions in time self.t_hr_discrete = [i / self.fs for i in rr_samples[1:]] # now let's create function which approximates the hr(t) relationship self.hr_func = interp1d(self.t_hr_discrete, self.hr_discrete) # we take 1024 samples for a linear time array for hr(t) nsamp = 1000 # linear time array for the heartrate self.t_hr_linear = np.linspace( self.t_hr_discrete[1], self.t_hr_discrete[len(self.t_hr_discrete) - 2], num=nsamp) # duration in secs of the heartrate array minus the ends b/c of the approx duration = self.t_hr_discrete[len(self.t_hr_discrete) - 2] - self.t_hr_discrete[1] # heartrate linearly approximated between discrete samples self.hr_linear = self.hr_func(self.t_hr_linear) model = LombScargleFast().fit(self.t_hr_discrete, self.hr_discrete, 1E-2) fmax = 1 fmin = 0.01 df = (fmax - fmin) / nsamp self.f_hr = model.score_frequency_grid(fmin, df, nsamp) self.f_hr_axis = fmin + df * np.arange(nsamp) # lf self.lf = 0 # hf self.hf = 0 for i in range(0, int(nsamp / 2)): if (self.f_hr_axis[i] >= 0.04) and (self.f_hr_axis[i] <= 0.15): self.lf = self.lf + self.f_hr[i] if (self.f_hr_axis[i] >= 0.15) and (self.f_hr_axis[i] <= 0.4): self.hf = self.hf + self.f_hr[i] # hf return self.lf / self.hf
def FitSin(time, flux, error, maxnum=5, nper=20000, minper=0.1, maxper=30.0, plim=0.25, per2=False, returnmodel=True, debug=False): ''' Use Lomb Scargle to find a periodic signal. If it is significant then fit a sine curve and subtract. Repeat this procedure until no more periodic signals are found, or until maximum number of iterations has been reached. Note: this is where major issues were found in the light curve fitting as of Davenport (2016), where the iterative fitting was not adequately subtracting "pointy" features, such as RR Lyr or EBs. Upgrades to the fitting step are needed! Or, don't use iterative sine fitting... Idea for future: if L-S returns a significant P, use a median fit of the phase-folded data at that P instead of a sine fit... Parameters ---------- time : 1-d numpy array flux : 1-d numpy array error : 1-d numpy array maxnum : int, optional maximum number of iterations to try finding periods at (default=5) nper : int, optional number of periods to search over with Lomb Scargle (defeault=20000) minper : float, optional minimum period (in units of time array, nominally days) to search for periods over (default=0.1) maxper : float, optional maximum period (in units of time array, nominally days) to search for periods over (default=30.0) plim : float, optional Lomb-Scargle power threshold needed to define a "significant" period (default=0.25) per2 : bool, optional if True, use the 2-sine model fit at each period. if False, use normal 1-sine model (default=False) returnmodel : bool, optional if True, return the combined sine model. If False, return the data - model (default=True) debug : bool, optional used to print out troubleshooting things (default=False) Returns ------- If returnmodel=True, output = combined sine model (default=True) If returnmodel=False, output = (data - model) ''' flux_out = np.array(flux, copy=True) sin_out = np.zeros_like(flux) # return the sin function! # total baseline of time window dt = np.nanmax(time) - np.nanmin(time) medflux = np.nanmedian(flux) # ti = time[dl[i]:dr[i]] for k in range(0, maxnum): # Use Jake Vanderplas faster version! pgram = LombScargleFast(fit_offset=False) pgram.optimizer.set(period_range=(minper, maxper)) pgram = pgram.fit(time, flux_out - medflux, error) df = (1. / minper - 1. / maxper) / nper f0 = 1. / maxper pwr = pgram.score_frequency_grid(f0, df, nper) freq = f0 + df * np.arange(nper) per = 1. / freq pok = np.where((per < dt) & (per > minper)) pk = per[pok][np.argmax(pwr[pok])] pp = np.max(pwr) if debug is True: print('trial (k): ' + str(k) + '. peak period (pk):' + str(pk) + '. peak power (pp):' + str(pp)) # if a period w/ enough power is detected if (pp > plim): # fit sin curve to window and subtract if per2 is True: p0 = [ pk, 3.0 * np.nanstd(flux_out - medflux), 0.0, pk / 2., 1.5 * np.nanstd(flux_out - medflux), 0.1, 0.0 ] try: pfit, pcov = curve_fit(_sinfunc2, time, flux_out - medflux, p0=p0) if debug is True: print('>>', pfit) except RuntimeError: pfit = [pk, 0., 0., 0., 0., 0., 0.] if debug is True: print('Curve_Fit2 no good') flux_out = flux_out - _sinfunc2(time, *pfit) sin_out = sin_out + _sinfunc2(time, *pfit) else: p0 = [pk, 3.0 * np.nanstd(flux_out - medflux), 0.0, 0.0] try: pfit, pcov = curve_fit(_sinfunc, time, flux_out - medflux, p0=p0) except RuntimeError: pfit = [pk, 0., 0., 0.] if debug is True: print('Curve_Fit no good') flux_out = flux_out - _sinfunc(time, *pfit) sin_out = sin_out + _sinfunc(time, *pfit) # add the median flux for this window BACK in sin_out = sin_out + medflux # if debug is True: # plt.figure() # plt.plot(time, flux) # plt.plot(time, flux_out, c='red') # plt.show() if returnmodel is True: return sin_out else: return flux_out
def FitSin(time, flux, error, maxnum=5, nper=20000, minper=0.1, maxper=30.0, plim=0.25, returnmodel=True, debug=False, per2=False): ''' Use Lomb Scargle to find periods, fit sins, remove, repeat. Parameters ---------- time: flux: error: maxnum: nper: int, optional number of periods to search over with Lomb Scargle minper: maxper: plim: debug: Returns ------- ''' # periods = np.linspace(minper, maxper, nper) flux_out = np.array(flux, copy=True) sin_out = np.zeros_like(flux) # return the sin function! # total baseline of time window dt = np.nanmax(time) - np.nanmin(time) medflux = np.nanmedian(flux) # ti = time[dl[i]:dr[i]] for k in range(0, maxnum): # Use Jake Vanderplas faster version! pgram = LombScargleFast(fit_offset=False) pgram.optimizer.set(period_range=(minper, maxper)) pgram = pgram.fit(time, flux_out - medflux, error) df = (1. / minper - 1. / maxper) / nper f0 = 1. / maxper pwr = pgram.score_frequency_grid(f0, df, nper) freq = f0 + df * np.arange(nper) per = 1. / freq pok = np.where((per < dt) & (per > minper)) pk = per[pok][np.argmax(pwr[pok])] pp = np.max(pwr) if debug is True: print('trial (k): ' + str(k) + '. peak period (pk):' + str(pk) + '. peak power (pp):' + str(pp)) # if a period w/ enough power is detected if (pp > plim): # fit sin curve to window and subtract if per2 is True: p0 = [ pk, 3.0 * np.nanstd(flux_out - medflux), 0.0, pk / 2., 1.5 * np.nanstd(flux_out - medflux), 0.1, 0.0 ] try: pfit, pcov = curve_fit(_sinfunc2, time, flux_out - medflux, p0=p0) if debug is True: print('>>', pfit) except RuntimeError: pfit = [pk, 0., 0., 0., 0., 0., 0.] if debug is True: print('Curve_Fit2 no good') flux_out = flux_out - _sinfunc2(time, *pfit) sin_out = sin_out + _sinfunc2(time, *pfit) else: p0 = [pk, 3.0 * np.nanstd(flux_out - medflux), 0.0, 0.0] try: pfit, pcov = curve_fit(_sinfunc, time, flux_out - medflux, p0=p0) except RuntimeError: pfit = [pk, 0., 0., 0.] if debug is True: print('Curve_Fit no good') flux_out = flux_out - _sinfunc(time, *pfit) sin_out = sin_out + _sinfunc(time, *pfit) # add the median flux for this window BACK in sin_out = sin_out + medflux # if debug is True: # plt.figure() # plt.plot(time, flux) # plt.plot(time, flux_out, c='red') # plt.show() if returnmodel is True: return sin_out else: return flux_out
def power_spectrum(self, verbose=False, noise=0.0, \ length=-1): ''' This function computes the power spectrum from the timeseries. The function checks to see if the timeseries has been read in, and if not it calls the read_timeseries function. The porperties of the power spectrum can be altered for a given timeseries via the noise, and length parameters. The frequency and power are stored in the object atributes self.freq and self,power. Parameters ---------------- verbose: Bool(False) Provide verbose output if set to True. noise: Float If noise is not zero then additional noise is added to the timeseries where the value of noise is the standard deviation of the additional noise. length: Int If length is not -1 then a subset of the timeseries is selected when n points will equal length. The subset of data is taken from the start of the time series. TODO this can be updated if neccessary. Returns ---------------- NA Examples ---------------- To read in a data set and create the power spectrum one need only run: >>> import K2data >>> star = K2data.Dataset(2001122017, '/home/davies/Data/ktwo_2001122017_llc.pow') >>> star.power_spectrum() ''' if len(self.time) < 1: self.read_timeseries(verbose=True) if noise > 0.0: self.flux_fix[:length] += np.random.randn(len(self.time_fix[:length])) * noise dtav = np.mean(np.diff(self.time_fix[:length])) dtmed = np.median(np.diff(self.time_fix[:length])) if dtmed == 0: dtmed = dtav fmin = 0 N = len(self.time_fix[:length]) #n-points df = 1.0 / dtmed / N #bw model = LombScargleFast().fit(self.time_fix[:length], \ self.flux_fix[:length], \ np.ones(N)) power = model.score_frequency_grid(fmin, df, N/2) freqs = fmin + df * np.arange(N/2) var = np.std(self.flux_fix[:length])**2 power /= np.sum(power) power *= var power /= df * 1e6 if len(freqs) < len(power): power = power[0:len(freqs)] if len(freqs) > len(power): freqs = freqs[0:len(power)] self.freq = freqs * 1e6 self.power = power if verbose: print("Frequency resolution : {}".format(self.freq[1])) print("Nyquist : ~".format(self.freq.max()))
plt.figure(0) plt.plot(t, x) #the frequency domain, component frequencies are clear (in hz) plt.figure(1) plt.plot(ang_f / ang, x_tilde_norm) end0 = datetime.datetime.now() print("SciPy took:", end0 - start0) ############################################################################### from gatspy.periodic import LombScargleFast start1 = datetime.datetime.now() N = 1000 fmin = 0.001 fmax = 10 df = (fmax - fmin) / N #NB uses same x and t in the above section model = LombScargleFast().fit(t, x) power = model.score_frequency_grid(fmin, df, N) freqs = fmin + df * np.arange(N) # plot the results plt.figure() plt.plot(freqs, ((x.std()) * np.sqrt(2 * power)), ls='', marker='+') end1 = datetime.datetime.now() print("Gatspy took:", end1 - start1)
t=np.ma.array(t,mask=to_mask) signal=np.ma.array(signal,mask=to_mask) #We choose the frequency range we want to check. An angular frequency is required #for the L-S diagram. fmin=0.01*f_real fmax=10.*f_real Nf= N df = (fmax - fmin) / Nf f = 2.0*np.pi*np.linspace(fmin, fmax, Nf) #We take the L-S of the signal. if hole_index == 0: pgram = LombScargleFast().fit(t, signal,sdev) elif hole_index == 1: pgram = LombScargleFast().fit(t[~t.mask], signal[~signal.mask],sdev) power = pgram.score_frequency_grid(fmin,df,Nf) #We plot the signal and the L-S fig, ax = plt.subplots(2, 1) ax[0].plot(t,signal, 'o', ms = 1.5) ax[0].set_xlim([0.,T_tot]) ax[0].set_xlabel('Time') ax[0].set_ylabel('Flux') ax[0].grid() #We want the frequency in Hz. ax[1].plot(f/2.0/np.pi/f_real, power, 'o') ax[1].set_xlim([fmin/2.0/np.pi/f_real,6]) ax[1].set_xlabel('Freq (1/orbital period)') ax[1].set_ylabel('L-S power') ax[1].grid()
def FFT_data(self, oversample=False): """------------------------------------------------------------------""" """perform FFT on timeseries, two options available: gatspy / astropy""" """------------------------------------------------------------------""" self.setTime() print("Pre-mask times length: %s" % len(self.times)) self.mask_data() #mask out useless data print("Post-mask times length: %s" % len(self.times)) N = len(self.times) if self.FFT_option == 'gatspy': print('... using gatspy') #gatspy LS from gatspy.periodic import LombScargleFast #ls = LombScargleFast().fit(self.times, self.df)#, self.error) #periods, self.power = ls.periodogram_auto(nyquist_factor=2) #self.frequencies = (1/periods)*(1e6) #frequencies in microHz fmin = 0 dtmed = np.median(np.diff(self.times)) df = 1. / (dtmed * N) ls = LombScargleFast().fit(self.times, self.df, np.ones(N)) power = ls.score_frequency_grid(fmin, df, N / 2) freqs = fmin + df * np.arange(N / 2) var = np.std(self.df)**2 power /= np.sum(power) # convert so sums to unity power *= var # Parseval's: time-series units [G], variance units [G^2] power /= df * 1e6 # convert to G^2/muHz self.power = power self.frequencies = freqs * 1e6 ts_energy = np.sum(np.abs(np.power(self.df, 2))) / N fd_energy = np.sum(np.abs(np.power(self.power, 2))) print('TS: %s' % ts_energy) print('FD: %s' % fd_energy) #df = pd.DataFrame({'periods':periods, 'power':power}) #df.to_csv('/mnt/storage/003. Data/004. HiSPARC Data/FFT.csv') elif self.FFT_option == 'astropy': print('... using astropy') #astropy LS from astropy.stats import LombScargle from scipy.integrate import simps """ts_energy = simps(np.power(self.df,2), self.times) print(ts_energy)""" dtmed = np.median(np.diff(self.times)) df = 1. / (dtmed * N) fmin = 0 if oversample != False: frequencies = fmin + ( df / oversample) * np.arange(oversample * N / 2 + 1) else: frequencies = fmin + df * np.arange(N / 2 + 1) power = LombScargle(self.times, self.df).power(frequencies) power[0] = 0.0 #frequencies, power = LombScargle(self.times, self.df).autopower(nyquist_factor=1) var = np.std(self.df)**2 ts_energy = np.sum(np.abs(np.power(self.df, 2))) / N power /= np.sum(power) # convert so sums to unity power *= var # Parseval's: time-series units [G], variance units [G^2] fd_energy = np.sum(np.abs(np.power(power, 1))) power /= df * 1e6 # convert to G^2/muHz self.power = power self.frequencies = frequencies * 1e6 #frequencies in microHz #ts_energy = np.sum(np.abs(np.power(self.df,2)))/N #fd_energy = np.sum(np.abs(np.power(self.power,2))) print('TS: %s' % ts_energy) print('TS_var: %s' % var) print('FD: %s' % fd_energy) return self.frequencies, self.power, self.times
plt.ylabel('NUV Flux \n' r'(x10$^{-15}$ erg s$^{-1}$ cm$^{-2}$ ${\rm\AA}^{-1}$)') plt.title('Flags = 0') minper = 10 # my windowing maxper = 200000 nper = 1000 pgram = LombScargleFast(fit_offset=False) pgram.optimizer.set(period_range=(minper, maxper)) pgram = pgram.fit(time_big - min(time_big), flux_big - np.nanmedian(flux_big)) df = (1. / minper - 1. / maxper) / nper f0 = 1. / maxper pwr = pgram.score_frequency_grid(f0, df, nper) freq = f0 + df * np.arange(nper) per = 1. / freq ## plt.figure() plt.plot(per, pwr, lw=0.75) plt.xlabel('Period (seconds)') plt.ylabel('L-S Power') plt.xscale('log') plt.xlim(10, 500) plt.savefig('periodogram.pdf', dpi=150, bbox_inches='tight', pad_inches=0.25) t_unix = Time(exp_data['NUV']['t0'] + 315964800, format='unix') mjd_time_med = t_unix.mjd