Ejemplo n.º 1
0
    def frequencies(self):
        """Get the central frequencies for the frequency bands, given the
           method of estimating the spectrum """

        self.method['Fs'] = self.method.get('Fs',self.input.sampling_rate)
        NFFT = self.method.get('NFFT',64)
        Fs = self.method.get('Fs')
        freqs = tsu.get_freqs(Fs,NFFT)
        lb_idx,ub_idx = tsu.get_bounds(freqs,self.lb,self.ub)
        
        return freqs[lb_idx:ub_idx]
Ejemplo n.º 2
0
    def frequencies(self):
        """Get the central frequencies for the frequency bands, given the
           method of estimating the spectrum """

        self.method['Fs'] = self.method.get('Fs', self.input.sampling_rate)
        NFFT = self.method.get('NFFT', 64)
        Fs = self.method.get('Fs')
        freqs = tsu.get_freqs(Fs, NFFT)
        lb_idx, ub_idx = tsu.get_bounds(freqs, self.lb, self.ub)

        return freqs[lb_idx:ub_idx]
Ejemplo n.º 3
0
    def frequencies(self):
        """Get the central frequencies for the frequency bands, given the
           method of estimating the spectrum """

        # Get the sampling rate from the seed time-series:
        self.method["Fs"] = self.method.get("Fs", self.seed.sampling_rate)
        NFFT = self.method.get("NFFT", 64)
        Fs = self.method.get("Fs")
        freqs = tsu.get_freqs(Fs, NFFT)
        lb_idx, ub_idx = tsu.get_bounds(freqs, self.lb, self.ub)

        return freqs[lb_idx:ub_idx]
Ejemplo n.º 4
0
def coherence_bavg(time_series, lb=0, ub=None, csd_method=None):
    r"""
    Compute the band-averaged coherence between the spectra of two time series.

    Input to this function is in the time domain.

    Parameters
    ----------
    time_series : float array
       An array of time series, time as the last dimension.

    lb, ub: float, optional
       The upper and lower bound on the frequency band to be used in averaging
       defaults to 1,max(f)

    csd_method: dict, optional.
       See :func:`get_spectra` documentation for details

    Returns
    -------
    c : float
       This is an upper-diagonal array, where c[i][j] is the band-averaged
       coherency between time_series[i] and time_series[j]
    """

    if csd_method is None:
        csd_method = {'this_method': 'welch'}  # The default

    f, fxy = get_spectra(time_series, csd_method)

    lb_idx, ub_idx = utils.get_bounds(f, lb, ub)

    if lb == 0:
        lb_idx = 1  # The lowest frequency band should be f0

    c = np.zeros((time_series.shape[0],
                  time_series.shape[0]))

    for i in range(time_series.shape[0]):
        for j in range(i, time_series.shape[0]):
            c[i][j] = _coherence_bavg(fxy[i][j][lb_idx:ub_idx],
                                      fxy[i][i][lb_idx:ub_idx],
                                      fxy[j][j][lb_idx:ub_idx])

    idx = tril_indices(time_series.shape[0], -1)
    c[idx[0], idx[1], ...] = c[idx[1], idx[0], ...].conj()  # Make it symmetric

    return c
Ejemplo n.º 5
0
def coherency_phase_delay(time_series, lb=0, ub=None, csd_method=None):
    """
    The temporal delay calculated from the coherency phase spectrum.

    Parameters
    ----------

    time_series: float array
       The time-series data for which the delay is calculated.

    lb, ub: float
       Frequency boundaries (in Hz), for the domain over which the delays are
       calculated. Defaults to 0-max(f)

    csd_method : dict, optional.
       See :func:`get_spectra`

    Returns
    -------
    f : float array
       The mid-frequencies for the frequency bands over which the calculation
       is done.
    p : float array
       Pairwise temporal delays between time-series (in seconds).

    """
    if csd_method is None:
        csd_method = {'this_method': 'welch'}  # The default

    f, fxy = get_spectra(time_series, csd_method)

    lb_idx, ub_idx = utils.get_bounds(f, lb, ub)

    if lb_idx == 0:
        lb_idx = 1

    p = np.zeros((time_series.shape[0], time_series.shape[0],
                  f[lb_idx:ub_idx].shape[-1]))

    for i in range(time_series.shape[0]):
        for j in range(i, time_series.shape[0]):
            p[i][j] = _coherency_phase_delay(f[lb_idx:ub_idx],
                                             fxy[i][j][lb_idx:ub_idx])
            p[j][i] = _coherency_phase_delay(
                                f[lb_idx:ub_idx],
                                fxy[i][j][lb_idx:ub_idx].conjugate())

    return f[lb_idx:ub_idx], p
