def _make_segment_spectrum(self, lc, segment_size): if not isinstance(lc, lightcurve.Lightcurve): raise TypeError("lc must be a lightcurve.Lightcurve object") if self.gti is None: self.gti = lc.gti check_gtis(self.gti) start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc.time) power_all = [] nphots_all = [] for start_ind, end_ind in zip(start_inds, end_inds): time = lc.time[start_ind:end_ind] counts = lc.counts[start_ind:end_ind] counts_err = lc.counts_err[start_ind:end_ind] lc_seg = lightcurve.Lightcurve(time, counts, err=counts_err, err_dist=lc.err_dist.lower()) power_seg = Powerspectrum(lc_seg, norm=self.norm) power_all.append(power_seg) nphots_all.append(np.sum(lc_seg.counts)) return power_all, nphots_all
def _make_matrix(self, lc): """ Create a matrix of powers for each time step (rows) and each frequency step (columns). Parameters ---------- lc : :class:`Lightcurve` object The :class:`Lightcurve` object from which to generate the dynamical power spectrum """ ps_all, _ = AveragedPowerspectrum._make_segment_spectrum( self, lc, self.segment_size) self.dyn_ps = np.array([ps.power for ps in ps_all]).T self.freq = ps_all[0].freq start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, self.segment_size, lc.time, dt=lc.dt) tstart = lc.time[start_inds] tend = lc.time[end_inds] self.time = tstart + 0.5*(tend - tstart) # Assign length of lightcurve as time resolution if only one value if len(self.time) > 1: self.dt = self.time[1] - self.time[0] else: self.dt = lc.n # Assign biggest freq. resolution if only one value if len(self.freq) > 1: self.df = self.freq[1] - self.freq[0] else: self.df = 1 / lc.n
def _make_matrix(self, lc): """ Create a matrix of powers for each time step (rows) and each frequency step (columns). Parameters ---------- lc : :class:`Lightcurve` object The :class:`Lightcurve` object from which to generate the dynamical power spectrum """ ps_all, _ = AveragedPowerspectrum._make_segment_spectrum( self, lc, self.segment_size) self.dyn_ps = np.array([ps.power for ps in ps_all]).T self.freq = ps_all[0].freq start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, self.segment_size, lc.time, dt=lc.dt) tstart = lc.time[start_inds] tend = lc.time[end_inds] self.time = tstart + 0.5 * (tend - tstart) # Assign length of lightcurve as time resolution if only one value if len(self.time) > 1: self.dt = self.time[1] - self.time[0] else: self.dt = lc.n # Assign biggest freq. resolution if only one value if len(self.freq) > 1: self.df = self.freq[1] - self.freq[0] else: self.df = 1 / lc.n
def _make_segment_spectrum(self, lc, segment_size): if not isinstance(lc, lightcurve.Lightcurve): raise TypeError("lc must be a lightcurve.Lightcurve object") if self.gti is None: self.gti = lc.gti check_gtis(self.gti) start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc.time) power_all = [] nphots_all = [] for start_ind, end_ind in zip(start_inds, end_inds): time = lc.time[start_ind:end_ind] counts = lc.counts[start_ind:end_ind] counts_err = lc.counts_err[start_ind: end_ind] lc_seg = lightcurve.Lightcurve(time, counts, err=counts_err, err_dist=lc.err_dist.lower()) power_seg = Powerspectrum(lc_seg, norm=self.norm) power_all.append(power_seg) nphots_all.append(np.sum(lc_seg.counts)) return power_all, nphots_all
def test_bin_intervals_from_gtis(self): """Test the division of start and end times to calculate spectra.""" times = np.arange(0.5, 13.5) start_bins, stop_bins = \ bin_intervals_from_gtis([[0, 5], [6, 8]], 2, times) assert np.allclose(start_bins, np.array([0, 2, 6])) assert np.allclose(stop_bins, np.array([2, 4, 8]))
def _make_segment_spectrum(self, lc1, lc2, segment_size): # TODO: need to update this for making cross spectra. assert isinstance(lc1, Lightcurve) assert isinstance(lc2, Lightcurve) if lc1.tseg != lc2.tseg: raise ValueError("Lightcurves do not have same tseg.") # If dt differs slightly, its propagated error must not be more than # 1/100th of the bin if not np.isclose(lc1.dt, lc2.dt, rtol=0.01 * lc1.dt / lc1.tseg): raise ValueError("Light curves do not have same time binning dt.") # In case a small difference exists, ignore it lc1.dt = lc2.dt if self.gti is None: self.gti = cross_two_gtis(lc1.gti, lc2.gti) lc1.gti = lc2.gti = self.gti lc1._apply_gtis() lc2._apply_gtis() check_gtis(self.gti) cs_all = [] nphots1_all = [] nphots2_all = [] start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc1.time, dt=lc1.dt) for start_ind, end_ind in zip(start_inds, end_inds): time_1 = lc1.time[start_ind:end_ind] counts_1 = lc1.counts[start_ind:end_ind] counts_1_err = lc1.counts_err[start_ind:end_ind] time_2 = lc2.time[start_ind:end_ind] counts_2 = lc2.counts[start_ind:end_ind] counts_2_err = lc2.counts_err[start_ind:end_ind] gti1 = np.array([[time_1[0] - lc1.dt / 2, time_1[-1] + lc1.dt / 2]]) gti2 = np.array([[time_2[0] - lc2.dt / 2, time_2[-1] + lc2.dt / 2]]) lc1_seg = Lightcurve(time_1, counts_1, err=counts_1_err, err_dist=lc1.err_dist, gti=gti1, dt=lc1.dt) lc2_seg = Lightcurve(time_2, counts_2, err=counts_2_err, err_dist=lc2.err_dist, gti=gti2, dt=lc2.dt) cs_seg = Crossspectrum(lc1_seg, lc2_seg, norm=self.norm) cs_all.append(cs_seg) nphots1_all.append(np.sum(lc1_seg.counts)) nphots2_all.append(np.sum(lc2_seg.counts)) return cs_all, nphots1_all, nphots2_all
def test_bin_intervals_from_gtis_frac(self): """Test the division of start and end times to calculate spectra.""" times = np.arange(0.5, 13.5) start_bins, stop_bins = \ bin_intervals_from_gtis([[0, 5], [6, 8]], 2, times, fraction_step=0.5) assert np.all(start_bins == np.array([0, 1, 2, 3, 6])) assert np.all(stop_bins == np.array([2, 3, 4, 5, 8]))
def test_bin_intervals_from_gtis_2(self): dt = 0.1 tstart = 0 tstop = 100 times = np.arange(tstart, tstop, dt) gti = np.array([[tstart - dt/2, tstop - dt/2]]) # Simulate something *clearly* non-constant counts = np.random.poisson( 10000 + 2000 * np.sin(2 * np.pi * times)) start_bins, stop_bins = bin_intervals_from_gtis(gti, 20, times) assert np.allclose(start_bins, [0, 200, 400, 600, 800])
def _make_segment_spectrum(self, lc, segment_size): """ Split the light curves into segments of size ``segment_size``, and calculate a power spectrum for each. Parameters ---------- lc : :class:`stingray.Lightcurve` objects\ The input light curve segment_size : ``numpy.float`` Size of each light curve segment to use for averaging. Returns ------- power_all : list of :class:`Powerspectrum` objects A list of power spectra calculated independently from each light curve segment nphots_all : ``numpy.ndarray`` List containing the number of photons for all segments calculated from ``lc`` """ if not isinstance(lc, lightcurve.Lightcurve): raise TypeError("lc must be a lightcurve.Lightcurve object") if self.gti is None: self.gti = lc.gti else: if not np.all(lc.gti == self.gti): self.gti = np.vstack([self.gti, lc.gti]) check_gtis(self.gti) start_inds, end_inds = \ bin_intervals_from_gtis(lc.gti, segment_size, lc.time, dt=lc.dt) power_all = [] nphots_all = [] for start_ind, end_ind in zip(start_inds, end_inds): time = lc.time[start_ind:end_ind] counts = lc.counts[start_ind:end_ind] counts_err = lc.counts_err[start_ind:end_ind] lc_seg = lightcurve.Lightcurve(time, counts, err=counts_err, err_dist=lc.err_dist.lower(), skip_checks=True, dt=lc.dt) power_seg = Powerspectrum(lc_seg, norm=self.norm) power_all.append(power_seg) nphots_all.append(np.sum(lc_seg.counts)) return power_all, nphots_all
def _make_segment_spectrum(self, lc1, lc2, segment_size): # TODO: need to update this for making cross spectra. assert isinstance(lc1, Lightcurve) assert isinstance(lc2, Lightcurve) if lc1.dt != lc2.dt: raise ValueError("Light curves do not have same time binning dt.") if lc1.tseg != lc2.tseg: raise ValueError("Lightcurves do not have same tseg.") if self.gti is None: self.gti = cross_two_gtis(lc1.gti, lc2.gti) check_gtis(self.gti) cs_all = [] nphots1_all = [] nphots2_all = [] start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc1.time) for start_ind, end_ind in zip(start_inds, end_inds): time_1 = lc1.time[start_ind:end_ind] counts_1 = lc1.counts[start_ind:end_ind] counts_1_err = lc1.counts_err[start_ind:end_ind] time_2 = lc2.time[start_ind:end_ind] counts_2 = lc2.counts[start_ind:end_ind] counts_2_err = lc2.counts_err[start_ind:end_ind] lc1_seg = Lightcurve(time_1, counts_1, err=counts_1_err, err_dist=lc1.err_dist) lc2_seg = Lightcurve(time_2, counts_2, err=counts_2_err, err_dist=lc2.err_dist) cs_seg = Crossspectrum(lc1_seg, lc2_seg, norm=self.norm) cs_all.append(cs_seg) nphots1_all.append(np.sum(lc1_seg.counts)) nphots2_all.append(np.sum(lc2_seg.counts)) return cs_all, nphots1_all, nphots2_all
def _make_segment_spectrum(self, lc, segment_size): """ Split the light curves into segments of size ``segment_size``, and calculate a power spectrum for each. Parameters ---------- lc : :class:`stingray.Lightcurve` objects\ The input light curve segment_size : ``numpy.float`` Size of each light curve segment to use for averaging. Returns ------- power_all : list of :class:`Powerspectrum` objects A list of power spectra calculated independently from each light curve segment nphots_all : ``numpy.ndarray`` List containing the number of photons for all segments calculated from ``lc`` """ if not isinstance(lc, lightcurve.Lightcurve): raise TypeError("lc must be a lightcurve.Lightcurve object") if self.gti is None: self.gti = lc.gti check_gtis(self.gti) start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc.time, dt=lc.dt) power_all = [] nphots_all = [] for start_ind, end_ind in zip(start_inds, end_inds): time = lc.time[start_ind:end_ind] counts = lc.counts[start_ind:end_ind] counts_err = lc.counts_err[start_ind: end_ind] lc_seg = lightcurve.Lightcurve(time, counts, err=counts_err, err_dist=lc.err_dist.lower()) power_seg = Powerspectrum(lc_seg, norm=self.norm) power_all.append(power_seg) nphots_all.append(np.sum(lc_seg.counts)) return power_all, nphots_all
def _make_segment_spectrum(self, lc1, lc2, segment_size): # TODO: need to update this for making cross spectra. assert isinstance(lc1, lightcurve.Lightcurve) assert isinstance(lc2, lightcurve.Lightcurve) if lc1.dt != lc2.dt: raise ValueError("Light curves do not have same time binning dt.") if lc1.tseg != lc2.tseg: raise ValueError("Lightcurves do not have same tseg.") if self.gti is None: self.gti = cross_two_gtis(lc1.gti, lc2.gti) check_gtis(self.gti) cs_all = [] nphots1_all = [] nphots2_all = [] start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc1.time) for start_ind, end_ind in zip(start_inds, end_inds): time_1 = lc1.time[start_ind:end_ind] counts_1 = lc1.counts[start_ind:end_ind] time_2 = lc2.time[start_ind:end_ind] counts_2 = lc2.counts[start_ind:end_ind] lc1_seg = lightcurve.Lightcurve(time_1, counts_1) lc2_seg = lightcurve.Lightcurve(time_2, counts_2) cs_seg = Crossspectrum(lc1_seg, lc2_seg, norm=self.norm) cs_all.append(cs_seg) nphots1_all.append(np.sum(lc1_seg.counts)) nphots2_all.append(np.sum(lc2_seg.counts)) return cs_all, nphots1_all, nphots2_all
def analyze_lc_chunks(self, chunk_length, func, fraction_step=1, **kwargs): """Analyze chunks of the light curve with any function. Parameters ---------- chunk_length : float Length in seconds of the light curve chunks func : function Function accepting a `Lightcurve` object as single argument, plus possible additional keyword arguments, and returning a number or a tuple - e.g., (result, error) where both result and error are numbers. Other parameters ---------------- fraction_step : float If the step is not a full chunk_length but less (e.g. a moving window), this indicates the ratio between step step and `chunk_length` (e.g. 0.5 means that the window shifts of half chunk_length) kwargs : keyword arguments These additional keyword arguments, if present, they will be passed to `func` Returns ------- start_times : array Lower time boundaries of all chunks. stop_times : array Higher time boundaries of all chunks. result : array of N elements The result of `func` for each chunk of the light curve Examples -------- >>> import numpy as np >>> time = np.arange(0, 10, 0.1) >>> counts = np.zeros_like(time) + 10 >>> lc = Lightcurve(time, counts) >>> # Define a function that calculates the mean >>> mean_func = lambda x: np.mean(x) >>> # Calculate the mean in chunks of 5 seconds >>> start, stop, res = lc.analyze_lc_chunks(5, mean_func) >>> len(res) == 2 True >>> np.all(res == 10) True """ start, stop = bin_intervals_from_gtis(self.gti, chunk_length, self.time, fraction_step=fraction_step, dt=self.dt) start_times = self.time[start] - self.dt * 0.5 # Remember that stop is one element above the last element, because # it's defined to be used in intervals start:stop stop_times = self.time[stop - 1] + self.dt * 1.5 results = [] for i, (st, sp) in enumerate(zip(start, stop)): lc_filt = self[st:sp] res = func(lc_filt, **kwargs) results.append(res) results = np.array(results) if len(results.shape) == 2: results = [results[:, i] for i in range(results.shape[1])] return start_times, stop_times, results
def _make_segment_spectrum(self, lc1, lc2, segment_size): """ Split the light curves into segments of size ``segment_size``, and calculate a cross spectrum for each. Parameters ---------- lc1, lc2 : :class:`stingray.Lightcurve` objects Two light curves used for computing the cross spectrum. segment_size : ``numpy.float`` Size of each light curve segment to use for averaging. Returns ------- cs_all : list of :class:`Crossspectrum`` objects A list of cross spectra calculated independently from each light curve segment nphots1_all, nphots2_all : ``numpy.ndarray` for each of ``lc1`` and ``lc2`` Two lists containing the number of photons for all segments calculated from ``lc1`` and ``lc2``. """ # TODO: need to update this for making cross spectra. assert isinstance(lc1, Lightcurve) assert isinstance(lc2, Lightcurve) if lc1.tseg != lc2.tseg: simon("Lightcurves do not have same tseg. This means that the data" "from the two channels are not completely in sync. This " "might or might not be an issue. Keep an eye on it.") # If dt differs slightly, its propagated error must not be more than # 1/100th of the bin if not np.isclose(lc1.dt, lc2.dt, rtol=0.01 * lc1.dt / lc1.tseg): raise ValueError("Light curves do not have same time binning dt.") # In case a small difference exists, ignore it lc1.dt = lc2.dt gti = cross_two_gtis(lc1.gti, lc2.gti) lc1.apply_gtis() lc2.apply_gtis() if self.gti is None: self.gti = gti else: if not np.all(self.gti == gti): self.gti = np.vstack([self.gti, gti]) check_gtis(self.gti) cs_all = [] nphots1_all = [] nphots2_all = [] start_inds, end_inds = \ bin_intervals_from_gtis(gti, segment_size, lc1.time, dt=lc1.dt) simon("Errorbars on cross spectra are not thoroughly tested. " "Please report any inconsistencies.") for start_ind, end_ind in zip(start_inds, end_inds): time_1 = lc1.time[start_ind:end_ind] counts_1 = lc1.counts[start_ind:end_ind] counts_1_err = lc1.counts_err[start_ind:end_ind] time_2 = lc2.time[start_ind:end_ind] counts_2 = lc2.counts[start_ind:end_ind] counts_2_err = lc2.counts_err[start_ind:end_ind] gti1 = np.array([[time_1[0] - lc1.dt / 2, time_1[-1] + lc1.dt / 2]]) gti2 = np.array([[time_2[0] - lc2.dt / 2, time_2[-1] + lc2.dt / 2]]) lc1_seg = Lightcurve(time_1, counts_1, err=counts_1_err, err_dist=lc1.err_dist, gti=gti1, dt=lc1.dt, skip_checks=True) lc2_seg = Lightcurve(time_2, counts_2, err=counts_2_err, err_dist=lc2.err_dist, gti=gti2, dt=lc2.dt, skip_checks=True) with warnings.catch_warnings(record=True) as w: cs_seg = Crossspectrum(lc1_seg, lc2_seg, norm=self.norm, power_type=self.power_type) cs_all.append(cs_seg) nphots1_all.append(np.sum(lc1_seg.counts)) nphots2_all.append(np.sum(lc2_seg.counts)) return cs_all, nphots1_all, nphots2_all
def _make_segment_spectrum(self, lc, segment_size, silent=False): """ Split the light curves into segments of size ``segment_size``, and calculate a power spectrum for each. Parameters ---------- lc : :class:`stingray.Lightcurve` objects\ The input light curve segment_size : ``numpy.float`` Size of each light curve segment to use for averaging. Other parameters ---------------- silent : bool, default False Suppress progress bars Returns ------- power_all : list of :class:`Powerspectrum` objects A list of power spectra calculated independently from each light curve segment nphots_all : ``numpy.ndarray`` List containing the number of photons for all segments calculated from ``lc`` """ if not isinstance(lc, Lightcurve): raise TypeError("lc must be a Lightcurve object") current_gtis = lc.gti if self.gti is None: self.gti = lc.gti else: if not np.allclose(lc.gti, self.gti): self.gti = np.vstack([self.gti, lc.gti]) check_gtis(self.gti) start_inds, end_inds = \ bin_intervals_from_gtis(current_gtis, segment_size, lc.time, dt=lc.dt) power_all = [] nphots_all = [] local_show_progress = show_progress if not self.show_progress or silent: def local_show_progress(a): return a for start_ind, end_ind in \ local_show_progress(zip(start_inds, end_inds)): time = lc.time[start_ind:end_ind] counts = lc.counts[start_ind:end_ind] counts_err = lc.counts_err[start_ind: end_ind] if np.sum(counts) == 0: warnings.warn( "No counts in interval {}--{}s".format(time[0], time[-1])) continue lc_seg = Lightcurve(time, counts, err=counts_err, err_dist=lc.err_dist.lower(), skip_checks=True, dt=lc.dt) power_seg = Powerspectrum(lc_seg, norm=self.norm) power_all.append(power_seg) nphots_all.append(np.sum(lc_seg.counts)) return power_all, nphots_all
def _make_segment_spectrum(self, lc1, lc2, segment_size): """ Split the light curves into segments of size ``segment_size``, and calculate a cross spectrum for each. Parameters ---------- lc1, lc2 : :class:`stingray.Lightcurve` objects Two light curves used for computing the cross spectrum. segment_size : ``numpy.float`` Size of each light curve segment to use for averaging. Returns ------- cs_all : list of :class:`Crossspectrum`` objects A list of cross spectra calculated independently from each light curve segment nphots1_all, nphots2_all : ``numpy.ndarray` for each of ``lc1`` and ``lc2`` Two lists containing the number of photons for all segments calculated from ``lc1`` and ``lc2``. """ # TODO: need to update this for making cross spectra. assert isinstance(lc1, Lightcurve) assert isinstance(lc2, Lightcurve) if lc1.tseg != lc2.tseg: raise ValueError("Lightcurves do not have same tseg.") # If dt differs slightly, its propagated error must not be more than # 1/100th of the bin if not np.isclose(lc1.dt, lc2.dt, rtol=0.01 * lc1.dt / lc1.tseg): raise ValueError("Light curves do not have same time binning dt.") # In case a small difference exists, ignore it lc1.dt = lc2.dt if self.gti is None: self.gti = cross_two_gtis(lc1.gti, lc2.gti) lc1.gti = lc2.gti = self.gti lc1._apply_gtis() lc2._apply_gtis() check_gtis(self.gti) cs_all = [] nphots1_all = [] nphots2_all = [] start_inds, end_inds = \ bin_intervals_from_gtis(self.gti, segment_size, lc1.time, dt=lc1.dt) for start_ind, end_ind in zip(start_inds, end_inds): time_1 = lc1.time[start_ind:end_ind] counts_1 = lc1.counts[start_ind:end_ind] counts_1_err = lc1.counts_err[start_ind:end_ind] time_2 = lc2.time[start_ind:end_ind] counts_2 = lc2.counts[start_ind:end_ind] counts_2_err = lc2.counts_err[start_ind:end_ind] gti1 = np.array([[time_1[0] - lc1.dt / 2, time_1[-1] + lc1.dt / 2]]) gti2 = np.array([[time_2[0] - lc2.dt / 2, time_2[-1] + lc2.dt / 2]]) lc1_seg = Lightcurve(time_1, counts_1, err=counts_1_err, err_dist=lc1.err_dist, gti=gti1, dt=lc1.dt) lc2_seg = Lightcurve(time_2, counts_2, err=counts_2_err, err_dist=lc2.err_dist, gti=gti2, dt=lc2.dt) cs_seg = Crossspectrum(lc1_seg, lc2_seg, norm=self.norm, power_type=self.power_type) cs_all.append(cs_seg) nphots1_all.append(np.sum(lc1_seg.counts)) nphots2_all.append(np.sum(lc2_seg.counts)) return cs_all, nphots1_all, nphots2_all
def test_decide_spectrum_lc_intervals_invalid(self): with pytest.raises(ValueError): a, b = bin_intervals_from_gtis([[0, 400]], 128, [500, 501]) with pytest.raises(ValueError): a, b = bin_intervals_from_gtis([[1000, 1400]], 128, [500, 501])