Пример #1
0
    def read(self, filename, format_='pickle'):
        """
        Read a :class:`Lightcurve` object from file. Currently supported formats are

        * pickle (not recommended for long-term storage)
        * HDF5
        * ASCII

        Parameters
        ----------
        filename: str
            Path and file name for the file to be read.

        format\_: str
            Available options are 'pickle', 'hdf5', 'ascii'

        Returns
        --------
        lc : ``astropy.table`` or ``dict`` or :class:`Lightcurve` object
            * If ``format\_`` is ``ascii``: ``astropy.table`` is returned.
            * If ``format\_`` is ``hdf5``: dictionary with key-value pairs is returned.
            * If ``format\_`` is ``pickle``: :class:`Lightcurve` object is returned.
        """

        if format_ == 'ascii' or format_ == 'hdf5':
            return io.read(filename, format_)

        elif format_ == 'pickle':
            self = io.read(filename, format_)

        else:
            utils.simon("Format not understood.")
Пример #2
0
    def write(self, filename, format_='pickle', **kwargs):
        """
        Write a :class:`Lightcurve` object to file. Currently supported formats are

        * pickle (not recommended for long-term storage)
        * HDF5
        * ASCII

        Parameters
        ----------
        filename: str
            Path and file name for the output file.

        format\_: str
            Available options are 'pickle', 'hdf5', 'ascii'
        """

        if format_ == 'ascii':
            io.write(np.array([self.time, self.counts]).T,
                     filename, format_, fmt=["%s", "%s"])

        elif format_ == 'pickle':
            io.write(self, filename, format_)

        elif format_ == 'hdf5':
            io.write(self, filename, format_)

        else:
            utils.simon("Format not understood.")
Пример #3
0
    def write(self, filename, format_='pickle', **kwargs):
        """
        Write a :class:`Lightcurve` object to file. Currently supported formats are

        * pickle (not recommended for long-term storage)
        * HDF5
        * ASCII

        Parameters
        ----------
        filename: str
            Path and file name for the output file.

        format\_: str
            Available options are 'pickle', 'hdf5', 'ascii'
        """

        if format_ == 'ascii':
            io.write(np.array([self.time, self.counts]).T,
                     filename, format_, fmt=["%s", "%s"])

        elif format_ == 'pickle':
            io.write(self, filename, format_)

        elif format_ == 'hdf5':
            io.write(self, filename, format_)

        else:
            utils.simon("Format not understood.")
Пример #4
0
def _save_hdf5_object(object, filename):
    """
    Save a class object in hdf5 format.

    Parameters
    ----------
    object: class instance
        A class object whose attributes would be saved in a dictionary format.

    filename: str
        The file name to save to
    """
    items = vars(object)
    attrs = [name for name in items]

    with h5py.File(filename, 'w') as hf:   
        for attr in attrs:
            data = items[attr]
            
            # If data is a single number, store as an attribute.
            if _isattribute(data):

                if isinstance(data, np.longdouble):
                    data = np.double(data) 
                    utils.simon("Casting data as double instead of longdouble.")
                hf.attrs[attr] = data
            
            # If data is a numpy array, create a dataset.
            else:

                if isinstance(data[0], np.longdouble):
                    data = np.double(data) 
                    utils.simon("Casting data as double instead of longdouble.")
                hf.create_dataset(attr, data=data) 