Ejemplo n.º 6
0
def coherency_phase_delay(time_series, lb=0, ub=None, csd_method=None):
    """
    The temporal delay calculated from the coherency phase spectrum.

    Parameters
    ----------

    time_series: float array
       The time-series data for which the delay is calculated.

    lb, ub: float
       Frequency boundaries (in Hz), for the domain over which the delays are
       calculated. Defaults to 0-max(f)

    csd_method : dict, optional.
       See :func:`get_spectra`

    Returns
    -------
    f : float array
       The mid-frequencies for the frequency bands over which the calculation
       is done.
    p : float array
       Pairwise temporal delays between time-series (in seconds).

    """
    if csd_method is None:
        csd_method = {'this_method': 'welch'}  # The default

    f, fxy = get_spectra(time_series, csd_method)

    lb_idx, ub_idx = utils.get_bounds(f, lb, ub)

    if lb_idx == 0:
        lb_idx = 1

    p = np.zeros((time_series.shape[0], time_series.shape[0],
                  f[lb_idx:ub_idx].shape[-1]))

    for i in range(time_series.shape[0]):
        for j in range(i, time_series.shape[0]):
            p[i][j] = _coherency_phase_delay(f[lb_idx:ub_idx],
                                             fxy[i][j][lb_idx:ub_idx])
            p[j][i] = _coherency_phase_delay(
                f[lb_idx:ub_idx], fxy[i][j][lb_idx:ub_idx].conjugate())

    return f[lb_idx:ub_idx], p
Ejemplo n.º 7
0
def coherence_bavg(time_series, lb=0, ub=None, csd_method=None):
    r"""
    Compute the band-averaged coherence between the spectra of two time series.

    Input to this function is in the time domain.

    Parameters
    ----------
    time_series : float array
       An array of time series, time as the last dimension.

    lb, ub: float, optional
       The upper and lower bound on the frequency band to be used in averaging
       defaults to 1,max(f)

    csd_method: dict, optional.
       See :func:`get_spectra` documentation for details

    Returns
    -------
    c : float
       This is an upper-diagonal array, where c[i][j] is the band-averaged
       coherency between time_series[i] and time_series[j]
    """

    if csd_method is None:
        csd_method = {'this_method': 'welch'}  # The default

    f, fxy = get_spectra(time_series, csd_method)

    lb_idx, ub_idx = utils.get_bounds(f, lb, ub)

    if lb == 0:
        lb_idx = 1  # The lowest frequency band should be f0

    c = np.zeros((time_series.shape[0], time_series.shape[0]))

    for i in range(time_series.shape[0]):
        for j in range(i, time_series.shape[0]):
            c[i][j] = _coherence_bavg(fxy[i][j][lb_idx:ub_idx],
                                      fxy[i][i][lb_idx:ub_idx],
                                      fxy[j][j][lb_idx:ub_idx])

    idx = tril_indices(time_series.shape[0], -1)
    c[idx[0], idx[1], ...] = c[idx[1], idx[0], ...].conj()  # Make it symmetric

    return c
Ejemplo n.º 8
0
def coherency_bavg(time_series, lb=0, ub=None, csd_method=None):
    r"""
    Compute the band-averaged coherency between the spectra of two time series.

    Input to this function is in the time domain.

    Parameters
    ----------
    time_series: n*t float array
       an array of n different time series of length t each

    lb, ub: float, optional
       the upper and lower bound on the frequency band to be used in averaging
       defaults to 1,max(f)

    csd_method: dict, optional.
       See :func:`get_spectra` documentation for details

    Returns
    -------
    c: float array
        This is an upper-diagonal array, where c[i][j] is the band-averaged
        coherency between time_series[i] and time_series[j]

    Notes
    -----

    This is an implementation of equation (A4) of Sun(2005):

    .. math::

        \bar{Coh_{xy}} (\bar{\lambda}) =
        \frac{\left|{\sum_\lambda{\hat{f_{xy}}}}\right|^2}
        {\sum_\lambda{\hat{f_{xx}}}\cdot sum_\lambda{\hat{f_{yy}}}}

    F.T. Sun and L.M. Miller and M. D'Esposito (2005). Measuring
    temporal dynamics of functional networks using phase spectrum of fMRI
    data. Neuroimage, 28: 227-37.
    """
    if csd_method is None:
        csd_method = {'this_method': 'welch'}  # The default

    f, fxy = get_spectra(time_series, csd_method)

    lb_idx, ub_idx = utils.get_bounds(f, lb, ub)

    if lb == 0:
        lb_idx = 1  # The lowest frequency band should be f0

    c = np.zeros((time_series.shape[0], time_series.shape[0]), dtype=complex)

    for i in range(time_series.shape[0]):
        for j in range(i, time_series.shape[0]):
            c[i][j] = _coherency_bavg(fxy[i][j][lb_idx:ub_idx],
                                      fxy[i][i][lb_idx:ub_idx],
                                      fxy[j][j][lb_idx:ub_idx])

    idx = tril_indices(time_series.shape[0], -1)
    c[idx[0], idx[1], ...] = c[idx[1], idx[0], ...].conj()  # Make it symmetric

    return c
