def MUA_estimation(asig, highpass_freq, lowpass_freq, MUA_rate, psd_overlap,
                   fft_samples):
    time_steps, channel_num = asig.shape
    fs = asig.sampling_rate.rescale('Hz')
    # fft_samples = int(np.round((fs/highpass_freq).magnitude))
    if fft_samples is None:
        fft_samples = int(np.round((fs / highpass_freq).magnitude))
    elif fft_samples < int(np.round((fs / highpass_freq).magnitude)):
        raise InputError("Too few fft samples to estimate the frequency "\
                       + "content in the range [{} {}]Hz."\
                         . format(highpass_freq, lowpass_freq))
    # MUA_rate can only be an int fraction of the orginal sampling_rate
    if MUA_rate is None:
        MUA_rate = highpass_freq
    if MUA_rate > fs:
        raise InputError("The requested MUA rate can not be larger than "\
                       + "the inital sampling rate!")
    subsample_order = int(np.round(fs / MUA_rate))
    eff_MUA_rate = fs / subsample_order
    print("effective MUA rate = {} Hz".format(eff_MUA_rate))

    if (fs / eff_MUA_rate).magnitude > fft_samples:
        raise ValueError("The given MUA_rate is too low to capture "\
                       + "the MUA estimate of all the signal with "\
                       + "sample size {}. ".format(fft_samples)\
                       + "Either increase the MUA_rate "\
                       + "or increase fft_samples.")

    subsample_idx = np.arange(0, len(asig.times), subsample_order)

    MUA_signal = np.zeros((len(subsample_idx), channel_num))

    for i, idx in enumerate(subsample_idx):
        start_idx = max([0, idx - int(fft_samples / 2)])
        stop_idx = min([start_idx + fft_samples, time_steps - 1])
        freqs, psd = welch_psd(asig.time_slice(t_start=start_idx / fs,
                                               t_stop=stop_idx / fs),
                               freq_res=highpass_freq,
                               overlap=psd_overlap,
                               window='hanning',
                               detrend='constant',
                               nfft=None)
        norm = np.median(psd, axis=-1)
        high_idx = (np.abs(freqs - lowpass_freq)).argmin()
        if high_idx < len(freqs) - 1:
            high_idx += 1
        # ToDo: log !?
        MUA_signal[i] = np.squeeze(
            np.log(np.mean(psd[:, 1:high_idx], axis=-1) / norm))

    MUA_asig = asig.duplicate_with_new_data(MUA_signal)
    MUA_asig.array_annotations = asig.array_annotations
    MUA_asig.sampling_rate = eff_MUA_rate
    MUA_asig.annotate(freq_band=[highpass_freq, lowpass_freq],
                      psd_freq_res=highpass_freq,
                      psd_overlap=psd_overlap,
                      psd_fs=fs)
    return MUA_asig
    def generate_prediction(self, model, **kwargs):
        psd = self.get_prediction(model)
        if psd is None:
            if kwargs:
                self.params.update(kwargs)

            spiketrains = model.produce_spiketrains(**self.params)

            self._set_default_param('binsze', 10 * pq.ms)
            self._set_default_param('num_seg', None)
            self._set_default_param('len_seg', None)
            self._set_default_param('freq_res', 1.)
            self._set_default_param('overlap', 0.5)
            self._set_default_param('fs', 100)
            self._set_default_param('window', 'hanning')
            self._set_default_param('nfft', None)
            self._set_default_param('detrend', 'constant')
            self._set_default_param('return_onesided', True)
            self._set_default_param('scaling', 'density')
            self._set_default_param('axis', -1)

            asignal = time_histogram(spiketrains,
                                     binsize=self.params['binsize'])
            freqs, psd = welch_psd(
                asignal,
                num_seg=self.params['num_seg'],
                len_seg=self.params['len_seg'],
                freq_res=self.params['freq_res'],
                overlap=self.params['overlap'],
                fs=self.params['fs'],
                window=self.params['window'],
                nfft=self.params['nfft'],
                detrend=self.params['detrend'],
                return_onesided=self.params['return_onesided'],
                scaling=self.params['scaling'],
                axis=self.params['axis'])
            model.psd_freqs = freqs
            # ToDo: How to quantitatively compare PSD distributions ??
            psd = np.squeeze(psd)
            self.set_prediction(model, psd)
        return psd
Example #3
0
                     help="lower bound of frequency band in Hz")
    CLI.add_argument("--lowpass_freq",
                     nargs='?',
                     type=none_or_float,
                     default='None',
                     help="upper bound of frequency band in Hz")
    CLI.add_argument("--psd_freq_res",
                     nargs='?',
                     type=float,
                     default=5,
                     help="frequency resolution of the power spectrum in Hz")
    CLI.add_argument("--psd_overlap",
                     nargs='?',
                     type=float,
                     default=0.5,
                     help="overlap parameter for Welch's algorithm [0-1]")
    args = CLI.parse_args()

    asig = load_neo(args.data, 'analogsignal')

    freqs, psd = welch_psd(asig,
                           freq_res=args.psd_freq_res * pq.Hz,
                           overlap=args.psd_overlap)

    plot_psd(freqs=freqs,
             psd=psd,
             highpass_freq=args.highpass_freq,
             lowpass_freq=args.lowpass_freq)

    save_plot(args.output)
