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
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')