Ejemplo n.º 9
0
def cache_fft(time_series, ij, lb=0, ub=None,
                  method=None, prefer_speed_over_memory=False,
                  scale_by_freq=True):
    """compute and cache the windowed FFTs of the time_series, in such a way
    that computing the psd and csd of any combination of them can be done
    quickly.

    Parameters
    ----------

    time_series : float array
       An ndarray with time-series, where time is the last dimension

    ij: list of tuples
      Each tuple in this variable should contain a pair of
      indices of the form (i,j). The resulting cache will contain the fft of
      time-series in the rows indexed by the unique elements of the union of i
      and j

    lb,ub: float
       Define a frequency band of interest, for which the fft will be cached

    method: dict, optional
        See :func:`get_spectra` for details on how this is used. For this set
        of functions, 'this_method' has to be 'welch'


    Returns
    -------
    freqs, cache

        where: cache =
             {'FFT_slices':FFT_slices,'FFT_conj_slices':FFT_conj_slices,
             'norm_val':norm_val}

    Notes
    -----

    - For these functions, only the Welch windowed periodogram ('welch') is
      available.

    - Detrending the input is not an option here, in order to save
      time on an empty function call.

    """
    if method is None:
        method = {'this_method': 'welch'}  # The default

    this_method = method.get('this_method', 'welch')

    if this_method == 'welch':
        NFFT = method.get('NFFT', 64)
        Fs = method.get('Fs', 2 * np.pi)
        window = method.get('window', mlab.window_hanning)
        n_overlap = method.get('n_overlap', int(np.ceil(NFFT / 2.0)))
    else:
        e_s = "For cache_fft, spectral estimation method must be welch"
        raise ValueError(e_s)
    time_series = utils.zero_pad(time_series, NFFT)

    #The shape of the zero-padded version:
    n_channels, n_time_points = time_series.shape

    # get all the unique channels in time_series that we are interested in by
    # checking the ij tuples
    all_channels = set()
    for i, j in ij:
        all_channels.add(i)
        all_channels.add(j)

    # for real time_series, ignore the negative frequencies
    if np.iscomplexobj(time_series):
        n_freqs = NFFT
    else:
        n_freqs = NFFT // 2 + 1

    #Which frequencies
    freqs = utils.get_freqs(Fs, NFFT)

    #If there are bounds, limit the calculation to within that band,
    #potentially include the DC component:
    lb_idx, ub_idx = utils.get_bounds(freqs, lb, ub)

    n_freqs = ub_idx - lb_idx
    #Make the window:
    if mlab.cbook.iterable(window):
        assert(len(window) == NFFT)
        window_vals = window
    else:
        window_vals = window(np.ones(NFFT, time_series.dtype))

    #Each fft needs to be normalized by the square of the norm of the window
    #and, for consistency with newer versions of mlab.csd (which, in turn, are
    #consistent with Matlab), normalize also by the sampling rate:

    if scale_by_freq:
        #This is the normalization factor for one-sided estimation, taking into
        #account the sampling rate. This makes the PSD a density function, with
        #units of dB/Hz, so that integrating over frequencies gives you the RMS
        #(XXX this should be in the tests!).
        norm_val = (np.abs(window_vals) ** 2).sum() * (Fs / 2)

    else:
        norm_val = (np.abs(window_vals) ** 2).sum() / 2

    # cache the FFT of every windowed, detrended NFFT length segement
    # of every channel.  If prefer_speed_over_memory, cache the conjugate
    # as well

    i_times = list(range(0, n_time_points - NFFT + 1, NFFT - n_overlap))
    n_slices = len(i_times)
    FFT_slices = {}
    FFT_conj_slices = {}

    for i_channel in all_channels:
        #dbg:
        #print i_channel
        Slices = np.zeros((n_slices, n_freqs), dtype=np.complex)
        for iSlice in range(n_slices):
            thisSlice = time_series[i_channel,
                                    i_times[iSlice]:i_times[iSlice] + NFFT]

            #Windowing:
            thisSlice = window_vals * thisSlice  # No detrending
            #Derive the fft for that slice:
            Slices[iSlice, :] = (fftpack.fft(thisSlice)[lb_idx:ub_idx])

        FFT_slices[i_channel] = Slices

        if prefer_speed_over_memory:
            FFT_conj_slices[i_channel] = np.conjugate(Slices)

    cache = {'FFT_slices': FFT_slices, 'FFT_conj_slices': FFT_conj_slices,
             'norm_val': norm_val, 'Fs': Fs, 'scale_by_freq': scale_by_freq}

    return freqs, cache