def logMUA_estimation(asig, highpass_freq, lowpass_freq, logMUA_rate,
                      psd_overlap, fft_slice):
    time_steps, channel_num = asig.shape
    fs = asig.sampling_rate.rescale('Hz')
    if fft_slice is None:
        fft_slice = (1/highpass_freq).rescale('s')
    elif fft_slice < 1/highpass_freq:
        raise ValueError("Too few fft samples to estimate the frequency "\
                       + "content in the range [{} {}]."\
                         . format(highpass_freq, lowpass_freq))
    # logMUA_rate can only be an int fraction of the orginal sampling_rate
    if logMUA_rate is None:
        logMUA_rate = highpass_freq
    if logMUA_rate > fs:
        raise ValueError("The requested logMUA rate can not be larger than "\
                       + "the inital sampling rate!")
    subsample_order = int(fs/logMUA_rate)
    eff_logMUA_rate = fs/subsample_order
    print("effective logMUA rate = {}".format(eff_logMUA_rate))

    if 1/eff_logMUA_rate > fft_slice:
        raise ValueError("The given logMUA_rate is too low to capture "\
                       + "the logMUA estimate of all the signal with "\
                       + "sample size {}. ".format(fft_slice)\
                       + "Either increase the logMUA_rate "\
                       + "or increase fft_slice.")

    subsample_times = np.arange(asig.t_start.rescale('s'),
                                asig.t_stop.rescale('s'),
                                1/eff_logMUA_rate) * asig.t_start.units

    logMUA_signal = np.zeros((len(subsample_times), channel_num))

    for i, t in enumerate(subsample_times):
        if t < asig.t_start.rescale('s') + fft_slice/2:
            t_start = asig.t_start.rescale('s')
        elif t > asig.t_stop.rescale('s') - fft_slice/2:
            t_start = asig.t_stop.rescale('s') - fft_slice
        else:
            t_start = t - fft_slice/2

        t_stop = np.min([t_start + fft_slice,
                         asig.t_stop.rescale('s')]) *pq.s

        freqs, psd = welch_psd(asig.time_slice(t_start=t_start,
                                               t_stop=t_stop),
                               freq_res=highpass_freq,
                               overlap=psd_overlap,
                               window='hanning',
                               detrend='linear',
                               nfft=None)

        high_idx = (np.abs(freqs - lowpass_freq)).argmin()
        if not i:
            print("logMUA signal estimated in frequency range "\
                + "{:.2f} - {:.2f}.".format(freqs[1], freqs[high_idx]))

        avg_power = np.mean(psd, axis=-1)
        avg_power_in_freq_band = np.mean(psd[:,1:high_idx], axis=-1)
        logMUA_signal[i] = np.squeeze(np.log(avg_power_in_freq_band/
                                          avg_power))

    logMUA_asig = asig.duplicate_with_new_data(logMUA_signal)
    logMUA_asig.array_annotations = asig.array_annotations
    logMUA_asig.sampling_rate = eff_logMUA_rate
    logMUA_asig.annotate(freq_band = [highpass_freq, lowpass_freq],
                      psd_freq_res = highpass_freq,
                      psd_overlap = psd_overlap,
                      psd_fs = fs)
    return logMUA_asig
    CLI.add_argument("--data", nargs='?', type=str)
    CLI.add_argument("--output", nargs='?', type=str)
    CLI.add_argument("--highpass_freq", nargs='?', type=none_or_float)
    CLI.add_argument("--lowpass_freq", nargs='?', type=none_or_float)
    CLI.add_argument("--psd_freq_res", nargs='?', type=float)
    CLI.add_argument("--psd_overlap", nargs='?', type=float)

    args = CLI.parse_args()

    with neo.NixIO(args.data) as io:
        block = io.read_block()

    check_analogsignal_shape(block.segments[0].analogsignals)

    freqs, psd = welch_psd(block.segments[0].analogsignals[0],
                           freq_res=args.psd_freq_res * pq.Hz,
                           overlap=args.psd_overlap)

    sns.set(style='ticks', palette="deep", context="notebook")
    fig, ax = plt.subplots()
    # ToDo: plot also the channel-wise power spectra ?
    for channel in psd:
        ax.semilogy(freqs, channel, alpha=0.7)
    ax.semilogy(freqs,
                np.mean(psd, axis=0),
                linewidth=2,
                color='k',
                label='channel average')
    ax.set_title('Power Spectrum')
    ax.set_xlabel('frequency [Hz]')
    ax.set_ylabel('power spectral density')