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