Ejemplo n.º 10
0
def coherency_bavg(time_series, lb=0, ub=None, csd_method=None):
    r"""
    Compute the band-averaged coherency between the spectra of two time series.

    Input to this function is in the time domain.

    Parameters
    ----------
    time_series: n*t float array
       an array of n different time series of length t each

    lb, ub: float, optional
       the upper and lower bound on the frequency band to be used in averaging
       defaults to 1,max(f)

    csd_method: dict, optional.
       See :func:`get_spectra` documentation for details

    Returns
    -------
    c: float array
        This is an upper-diagonal array, where c[i][j] is the band-averaged
        coherency between time_series[i] and time_series[j]

    Notes
    -----

    This is an implementation of equation (A4) of Sun(2005):

    .. math::

        \bar{Coh_{xy}} (\bar{\lambda}) =
        \frac{\left|{\sum_\lambda{\hat{f_{xy}}}}\right|^2}
        {\sum_\lambda{\hat{f_{xx}}}\cdot sum_\lambda{\hat{f_{yy}}}}

    F.T. Sun and L.M. Miller and M. D'Esposito (2005). Measuring
    temporal dynamics of functional networks using phase spectrum of fMRI
    data. Neuroimage, 28: 227-37.
    """
    if csd_method is None:
        csd_method = {'this_method': 'welch'}  # The default

    f, fxy = get_spectra(time_series, csd_method)

    lb_idx, ub_idx = utils.get_bounds(f, lb, ub)

    if lb == 0:
        lb_idx = 1  # The lowest frequency band should be f0

    c = np.zeros((time_series.shape[0],
                  time_series.shape[0]), dtype=complex)

    for i in range(time_series.shape[0]):
        for j in range(i, time_series.shape[0]):
            c[i][j] = _coherency_bavg(fxy[i][j][lb_idx:ub_idx],
                                      fxy[i][i][lb_idx:ub_idx],
                                      fxy[j][j][lb_idx:ub_idx])

    idx = tril_indices(time_series.shape[0], -1)
    c[idx[0], idx[1], ...] = c[idx[1], idx[0], ...].conj()  # Make it symmetric

    return c
Ejemplo n.º 11
0
def plot_snr_diff(tseries1, tseries2, lb=0, ub=None, fig=None,
                  ts_names=['1', '2'],
                  bandwidth=None, adaptive=False, low_bias=True):

    """
    Show distributions of differences between two time-series in the
    amount of snr (freq band by freq band) and information. For example,
    for comparing two stimulus conditions

    Parameters
    ----------
    tseries1, tseries2 : nitime TimeSeries objects
       These are the time-series to compare, with each of them having the
       dims: (n_channels, n_reps, time), where n_channels1 = n_channels2

    lb,ub: float
       Lower and upper bounds on the frequency range over which to
       calculate the information rate (default to [0,Nyquist]).

    fig: matplotlib figure object
       If you want to do this on already existing figure. Otherwise, a new
       figure object will be generated.

    ts_names: list of str
       Labels for the two inputs, to be used in plotting (defaults to
       ['1','2'])

    bandwidth, adaptive, low_bias: See :func:`nta.SNRAnalyzer` for details


    Returns
    -------

    A tuple containing:

    fig: a matplotlib figure object
        This figure displays:
        1. The histogram of the information differences between the two
        time-series
        2. The frequency-dependent SNR for the two time-series

    info1, info2: float arrays
        The frequency-dependent information rates (in bits/sec)

    s_n_r1, s_n_r2: float arrays
         The frequncy-dependent signal-to-noise ratios

    """
    if fig is None:
        fig = plt.figure()
    ax_scatter = fig.add_subplot(1, 2, 1)
    ax_snr = fig.add_subplot(1, 2, 2)

    SNR1 = []
    s_n_r1 = []
    info1 = []
    SNR2 = []
    info2 = []
    s_n_r2 = []

    #If you only have one channel, make sure that everything still works by
    #adding an axis
    if len(tseries1.data.shape) < 3:
        this1 = tseries1.data[np.newaxis, :, :]
        this2 = tseries2.data[np.newaxis, :, :]
    else:
        this1 = tseries1.data
        this2 = tseries2.data

    for i in range(this1.shape[0]):
        SNR1.append(nta.SNRAnalyzer(ts.TimeSeries(this1[i],
                                    sampling_rate=tseries1.sampling_rate),
                                bandwidth=bandwidth,
                                adaptive=adaptive,
                                low_bias=low_bias))
        info1.append(SNR1[-1].mt_information)
        s_n_r1.append(SNR1[-1].mt_snr)

        SNR2.append(nta.SNRAnalyzer(ts.TimeSeries(this2[i],
                                    sampling_rate=tseries2.sampling_rate),
                                bandwidth=bandwidth,
                                adaptive=adaptive,
                                low_bias=low_bias))

        info2.append(SNR2[-1].mt_information)
        s_n_r2.append(SNR2[-1].mt_snr)

    freqs = SNR1[-1].mt_frequencies

    lb_idx, ub_idx = tsu.get_bounds(freqs, lb, ub)
    freqs = freqs[lb_idx:ub_idx]

    info1 = np.array(info1)
    info_sum1 = np.sum(info1[:, lb_idx:ub_idx], -1)
    info2 = np.array(info2)
    info_sum2 = np.sum(info2[:, lb_idx:ub_idx], -1)

    ax_scatter.scatter(info_sum1, info_sum2)
    ax_scatter.errorbar(np.mean(info_sum1), np.mean(info_sum2),
                 yerr=np.std(info_sum2),
                 xerr=np.std(info_sum1))

    plot_min = min(min(info_sum1), min(info_sum2))
    plot_max = max(max(info_sum1), max(info_sum2))
    ax_scatter.plot([plot_min, plot_max], [plot_min, plot_max], 'k--')
    ax_scatter.set_xlabel('Information %s (bits/sec)' % ts_names[0])
    ax_scatter.set_ylabel('Information %s (bits/sec)' % ts_names[1])

    snr_mean1 = np.mean(s_n_r1, 0)
    snr_mean2 = np.mean(s_n_r2, 0)

    ax_snr.plot(freqs, snr_mean1[lb_idx:ub_idx], label=ts_names[0])
    ax_snr.plot(freqs, snr_mean2[lb_idx:ub_idx], label=ts_names[1])
    ax_snr.legend()
    ax_snr.set_xlabel('Frequency (Hz)')
    ax_snr.set_ylabel('SNR')

    return fig, info1, info2, s_n_r1, s_n_r2
