def __default_mtm_kwargs(self, n): kw = dict() kw['NW'] = self.NW kw['adaptive_weights'] = self.adaptive kw['fs'] = self.parent.x_scale**-1.0 kw['nfft'] = nextpow2(n) if self.pow2 else n kw['jackknife'] = False return kw
def lowpass_envelope(x: 'ElectrodeDataSource', n: int, lowpass: float, design_kws=None, filt_kws=None): """ Compute the lowpass envelope of the timeseries rows in array x in blockwise fashion. x : ndarray, n_chans x n_points n : int block size over which to compute Hilbert transform lowpass : float, 0 < lowpass < 1 the corner frequency of the lowpass filter in unit frequency design_kws : dict any extra filter design arguments (sampling rate will be forced to 1) filt_kws : dict any extra filter processing arguments (e.g. "filtfilt" : {T/F} ) """ return_ndarray = False if isinstance(x, np.ndarray): return_ndarray = True from ecogdata.datasource import PlainArraySource x = PlainArraySource(x) if filt_kws is None: filt_kws = dict() n = nextpow2(n) # do a little bit of overlap to avoid hilbert transform edge effects overlap = int(0.1 * n) for block, sl in x.iter_blocks(block_length=n, overlap=overlap, return_slice=True, partial_block=True): chan_slice, time_slice = sl start = time_slice.start stop = time_slice.stop new_sl = (chan_slice, slice(start + overlap, stop - overlap)) sl_length = stop - start - 2 * overlap block_a = signal.hilbert(block, N=n) x[new_sl] = np.abs(block_a[..., overlap:overlap + sl_length]) if lowpass == 1: return x if design_kws is None: design_kws = dict() if filt_kws is None: filt_kws = dict() filt_kws.setdefault('filtfilt', True) # hope for the best design_kws['Fs'] = 1.0 design_kws['hi'] = lowpass design_kws.setdefault('ord', 5) x = x.filter_array(inplace=True, design_kwargs=design_kws, filt_kwargs=filt_kws) return x.data_buffer if return_ndarray else x
def __default_mtm_kwargs(self, strip): kw = dict() kw['NW'] = self.NW kw['adaptive_weights'] = self.adaptive Fs = self.parent.x_scale**-1.0 kw['Fs'] = Fs kw['jackknife'] = False kw['detrend'] = 'linear' if self.detrend else '' lag = int(Fs * self.lag / 1000.) if strip is None: strip = int(Fs * self.strip / 1000.) kw['nfft'] = nextpow2(strip) kw['pl'] = 1.0 - float(lag) / strip if self.high_res: kw['samp_factor'] = self.over_samp kw['low_bias'] = 0.95 return kw, strip
def hilbert_envelope_filter(x, y): itr = block_itr_factory(x, axis=1, out=y) for xc in tqdm(itr, desc='Hilbert Transform', leave=True, total=itr.n_blocks): # xc = x[sl] n = xc.shape[1] nfft = nextpow2(n) # if n is closer to the previous power of 2, then split this block into two computations if (nfft - n) > (n - nfft / 2): n1 = int(n / 2) nfft = int(nfft / 2) y1 = hilbert(xc[..., :n1], N=nfft)[..., :n1] y2 = hilbert(xc[..., n1:], N=nfft)[..., :n - n1] # y[sl] = np.hstack((np.abs(y1), np.abs(y2))) itr.write_out(np.hstack((np.abs(y1), np.abs(y2)))) else: y1 = hilbert(xc, N=nfft)[..., :n] # y[sl] = np.abs(y1) itr.write_out(np.abs(y1))
def overlap_add(x, w, progress=False): M = len(w) N = 0 nfft = nextpow2(M) / 2 while N < M: nfft *= 2 # segment size > M and long enough not to cause circular convolution N = nfft - M + 1 blocks = BlockedSignal(x, N, axis=-1) xf = np.zeros_like(x) #blocks_f = BlockedSignal(xf, nfft, overlap=1.0 - float(N)/nfft, axis=-1) blocks_f = BlockedSignal(xf, nfft, overlap=M-1, axis=-1) nb1 = len(blocks) nb2 = len(blocks_f) #print M, N, nfft, nb1, nb2 # centered kernel (does not work!) ## pre_z = (nfft-M) // 2 ## post_z = nfft - M - pre_z ## kern = fft( np.r_[ np.zeros(pre_z), w, np.zeros(post_z) ] ) # not-centered kernel kern = fft(np.r_[w, np.zeros(nfft-M)]) # centered padding (does not work!) ## pre_z = (nfft-N) // 2 ## post_z = nfft - N - pre_z ## z1 = np.zeros( x.shape[:-1] + (pre_z,) ) ## z2 = np.zeros( x.shape[:-1] + (post_z,) ) # not-centered padding z = np.zeros( x.shape[:-1] + (nfft-N,) ) # if progress: # itr = tqdm(range(nb1)) # else: itr = range(nb1) for n in itr: b = blocks.block(n) if b.shape[-1] == N: b = fft(np.concatenate( (b, z), axis=-1 ), axis=-1) #b = fft(np.concatenate( (z1, b, z2), axis=-1 ), axis=-1) else: # zero pad final segment z_ = np.zeros( b.shape[:-1] + (nfft-b.shape[-1],) ) b = fft(np.concatenate( (b, z_), axis=-1 ), axis=-1) b = ifft(b * kern).real if n < nb2: bf = blocks_f.block(n) b_sl = [slice(None)] * bf.ndim b_sl[-1] = slice(0, bf.shape[-1]) else: # keep the final filtered block and advance the overlap-index # within it b_sl = [slice(None)] * bf.ndim b_sl[-1] = slice(0, bf.shape[-1]) bf[:] += b[b_sl] #bf[:] += b[..., :bf.shape[-1]] if nb2 > nb1: print('more output blocks than input blocks??') return xf
def _ep_trigger_avg(x, trig_code, pre=0, post=0, iqr_thresh=-1, envelope=False): """ Average response to 1 or more experimental conditions Arguments --------- x: data (nchan, npts) trig_code : sequence-type (2, stim) or StimulatedExperiment First row is the trigger indices, second row is a condition ID (integer). Condition ID -1 codes for a flagged trial to be skipped. If a StimulatedExperiment, then triggers and conditions are available from this object. pre, post : ints Number of pre- and post-stim samples in interval. post + pre > 0 default: 0 and stim-to-stim interval sum_limit : int Do partial sum up to this many terms iqr_thresh : float If set, do simple outlier detection on all groups of repeated conditions based on RMS power in the epoch interval. The iqr_thresh multiplies the width of the inter-quartile range to determine the "inlier" range of RMS power. Returns ------- avg (nchan, ncond, epoch_length) n_avg number of triggers found for each condition skipped (nskip, nchan, epoch_length) epochs that were not averaged """ x.shape = (1,) + x.shape if x.ndim == 1 else x.shape #pos_edge = trig_code[0]; conds = trig_code[1] pos_edge, conds = trigs_and_conds(trig_code) epoch_len = int(np.round(np.median(np.diff(pos_edge)))) n_cond = len(np.unique(conds)) n_pt = x.shape[1] if not (post or pre): post = epoch_len # this formula should provide consistent epoch lengths, # no matter the offset epoch_len = int(round(post + pre)) pre = int(round(pre)) post = epoch_len - pre # edit trigger list to exclude out-of-bounds epochs while pos_edge[0] - pre < 0: pos_edge = pos_edge[1:] conds = conds[1:] while pos_edge[-1] + post >= n_pt: pos_edge = pos_edge[:-1] conds = conds[:-1] avg = np.zeros((x.shape[0], n_cond, epoch_len), x.dtype) n_avg = np.zeros((x.shape[0], n_cond), 'i') for n, c in enumerate(np.unique(conds)): trials = np.where(conds == c)[0] if not len(trials): continue epochs = extract_epochs(x, pos_edge, trials, pre, post) if iqr_thresh > 0: pwr = np.sqrt(np.sum(epochs**2, axis=-1)) # analyze outlier trials per channel out_mask = ut.fenced_out( pwr, thresh=iqr_thresh, axis=1, low=False ) epochs = epochs * out_mask[:, :, None] n_avg[:, n] = np.sum(out_mask, axis=1) else: n_avg[:, n] = len(trials) if envelope: epochs = signal.hilbert( epochs, N=ut.nextpow2(epoch_len), axis=-1 ) epochs = np.abs(epochs[..., :epoch_len])**2 avg[:, c - 1, :] = np.sum(epochs, axis=1) / n_avg[:, c - 1][:, None] x.shape = [x for x in x.shape if x > 1] if envelope: np.sqrt(avg, avg) return avg, n_avg
def __call__(self, array): n = array.shape[1] nfft = nextpow2(n) xa = hilbert(array, N=nfft, axis=-1) return np.abs(xa[:, :n])