def test_mirror_spline(self): freqs = [2.0, 25.0, 40.0] sr = 1e3 dt = 1.0 / sr t = np.arange(0.0, 1.0 + dt, dt) s = np.zeros([len(t)]) #add a few sine waves up for f in freqs: s += np.sin(2 * np.pi * f * t) #identity maxima and minima mini, maxi = find_extrema(s) mini_orig = copy.copy(mini) maxi_orig = copy.copy(maxi) #extrapolate and build splines using mirroring low_spline, high_spline = create_mirrored_spline(mini, maxi, s) #evaluate splines ti = np.arange(len(t)) low_env = splev(ti, low_spline) high_env = splev(ti, high_spline) env = (high_env + low_env) / 2.0 """
def compute_emd(self, s): """ Perform the empirical mode decomposition on a signal s. """ self.imfs = list() #make a copy of the signal that will hold the residual r = copy.copy(s) stop = False while not stop: #compute the IMF from the signal if self.ensemble_num_samples == 1: imf_mean = self.compute_imf(r) if imf_mean is None: stop = True break imf_std = np.zeros_like(imf_mean) else: imf_mean, imf_std = self.compute_imf_ensemble(r) #compute the normalized hilbert transform #am,fm,phase,ifreq = self.normalized_hilbert(imf_mean) amplitude, phase = self.hilbert(imf_mean) #construct an IMF object imf = IMF() imf.imf = imf_mean imf.std = imf_std imf.amplitude = amplitude imf.phase = phase self.imfs.append(imf) #subtract the IMF off to produce a new residual r -= imf_mean #compute extrema for detecting a trend IMF maxi, mini = find_extrema(r) #compute convergence criteria if np.abs(r).sum() < self.emd_resid_tol or len( self.imfs) == self.emd_max_modes or (len(maxi) == 0 and len(mini) == 0): stop = True #append the residual as the last mode self.emd_residual = r
def normalized_hilbert(self, s): """ Perform the "Normalized" Hilbert transform (Huang 2008 sec. 3.1) on the IMF s, decomposing the signal s into AM and FM components. Returns am,fm,phase,ifreq - am is the AM component, fm is the FM component, phase is the the arccos of fm, and ifreq is the instantaneous frequency. """ x = copy.copy(s) iter = 0 converged = False while not converged: #take the absolute value of the IMF and find the extrema absx = np.abs(x) mini, maxi = find_extrema(absx) if len(mini) == 0 or len(maxi) == 0: converged = True break spline_order = 3 #reflect first and last maxima to remove edge effects for interpolation left_padding = maxi[0] right_padding = len(x) - maxi[-1] x_padded = np.zeros([len(x) + left_padding + right_padding]) x_padded[left_padding:-right_padding] = absx x_padded[0] = absx[maxi[0]] x_padded[-1] = absx[maxi[-1]] #create new array of extremas new_maxi = [i + left_padding for i in maxi] new_maxi.insert(0, 0) new_maxi.append(len(x_padded) - 1) #fit a cubic spline to the extrema of the absolute value of the signal if len(maxi) <= 3: spline_order = 1 max_spline = splrep(new_maxi, x_padded[new_maxi], k=spline_order) #use the spline to interpolate over the course of the signal t = np.arange(0, len(x_padded)) fit_index = range(left_padding, len(x_padded) - right_padding) env = splev(t[fit_index], max_spline) """ plt.figure() plt.plot(t, x_padded, 'k-') plt.plot(new_maxi, x_padded[new_maxi], 'ro-', markersize=8) plt.axis('tight') plt.suptitle('Iter %d' % iter) """ #divide by envelope x /= env #check for convergence iter += 1 if iter >= self.hilbert_max_iter: converged = True if (x.max() - 1.0) <= 1e-6: converged = True if len(mini) == 0 or len(maxi) == 0: converged = True #compute the FM and AM components fm = x am = s / fm #compute the phase phase = np.arccos(fm) #compute the instantaneous frequency ifreq = np.zeros([len(phase)]) ifreq[1:] = np.diff(phase) * self.sample_rate return am, fm, phase, ifreq
def compute_imf(self, s, plot=False): """ Compute an intrinsic mode function from a signal s using sifting. """ stop = False #make a copy of the signal imf = copy.copy(s) #find extrema for first iteration mini, maxi = find_extrema(s) if len(mini) == 0 or len(maxi) == 0: return None #keep track of extrema difference num_extrema = np.zeros( [self.sift_stoppage_S, 2]) # first column are maxima, second column are minima num_extrema[-1, :] = [len(maxi), len(mini)] iter = 0 while not stop: #set some things up for the iteration s_used = s left_padding = 0 right_padding = 0 #add an extra oscillation at the beginning and end of the signal to reduce edge effects; from Rato et. al (2008) section 3.2.2 if self.sift_remove_edge_effects: Tl = maxi[0] # index of left-hand (first) maximum tl = mini[0] # index of left-hand (first) minimum Tr = maxi[-1] # index of right hand (last) maximum tr = mini[-1] # index of right hand (last) minimum #to reduce end effects, we need to extend the signal on both sides and reflect the first and last extrema #so that interpolation works better at the edges left_padding = max(Tl, tl) right_padding = len(s) - min(Tr, tr) #pad the original signal with zeros and reflected extrema s_used = np.zeros([len(s) + left_padding + right_padding]) s_used[left_padding:-right_padding] = s #reflect the maximum on the left side imax_left = left_padding - tl s_used[imax_left] = s[Tl] #reflect the minimum on the left side imin_left = left_padding - Tl s_used[imin_left] = s[tl] #correct the indices on the right hand side so they're useful trr = len(s) - tr Trr = len(s) - Tr #reflect the maximum on the right side roffset = left_padding + len(s) imax_right = roffset + trr - 1 s_used[imax_right] = s[Tr] #reflect the minimum on the right side imin_right = roffset + Trr - 1 s_used[imin_right] = s[tr] #extend the array of maxima new_maxi = [i + left_padding for i in maxi] new_maxi.insert(0, imax_left) new_maxi.append(imax_right) maxi = new_maxi #extend the array of minima new_mini = [i + left_padding for i in mini] new_mini.insert(0, imin_left) new_mini.append(imin_right) mini = new_mini t = np.arange(0, len(s_used)) fit_index = range(left_padding, len(s_used) - right_padding) #fit minimums with cubic splines spline_order = 3 if len(mini) <= 3: spline_order = 1 min_spline = splrep(mini, s_used[mini], k=spline_order) min_fit = splev(t[fit_index], min_spline) #fit maximums with cubic splines spline_order = 3 if len(maxi) <= 3: spline_order = 1 max_spline = splrep(maxi, s_used[maxi], k=spline_order) max_fit = splev(t[fit_index], max_spline) if plot: plt.figure() plt.plot(t[fit_index], max_fit, 'r-') plt.plot(maxi, s_used[maxi], 'ro') plt.plot(left_padding, 0.0, 'kx', markersize=10.0) plt.plot(left_padding + len(s), 0.0, 'kx', markersize=10.0) plt.plot(t, s_used, 'k-') plt.plot(t[fit_index], min_fit, 'b-') plt.plot(mini, s_used[mini], 'bo') plt.suptitle('Iteration %d' % iter) #take average of max and min splines z = (max_fit + min_fit) / 2.0 #compute a factor used to dampen the subtraction of the mean spline; Rato et. al 2008, sec 3.2.3 alpha, palpha = pearsonr(imf, z) alpha = min(alpha, 1e-2) #subtract off average of the two splines d = imf - alpha * z #set the IMF to the residual for next iteration imf = d #check for IMF S-stoppage criteria mini, maxi = find_extrema(imf) num_extrema = np.roll(num_extrema, -1, axis=0) num_extrema[-1, :] = [len(mini), len(maxi)] if iter >= self.sift_stoppage_S: num_extrema_change = np.diff(num_extrema, axis=0) de = np.abs(num_extrema[-1, 0] - num_extrema[-1, 1]) if np.abs(num_extrema_change).sum() == 0 and de < 2 and np.abs( imf.mean()) < self.sift_mean_tol: stop = True if iter > self.sift_max_iter: stop = True print 'Iter %d: len(mini)=%d, len(maxi=%d), imf.mean()=%0.6f, alpha=%0.2f' % ( iter, len(mini), len(maxi), imf.mean(), alpha) #print 'num_extrema=',num_extrema iter += 1 return imf
def compute_mean_envelope(s, nsamps=1000): """ Use random sampling to compute the mean envelope of a multi-dimensional signal. Args: s (np.ndarray): an NxT matrix describing a multi-variate signal. N is the number of channels, T is the number of time points. nsamps (int): the number of N dimensional projections to use in computing the multi-variate envelope. Returns: env (np.ndarray): an NxT matrix giving the multi-dimensional envelope of s. """ N,T = s.shape #pre-allocate the mean envelope matrix mean_env = np.zeros([N, T]) #generate quasi-random points on an N-dimensional sphere #stime = time.time() R = quasirand(N, nsamps, spherical=True) #etime = time.time() - stime #print 'Elapsed time for quasirand: %d seconds' % int(etime) stime = time.time() for k in range(nsamps): #istime = time.time() r = R[:, k].squeeze() #print 'k=%d, s.shape=%s, r.shape=%s' % (k, str(s.shape), str(r.shape)) #project s onto a scalar time series using random vector #dtime = time.time() p = np.dot(s.T, r) #detime = time.time() - dtime #print '\t[%d] Time to do dot %0.6f s' % (k, detime) #print 'p.shape=',p.shape #identify minima and maxima of projection #eetime = time.time() mini_p,maxi_p = find_extrema(p) #eeetime = time.time() - eetime #print '\t[%d] time to find extrema: %0.6fs' % (k, eeetime) #for each signal dimension, fit maxima with cubic spline to produce envelope t = np.arange(T) for n in range(N): #sptime = time.time() mini = copy.copy(mini_p) maxi = copy.copy(maxi_p) if len(mini) < 4 or len(maxi) < 4: return None #extrapolate edges using mirroring lower_env_spline, upper_env_spline = create_mirrored_spline(mini, maxi, s[n, :].squeeze()) if lower_env_spline is None or upper_env_spline is None: return None #evaluate upper and lower envelopes upper_env = splev(t, upper_env_spline) lower_env = splev(t, lower_env_spline) #compute the envelope for this projected dimension env = (upper_env + lower_env) / 2.0 #update the mean envelope for this dimension in an online way delta = env - mean_env[n, :] mean_env[n, :] += delta / (k+1) #esptime = time.time() - sptime #print '\t[%d] time for spline iteration on dimension %d: %0.6fs' % (k, n, esptime) #ietime = time.time() - istime #print '\t[%d] took %0.6f seconds' % (k, ietime) etime = time.time() - stime #print '%d samples took %0.6f seconds' % (nsamps, etime) return mean_env
def get_tuning_curves(self, decomps=(('spike_rate', -1), ('full_psds', 0), ('full_psds', 1), ('full_psds', 2))): """ Get tuning curves for all sites and acoustic props, by decomposition. """ all_curves = list() all_curves_x = list() data = { 'bird': list(), 'block': list(), 'segment': list(), 'hemi': list(), 'electrode': list(), 'cell_index': list(), 'decomp': list(), 'band': list(), 'xindex': list(), 'r2': list(), 'aprop': list(), 'dist_l2a': list(), 'dist_midline': list(), 'region': list(), 'amp_slope': list(), 'center_freq': list() } edata = pd.read_csv( os.path.join('/auto/tdrive/mschachter/data', 'aggregate', 'electrode_data+dist.csv')) i = edata.bird != 'BlaBro09xxF' edata = edata[i] perf_thresh = 0.05 current_index = 0 for k, aprop in enumerate(self.acoustic_props[0]): for j, (decomp, band_index) in enumerate(decomps): i = self.df.decomp == decomp assert i.sum() > 0, 'decomp=%s' % decomp # aggregate tuning curves across sites for (bird, block, segment, hemi), gdf in self.df[i].groupby( ['bird', 'block', 'segment', 'hemi']): wkey = '%s_%s_%s_%s_%s' % (bird, block, segment, hemi, decomp) eperfs = self.encoder_perfs[wkey] tuning_curves = self.tuning_curves[wkey] tuning_curves_x = self.tuning_curves_x[wkey] acoustic_feature_index = list( self.acoustic_props[0]).index(aprop) index2electrode = self.index2electrode[wkey] cell_index2electrode = self.cell_index2electrode[wkey] if decomp == 'spike_rate': ncells, nprops, nbins = tuning_curves.shape aprop_tc = tuning_curves[:, acoustic_feature_index, :] aprop_tc_x = tuning_curves_x[:, acoustic_feature_index, :] for k in range(ncells): e = cell_index2electrode[k] ei = (edata.bird == bird) & (edata.block == block) & ( edata.hemisphere == hemi) & (edata.electrode == e) assert ei.sum() == 1 reg = clean_region(edata.region[ei].values[0]) dist_l2a = edata.dist_l2a[ei].values[0] dist_midline = edata.dist_midline[ei].values[0] if bird == 'GreBlu9508M': dist_l2a *= 4 data['bird'].append(bird) data['block'].append(block) data['segment'].append(segment) data['hemi'].append(hemi) data['decomp'].append(decomp) data['band'].append(band_index) data['r2'].append(eperfs[k]) data['xindex'].append(current_index) data['aprop'].append(aprop) data['electrode'].append(e) data['cell_index'].append(k) data['dist_l2a'].append(dist_l2a) data['dist_midline'].append(dist_midline) data['region'].append(reg) slope = 0. if aprop in [ 'maxAmp', 'meanspect', 'sal', 'skewtime' ]: # compute slope of tuning curve _tc = aprop_tc[k, :] _tc_x = aprop_tc_x[k, :] if ~np.any(np.isnan(_tc)) & ~np.any( np.isinf(_tc)) & (np.sum(np.abs(_tc)) != 0): try: slope, b = np.polyfit(_tc_x, _tc, deg=1) except ValueError: print('Problem estimating slope for aprop=%s, decomp=spike_rate, cell+index=%d, bird=%s, block=%s, segment=%s, hemi=%s' % \ (aprop, k, bird, block, segment, hemi)) slope = 0. cfreq = 0 if aprop == 'meanspect': # compute center frequency _tc = aprop_tc[k, :] _tc_x = aprop_tc_x[k, :] mini, maxi = find_extrema(_tc) if len(maxi) > 0: cfreq = _tc_x[maxi[0]] data['amp_slope'].append(slope) data['center_freq'].append(cfreq) current_index += 1 elif decomp == 'full_psds': nelectrodes, nfreqs, nprops, nbins = tuning_curves.shape aprop_tc = tuning_curves[:, band_index, acoustic_feature_index, :] aprop_tc_x = tuning_curves_x[:, band_index, acoustic_feature_index, :] for k, e in enumerate(index2electrode): ei = (edata.bird == bird) & (edata.block == block) & ( edata.hemisphere == hemi) & (edata.electrode == e) assert ei.sum() == 1 reg = clean_region(edata.region[ei].values[0]) dist_l2a = edata.dist_l2a[ei].values[0] dist_midline = edata.dist_midline[ei].values[0] if bird == 'GreBlu9508M': dist_l2a *= 4 data['bird'].append(bird) data['block'].append(block) data['segment'].append(segment) data['hemi'].append(hemi) data['decomp'].append(decomp) data['band'].append(band_index) data['r2'].append(eperfs[k, band_index]) data['xindex'].append(current_index) data['aprop'].append(aprop) data['electrode'].append(e) data['cell_index'].append(-1) data['dist_l2a'].append(dist_l2a) data['dist_midline'].append(dist_midline) data['region'].append(reg) slope = 0. if aprop in [ 'maxAmp', 'meanspect', 'sal', 'skewtime' ]: # compute slope of tuning curve _tc = aprop_tc[k, :] _tc_x = aprop_tc_x[k, :] if ~np.any(np.isnan(_tc)) & ~np.any( np.isinf(_tc)) & (np.sum(np.abs(_tc)) != 0): try: slope, b = np.polyfit(_tc_x, _tc, deg=1) except ValueError: print('Problem estimating slope for aprop=%s, decomp=lfp_psd, band_index=%d, cell+index=%d, bird=%s, block=%s, segment=%s, hemi=%s' % \ (aprop, band_index, k, bird, block, segment, hemi)) print(_tc) slope = 0. cfreq = 0 if aprop == 'meanspect': # compute center frequency _tc = aprop_tc[k, :] _tc_x = aprop_tc_x[k, :] mini, maxi = find_extrema(_tc) if len(maxi) > 0: cfreq = _tc_x[maxi[0]] data['amp_slope'].append(slope) data['center_freq'].append(cfreq) current_index += 1 else: continue all_curves.extend(aprop_tc) all_curves_x.extend(aprop_tc_x) all_curves = np.array(all_curves) all_curves_x = np.array(all_curves_x) df = pd.DataFrame(data) """ # get rid of bad tuning curves tc_sum = np.abs(all_curves).sum(axis=1) # good_i = (tc_sum > 0) & (all_perfs > perf_thresh) tc_diff = np.diff(all_curves, axis=1) tc_diff_max = tc_diff.max(axis=1) tc_diff_min = tc_diff.min(axis=1) tc_max = all_curves.max(axis=1) tc_min = all_curves.min(axis=1) good_i = (all_perfs > perf_thresh) & (tc_diff_max < 1.5) & (tc_diff_min > -1.5) & (tc_max < 2) & (tc_min > -2) & (tc_max > -0.5) & (tc_sum > 0) """ return all_curves_x, all_curves, df