Ejemplo n.º 12
0
def plot_snr(tseries, lb=0, ub=None, fig=None):
    """
    Show the coherence, snr and information of an SNRAnalyzer

    Parameters
    ----------
    tseries: nitime TimeSeries object
       Multi-trial data in response to one stimulus/protocol with the dims:
       (n_channels,n_repetitions,time)

    lb,ub: float
       Lower and upper bounds on the frequency range over which to
       calculate (default to [0,Nyquist]).

    Returns
    -------

    A tuple containing:

    fig: a matplotlib figure object
        This figure displays:
        1. Coherence
        2. SNR
        3. Information
    """

    if fig is None:
        fig = plt.figure()

    ax_spectra = fig.add_subplot(1, 2, 1)
    ax_snr_info = fig.add_subplot(1, 2, 2)

    A = []
    info = []
    s_n_r = []
    coh = []
    noise_spectra = []
    signal_spectra = []
    #If you only have one channel, make sure that everything still works by
    #adding an axis
    if len(tseries.data.shape) < 3:
        this = tseries.data[np.newaxis, :, :]
    else:
        this = tseries.data

    for i in range(this.shape[0]):
        A.append(nta.SNRAnalyzer(ts.TimeSeries(this[i],
                                    sampling_rate=tseries.sampling_rate)))
        info.append(A[-1].mt_information)
        s_n_r.append(A[-1].mt_snr)
        coh.append(A[-1].mt_coherence)
        noise_spectra.append(A[-1].mt_noise_psd)
        signal_spectra.append(A[-1].mt_signal_psd)

    freqs = A[-1].mt_frequencies

    lb_idx, ub_idx = tsu.get_bounds(freqs, lb, ub)
    freqs = freqs[lb_idx:ub_idx]

    coh_mean = np.mean(coh, 0)
    snr_mean = np.mean(s_n_r, 0)
    info_mean = np.mean(info, 0)
    n_spec_mean = np.mean(noise_spectra, 0)
    s_spec_mean = np.mean(signal_spectra, 0)

    ax_spectra.plot(freqs, np.log(s_spec_mean[lb_idx:ub_idx]), label='Signal')
    ax_spectra.plot(freqs, np.log(n_spec_mean[lb_idx:ub_idx]), label='Noise')
    ax_spectra.set_xlabel('Frequency (Hz)')
    ax_spectra.set_ylabel('Spectral power (dB)')

    ax_snr_info.plot(freqs, snr_mean[lb_idx:ub_idx], label='SNR')
    ax_snr_info.plot(np.nan, np.nan, 'r', label='Info')
    ax_snr_info.set_ylabel('SNR')
    ax_snr_info.set_xlabel('Frequency (Hz)')
    ax_info = ax_snr_info.twinx()
    ax_info.plot(freqs, np.cumsum(info_mean[lb_idx:ub_idx]), 'r')
    ax_info.set_ylabel('Cumulative information rate (bits/sec)')
    return fig