Пример #5
0
def write(input_, filename, format_='pickle', **kwargs):
    """
    Pickle a class instance. For parameters depending on
    `format_`, see individual function definitions.

    Parameters
    ----------
    object: a class instance
    filename: str
        name of the file to be created.
    format_: str
        pickle, hdf5, ascii ...
    """

    if format_ == 'pickle':
        _save_pickle_object(input_, filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            _save_hdf5_object(input_, filename)

        else:
            utils.simon('h5py not installed, using pickle instead to save object.')
            _save_pickle_object(input_, filename.split('.')[0]+'.pickle')

    elif format_ == 'ascii':
        _save_ascii_object(input_, filename, **kwargs)

    else:
        utils.simon('Format not understood.')
Пример #6
0
    def _spectrum_function(self):

        lag_spec = np.zeros(len(self.energy_intervals))
        lag_spec_err = np.zeros_like(lag_spec)
        for i, eint in enumerate(self.energy_intervals):
            base_lc, ref_lc = self._construct_lightcurves(eint)
            try:
                xspect = AveragedCrossspectrum(base_lc, ref_lc,
                                               segment_size=self.segment_size)
            except AssertionError as e:
                # Avoid assertions in AveragedCrossspectrum.
                simon("AssertionError: " + str(e))
            else:
                good = (xspect.freq >= self.freq_interval[0]) & \
                       (xspect.freq < self.freq_interval[1])
                lag, lag_err = xspect.time_lag()
                good_lag, good_lag_err = lag[good], lag_err[good]
                coh, coh_err = xspect.coherence()
                lag_spec[i] = np.mean(good_lag)
                coh_check = coh > 1.2 / (1 + 0.2 * xspect.m)
                if not np.all(coh_check[good]):
                    simon("Coherence is not ideal over the specified energy "
                          "range. Lag values and uncertainties might be "
                          "underestimated. See Epitropakis and Papadakis, "
                          "A\&A 591, 1113, 2016")

                # Root squared sum of errors of the spectrum
                # Verified!
                lag_spec_err[i] = \
                    np.sqrt(np.sum(good_lag_err**2) / len(good_lag))

        return lag_spec, lag_spec_err
Пример #7
0
    def _spectrum_function(self):

        rms_spec = np.zeros(len(self.energy_intervals))
        rms_spec_err = np.zeros_like(rms_spec)
        for i, eint in enumerate(self.energy_intervals):
            base_lc, ref_lc = self._construct_lightcurves(eint,
                                                          exclude=False)
            try:
                xspect = AveragedCrossspectrum(base_lc, ref_lc,
                                               segment_size=self.segment_size,
                                               norm='frac')
            except AssertionError as e:
                # Avoid "Mean count rate is <= 0. Something went wrong" assertion.
                simon("AssertionError: " + str(e))
            else:
                good = (xspect.freq >= self.freq_interval[0]) & \
                       (xspect.freq < self.freq_interval[1])
                rms_spec[i] = np.sqrt(np.sum(xspect.power[good] * xspect.df))

                # Root squared sum of errors of the spectrum
                root_sq_err_sum = \
                    np.sqrt(np.sum((xspect.power_err[good] * xspect.df) ** 2))
                # But the rms is the squared root. So,
                # Error propagation
                rms_spec_err[i] = 1 / (2 * rms_spec[i]) * root_sq_err_sum

        return rms_spec, rms_spec_err
Пример #8
0
    def _operation_with_other_lc(self, other, operation):
        """
        Helper method to codify an operation of one light curve with another (e.g. add, subtract, ...).
        Takes into account the GTIs correctly, and returns a new :class:`Lightcurve` object.

        Parameters
        ----------
        other : :class:`Lightcurve` object
            A second light curve object

        operation : function
            An operation between the :class:`Lightcurve` object calling this method, and ``other``,
            operating on the ``counts`` attribute in each :class:`Lightcurve` object

        Returns
        -------
        lc_new : Lightcurve object
            The new light curve calculated in ``operation``
        """
        if self.mjdref != other.mjdref:
            raise ValueError("MJDref is different in the two light curves")

        common_gti = cross_two_gtis(self.gti, other.gti)
        mask_self = create_gti_mask(self.time, common_gti, dt=self.dt)
        mask_other = create_gti_mask(other.time, common_gti, dt=other.dt)

        # ValueError is raised by Numpy while asserting np.equal over arrays
        # with different dimensions.
        try:
            diff = np.abs((self.time[mask_self] - other.time[mask_other]))
            assert np.all(diff < self.dt / 100)
        except (ValueError, AssertionError):
            raise ValueError("GTI-filtered time arrays of both light curves "
                             "must be of same dimension and equal.")

        new_time = self.time[mask_self]
        new_counts = operation(self.counts[mask_self],
                               other.counts[mask_other])

        if self.err_dist.lower() != other.err_dist.lower():
            simon("Lightcurves have different statistics!"
                  "We are setting the errors to zero to avoid complications.")
            new_counts_err = np.zeros_like(new_counts)
        elif self.err_dist.lower() in valid_statistics:
            new_counts_err = \
                np.sqrt(np.add(self.counts_err[mask_self]**2,
                               other.counts_err[mask_other]**2))
        # More conditions can be implemented for other statistics
        else:
            raise StingrayError("Statistics not recognized."
                                " Please use one of these: "
                                "{}".format(valid_statistics))

        lc_new = Lightcurve(new_time,
                            new_counts,
                            err=new_counts_err,
                            gti=common_gti,
                            mjdref=self.mjdref)

        return lc_new
Пример #9
0
    def _spectrum_function(self):

        rms_spec = np.zeros(len(self.energy_intervals))
        rms_spec_err = np.zeros_like(rms_spec)
        for i, eint in enumerate(self.energy_intervals):
            base_lc, ref_lc = self._construct_lightcurves(eint,
                                                          exclude=False)
            try:
                xspect = AveragedCrossspectrum(base_lc, ref_lc,
                                               segment_size=self.segment_size,
                                               norm='frac')
            except AssertionError as e:
                # Avoid "Mean count rate is <= 0. Something went wrong" assertion.
                simon("AssertionError: " + str(e))
            else:
                good = (xspect.freq >= self.freq_interval[0]) & \
                       (xspect.freq < self.freq_interval[1])
                rms_spec[i] = np.sqrt(np.sum(xspect.power[good]*xspect.df))

                # Root squared sum of errors of the spectrum
                root_sq_err_sum = np.sqrt(np.sum((xspect.power_err[good]*xspect.df)**2))
                # But the rms is the squared root. So,
                # Error propagation
                rms_spec_err[i] = 1 / (2 * rms_spec[i]) * root_sq_err_sum

        return rms_spec, rms_spec_err
Пример #10
0
    def _construct_energy_covar(self, energy_events, energy_covar,
                                xs_var=None):
        """Form the actual output covaraince dictionary and array."""
        self._init_energy_covar(energy_events, energy_covar)

        if not self.avg_covar:
            xs_var = dict()

        for energy in energy_covar.keys():
            lc, lc_ref = self._create_lc_and_lc_ref(energy, energy_events)

            covar = self._compute_covariance(lc, lc_ref)

            energy_covar[energy] = covar
            if not self.avg_covar:
                self.covar_error[energy] = self._calculate_covariance_error(
                                                lc, lc_ref)

            # Excess variance in ref band
            xs_var[energy] = self._calculate_excess_variance(lc_ref)

        for key, value in energy_covar.items():
            if not xs_var[key] > 0:
                utils.simon("The excess variance in the reference band is "
                            "negative. This implies that the reference "
                            "band was badly chosen. Beware that the "
                            "covariance spectra will have NaNs!")

        if not self.avg_covar:
            self.unnorm_covar = np.vstack(energy_covar.items())
            energy_covar[key] = value / (xs_var[key])**0.5

            self.covar = np.vstack(energy_covar.items())

            self.covar_error = np.vstack(self.covar_error.items())
Пример #11
0
def read(filename, format_='pickle', **kwargs):
    """
    Return a pickled class instance.

    Parameters
    ----------
    filename: str
        name of the file to be retrieved.
    format_: str
        pickle, hdf5, ascii ...
    """

    if format_ == 'pickle':
        return _retrieve_pickle_object(filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            return _retrieve_hdf5_object(filename)
        else:
            utils.simon('h5py not installed, cannot read an hdf5 object.')

    elif format_ == 'ascii':
        return _retrieve_ascii_object(filename, **kwargs)
    
    else:
        utils.simon('Format not understood.')
Пример #12
0
    def _spectrum_function(self):

        lag_spec = np.zeros(len(self.energy_intervals))
        lag_spec_err = np.zeros_like(lag_spec)
        for i, eint in enumerate(self.energy_intervals):
            base_lc, ref_lc = self._construct_lightcurves(eint)
            xspect = AveragedCrossspectrum(base_lc,
                                           ref_lc,
                                           segment_size=self.segment_size)
            good = (xspect.freq >= self.freq_interval[0]) & \
                   (xspect.freq < self.freq_interval[1])
            lag, lag_err = xspect.time_lag()
            good_lag, good_lag_err = lag[good], lag_err[good]
            coh, coh_err = xspect.coherence()
            lag_spec[i] = np.mean(good_lag)
            coh_check = coh > 1.2 / (1 + 0.2 * xspect.m)
            if not np.all(coh_check[good]):
                simon("Coherence is not ideal over the specified energy range."
                      " Lag values and uncertainties might be underestimated. "
                      "See Epitropakis and Papadakis, A\&A 591, 1113, 2016")

            # Root squared sum of errors of the spectrum
            # Verified!
            lag_spec_err[i] = np.sqrt(np.sum(good_lag_err**2) / len(good_lag))

        return lag_spec, lag_spec_err
Пример #13
0
    def read(self, filename, format_='pickle'):
        """
        Read a :class:`Lightcurve` object from file. Currently supported formats are

        * pickle (not recommended for long-term storage)
        * HDF5
        * ASCII

        Parameters
        ----------
        filename: str
            Path and file name for the file to be read.

        format\_: str
            Available options are 'pickle', 'hdf5', 'ascii'

        Returns
        --------
        lc : ``astropy.table`` or ``dict`` or :class:`Lightcurve` object
            * If ``format\_`` is ``ascii``: ``astropy.table`` is returned.
            * If ``format\_`` is ``hdf5``: dictionary with key-value pairs is returned.
            * If ``format\_`` is ``pickle``: :class:`Lightcurve` object is returned.
        """

        if format_ == 'ascii' or format_ == 'hdf5':
            return io.read(filename, format_)

        elif format_ == 'pickle':
            self = io.read(filename, format_)

        else:
            utils.simon("Format not understood.")
Пример #14
0
def savefig(filename, **kwargs):
    """
    Save a figure plotted by Matplotlib.

    Note : This function is supposed to be used after the ``plot``
    function. Otherwise it will save a blank image with no plot.

    Parameters
    ----------
    filename : str
        The name of the image file. Extension must be specified in the
        file name. For example filename with `.png` extension will give a
        rasterized image while `.pdf` extension will give a vectorized
        output.

    kwargs : keyword arguments
        Keyword arguments to be passed to ``savefig`` function of
        ``matplotlib.pyplot``. For example use `bbox_inches='tight'` to
        remove the undesirable whitepace around the image.
    """

    try:
        import matplotlib.pyplot as plt
    except ImportError:
        raise ImportError("Matplotlib required for savefig()")

    if not plt.fignum_exists(1):
        utils.simon("use ``plot`` function to plot the image first and "
                    "then use ``savefig`` to save the figure.")

    plt.savefig(filename, **kwargs)
Пример #15
0
def savefig(filename, **kwargs):
    """
    Save a figure plotted by ``matplotlib``.

    Note : This function is supposed to be used after the ``plot``
    function. Otherwise it will save a blank image with no plot.

    Parameters
    ----------
    filename : str
        The name of the image file. Extension must be specified in the
        file name. For example filename with `.png` extension will give a
        rasterized image while ``.pdf`` extension will give a vectorized
        output.

    kwargs : keyword arguments
        Keyword arguments to be passed to ``savefig`` function of
        ``matplotlib.pyplot``. For example use `bbox_inches='tight'` to
        remove the undesirable whitepace around the image.
    """

    try:
        import matplotlib.pyplot as plt
    except ImportError:
        raise ImportError("Matplotlib required for savefig()")

    if not plt.fignum_exists(1):
        utils.simon("use ``plot`` function to plot the image first and "
                    "then use ``savefig`` to save the figure.")

    plt.savefig(filename, **kwargs)
Пример #16
0
    def read(self, filename, format_='pickle'):
        """
        Imports LightCurve object.

        Parameters
        ----------
        filename: str
            Name of the LightCurve object to be read.

        format\_: str
            Available options are 'pickle', 'hdf5', 'ascii'

        Returns
        --------
        If format\_ is 'ascii': astropy.table is returned.
        If format\_ is 'hdf5': dictionary with key-value pairs is returned.
        If format\_ is 'pickle': class object is set.
        """

        if format_ == 'ascii' or format_ == 'hdf5':
            return io.read(filename, format_)

        elif format_ == 'pickle':
            self = io.read(filename, format_)

        else:
            utils.simon("Format not understood.")
Пример #17
0
    def write(self, filename, format_='pickle', **kwargs):
        """
        Exports LightCurve object.

        Parameters
        ----------
        filename: str
            Name of the LightCurve object to be created.

        format\_: str
            Available options are 'pickle', 'hdf5', 'ascii'
        """

        if format_ == 'ascii':
            io.write(np.array([self.time, self.counts]).T,
                     filename, format_, fmt=["%s", "%s"])

        elif format_ == 'pickle':
            io.write(self, filename, format_)

        elif format_ == 'hdf5':
            io.write(self, filename, format_)

        else:
            utils.simon("Format not understood.")
Пример #18
0
    def _construct_covar(self):
        """
        Helper method to construct the covariance attribute and fill it with values.
        """
        self.avg_covar = True

        start_time = self.lcs[0].time[0]

        covar = np.zeros(len(self.lcs))
        covar_err = np.zeros(len(self.lcs))
        xs_var = np.zeros(len(self.lcs))

        for i in range(len(self.lcs)):
            lc = self.lcs[i]

            if np.size(self.ref_band_lcs) == 1:
                lc_ref = self.ref_band_lcs
            else:
                lc_ref = self.ref_band_lcs[i]

            tstart = start_time
            tend = start_time + self.segment_size
            cv = 0.0
            cv_err = 0.0
            xs = 0.0

            self.nbins = int((tend - tstart)/self.segment_size)
            for k in range(self.nbins):
                start_ind = lc.time.searchsorted(tstart)
                end_ind = lc.time.searchsorted(tend)

                lc_seg = lc.truncate(start=start_ind, stop=end_ind)
                lc_ref_seg = lc_ref.truncate(start=start_ind, stop=end_ind)

                cv += self._compute_covariance(lc_seg, lc_ref_seg)
                cv_err += self._calculate_covariance_error(lc_seg, lc_ref_seg)
                xs += self._calculate_excess_variance(lc_ref_seg)
                if not xs > 0:
                    utils.simon("The excess variance in the reference band is "
                                "negative. This implies that the reference "
                                "band was badly chosen. Beware that the "
                                "covariance spectra will have NaNs!")

                tstart += self.segment_size
                tend += self.segment_size


            covar[i] = cv/self.nbins
            covar_err[i] = cv_err/self.nbins
            xs_var[i] = xs/self.nbins

        self.unnorm_covar = covar
        energy_covar = covar / xs_var**0.5

        self.covar = energy_covar

        self.covar_error = covar_err

        return
Пример #19
0
    def coherence(self):
        """
        Compute an averaged Coherence function of cross spectrum by computing
        coherence function of each segment and averaging them. The return type
        is a tuple with first element as the coherence function and the second
        element as the corresponding uncertainty[1] associated with it.

        Note : The uncertainty in coherence function is strictly valid for
               Gaussian statistics only.

        Returns
        -------
        tuple : tuple of np.ndarray
            Tuple of coherence function and uncertainty.

        References
        ----------
        .. [1] http://iopscience.iop.org/article/10.1086/310430/pdf

        """
        if self.m < 50:
            simon("Number of segments used in averaging is "
                  "significantly low. The result might not follow the "
                  "expected statistical distributions.")

        # Calculate average coherence
        unnorm_power_avg = np.zeros_like(self.cs_all[0].unnorm_power)
        for cs in self.cs_all:
            unnorm_power_avg += cs.unnorm_power

        unnorm_power_avg /= self.m
        num = np.absolute(unnorm_power_avg)**2

        # this computes the averaged power spectrum, but using the
        # cross spectrum code to avoid circular imports
        aps1 = AveragedCrossspectrum(self.lc1,
                                     self.lc1,
                                     segment_size=self.segment_size)
        aps2 = AveragedCrossspectrum(self.lc2,
                                     self.lc2,
                                     segment_size=self.segment_size)

        unnorm_powers_avg_1 = np.zeros_like(aps1.cs_all[0].unnorm_power.real)
        for ps in aps1.cs_all:
            unnorm_powers_avg_1 += ps.unnorm_power.real
        unnorm_powers_avg_1 /= aps1.m

        unnorm_powers_avg_2 = np.zeros_like(aps2.cs_all[0].unnorm_power.real)
        for ps in aps2.cs_all:
            unnorm_powers_avg_2 += ps.unnorm_power.real
        unnorm_powers_avg_2 /= aps2.m

        coh = num / (unnorm_powers_avg_1 * unnorm_powers_avg_2)

        # Calculate uncertainty
        uncertainty = (2**0.5 * coh * (1 - coh)) / (np.abs(coh) * self.m**0.5)

        return (coh, uncertainty)
Пример #20
0
    def _construct_covar(self):
        """
        Helper method to construct the covariance attribute and fill it with values.
        """
        self.avg_covar = True

        start_time = self.lcs[0].time[0]

        covar = np.zeros(len(self.lcs))
        covar_err = np.zeros(len(self.lcs))
        xs_var = np.zeros(len(self.lcs))

        for i in range(len(self.lcs)):
            lc = self.lcs[i]

            if np.size(self.ref_band_lcs) == 1:
                lc_ref = self.ref_band_lcs
            else:
                lc_ref = self.ref_band_lcs[i]

            tstart = start_time
            tend = start_time + self.segment_size
            cv = 0.0
            cv_err = 0.0
            xs = 0.0

            self.nbins = int((tend - tstart) / self.segment_size)
            for k in range(self.nbins):
                start_ind = lc.time.searchsorted(tstart)
                end_ind = lc.time.searchsorted(tend)

                lc_seg = lc.truncate(start=start_ind, stop=end_ind)
                lc_ref_seg = lc_ref.truncate(start=start_ind, stop=end_ind)

                cv += self._compute_covariance(lc_seg, lc_ref_seg)
                cv_err += self._calculate_covariance_error(lc_seg, lc_ref_seg)
                xs += self._calculate_excess_variance(lc_ref_seg)
                if not xs > 0:
                    utils.simon("The excess variance in the reference band is "
                                "negative. This implies that the reference "
                                "band was badly chosen. Beware that the "
                                "covariance spectra will have NaNs!")

                tstart += self.segment_size
                tend += self.segment_size

            covar[i] = cv / self.nbins
            covar_err[i] = cv_err / self.nbins
            xs_var[i] = xs / self.nbins

        self.unnorm_covar = covar
        energy_covar = covar / xs_var**0.5

        self.covar = energy_covar

        self.covar_error = covar_err

        return
Пример #21
0
    def _operation_with_other_lc(self, other, operation):
        """
        Helper method to codify an operation of one light curve with another (e.g. add, subtract, ...).
        Takes into account the GTIs correctly, and returns a new :class:`Lightcurve` object.

        Parameters
        ----------
        other : :class:`Lightcurve` object
            A second light curve object

        operation : function
            An operation between the :class:`Lightcurve` object calling this method, and ``other``,
            operating on the ``counts`` attribute in each :class:`Lightcurve` object

        Returns
        -------
        lc_new : Lightcurve object
            The new light curve calculated in ``operation``
        """
        if self.mjdref != other.mjdref:
            raise ValueError("MJDref is different in the two light curves")

        common_gti = cross_two_gtis(self.gti, other.gti)
        mask_self = create_gti_mask(self.time, common_gti, dt=self.dt)
        mask_other = create_gti_mask(other.time, common_gti, dt=other.dt)

        # ValueError is raised by Numpy while asserting np.equal over arrays
        # with different dimensions.
        try:
            diff = np.abs((self.time[mask_self] - other.time[mask_other]))
            assert np.all(diff < self.dt / 100)
        except (ValueError, AssertionError):
            raise ValueError("GTI-filtered time arrays of both light curves "
                             "must be of same dimension and equal.")

        new_time = self.time[mask_self]
        new_counts = operation(self.counts[mask_self],
                               other.counts[mask_other])

        if self.err_dist.lower() != other.err_dist.lower():
            simon("Lightcurves have different statistics!"
                  "We are setting the errors to zero to avoid complications.")
            new_counts_err = np.zeros_like(new_counts)
        elif self.err_dist.lower() in valid_statistics:
                new_counts_err = \
                    np.sqrt(np.add(self.counts_err[mask_self]**2,
                                   other.counts_err[mask_other]**2))
            # More conditions can be implemented for other statistics
        else:
            raise StingrayError("Statistics not recognized."
                                " Please use one of these: "
                                "{}".format(valid_statistics))

        lc_new = Lightcurve(new_time, new_counts,
                            err=new_counts_err, gti=common_gti,
                            mjdref=self.mjdref)

        return lc_new
Пример #22
0
def classical_pvalue(power, nspec):
    """
    Compute the probability of detecting the current power under
    the assumption that there is no periodic oscillation in the data.

    This computes the single-trial p-value that the power was
    observed under the null hypothesis that there is no signal in
    the data.

    Important: the underlying assumptions that make this calculation valid
    are:
    (1) the powers in the power spectrum follow a chi-square distribution
    (2) the power spectrum is normalized according to Leahy (1984), such
    that the powers have a mean of 2 and a variance of 4
    (3) there is only white noise in the light curve. That is, there is no
    aperiodic variability that would change the overall shape of the power
    spectrum.

    Also note that the p-value is for a *single trial*, i.e. the power currently
    being tested. If more than one power or more than one power spectrum are
    being tested, the resulting p-value must be corrected for the number
    of trials (Bonferroni correction).

    Mathematical formulation in Groth, 1975.
    Original implementation in IDL by Anna L. Watts.

    Parameters
    ----------
    power :  float
        The squared Fourier amplitude of a spectrum to be evaluated

    nspec : int
        The number of spectra or frequency bins averaged in `power`.
        This matters because averaging spectra or frequency bins increases
        the signal-to-noise ratio, i.e. makes the statistical distributions
        of the noise narrower, such that a smaller power might be very
        significant in averaged spectra even though it would not be in a single
        power spectrum.

    """

    assert np.isfinite(power), "power must be a finite floating point number!"
    assert power > 0, "power must be a positive real number!"
    assert np.isfinite(nspec), "nspec must be a finite integer number"
    assert nspec >= 1, "nspec must be larger or equal to 1"
    assert np.isclose(nspec % 1, 0), "nspec must be an integer number!"

    ## If the power is really big, it's safe to say it's significant,
    ## and the p-value will be nearly zero
    if power * nspec > 30000:
        simon("Probability of no signal too miniscule to calculate.")
        return 0.0

    else:
        pval = _pavnosigfun(power, nspec)
        return pval
Пример #23
0
def classical_pvalue(power, nspec):
    """
    Compute the probability of detecting the current power under
    the assumption that there is no periodic oscillation in the data.

    This computes the single-trial p-value that the power was
    observed under the null hypothesis that there is no signal in
    the data.

    Important: the underlying assumptions that make this calculation valid
    are:
    (1) the powers in the power spectrum follow a chi-square distribution
    (2) the power spectrum is normalized according to Leahy (1984), such
    that the powers have a mean of 2 and a variance of 4
    (3) there is only white noise in the light curve. That is, there is no
    aperiodic variability that would change the overall shape of the power
    spectrum.

    Also note that the p-value is for a *single trial*, i.e. the power currently
    being tested. If more than one power or more than one power spectrum are
    being tested, the resulting p-value must be corrected for the number
    of trials (Bonferroni correction).

    Mathematical formulation in Groth, 1975.
    Original implementation in IDL by Anna L. Watts.

    Parameters
    ----------
    power :  float
        The squared Fourier amplitude of a spectrum to be evaluated

    nspec : int
        The number of spectra or frequency bins averaged in `power`.
        This matters because averaging spectra or frequency bins increases
        the signal-to-noise ratio, i.e. makes the statistical distributions
        of the noise narrower, such that a smaller power might be very
        significant in averaged spectra even though it would not be in a single
        power spectrum.

    """

    assert np.isfinite(power), "power must be a finite floating point number!"
    assert power > 0.0, "power must be a positive real number!"
    assert np.isfinite(nspec), "nspec must be a finite integer number"
    assert nspec >= 1, "nspec must be larger or equal to 1"
    assert np.isclose(nspec % 1, 0), "nspec must be an integer number!"

    ## If the power is really big, it's safe to say it's significant,
    ## and the p-value will be nearly zero
    if power*nspec > 30000:
        simon("Probability of no signal too miniscule to calculate.")
        return 0.0

    else:
        pval = _pavnosigfun(power, nspec)
        return pval
Пример #24
0
    def coherence(self):
        """
        Compute an averaged Coherence function of cross spectrum by computing
        coherence function of each segment and averaging them. The return type
        is a tuple with first element as the coherence function and the second
        element as the corresponding uncertainty[1] associated with it.

        Note : The uncertainty in coherence function is strictly valid for
               Gaussian statistics only.

        Returns
        -------
        tuple : tuple of np.ndarray
            Tuple of coherence function and uncertainty.

        References
        ----------
        .. [1] http://iopscience.iop.org/article/10.1086/310430/pdf

        """
        if self.m < 50:
            utils.simon("Number of segments used in averaging is "
                        "significantly low. The result might not follow the "
                        "expected statistical distributions.")

        # Calculate average coherence
        unnorm_power_avg = np.zeros_like(self.cs_all[0].unnorm_power)
        for cs in self.cs_all:
            unnorm_power_avg += cs.unnorm_power

        unnorm_power_avg /= self.m
        num = np.abs(unnorm_power_avg)**2

        # this computes the averaged power spectrum, but using the
        # cross spectrum code to avoid circular imports
        aps1 = AveragedCrossspectrum(self.lc1, self.lc1,
                                     segment_size=self.segment_size)
        aps2 = AveragedCrossspectrum(self.lc2, self.lc2,
                                     segment_size=self.segment_size)

        unnorm_powers_avg_1 = np.zeros_like(aps1.cs_all[0].unnorm_power)
        for ps in aps1.cs_all:
            unnorm_powers_avg_1 += ps.unnorm_power
        unnorm_powers_avg_1 /= aps1.m

        unnorm_powers_avg_2 = np.zeros_like(aps2.cs_all[0].unnorm_power)
        for ps in aps2.cs_all:
            unnorm_powers_avg_2 += ps.unnorm_power
        unnorm_powers_avg_2 /= aps2.m

        coh = num / (unnorm_powers_avg_1 * unnorm_powers_avg_2)

        # Calculate uncertainty
        uncertainty = (2**0.5 * coh * (1 - coh)) / (np.abs(coh) * self.m**0.5)

        return (coh, uncertainty)
Пример #25
0
    def __sub__(self, other):
        """
        Subtract two light curves element by element having the same time array.

        This magic method subtracts two Lightcurve objects having the same
        time array such that the corresponding counts arrays interferes with
        each other.

        GTIs are crossed, so that only common intervals are saved.

        Example
        -------
        >>> time = [10, 20, 30]
        >>> count1 = [600, 1200, 800]
        >>> count2 = [300, 100, 400]
        >>> gti1 = [[0, 20]]
        >>> gti2 = [[0, 25]]
        >>> lc1 = Lightcurve(time, count1, gti=gti1)
        >>> lc2 = Lightcurve(time, count2, gti=gti2)
        >>> lc = lc1 - lc2
        >>> lc.counts
        array([ 300, 1100,  400])
        """

        # ValueError is raised by Numpy while asserting np.equal over arrays
        # with different dimensions.
        try:
            assert np.all(np.equal(self.time, other.time))
        except (ValueError, AssertionError):
            raise ValueError("Time arrays of both light curves must be "
                             "of same dimension and equal.")

        new_counts = np.subtract(self.counts, other.counts)

        if self.err_dist.lower() != other.err_dist.lower():
            simon("Lightcurves have different statistics!"
                  "We are setting the errors to zero to avoid complications.")
            new_counts_err = np.zeros_like(new_counts)
        elif self.err_dist.lower() in valid_statistics:
            new_counts_err = np.sqrt(np.add(self.counts_err**2,
                                            other.counts_err**2))
            # More conditions can be implemented for other statistics
        else:
            raise StingrayError("Statistics not recognized."
                                " Please use one of these: "
                                "{}".format(valid_statistics))

        common_gti = cross_two_gtis(self.gti, other.gti)

        lc_new = Lightcurve(self.time, new_counts,
                            err=new_counts_err, gti=common_gti)

        return lc_new
Пример #26
0
    def coherence(self):
        """Averaged Coherence function.


        Coherence is defined in Vaughan and Nowak, 1996 [vaughan-1996].
        It is a Fourier frequency dependent measure of the linear correlation
        between time series measured simultaneously in two energy channels.

        Compute an averaged Coherence function of cross spectrum by computing
        coherence function of each segment and averaging them. The return type
        is a tuple with first element as the coherence function and the second
        element as the corresponding uncertainty associated with it.

        Note : The uncertainty in coherence function is strictly valid for Gaussian \
               statistics only.

        Returns
        -------
        (coh, uncertainty) : tuple of np.ndarray
            Tuple comprising the coherence function and uncertainty.

        References
        ----------
        .. [vaughan-1996] http://iopscience.iop.org/article/10.1086/310430/pdf

        """
        if np.any(self.m < 50):
            simon("Number of segments used in averaging is "
                  "significantly low. The result might not follow the "
                  "expected statistical distributions.")

        # Calculate average coherence
        unnorm_power_avg = self.unnorm_power

        num = np.absolute(unnorm_power_avg)**2

        # The normalization was 'none'!
        unnorm_powers_avg_1 = self.pds1.power.real
        unnorm_powers_avg_2 = self.pds2.power.real

        coh = num / (unnorm_powers_avg_1 * unnorm_powers_avg_2)
        coh[~np.isfinite(coh)] = 0.0

        # Calculate uncertainty
        uncertainty = \
            (2 ** 0.5 * coh * (1 - coh)) / (np.abs(coh) * self.m ** 0.5)

        uncertainty[coh == 0] = 0.0

        return (coh, uncertainty)
Пример #27
0
    def __init__(
        self,
        events,
        freq_interval,
        energy_spec,
        ref_band=None,
        bin_time=1,
        use_pi=False,
        segment_size=None,
        events2=None,
        return_complex=False,
    ):
        self.events1 = events
        self.events2 = assign_value_if_none(events2, events)
        self._analyze_inputs()
        # This will be set to True in ComplexCovariance
        self.return_complex = return_complex

        self.freq_interval = freq_interval
        self.use_pi = use_pi
        self.bin_time = bin_time

        if isinstance(energy_spec, tuple):
            energies = _decode_energy_specification(energy_spec)
        else:
            energies = np.asarray(energy_spec)

        self.energy_intervals = list(zip(energies[0:-1], energies[1:]))

        self.ref_band = np.asarray(assign_value_if_none(ref_band, [0, np.inf]))

        if len(self.ref_band.shape) <= 1:
            self.ref_band = np.asarray([self.ref_band])

        self.segment_size = self.delta_nu = None
        if segment_size is not None:
            self.segment_size = segment_size
            self.delta_nu = 1 / self.segment_size

        self._create_empty_spectrum()

        if len(events.time) == 0:
            simon("There are no events in your event list!" +
                  "Can't make a spectrum!")
        else:
            self._spectrum_function()
Пример #28
0
    def coherence(self):
        """
        Compute an averaged Coherence function of cross spectrum by computing
        coherence function of each segment and averaging them. The return type
        is a tuple with first element as the coherence function and the second
        element as the corresponding uncertainty[1] associated with it.

        Note : The uncertainty in coherence function is strictly valid for
               Gaussian statistics only.

        Returns
        -------
        tuple : tuple of np.ndarray
            Tuple of coherence function and uncertainty.

        References
        ----------
        .. [1] http://iopscience.iop.org/article/10.1086/310430/pdf

        """
        if np.any(self.m < 50):
            simon("Number of segments used in averaging is "
                  "significantly low. The result might not follow the "
                  "expected statistical distributions.")

        # Calculate average coherence
        unnorm_power_avg = np.zeros_like(self.cs_all[0].unnorm_power)
        for cs in self.cs_all:
            unnorm_power_avg += cs.unnorm_power

        unnorm_power_avg /= self.m
        num = np.absolute(unnorm_power_avg)**2

        # The normalization was 'none'!
        unnorm_powers_avg_1 = self.pds1.power.real
        unnorm_powers_avg_2 = self.pds2.power.real

        coh = num / (unnorm_powers_avg_1 * unnorm_powers_avg_2)

        # Calculate uncertainty
        uncertainty = (2**0.5 * coh * (1 - coh)) / (np.abs(coh) * self.m**0.5)

        return (coh, uncertainty)
Пример #29
0
    def _operation_with_other_lc(self, other, operation):
        if self.mjdref != other.mjdref:
            raise ValueError("MJDref is different in the two light curves")

        common_gti = cross_two_gtis(self.gti, other.gti)
        mask_self = create_gti_mask(self.time, common_gti)
        mask_other = create_gti_mask(other.time, common_gti)

        # ValueError is raised by Numpy while asserting np.equal over arrays
        # with different dimensions.
        try:
            assert np.all(
                np.equal(self.time[mask_self], other.time[mask_other]))
        except (ValueError, AssertionError):
            raise ValueError("GTI-filtered time arrays of both light curves "
                             "must be of same dimension and equal.")

        new_time = self.time[mask_self]
        new_counts = operation(self.counts[mask_self],
                               other.counts[mask_other])

        if self.err_dist.lower() != other.err_dist.lower():
            simon("Lightcurves have different statistics!"
                  "We are setting the errors to zero to avoid complications.")
            new_counts_err = np.zeros_like(new_counts)
        elif self.err_dist.lower() in valid_statistics:
            new_counts_err = np.sqrt(
                np.add(self.counts_err[mask_self]**2,
                       other.counts_err[mask_other]**2))
        # More conditions can be implemented for other statistics
        else:
            raise StingrayError("Statistics not recognized."
                                " Please use one of these: "
                                "{}".format(valid_statistics))

        lc_new = Lightcurve(new_time,
                            new_counts,
                            err=new_counts_err,
                            gti=common_gti,
                            mjdref=self.mjdref)

        return lc_new
Пример #30
0
    def _construct_covar(self):
        """
        Helper method to construct the covariance attribute and fill it with values.
        """

        self.avg_covar = False
        covar = np.zeros(len(self.lcs))
        covar_err = np.zeros(len(self.lcs))
        xs_var = np.zeros(len(self.lcs))

        for i in range(len(self.lcs)):
            lc = self.lcs[i]

            if np.size(self.ref_band_lcs) == 1 or isinstance(
                    self.ref_band_lcs, Lightcurve):
                lc_ref = self.ref_band_lcs
            else:
                lc_ref = self.ref_band_lcs[i]

            cv = self._compute_covariance(lc, lc_ref)
            cv_err = self._calculate_covariance_error(lc, lc_ref)

            covar[i] = cv
            covar_err[i] = cv_err

            xs = self._calculate_excess_variance(lc_ref)
            if not xs > 0:
                utils.simon("The excess variance in the reference band is "
                            "negative. This implies that the reference "
                            "band was badly chosen. Beware that the "
                            "covariance spectra will have NaNs!")

            xs_var[i] = xs

        self.unnorm_covar = covar
        energy_covar = covar / xs_var**0.5

        self.covar = energy_covar

        self.covar_error = covar_err

        return
Пример #31
0
    def _construct_covar(self):
        """
        Helper method to construct the covariance attribute and fill it with values.
        """

        self.avg_covar = False
        covar = np.zeros(len(self.lcs))
        covar_err = np.zeros(len(self.lcs))
        xs_var = np.zeros(len(self.lcs))

        for i in range(len(self.lcs)):
            lc = self.lcs[i]

            if np.size(self.ref_band_lcs) == 1 or isinstance(self.ref_band_lcs,
                                                             Lightcurve):
                lc_ref = self.ref_band_lcs
            else:
                lc_ref = self.ref_band_lcs[i]

            cv = self._compute_covariance(lc, lc_ref)
            cv_err = self._calculate_covariance_error(lc, lc_ref)

            covar[i] = cv
            covar_err[i] = cv_err

            xs = self._calculate_excess_variance(lc_ref)
            if not xs > 0:
                utils.simon("The excess variance in the reference band is "
                            "negative. This implies that the reference "
                            "band was badly chosen. Beware that the "
                            "covariance spectra will have NaNs!")

            xs_var[i] = xs

        self.unnorm_covar = covar
        energy_covar = covar / xs_var**0.5

        self.covar = energy_covar

        self.covar_error = covar_err

        return
Пример #32
0
    def coherence(self):
        """
        Compute an averaged Coherence function of cross spectrum by computing
        coherence function of each segment and averaging them. The return type
        is a tuple with first element as the coherence function and the second
        element as the corresponding uncertainty[1] associated with it.

        Note : The uncertainty in coherence function is strictly valid for
               Gaussian statistics only.

        Returns
        -------
        tuple : tuple of np.ndarray
            Tuple of coherence function and uncertainty.

        References
        ----------
        .. [1] http://iopscience.iop.org/article/10.1086/310430/pdf

        """
        if np.any(self.m < 50):
            simon("Number of segments used in averaging is "
                  "significantly low. The result might not follow the "
                  "expected statistical distributions.")

        # Calculate average coherence
        unnorm_power_avg = self.unnorm_power

        num = np.absolute(unnorm_power_avg) ** 2

        # The normalization was 'none'!
        unnorm_powers_avg_1 = self.pds1.power.real
        unnorm_powers_avg_2 = self.pds2.power.real

        coh = num / (unnorm_powers_avg_1 * unnorm_powers_avg_2)

        # Calculate uncertainty
        uncertainty = (2 ** 0.5 * coh * (1 - coh)) / (
        np.abs(coh) * self.m ** 0.5)

        return (coh, uncertainty)
Пример #33
0
def read(filename, format_='pickle', **kwargs):
    """
    Return a saved class instance.

    Parameters
    ----------
    filename: str
        The name of the file to be retrieved.

    format_: str
        The format used to store file. Supported formats are
        pickle, hdf5, ascii or fits.

    Returns
    -------
    data : {``object`` | ``astropy.table`` | ``dict``}

        * If ``format_`` is ``pickle``, an object is returned.
        * If ``format_`` is ``ascii``, `astropy.table` object is returned.
        * If ``format_`` is ``hdf5`` or 'fits``, a dictionary object is returned.
    """

    if format_ == 'pickle':
        return _retrieve_pickle_object(filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            return _retrieve_hdf5_object(filename)
        else:
            utils.simon('h5py not installed, cannot read an'
                        'hdf5 object.')

    elif format_ == 'ascii':
        return _retrieve_ascii_object(filename, **kwargs)

    elif format_ == 'fits':
        return _retrieve_fits_object(filename, **kwargs)

    else:
        utils.simon('Format not understood.')
Пример #34
0
def read(filename, format_='pickle', **kwargs):
    """
    Return a saved class instance.

    Parameters
    ----------
    filename: str
        The name of the file to be retrieved.

    format_: str
        The format used to store file. Supported formats are
        pickle, hdf5, ascii or fits.

    Returns
    -------
    data : {``object`` | ``astropy.table`` | ``dict``}

        * If ``format_`` is ``pickle``, an object is returned.
        * If ``format_`` is ``ascii``, `astropy.table` object is returned.
        * If ``format_`` is ``hdf5`` or 'fits``, a dictionary object is returned.
    """

    if format_ == 'pickle':
        return _retrieve_pickle_object(filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            return _retrieve_hdf5_object(filename)
        else:
            utils.simon('h5py not installed, cannot read an'
                        'hdf5 object.')

    elif format_ == 'ascii':
        return _retrieve_ascii_object(filename, **kwargs)

    elif format_ == 'fits':
        return _retrieve_fits_object(filename, **kwargs)

    else:
        utils.simon('Format not understood.')
Пример #35
0
    def _init_vars(self, event_list, dt, band_interest,
                   ref_band_interest, std):
        """
        Check for consistency with input variables and declare public ones.
        """
        if not np.all(np.diff(event_list, axis=0).T[0] >= 0):
            utils.simon("The event list must be sorted with respect to "
                        "times of arrivals.")
            event_list = event_list[event_list[:, 0].argsort()]

        self.event_list = event_list

        self.event_list_T = event_list.T

        self._init_special_vars()

        if ref_band_interest is None:
            ref_band_interest = (self.min_energy, self.max_energy)

        assert type(ref_band_interest) in (list, tuple), "Ref Band interest " \
                                                         "should be either " \
                                                         "tuple or list."

        assert len(ref_band_interest) == 2, "Band interest should be a tuple" \
                                            " with min and max energy value " \
                                            "for the reference band."
        self.ref_band_interest = ref_band_interest

        if band_interest is not None:
            for element in list(band_interest):
                assert type(element) in (list, tuple), \
                    "band_interest should be iterable of either tuple or list."
                assert len(element) == 2, "Band interest should be a tuple " \
                                          "with min and max energy values."

        self.band_interest = band_interest
        self.dt = dt

        self.std = std
Пример #36
0
    def _init_vars(self, event_list, dt, band_interest, ref_band_interest,
                   std):
        """
        Check for consistency with input variables and declare public ones.
        """
        if not np.all(np.diff(event_list, axis=0).T[0] >= 0):
            utils.simon("The event list must be sorted with respect to "
                        "times of arrivals.")
            event_list = event_list[event_list[:, 0].argsort()]

        self.event_list = event_list

        self.event_list_T = event_list.T

        self._init_special_vars()

        if ref_band_interest is None:
            ref_band_interest = (self.min_energy, self.max_energy)

        assert type(ref_band_interest) in (list, tuple), "Ref Band interest " \
                                                         "should be either " \
                                                         "tuple or list."

        assert len(ref_band_interest) == 2, "Band interest should be a tuple" \
                                            " with min and max energy value " \
                                            "for the reference band."
        self.ref_band_interest = ref_band_interest

        if band_interest is not None:
            for element in list(band_interest):
                assert type(element) in (list, tuple), \
                    "band_interest should be iterable of either tuple or list."
                assert len(element) == 2, "Band interest should be a tuple " \
                                          "with min and max energy values."

        self.band_interest = band_interest
        self.dt = dt

        self.std = std
Пример #37
0
    def _operation_with_other_lc(self, other, operation):
        if self.mjdref != other.mjdref:
            raise ValueError("MJDref is different in the two light curves")

        common_gti = cross_two_gtis(self.gti, other.gti)
        mask_self = create_gti_mask(self.time, common_gti)
        mask_other = create_gti_mask(other.time, common_gti)

        # ValueError is raised by Numpy while asserting np.equal over arrays
        # with different dimensions.
        try:
            assert np.all(np.equal(self.time[mask_self],
                                   other.time[mask_other]))
        except (ValueError, AssertionError):
            raise ValueError("GTI-filtered time arrays of both light curves "
                             "must be of same dimension and equal.")

        new_time = self.time[mask_self]
        new_counts = operation(self.counts[mask_self],
                               other.counts[mask_other])

        if self.err_dist.lower() != other.err_dist.lower():
            simon("Lightcurves have different statistics!"
                  "We are setting the errors to zero to avoid complications.")
            new_counts_err = np.zeros_like(new_counts)
        elif self.err_dist.lower() in valid_statistics:
                new_counts_err = np.sqrt(np.add(self.counts_err[mask_self]**2,
                                                other.counts_err[mask_other]**2))
            # More conditions can be implemented for other statistics
        else:
            raise StingrayError("Statistics not recognized."
                                " Please use one of these: "
                                "{}".format(valid_statistics))

        lc_new = Lightcurve(new_time, new_counts,
                            err=new_counts_err, gti=common_gti,
                            mjdref=self.mjdref)

        return lc_new
Пример #38
0
def read(filename, format_='pickle', **kwargs):
    """
    Return a pickled class instance.

    Parameters
    ----------
    filename: str
        The name of the file to be retrieved.

    format_: str
        The format used to store file. Supported formats are
        pickle, hdf5, ascii or fits.
    
    Returns
    -------
    If format_ is 'pickle', a class object is returned.
    If format_ is 'ascii', astropy.table object is returned.
    If format_ is 'hdf5' or 'fits', a dictionary object is returned.
    """

    if format_ == 'pickle':
        return _retrieve_pickle_object(filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            return _retrieve_hdf5_object(filename)
        else:
            utils.simon('h5py not installed, cannot read an' \
                'hdf5 object.')

    elif format_ == 'ascii':
        return _retrieve_ascii_object(filename, **kwargs)

    elif format_ == 'fits':
        return _retrieve_fits_object(filename, **kwargs)
    
    else:
        utils.simon('Format not understood.')
Пример #39
0
def write(input_, filename, format_='pickle', **kwargs):
    """
    Pickle a class instance. For parameters depending on
    ``format_``, see individual function definitions.

    Parameters
    ----------
    object: a class instance
        The object to be stored

    filename: str
        The name of the file to be created

    format_: str
        The format in which to store file. Formats supported
        are ``pickle``, ``hdf5``, ``ascii`` or ``fits``
    """

    if format_ == 'pickle':
        _save_pickle_object(input_, filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            _save_hdf5_object(input_, filename)
        else:
            utils.simon('h5py not installed, using pickle instead'
                        'to save object.')
            _save_pickle_object(input_, filename.split('.')[0] +
                                '.pickle')

    elif format_ == 'ascii':
        _save_ascii_object(input_, filename, **kwargs)

    elif format_ == 'fits':
        _save_fits_object(input_, filename, **kwargs)

    else:
        utils.simon('Format not understood.')
Пример #40
0
def read(filename, format_='pickle', **kwargs):
    """
    Return a pickled class instance.

    Parameters
    ----------
    filename: str
        The name of the file to be retrieved.

    format_: str
        The format used to store file. Supported formats are
        pickle, hdf5, ascii or fits.
    
    Returns
    -------
    If format_ is 'pickle', a class object is returned.
    If format_ is 'ascii', astropy.table object is returned.
    If format_ is 'hdf5' or 'fits', a dictionary object is returned.
    """

    if format_ == 'pickle':
        return _retrieve_pickle_object(filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            return _retrieve_hdf5_object(filename)
        else:
            utils.simon('h5py not installed, cannot read an' \
                'hdf5 object.')

    elif format_ == 'ascii':
        return _retrieve_ascii_object(filename, **kwargs)

    elif format_ == 'fits':
        return _retrieve_fits_object(filename, **kwargs)

    else:
        utils.simon('Format not understood.')
Пример #41
0
def write(input_, filename, format_='pickle', **kwargs):
    """
    Pickle a class instance. For parameters depending on
    ``format_``, see individual function definitions.

    Parameters
    ----------
    object: a class instance
        The object to be stored

    filename: str
        The name of the file to be created

    format_: str
        The format in which to store file. Formats supported
        are ``pickle``, ``hdf5``, ``ascii`` or ``fits``
    """

    if format_ == 'pickle':
        _save_pickle_object(input_, filename)

    elif format_ == 'hdf5':
        if _H5PY_INSTALLED:
            _save_hdf5_object(input_, filename)
        else:
            utils.simon('h5py not installed, using pickle instead'
                        'to save object.')
            _save_pickle_object(input_, filename.split('.')[0] +
                                '.pickle')

    elif format_ == 'ascii':
        _save_ascii_object(input_, filename, **kwargs)

    elif format_ == 'fits':
        _save_fits_object(input_, filename, **kwargs)

    else:
        utils.simon('Format not understood.')
Пример #42
0
    def _construct_energy_covar(self,
                                energy_events,
                                energy_covar,
                                xs_var=None):
        """Form the actual output covariance dictionary and array."""
        self._init_energy_covar(energy_events, energy_covar)

        if not self.avg_covar:
            xs_var = dict()

        for energy in energy_covar.keys():
            lc, lc_ref = self._create_lc_and_lc_ref(energy, energy_events)

            covar = self._compute_covariance(lc, lc_ref)

            energy_covar[energy] = covar
            if not self.avg_covar:
                self.covar_error[energy] = self._calculate_covariance_error(
                    lc, lc_ref)

            # Excess variance in ref band
            xs_var[energy] = self._calculate_excess_variance(lc_ref)

        for key, value in energy_covar.items():
            if not xs_var[key] > 0:
                utils.simon("The excess variance in the reference band is "
                            "negative. This implies that the reference "
                            "band was badly chosen. Beware that the "
                            "covariance spectra will have NaNs!")

        if not self.avg_covar:
            self.unnorm_covar = np.vstack(energy_covar.items())
            energy_covar[key] = value / (xs_var[key])**0.5

            self.covar = np.vstack(energy_covar.items())

            self.covar_error = np.vstack(self.covar_error.items())
Пример #43
0
    def __init__(self,
                 events,
                 freq_interval,
                 energy_spec,
                 ref_band=None,
                 bin_time=1,
                 use_pi=False,
                 segment_size=None,
                 events2=None):

        self.events1 = events
        self.events2 = assign_value_if_none(events2, events)
        self.freq_interval = freq_interval
        self.use_pi = use_pi
        self.bin_time = bin_time
        if isinstance(energy_spec, tuple):
            energies = _decode_energy_specification(energy_spec)
        else:
            energies = np.asarray(energy_spec)

        self.energy_intervals = list(zip(energies[0:-1], energies[1:]))

        self.ref_band = np.asarray(assign_value_if_none(ref_band, [0, np.inf]))

        if len(self.ref_band.shape) <= 1:
            self.ref_band = np.asarray([self.ref_band])

        self.segment_size = segment_size

        if len(events.time) == 0:
            simon("There are no events in your event list!" +
                  "Can't make a spectrum!")
            self.spectrum = 0
            self.spectrum_error = 0
        else:
            self.spectrum, self.spectrum_error = self._spectrum_function()
Пример #44
0
    def plot(self, witherrors=False, labels=None, axis=None, title=None,
             marker='-', save=False, filename=None):
        """
        Plot the Lightcurve using Matplotlib.

        Plot the Lightcurve object on a graph ``self.time`` on x-axis and
        ``self.counts`` on y-axis with ``self.counts_err`` optionaly
        as error bars.

        Parameters
        ----------
        witherrors: boolean, default False
            Whether to plot the Lightcurve with errorbars or not

        labels : iterable, default None
            A list of tuple with xlabel and ylabel as strings.

        axis : list, tuple, string, default None
            Parameter to set axis properties of Matplotlib figure. For example
            it can be a list like ``[xmin, xmax, ymin, ymax]`` or any other
            acceptable argument for `matplotlib.pyplot.axis()` function.

        title : str, default None
            The title of the plot.

        marker : str, default '-'
            Line style and color of the plot. Line styles and colors are
            combined in a single format string, as in ``'bo'`` for blue
            circles. See `matplotlib.pyplot.plot` for more options.

        save : boolean, optional (default=False)
            If True, save the figure with specified filename.

        filename : str
            File name of the image to save. Depends on the boolean ``save``.
        """
        try:
            import matplotlib.pyplot as plt
        except ImportError:
            raise ImportError("Matplotlib required for plot()")

        fig = plt.figure()
        if witherrors:
            fig = plt.errorbar(self.time, self.counts, yerr=self.counts_err,
                               fmt=marker)
        else:
            fig = plt.plot(self.time, self.counts, marker)

        if labels is not None:
            try:
                plt.xlabel(labels[0])
                plt.ylabel(labels[1])
            except TypeError:
                utils.simon("``labels`` must be either a list or tuple with "
                            "x and y labels.")
                raise
            except IndexError:
                utils.simon("``labels`` must have two labels for x and y "
                            "axes.")
                # Not raising here because in case of len(labels)==1, only
                # x-axis will be labelled.

        if axis is not None:
            plt.axis(axis)

        if title is not None:
            plt.title(title)

        if save:
            if filename is None:
                plt.savefig('out.png')
            else:
                plt.savefig(filename)
Пример #45
0
    def join(self, other):
        """
        Join two lightcurves into a single object.

        The new Lightcurve object will contain time stamps from both the
        objects. The count per bin in the resulting object will be the
        individual count per bin, or the average in case of overlapping
        time arrays of both lightcurve objects.

        Good Time intervals are also joined.

        Note : Time array of both lightcurves should not overlap each other.

        Parameters
        ----------
        other : Lightcurve object
            The other Lightcurve object which is supposed to be joined with.

        Returns
        -------
        lc_new : Lightcurve object
            The resulting lightcurve object.

        Example
        -------
        >>> time1 = [5, 10, 15]
        >>> count1 = [300, 100, 400]
        >>> time2 = [20, 25, 30]
        >>> count2 = [600, 1200, 800]
        >>> lc1 = Lightcurve(time1, count1)
        >>> lc2 = Lightcurve(time2, count2)
        >>> lc = lc1.join(lc2)
        >>> lc.time
        array([ 5, 10, 15, 20, 25, 30])
        >>> lc.counts
        array([ 300,  100,  400,  600, 1200,  800])
        """
        if self.mjdref != other.mjdref:
            raise ValueError("MJDref is different in the two light curves")

        if self.dt != other.dt:
            utils.simon("The two light curves have different bin widths.")

        if( self.tstart < other.tstart ):
            first_lc = self
            second_lc = other
        else:
            first_lc = other
            second_lc = self

        if len(np.intersect1d(self.time, other.time) > 0):

            utils.simon("The two light curves have overlapping time ranges. "
                        "In the common time range, the resulting count will "
                        "be the average of the counts in the two light "
                        "curves. If you wish to sum, use `lc_sum = lc1 + "
                        "lc2`.")
            valid_err = False

            if self.err_dist.lower() != other.err_dist.lower():
                simon("Lightcurves have different statistics!"
                      "We are setting the errors to zero.")


            elif self.err_dist.lower() in valid_statistics:
                valid_err = True
            # More conditions can be implemented for other statistics 
            else:
                raise StingrayError("Statistics not recognized."
                                    " Please use one of these: "
                                    "{}".format(valid_statistics))


            from collections import Counter
            counts = Counter()
            counts_err = Counter()

            for i, time in enumerate(first_lc.time):
                counts[time] = first_lc.counts[i]
                counts_err[time] = first_lc.counts_err[i]

            for i, time in enumerate(second_lc.time):
            
                if (counts.get(time) != None): #Common time

                    counts[time] = (counts[time] + second_lc.counts[i]) / 2  #avg
                    counts_err[time] = np.sqrt(( ((counts_err[time]**2) + (second_lc.counts_err[i] **2)) / 2))

                else:
                    counts[time] = second_lc.counts[i]
                    counts_err[time] = second_lc.counts_err[i]

            new_time = list(counts.keys())
            new_counts = list(counts.values())
            if(valid_err):
                new_counts_err = list(counts_err.values())
            else:
                new_counts_err = np.zeros_like(new_counts)
            
            del[counts, counts_err]

        else:

            new_time = np.concatenate([first_lc.time, second_lc.time])
            new_counts = np.concatenate([first_lc.counts, second_lc.counts])
            new_counts_err = np.concatenate([first_lc.counts_err, second_lc.counts_err])

        new_time = np.asarray(new_time)
        new_counts = np.asarray(new_counts)
        new_counts_err = np.asarray(new_counts_err)
        gti = join_gtis(self.gti, other.gti)

        lc_new = Lightcurve(new_time, new_counts, err=new_counts_err, gti=gti,
                            mjdref=self.mjdref, dt=self.dt)

        return lc_new
Пример #46
0
    def __init__(self, time, counts, err=None, input_counts=True,
                 gti=None, err_dist='poisson', mjdref=0, dt=None):
        """
        Make a light curve object from an array of time stamps and an
        array of counts.

        Parameters
        ----------
        time: iterable
            A list or array of time stamps for a light curve

        counts: iterable, optional, default None
            A list or array of the counts in each bin corresponding to the
            bins defined in `time` (note: use `input_counts=False` to
            input the count range, i.e. counts/second, otherwise use
            counts/bin).

        err: iterable, optional, default None:
            A list or array of the uncertainties in each bin corresponding to
            the bins defined in `time` (note: use `input_counts=False` to
            input the count rage, i.e. counts/second, otherwise use
            counts/bin). If None, we assume the data is poisson distributed
            and calculate the error from the average of the lower and upper 
            1-sigma confidence intervals for the Poissonian distribution with 
            mean equal to `counts`.

        input_counts: bool, optional, default True
            If True, the code assumes that the input data in 'counts'
            is in units of counts/bin. If False, it assumes the data
            in 'counts' is in counts/second.

        gti: 2-d float array, default None
            [[gti0_0, gti0_1], [gti1_0, gti1_1], ...]
            Good Time Intervals. They are *not* applied to the data by default.
            They will be used by other methods to have an indication of the
            "safe" time intervals to use during analysis.

        err_dist: str, optional, default=None
            Statistic of the Lightcurve, it is used to calculate the
            uncertainties and other statistical values apropriately.
            Default makes no assumptions and keep errors equal to zero.

        mjdref: float
            MJD reference (useful in most high-energy mission data)


        Attributes
        ----------
        time: numpy.ndarray
            The array of midpoints of time bins.

        bin_lo:
            The array of lower time stamp of time bins.

        bin_hi:
            The array of higher time stamp of time bins.

        counts: numpy.ndarray
            The counts per bin corresponding to the bins in `time`.

        counts_err: numpy.ndarray
            The uncertainties corresponding to `counts`

        countrate: numpy.ndarray
            The counts per second in each of the bins defined in `time`.

        countrate_err: numpy.ndarray
            The uncertainties corresponding to `countrate`

        meanrate: float
            The mean count rate of the light curve.

        meancounts: float
            The mean counts of the light curve.

        n: int
            The number of data points in the light curve.

        dt: float
            The time resolution of the light curve.

        mjdref: float
            MJD reference date (tstart / 86400 gives the date in MJD at the
            start of the observation)

        tseg: float
            The total duration of the light curve.

        tstart: float
            The start time of the light curve.

        gti: 2-d float array
            [[gti0_0, gti0_1], [gti1_0, gti1_1], ...]
            Good Time Intervals. They indicate the "safe" time intervals
            to be used during the analysis of the light curve.

        err_dist: string
            Statistic of the Lightcurve, it is used to calculate the
            uncertainties and other statistical values apropriately.
            It propagates to Spectrum classes.

        """

        if not np.all(np.isfinite(time)):
            raise ValueError("There are inf or NaN values in "
                             "your time array!")

        if not np.all(np.isfinite(counts)):
            raise ValueError("There are inf or NaN values in "
                             "your counts array!")

        if len(time) != len(counts):

            raise StingrayError("time and counts array are not "
                                "of the same length!")

        if len(time) <= 1:
            raise StingrayError("A single or no data points can not create "
                                "a lightcurve!")

        if err is not None:
            if not np.all(np.isfinite(err)):
                raise ValueError("There are inf or NaN values in "
                                 "your err array")
        else:
            if err_dist.lower() not in valid_statistics:
                # err_dist set can be increased with other statistics
                raise StingrayError("Statistic not recognized."
                                    "Please select one of these: ",
                                    "{}".format(valid_statistics))
            if err_dist.lower() == 'poisson':
                # Instead of the simple square root, we use confidence
                # intervals (should be valid for low fluxes too)
                err_low, err_high = poisson_conf_interval(np.asarray(counts),
                    interval='frequentist-confidence', sigma=1)
                # calculate approximately symmetric uncertainties
                err_low -= np.asarray(counts)
                err_high -= np.asarray(counts)
                err = (np.absolute(err_low) + np.absolute(err_high))/2.0
                # other estimators can be implemented for other statistics
            else:
                simon("Stingray only uses poisson err_dist at the moment, "
                      "We are setting your errors to zero. "
                      "Sorry for the inconvenience.")
                err = np.zeros_like(counts)

        self.mjdref = mjdref
        self.time = np.asarray(time)
        if dt is None:
            self.dt = np.median(self.time[1:] - self.time[:-1])
        else:
            self.dt = dt

        self.bin_lo = self.time - 0.5 * self.dt
        self.bin_hi = self.time + 0.5 * self.dt

        self.err_dist = err_dist

        self.tstart = self.time[0] - 0.5*self.dt
        self.tseg = self.time[-1] - self.time[0] + self.dt

        self.gti = \
            np.asarray(assign_value_if_none(gti,
                                            [[self.tstart,
                                              self.tstart + self.tseg]]))
        check_gtis(self.gti)

        good = create_gti_mask(self.time, self.gti)

        self.time = self.time[good]
        if input_counts:
            self.counts = np.asarray(counts)[good]
            self.countrate = self.counts / self.dt
            self.counts_err = np.asarray(err)[good]
            self.countrate_err = np.asarray(err)[good] / self.dt
        else:
            self.countrate = np.asarray(counts)[good]
            self.counts = self.countrate * self.dt
            self.counts_err = np.asarray(err)[good] * self.dt
            self.countrate_err = np.asarray(err)[good]

        self.meanrate = np.mean(self.countrate)
        self.meancounts = np.mean(self.counts)
        self.n = self.counts.shape[0]

        # Issue a warning if the input time iterable isn't regularly spaced,
        # i.e. the bin sizes aren't equal throughout.
        dt_array = np.diff(self.time)
        if not (np.allclose(dt_array, np.repeat(self.dt, dt_array.shape[0]))):
            simon("Bin sizes in input time array aren't equal throughout! "
                  "This could cause problems with Fourier transforms. "
                  "Please make the input time evenly sampled.")
Пример #47
0
    def __init__(self, time, counts, input_counts=True, gti=None):
        """
        Make a light curve object from an array of time stamps and an
        array of counts.

        Parameters
        ----------
        time: iterable
            A list or array of time stamps for a light curve

        counts: iterable, optional, default None
            A list or array of the counts in each bin corresponding to the
            bins defined in `time` (note: **not** the count rate, i.e.
            counts/second, but the counts/bin).

        input_counts: bool, optional, default True
            If True, the code assumes that the input data in 'counts'
            is in units of counts/bin. If False, it assumes the data
            in 'counts' is in counts/second.

        gti: 2-d float array, default None
            [[gti0_0, gti0_1], [gti1_0, gti1_1], ...]
            Good Time Intervals. They are *not* applied to the data by default.
            They will be used by other methods to have an indication of the
            "safe" time intervals to use during analysis.

        Attributes
        ----------
        time: numpy.ndarray
            The array of midpoints of time bins.

        bin_lo:
            The array of lower time stamp of time bins.

        bin_hi:
            The array of higher time stamp of time bins.

        counts: numpy.ndarray
            The counts per bin corresponding to the bins in `time`.

        countrate: numpy.ndarray
            The counts per second in each of the bins defined in `time`.

        meanrate: float
            The mean count rate of the light curve.
            
        meancounts: float
            The mean counts of the light curve.

        n: int
            The number of data points in the light curve.

        dt: float
            The time resolution of the light curve.

        tseg: float
            The total duration of the light curve.

        tstart: float
            The start time of the light curve.

        gti: 2-d float array
            [[gti0_0, gti0_1], [gti1_0, gti1_1], ...]
            Good Time Intervals. They indicate the "safe" time intervals
            to be used during the analysis of the light curve.

        """
        if not np.all(np.isfinite(time)):
            raise ValueError("There are inf or NaN values in "
                             "your time array!")

        if not np.all(np.isfinite(counts)):
            raise ValueError("There are inf or NaN values in "
                             "your counts array!")

        if len(time) != len(counts):

            raise StingrayError("time and counts array are not "
                                "of the same length!")

        if len(time) <= 1:
            raise StingrayError("A single or no data points can not create "
                                "a lightcurve!")

        self.time = np.asarray(time)
        self.dt = time[1] - time[0]

        self.bin_lo = self.time - 0.5 * self.dt
        self.bin_hi = self.time + 0.5 * self.dt

        if input_counts:
            self.counts = np.asarray(counts)
            self.countrate = self.counts / self.dt
        else:
            self.countrate = np.asarray(counts)
            self.counts = self.countrate * self.dt

        self.meanrate = np.mean(self.countrate)
        self.meancounts = np.mean(self.counts)
        self.n = self.counts.shape[0]

        # Issue a warning if the input time iterable isn't regularly spaced,
        # i.e. the bin sizes aren't equal throughout.
        dt_array = np.diff(self.time)
        if not (np.allclose(dt_array, np.repeat(self.dt, dt_array.shape[0]))):
            simon("Bin sizes in input time array aren't equal throughout! "
                  "This could cause problems with Fourier transforms. "
                  "Please make the input time evenly sampled.")

        self.tseg = self.time[-1] - self.time[0] + self.dt
        self.tstart = self.time[0] - 0.5 * self.dt
        self.gti = \
            np.asarray(assign_value_if_none(gti,
                                            [[self.tstart,
                                              self.tstart + self.tseg]]))
        check_gtis(self.gti)
Пример #48
0
    def __init__(self, time, counts, input_counts=True, gti=None):
        """
        Make a light curve object from an array of time stamps and an
        array of counts.

        Parameters
        ----------
        time: iterable
            A list or array of time stamps for a light curve

        counts: iterable, optional, default None
            A list or array of the counts in each bin corresponding to the
            bins defined in `time` (note: **not** the count rate, i.e.
            counts/second, but the counts/bin).

        input_counts: bool, optional, default True
            If True, the code assumes that the input data in 'counts'
            is in units of counts/bin. If False, it assumes the data
            in 'counts' is in counts/second.

        gti: 2-d float array, default None
            [[gti0_0, gti0_1], [gti1_0, gti1_1], ...]
            Good Time Intervals. They are *not* applied to the data by default.
            They will be used by other methods to have an indication of the
            "safe" time intervals to use during analysis.

        Attributes
        ----------
        time: numpy.ndarray
            The array of midpoints of time bins.

        counts: numpy.ndarray
            The counts per bin corresponding to the bins in `time`.

        countrate: numpy.ndarray
            The counts per second in each of the bins defined in `time`.

        meanrate: float
            The mean count rate of the light curve.
            
        meancounts: float
            The mean counts of the light curve.

        n: int
            The number of data points in the light curve.

        dt: float
            The time resolution of the light curve.

        tseg: float
            The total duration of the light curve.

        tstart: float
            The start time of the light curve.

        gti: 2-d float array
            [[gti0_0, gti0_1], [gti1_0, gti1_1], ...]
            Good Time Intervals. They indicate the "safe" time intervals
            to be used during the analysis of the light curve.

        """
        if not np.all(np.isfinite(time)):
            raise ValueError("There are inf or NaN values in "
                             "your time array!")

        if not np.all(np.isfinite(counts)):
            raise ValueError("There are inf or NaN values in "
                             "your counts array!")

        if len(time) != len(counts):

            raise StingrayError("time are counts array are not "
                                "of the same length!")

        if len(time) <= 1:
            raise StingrayError("A single or no data points can not create "
                                "a lightcurve!")

        self.time = np.asarray(time)
        self.dt = time[1] - time[0]

        if input_counts:
            self.counts = np.asarray(counts)
            self.countrate = self.counts / self.dt
        else:
            self.countrate = np.asarray(counts)
            self.counts = self.countrate * self.dt

        self.meanrate = np.mean(self.countrate)
        self.meancounts = np.mean(self.counts)
        self.n = self.counts.shape[0]

        # Issue a warning if the input time iterable isn't regularly spaced,
        # i.e. the bin sizes aren't equal throughout.
        dt_array = np.diff(self.time)
        if not (np.allclose(dt_array, np.repeat(self.dt, dt_array.shape[0]))):
            simon("Bin sizes in input time array aren't equal throughout! "
                  "This could cause problems with Fourier transforms. "
                  "Please make the input time evenly sampled.")

        self.tseg = self.time[-1] - self.time[0] + self.dt
        self.tstart = self.time[0] - 0.5*self.dt
        self.gti = \
            np.asarray(assign_value_if_none(gti,
                                            [[self.tstart,
                                              self.tstart + self.tseg]]))
        check_gtis(self.gti)
Пример #49
0
    def __init__(self, data, dt=None, band_interest=None,
                 ref_band_interest=None, std=None):

        self.dt = dt
        self.std = std

        # check whether data is an EventList object:
        if isinstance(data, EventList):
            data = np.vstack([data.time, data.energy]).T

        # check whether the data contains a list of Lightcurve objects
        if isinstance(data[0], Lightcurve):
            self.use_lc = True
            self.lcs = data
        else:
            self.use_lc = False

        # if band_interest is None, extract the energy bins and make an array
        # with the lower and upper bounds of the energy bins
        if not band_interest:
            if not self.use_lc:
                self._create_band_interest(data)
            else:
                self.band_interest = np.vstack([np.arange(len(data)),
                                                np.arange(1, len(data)+1, 1)]).T
        else:
            if np.size(band_interest) < 2:
                raise ValueError('band_interest must contain at least 2 values '
                                 '(minimum and maximum values for each band) '
                                 'and be a 2D array!')

            self.band_interest = np.atleast_2d(band_interest)

        if self.use_lc is False and not dt:
            raise ValueError("If the input data is event data, the dt keyword "
                             "must be set and supply a time resolution for "
                             "creating light curves!")

        # if we don't have light curves already, make them:
        if not self.use_lc:
            if not np.all(np.diff(data, axis=0).T[0] >= 0):
                utils.simon("The event list must be sorted with respect to "
                            "times of arrivals.")
                data = data[data[:, 0].argsort()]

            self.lcs = self._make_lightcurves(data)

        # check whether band of interest contains a Lightcurve object:
        if np.size(ref_band_interest) == 1  or isinstance(ref_band_interest,
                                                          Lightcurve):
            if isinstance(ref_band_interest, Lightcurve):
                self.ref_band_lcs = ref_band_interest
            # ref_band_interest must either be a Lightcurve, or must have
            # multiple entries

            elif ref_band_interest is None:
                if self.use_lc:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_lightcurves(ref_band_interest)
                else:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_event_data(data)
            else:
                raise ValueError("ref_band_interest must contain either "
                                 "a Lightcurve object, a list of Lightcurve "
                                 "objects or a tuple of length 2.")
        else:
            # check whether ref_band_interest is a list of light curves
            if isinstance(ref_band_interest[0], Lightcurve):
                self.ref_band_lcs = ref_band_interest
                assert len(ref_band_interest) == len(self.lcs), "The list of " \
                                                                "reference light " \
                                                                "curves must have " \
                                                                "the same length as " \
                                                                "the list of light curves" \
                                                                "of interest."
            # if not, it must be a tuple, so we're going to make a list of light
            # curves
            else:
                if self.use_lc:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_lightcurves(bounds=
                                                                    ref_band_interest)
                else:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_event_data(data)

        self._construct_covar()
Пример #50
0
    def join(self, other):
        """
        Join two lightcurves into a single object.

        The new Lightcurve object will contain time stamps from both the
        objects. The count per bin in the resulting object will be the
        individual count per bin, or the average in case of overlapping
        time arrays of both lightcurve objects.

        Good Time intervals are also joined.

        Note : Time array of both lightcurves should not overlap each other.

        Parameters
        ----------
        other : Lightcurve object
            The other Lightcurve object which is supposed to be joined with.

        Returns
        -------
        lc_new : Lightcurve object
            The resulting lightcurve object.

        Example
        -------
        >>> time1 = [5, 10, 15]
        >>> count1 = [300, 100, 400]
        >>> time2 = [20, 25, 30]
        >>> count2 = [600, 1200, 800]
        >>> lc1 = Lightcurve(time1, count1)
        >>> lc2 = Lightcurve(time2, count2)
        >>> lc = lc1.join(lc2)
        >>> lc.time
        array([ 5, 10, 15, 20, 25, 30])
        >>> lc.counts
        array([ 300,  100,  400,  600, 1200,  800])
        """
        if self.dt != other.dt:
            utils.simon("The two light curves have different bin widths.")

        if self.tstart <= other.tstart:
            new_time = np.unique(np.concatenate([self.time, other.time]))
        else:
            new_time = np.unique(np.concatenate([other.time, self.time]))

        if len(new_time) != len(self.time) + len(other.time):
            utils.simon("The two light curves have overlapping time ranges. "
                        "In the common time range, the resulting count will "
                        "be the average of the counts in the two light "
                        "curves. If you wish to sum, use `lc_sum = lc1 + "
                        "lc2`.")

        new_counts = []

        # For every time stamp, get the individual time counts and add them.
        for time in new_time:
            try:
                count1 = self.counts[np.where(self.time == time)[0][0]]
            except IndexError:
                count1 = None

            try:
                count2 = other.counts[np.where(other.time == time)[0][0]]
            except IndexError:
                count2 = None

            if count1 is not None:
                if count2 is not None:
                    # Average the overlapping counts
                    new_counts.append((count1 + count2) / 2)
                else:
                    new_counts.append(count1)
            else:
                new_counts.append(count2)

        new_counts = np.asarray(new_counts)

        gti = join_gtis(self.gti, other.gti)

        lc_new = Lightcurve(new_time, new_counts, gti=gti)

        return lc_new
Пример #51
0
    def plot(self,
             labels=None,
             axis=None,
             title=None,
             marker='-',
             save=False,
             filename=None,
             ax=None):
        """
        Plot the :class:`Crosscorrelation` as function using Matplotlib.
        Plot the Crosscorrelation object on a graph ``self.time_lags`` on x-axis and
        ``self.corr`` on y-axis

        Parameters
        ----------
        labels : iterable, default ``None``
            A list of tuple with ``xlabel`` and ``ylabel`` as strings.

        axis : list, tuple, string, default ``None``
            Parameter to set axis properties of ``matplotlib`` figure. For example
            it can be a list like ``[xmin, xmax, ymin, ymax]`` or any other
            acceptable argument for ``matplotlib.pyplot.axis()`` function.

        title : str, default ``None``
            The title of the plot.

        marker : str, default ``-``
            Line style and color of the plot. Line styles and colors are
            combined in a single format string, as in ``'bo'`` for blue
            circles. See ``matplotlib.pyplot.plot`` for more options.

        save : boolean, optional (default=False)
            If True, save the figure with specified filename.

        filename : str
            File name of the image to save. Depends on the boolean ``save``.

        ax : ``matplotlib.Axes`` object
            An axes object to fill with the cross correlation plot.
        """

        try:
            import matplotlib.pyplot as plt
        except ImportError:
            raise ImportError("Matplotlib required for plot()")

        if ax is None:
            fig, ax = plt.subplots(1, 1, figsize=(6, 4))

        ax.plot(self.time_lags, self.corr, marker)
        if labels is not None:
            try:
                ax.set_xlabel(labels[0])
                ax.set_ylabel(labels[1])
            except TypeError:
                utils.simon("``labels`` must be either a list or tuple with "
                            "x and y labels.")
                raise
            except IndexError:
                utils.simon("``labels`` must have two labels for x and y "
                            "axes.")
                # Not raising here because in case of len(labels)==1, only
                # x-axis will be labelled.

        # axis is a tuple containing formatting information
        if axis is not None:
            ax.axis(axis)

        if title is not None:
            ax.set_title(title)

        if save:
            if filename is None:
                plt.savefig('corr.pdf', format="pdf")
            else:
                plt.savefig(filename)
        else:
            plt.show(block=False)

        return ax
Пример #52
0
    def _make_crossspectrum(self, lc1, lc2):

        # make sure the inputs work!
        if not isinstance(lc1, Lightcurve):
            raise TypeError("lc1 must be a lightcurve.Lightcurve object")

        if not isinstance(lc2, Lightcurve):
            raise TypeError("lc2 must be a lightcurve.Lightcurve object")

        # Then check that GTIs make sense
        if self.gti is None:
            self.gti = cross_two_gtis(lc1.gti, lc2.gti)

        check_gtis(self.gti)

        if self.gti.shape[0] != 1:
            raise TypeError("Non-averaged Cross Spectra need "
                            "a single Good Time Interval")

        lc1 = lc1.split_by_gti()[0]
        lc2 = lc2.split_by_gti()[0]

        # total number of photons is the sum of the
        # counts in the light curve
        self.nphots1 = np.float64(np.sum(lc1.counts))
        self.nphots2 = np.float64(np.sum(lc2.counts))

        self.meancounts1 = lc1.meancounts
        self.meancounts2 = lc2.meancounts

        # the number of data points in the light curve

        if lc1.n != lc2.n:
            raise StingrayError("Light curves do not have same number "
                                "of time bins per segment.")

        if lc1.dt != lc2.dt:
            raise StingrayError("Light curves do not have "
                                "same time binning dt.")

        self.n = lc1.n

        # the frequency resolution
        self.df = 1.0 / lc1.tseg

        # the number of averaged periodograms in the final output
        # This should *always* be 1 here
        self.m = 1

        # make the actual Fourier transform and compute cross spectrum
        self.freq, self.unnorm_power = self._fourier_cross(lc1, lc2)

        # If co-spectrum is desired, normalize here. Otherwise, get raw back
        # with the imaginary part still intact.
        self.power = self._normalize_crossspectrum(self.unnorm_power, lc1.tseg)

        if lc1.err_dist.lower() != lc2.err_dist.lower():
            simon("Your lightcurves have different statistics."
                  "The errors in the Crossspectrum will be incorrect.")
        elif lc1.err_dist.lower() != "poisson":
            simon("Looks like your lightcurve statistic is not poisson."
                  "The errors in the Powerspectrum will be incorrect.")

        if self.__class__.__name__ in [
                'Powerspectrum', 'AveragedPowerspectrum'
        ]:
            self.power_err = self.power / np.sqrt(self.m)
        elif self.__class__.__name__ in [
                'Crossspectrum', 'AveragedCrossspectrum'
        ]:
            # This is clearly a wild approximation.
            simon("Errorbars on cross spectra are not thoroughly tested. "
                  "Please report any inconsistencies.")
            unnorm_power_err = np.sqrt(2) / np.sqrt(self.m)  # Leahy-like
            unnorm_power_err /= (2 / np.sqrt(self.nphots1 * self.nphots2))
            unnorm_power_err += np.zeros_like(self.power)

            self.power_err = \
                self._normalize_crossspectrum(unnorm_power_err, lc1.tseg)
        else:
            self.power_err = np.zeros(len(self.power))
Пример #53
0
    def join(self, other):
        """
        Join two lightcurves into a single object.

        The new Lightcurve object will contain time stamps from both the
        objects. The count per bin in the resulting object will be the
        individual count per bin, or the average in case of overlapping
        time arrays of both lightcurve objects.

        Good Time intervals are also joined.

        Note : Time array of both lightcurves should not overlap each other.

        Parameters
        ----------
        other : Lightcurve object
            The other Lightcurve object which is supposed to be joined with.

        Returns
        -------
        lc_new : Lightcurve object
            The resulting lightcurve object.

        Example
        -------
        >>> time1 = [5, 10, 15]
        >>> count1 = [300, 100, 400]
        >>> time2 = [20, 25, 30]
        >>> count2 = [600, 1200, 800]
        >>> lc1 = Lightcurve(time1, count1)
        >>> lc2 = Lightcurve(time2, count2)
        >>> lc = lc1.join(lc2)
        >>> lc.time
        array([ 5, 10, 15, 20, 25, 30])
        >>> lc.counts
        array([ 300,  100,  400,  600, 1200,  800])
        """
        if self.dt != other.dt:
            utils.simon("The two light curves have different bin widths.")

        if self.tstart <= other.tstart:
            new_time = np.unique(np.concatenate([self.time, other.time]))
        else:
            new_time = np.unique(np.concatenate([other.time, self.time]))

        if len(new_time) != len(self.time) + len(other.time):
            utils.simon("The two light curves have overlapping time ranges. "
                        "In the common time range, the resulting count will "
                        "be the average of the counts in the two light "
                        "curves. If you wish to sum, use `lc_sum = lc1 + "
                        "lc2`.")

        new_counts = []

        # For every time stamp, get the individual time counts and add them.
        for time in new_time:
            try:
                count1 = self.counts[np.where(self.time == time)[0][0]]
            except IndexError:
                count1 = None

            try:
                count2 = other.counts[np.where(other.time == time)[0][0]]
            except IndexError:
                count2 = None

            if count1 is not None:
                if count2 is not None:
                    # Average the overlapping counts
                    new_counts.append((count1 + count2) / 2)
                else:
                    new_counts.append(count1)
            else:
                new_counts.append(count2)

        new_counts = np.asarray(new_counts)

        gti = join_gtis(self.gti, other.gti)

        lc_new = Lightcurve(new_time, new_counts, gti=gti)

        return lc_new
Пример #54
0
    def __init__(self, data, dt=None, band_interest=None,
                 ref_band_interest=None, std=None):

        """
        Compute a covariance spectrum for the data. The input data can be
        either in event data or pre-made light curves. Event data can either
        be in the form of a numpy.ndarray with (time stamp, energy) pairs or
        a `stingray.events.EventList` object. If light curves are formed ahead
        of time, then a list of `Lightcurve` objects should be passed to the
        object, ideally one light curve for each band of interest.

        For the case where the data is input as a list of `Lightcurve` objects,
        the reference band(s) should either be (1) a single `Lightcurve` object,
        (2) a list of `Lightcurve` objects with the reference band for each band
        of interest pre-made, or (3) `None`, in which case reference bands will
        formed by combining all light curves *except* for the band of interest.

        In the case of event data, `band_interest` and `ref_band_interest` can
        be (multiple) pairs of energies, and the light curves for the bands of
        interest and reference bands will be produced dynamically.


        Parameters
        ----------
        data : {numpy.ndarray | EventList object | list of Lightcurve objects}
            `data` contains the time series data, either in the form of a
            2-D array of (time stamp, energy) pairs for event data, or as a
            list of light curves.
            Note : The event list must be in sorted order with respect to the
            times of arrivals.

        dt : float
            The time resolution of the Lightcurve formed from the energy bin.
            Only used if `data` is an event list.

        band_interest : {None, iterable of tuples}
            If None, all possible energy values will be assumed to be of
            interest, and a covariance spectrum in the highest resolution
            will be produced.
            Note;
            If the input is a list of Lightcurve objects, then the user may
            supply their energy values here, for construction of a
            reference band.

        ref_band_interest : {None, tuple, Lightcurve, list of Lightcurves}
            Defines the reference band to be used for comparison with the
            bands of interest. If None, all bands *except* the band of
            interest will be used for each band of interest, respectively.
            Alternatively, a tuple can be given for event list data, which will
            extract the reference band (always excluding the band of interest),
            or one may put in a single Lightcurve object to be used (the same
            for each band of interest) or a list of Lightcurve objects, one for
            each band of interest.

        std : float or np.array or list of numbers
            The term std is used to calculate the excess variance of a band.
            If std is set to None, default Poisson case is taken and the
            std is calculated as `mean(lc)**0.5`. In the case of a single
            float as input, the same is used as the standard deviation which
            is also used as the std. And if the std is an iterable of
            numbers, their mean is used for the same purpose.

        Attributes
        ----------
        unnorm_covar : np.ndarray
            An array of arrays with mid point band_interest and their
            covariance. It is the array-form of the dictionary `energy_covar`.
            The covariance values are unnormalized.

        covar : np.ndarray
            Normalized covariance spectrum.

        covar_error : np.ndarray
            Errors of the normalized covariance spectrum.

        References
        ----------
        [1] Wilkinson, T. and Uttley, P. (2009), Accretion disc variability
            in the hard state of black hole X-ray binaries. Monthly Notices
            of the Royal Astronomical Society, 397: 666–676.
            doi: 10.1111/j.1365-2966.2009.15008.x

        Examples
        --------
        See https://github.com/StingraySoftware/notebooks repository for
        detailed notebooks on the code.

        """

        self.dt = dt
        self.std = std

        # check whether data is an EventList object:
        if isinstance(data, EventList):
            data = np.vstack([data.time, data.energy]).T

        # check whether the data contains a list of Lightcurve objects
        if isinstance(data[0], Lightcurve):
            self.use_lc = True
            self.lcs = data
        else:
            self.use_lc = False

        # if band_interest is None, extract the energy bins and make an array
        # with the lower and upper bounds of the energy bins
        if not band_interest:
            if not self.use_lc:
                self._create_band_interest(data)
            else:
                self.band_interest = np.vstack([np.arange(len(data)),
                                                np.arange(1, len(data)+1, 1)]).T
        else:
            if np.size(band_interest) < 2:
                raise ValueError('band_interest must contain at least 2 values '
                                 '(minimum and maximum values for each band) '
                                 'and be a 2D array!')

            self.band_interest = np.atleast_2d(band_interest)

        if self.use_lc is False and not dt:
            raise ValueError("If the input data is event data, the dt keyword "
                             "must be set and supply a time resolution for "
                             "creating light curves!")

        # if we don't have light curves already, make them:
        if not self.use_lc:
            if not np.all(np.diff(data, axis=0).T[0] >= 0):
                utils.simon("The event list must be sorted with respect to "
                            "times of arrivals.")
                data = data[data[:, 0].argsort()]

            self.lcs = self._make_lightcurves(data)

        # check whether band of interest contains a Lightcurve object:
        if np.size(ref_band_interest) == 1  or isinstance(ref_band_interest,
                                                          Lightcurve):
            if isinstance(ref_band_interest, Lightcurve):
                self.ref_band_lcs = ref_band_interest
            # ref_band_interest must either be a Lightcurve, or must have
            # multiple entries

            elif ref_band_interest is None:
                if self.use_lc:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_lightcurves(ref_band_interest)
                else:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_event_data(data)
            else:
                raise ValueError("ref_band_interest must contain either "
                                 "a Lightcurve object, a list of Lightcurve "
                                 "objects or a tuple of length 2.")
        else:
            # check whether ref_band_interest is a list of light curves
            if isinstance(ref_band_interest[0], Lightcurve):
                self.ref_band_lcs = ref_band_interest
                assert len(ref_band_interest) == len(self.lcs), "The list of " \
                                                                "reference light " \
                                                                "curves must have " \
                                                                "the same length as " \
                                                                "the list of light curves" \
                                                                "of interest."
            # if not, it must be a tuple, so we're going to make a list of light
            # curves
            else:
                if self.use_lc:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_lightcurves(bounds=
                                                                    ref_band_interest)
                else:
                    self.ref_band_lcs = \
                        self._make_reference_bands_from_event_data(data)

        self._construct_covar()
Пример #55
0
    def __init__(self, time, counts, input_counts=True):
        """
        Make a light curve object from an array of time stamps and an
        array of counts.

        Parameters
        ----------
        time: iterable
            A list or array of time stamps for a light curve

        counts: iterable, optional, default None
            A list or array of the counts in each bin corresponding to the
            bins defined in `time` (note: **not** the count rate, i.e.
            counts/second, but the counts/bin).

        input_counts: bool, optional, default True
            If True, the code assumes that the input data in 'counts'
            is in units of counts/bin. If False, it assumes the data
            in 'counts' is in counts/second.

        Attributes
        ----------
        time: numpy.ndarray
            The array of midpoints of time bins

        counts: numpy.ndarray
            The counts per bin corresponding to the bins in `time`.

        countrate: numpy.ndarray
            The counts per second in each of the bins defined in `time`.

        ncounts: int
            The number of data points in the light curve.

        dt: float
            The time resolution of the light curve.

        tseg: float
            The total duration of the light curve.

        tstart: float
            The start time of the light curve.

        """

        assert np.all(np.isfinite(time)), "There are inf or NaN values in " \
                                          "your time array!"

        assert np.all(np.isfinite(counts)), "There are inf or NaN values in " \
                                            "your counts array!"

        assert len(time) == len(counts), "time are counts array are not " \
                                         "of the same length!"

        assert len(time) > 1, "A single or no data points can not create " \
                              "a lightcurve!"

        self.time = np.asarray(time)
        self.dt = time[1] - time[0]

        if input_counts:
            self.counts = np.asarray(counts)
            self.countrate = self.counts / self.dt
        else:
            self.countrate = np.asarray(counts)
            self.counts = self.countrate * self.dt

        self.ncounts = self.counts.shape[0]

        # Issue a warning if the input time iterable isn't regularly spaced,
        # i.e. the bin sizes aren't equal throughout.
        dt_array = np.diff(self.time)
        if not (np.allclose(dt_array, np.repeat(self.dt, dt_array.shape[0]))):
            simon("Bin sizes in input time array aren't equal throughout! "
                  "This could cause problems with Fourier transforms. "
                  "Please make the input time evenly sampled.")

        self.tseg = self.time[-1] - self.time[0] + self.dt
        self.tstart = self.time[0] - 0.5*self.dt