Ejemplo n.º 13
0
def cache_fft(time_series,
              ij,
              lb=0,
              ub=None,
              method=None,
              prefer_speed_over_memory=False,
              scale_by_freq=True):
    """compute and cache the windowed FFTs of the time_series, in such a way
    that computing the psd and csd of any combination of them can be done
    quickly.

    Parameters
    ----------

    time_series : float array
       An ndarray with time-series, where time is the last dimension

    ij: list of tuples
      Each tuple in this variable should contain a pair of
      indices of the form (i,j). The resulting cache will contain the fft of
      time-series in the rows indexed by the unique elements of the union of i
      and j

    lb,ub: float
       Define a frequency band of interest, for which the fft will be cached

    method: dict, optional
        See :func:`get_spectra` for details on how this is used. For this set
        of functions, 'this_method' has to be 'welch'


    Returns
    -------
    freqs, cache

        where: cache =
             {'FFT_slices':FFT_slices,'FFT_conj_slices':FFT_conj_slices,
             'norm_val':norm_val}

    Notes
    -----

    - For these functions, only the Welch windowed periodogram ('welch') is
      available.

    - Detrending the input is not an option here, in order to save
      time on an empty function call.

    """
    if method is None:
        method = {'this_method': 'welch'}  # The default

    this_method = method.get('this_method', 'welch')

    if this_method == 'welch':
        NFFT = method.get('NFFT', 64)
        Fs = method.get('Fs', 2 * np.pi)
        window = method.get('window', mlab.window_hanning)
        n_overlap = method.get('n_overlap', int(np.ceil(NFFT / 2.0)))
    else:
        e_s = "For cache_fft, spectral estimation method must be welch"
        raise ValueError(e_s)
    time_series = utils.zero_pad(time_series, NFFT)

    # The shape of the zero-padded version:
    n_channels, n_time_points = time_series.shape

    # get all the unique channels in time_series that we are interested in by
    # checking the ij tuples
    all_channels = set()
    for i, j in ij:
        all_channels.add(i)
        all_channels.add(j)

    # for real time_series, ignore the negative frequencies
    if np.iscomplexobj(time_series):
        n_freqs = NFFT
    else:
        n_freqs = NFFT // 2 + 1

    # Which frequencies
    freqs = utils.get_freqs(Fs, NFFT)

    # If there are bounds, limit the calculation to within that band,
    # potentially include the DC component:
    lb_idx, ub_idx = utils.get_bounds(freqs, lb, ub)

    n_freqs = ub_idx - lb_idx
    # Make the window:
    if mlab.cbook.iterable(window):
        assert (len(window) == NFFT)
        window_vals = window
    else:
        window_vals = window(np.ones(NFFT, time_series.dtype))

    # Each fft needs to be normalized by the square of the norm of the window
    # and, for consistency with newer versions of mlab.csd (which, in turn, are
    # consistent with Matlab), normalize also by the sampling rate:

    if scale_by_freq:
        # This is the normalization factor for one-sided estimation, taking
        # into account the sampling rate. This makes the PSD a density
        # function, with units of dB/Hz, so that integrating over
        # frequencies gives you the RMS. (XXX this should be in the tests!).
        norm_val = (np.abs(window_vals)**2).sum() * (Fs / 2)

    else:
        norm_val = (np.abs(window_vals)**2).sum() / 2

    # cache the FFT of every windowed, detrended NFFT length segment
    # of every channel.  If prefer_speed_over_memory, cache the conjugate
    # as well

    i_times = list(range(0, n_time_points - NFFT + 1, NFFT - n_overlap))
    n_slices = len(i_times)
    FFT_slices = {}
    FFT_conj_slices = {}

    for i_channel in all_channels:
        Slices = np.zeros((n_slices, n_freqs), dtype=np.complex)
        for iSlice in range(n_slices):
            thisSlice = time_series[i_channel,
                                    i_times[iSlice]:i_times[iSlice] + NFFT]

            # Windowing:
            thisSlice = window_vals * thisSlice  # No detrending
            # Derive the fft for that slice:
            Slices[iSlice, :] = (fftpack.fft(thisSlice)[lb_idx:ub_idx])

        FFT_slices[i_channel] = Slices

        if prefer_speed_over_memory:
            FFT_conj_slices[i_channel] = np.conjugate(Slices)

    cache = {
        'FFT_slices': FFT_slices,
        'FFT_conj_slices': FFT_conj_slices,
        'norm_val': norm_val,
        'Fs': Fs,
        'scale_by_freq': scale_by_freq
    }

    return freqs, cache
Ejemplo n.º 14
0
                                           Fs=inp_sampling_rate,
                                           BW=None,
                                           adaptive=True,
                                           low_bias=True)
_, noise_spectra, _ = tsa.multi_taper_psd(restored - signal,
                                          Fs=inp_sampling_rate,
                                          BW=None,
                                          adaptive=True,
                                          low_bias=True)

freqs = np.linspace(0, inp_sampling_rate / 2, signal.shape[-1] / 2 + 1)

f = plt.figure()
ax = f.add_subplot(1, 2, 1)
ax_snr_info = f.add_subplot(1, 2, 2)
lb_idx, ub_idx = tsu.get_bounds(freqs, lb, ub)
freqs = freqs[lb_idx:ub_idx]
snr = signal_spectra / noise_spectra

ax.plot(freqs, np.log(signal_spectra[lb_idx:ub_idx]), label='Signal')
ax.plot(freqs, np.log(noise_spectra[lb_idx:ub_idx]), label='Noise')

ax_snr_info.plot(freqs, snr[lb_idx:ub_idx], label='SNR')
ax_snr_info.plot(np.nan, np.nan, 'r', label='Info')
ax_snr_info.set_ylabel('SNR')
ax_snr_info.set_xlabel('Frequency (Hz)')

ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Spectral power (dB)')

plt.show()
Ejemplo n.º 15
0
def plot_snr_diff(tseries1,
                  tseries2,
                  lb=0,
                  ub=None,
                  fig=None,
                  ts_names=['1', '2'],
                  bandwidth=None,
                  adaptive=False,
                  low_bias=True):
    """
    Show distributions of differences between two time-series in the
    amount of snr (freq band by freq band) and information. For example,
    for comparing two stimulus conditions

    Parameters
    ----------
    tseries1, tseries2 : nitime TimeSeries objects
       These are the time-series to compare, with each of them having the
       dims: (n_channels, n_reps, time), where n_channels1 = n_channels2

    lb,ub: float
       Lower and upper bounds on the frequency range over which to
       calculate the information rate (default to [0,Nyquist]).

    fig: matplotlib figure object
       If you want to do this on already existing figure. Otherwise, a new
       figure object will be generated.

    ts_names: list of str
       Labels for the two inputs, to be used in plotting (defaults to
       ['1','2'])

    bandwidth, adaptive, low_bias: See :func:`nta.SNRAnalyzer` for details


    Returns
    -------

    A tuple containing:

    fig: a matplotlib figure object
        This figure displays:
        1. The histogram of the information differences between the two
        time-series
        2. The frequency-dependent SNR for the two time-series

    info1, info2: float arrays
        The frequency-dependent information rates (in bits/sec)

    s_n_r1, s_n_r2: float arrays
         The frequncy-dependent signal-to-noise ratios

    """
    if fig is None:
        fig = plt.figure()
    ax_scatter = fig.add_subplot(1, 2, 1)
    ax_snr = fig.add_subplot(1, 2, 2)

    SNR1 = []
    s_n_r1 = []
    info1 = []
    SNR2 = []
    info2 = []
    s_n_r2 = []

    #If you only have one channel, make sure that everything still works by
    #adding an axis
    if len(tseries1.data.shape) < 3:
        this1 = tseries1.data[np.newaxis, :, :]
        this2 = tseries2.data[np.newaxis, :, :]
    else:
        this1 = tseries1.data
        this2 = tseries2.data

    for i in xrange(this1.shape[0]):
        SNR1.append(
            nta.SNRAnalyzer(ts.TimeSeries(
                this1[i], sampling_rate=tseries1.sampling_rate),
                            bandwidth=bandwidth,
                            adaptive=adaptive,
                            low_bias=low_bias))
        info1.append(SNR1[-1].mt_information)
        s_n_r1.append(SNR1[-1].mt_snr)

        SNR2.append(
            nta.SNRAnalyzer(ts.TimeSeries(
                this2[i], sampling_rate=tseries2.sampling_rate),
                            bandwidth=bandwidth,
                            adaptive=adaptive,
                            low_bias=low_bias))

        info2.append(SNR2[-1].mt_information)
        s_n_r2.append(SNR2[-1].mt_snr)

    freqs = SNR1[-1].mt_frequencies

    lb_idx, ub_idx = tsu.get_bounds(freqs, lb, ub)
    freqs = freqs[lb_idx:ub_idx]

    info1 = np.array(info1)
    info_sum1 = np.sum(info1[:, lb_idx:ub_idx], -1)
    info2 = np.array(info2)
    info_sum2 = np.sum(info2[:, lb_idx:ub_idx], -1)

    ax_scatter.scatter(info_sum1, info_sum2)
    ax_scatter.errorbar(np.mean(info_sum1),
                        np.mean(info_sum2),
                        yerr=np.std(info_sum2),
                        xerr=np.std(info_sum1))

    plot_min = min(min(info_sum1), min(info_sum2))
    plot_max = max(max(info_sum1), max(info_sum2))
    ax_scatter.plot([plot_min, plot_max], [plot_min, plot_max], 'k--')
    ax_scatter.set_xlabel('Information %s (bits/sec)' % ts_names[0])
    ax_scatter.set_ylabel('Information %s (bits/sec)' % ts_names[1])

    snr_mean1 = np.mean(s_n_r1, 0)
    snr_mean2 = np.mean(s_n_r2, 0)

    ax_snr.plot(freqs, snr_mean1[lb_idx:ub_idx], label=ts_names[0])
    ax_snr.plot(freqs, snr_mean2[lb_idx:ub_idx], label=ts_names[1])
    ax_snr.legend()
    ax_snr.set_xlabel('Frequency (Hz)')
    ax_snr.set_ylabel('SNR')

    return fig, info1, info2, s_n_r1, s_n_r2
Ejemplo n.º 16
0
def plot_snr(tseries, lb=0, ub=None, fig=None):
    """
    Show the coherence, snr and information of an SNRAnalyzer

    Parameters
    ----------
    tseries: nitime TimeSeries object
       Multi-trial data in response to one stimulus/protocol with the dims:
       (n_channels,n_repetitions,time)

    lb,ub: float
       Lower and upper bounds on the frequency range over which to
       calculate (default to [0,Nyquist]).

    Returns
    -------

    A tuple containing:

    fig: a matplotlib figure object
        This figure displays:
        1. Coherence
        2. SNR
        3. Information
    """

    if fig is None:
        fig = plt.figure()

    ax_spectra = fig.add_subplot(1, 2, 1)
    ax_snr_info = fig.add_subplot(1, 2, 2)

    A = []
    info = []
    s_n_r = []
    coh = []
    noise_spectra = []
    signal_spectra = []
    #If you only have one channel, make sure that everything still works by
    #adding an axis
    if len(tseries.data.shape) < 3:
        this = tseries.data[np.newaxis, :, :]
    else:
        this = tseries.data

    for i in xrange(this.shape[0]):
        A.append(
            nta.SNRAnalyzer(
                ts.TimeSeries(this[i], sampling_rate=tseries.sampling_rate)))
        info.append(A[-1].mt_information)
        s_n_r.append(A[-1].mt_snr)
        coh.append(A[-1].mt_coherence)
        noise_spectra.append(A[-1].mt_noise_psd)
        signal_spectra.append(A[-1].mt_signal_psd)

    freqs = A[-1].mt_frequencies

    lb_idx, ub_idx = tsu.get_bounds(freqs, lb, ub)
    freqs = freqs[lb_idx:ub_idx]

    coh_mean = np.mean(coh, 0)
    snr_mean = np.mean(s_n_r, 0)
    info_mean = np.mean(info, 0)
    n_spec_mean = np.mean(noise_spectra, 0)
    s_spec_mean = np.mean(signal_spectra, 0)

    ax_spectra.plot(freqs, np.log(s_spec_mean[lb_idx:ub_idx]), label='Signal')
    ax_spectra.plot(freqs, np.log(n_spec_mean[lb_idx:ub_idx]), label='Noise')
    ax_spectra.set_xlabel('Frequency (Hz)')
    ax_spectra.set_ylabel('Spectral power (dB)')

    ax_snr_info.plot(freqs, snr_mean[lb_idx:ub_idx], label='SNR')
    ax_snr_info.plot(np.nan, np.nan, 'r', label='Info')
    ax_snr_info.set_ylabel('SNR')
    ax_snr_info.set_xlabel('Frequency (Hz)')
    ax_info = ax_snr_info.twinx()
    ax_info.plot(freqs, np.cumsum(info_mean[lb_idx:ub_idx]), 'r')
    ax_info.set_ylabel('Cumulative information rate (bits/sec)')
    return fig
Ejemplo n.º 17
0
inp_sampling_rate = 1000.0
lb = 0  # Hz
ub = 1000  # Hz

_, signal_spectra, _ = tsa.multi_taper_psd(signal, Fs=inp_sampling_rate, BW=None, adaptive=True, low_bias=True)
_, noise_spectra, _ = tsa.multi_taper_psd(
    restored - signal, Fs=inp_sampling_rate, BW=None, adaptive=True, low_bias=True
)


freqs = np.linspace(0, inp_sampling_rate / 2, signal.shape[-1] / 2 + 1)

f = plt.figure()
ax = f.add_subplot(1, 2, 1)
ax_snr_info = f.add_subplot(1, 2, 2)
lb_idx, ub_idx = tsu.get_bounds(freqs, lb, ub)
freqs = freqs[lb_idx:ub_idx]
snr = signal_spectra / noise_spectra

ax.plot(freqs, np.log(signal_spectra[lb_idx:ub_idx]), label="Signal")
ax.plot(freqs, np.log(noise_spectra[lb_idx:ub_idx]), label="Noise")

ax_snr_info.plot(freqs, snr[lb_idx:ub_idx], label="SNR")
ax_snr_info.plot(np.nan, np.nan, "r", label="Info")
ax_snr_info.set_ylabel("SNR")
ax_snr_info.set_xlabel("Frequency (Hz)")

ax.set_xlabel("Frequency (Hz)")
ax.set_ylabel("Spectral power (dB)")

plt.show()