def hilbert_transform(X, rate, filters=None, phase=None, X_fft_h=None): """ Apply bandpass filtering with Hilbert transform using a prespecified set of filters. Parameters ---------- X : ndarray (n_channels, n_time) Input data, dimensions rate : float Number of samples per second. filters : filter or list of filters (optional) One or more bandpass filters Returns ------- Xh : ndarray, complex Bandpassed analytic signal """ if not isinstance(filters, list): filters = [filters] time = X.shape[-1] freq = fftfreq(time, 1. / rate) Xh = np.zeros((len(filters),) + X.shape, dtype=np.complex) if X_fft_h is None: # Heavyside filter h = np.zeros(len(freq)) h[freq > 0] = 2. h[0] = 1. h = h[np.newaxis, :] X_fft_h = fft(X) * h if phase is not None: X_fft_h *= phase for ii, f in enumerate(filters): if f is None: Xh[ii] = ifft(X_fft_h) else: f = f / np.linalg.norm(f) Xh[ii] = ifft(X_fft_h * f) if Xh.shape[0] == 1: return Xh[0], X_fft_h return Xh, X_fft_h
def hilbert_transform(X, rate, filters=None): """ Apply bandpass filtering with Hilbert transform using a prespecified set of filters. Parameters ---------- X : ndarray (n_channels, n_time) Input data, dimensions rate : float Number of samples per second. filters : filter or list of filters (optional) One or more bandpass filters Returns ------- Xc : array Bandpassed analytical signal (dtype: complex) """ if not isinstance(filters, list): filters = [filters] n_channels, time = X.shape freq = fftfreq(time, 1. / rate) # Heavyside filter h = np.zeros(len(freq)) h[freq > 0] = 2. h[0] = 1. h = h[np.newaxis, :] Xh = np.zeros((len(filters), ) + X.shape, dtype=np.complex) X_fft_h = fft(X) * h for ii, f in enumerate(filters): if f is None: Xh[ii] = ifft(X_fft_h) else: Xh[ii] = ifft(X_fft_h * f) if Xh.shape[0] == 1: return Xh[0] return Xh
def spec(x, dt, axis=-1): """ Returns the normalized power spectrum (1 / sqrt(n) forward, 1 / sqrt(n) backwards). This is so that Perseval's rule may be used easily. Usage: [f, g] = spec(x, dt) INPUTS: x - Array of data to perform the FFT on. dt - Time element between array elements. (Scalar). Used to generate the frequency array. OUTPUTS: f - Frequency Array g - FFT array. """ n = x.shape[axis] f0 = 1/abs(n * dt) P = fp.fft(x, axis=axis)/sqrt(n) #Generate an array 1:N, subtract (N + 1) /2 if odd, (N+2)/2 if even. f = (linspace(1, n, n) - (n + (1 + (n % 2 ==0 )))/2) * f0 P = spfp.fftshift(P, axes=axis) return f, P
def wavelet(data, freqs, srate, wave_num, demean=True): ''' @title Wavelet Transformation With Phase @param data - vector of time series to be decomposed @param freqs - vector of center frequencies for decomposition @param srate - sample rate (in Hz) @param wave_num - desired number of cycles in wavelet (typically 3-20 for frequencies 2-200). @param demean - whether to de-mean data first? @details Wavelet Function for decomposing time series data into time-frequency representation (spectral decomposition) using wavelet transform. Employs Morlet wavelet method (gaussian taper sine wave) to obtain the analytic signal for specified frequencies (via convolution). Inputs: data - vector of time series to be decomposed freqs - vector of center frequencies for decomposition srate - sample rate (in Hz) wave_num - desired number of cycles in wavelet (typically 5-10). Translated from Matlab script written by Brett Foster, Stanford Memory Lab, Feb. 2015 ''' srate = round(srate); # wave_num can be an array equals to length of freqs or just two if not len(wave_num) == len(freqs): ratio = (np.log(np.max(wave_num)) - np.log(np.min(wave_num))) / (np.log(np.max(freqs)) - np.log(np.min(freqs))) wavelet_cycles = np.exp((np.log(freqs) - np.log(np.min(freqs))) * ratio + np.log(np.min(wave_num))) else: wavelet_cycles = np.array(wave_num) f_l = len(freqs) d_l = data.shape[-1] # normalize data, and fft if demean: fft_data = fft(data - np.mean(data)) else: fft_data = fft(data) # wavelet window calc - each columns of final wave is a wavelet kernel (after fft) # sts = wavelet_cycles / (2 * pi * freqs) # wavelet_wins = cbind(-3 * sts, 3 * sts) # calculate and fft wavelet kernels fft_waves = np.ndarray(shape = [f_l, d_l], dtype = np.complex128) for ii in range(f_l): fq = freqs[ii] cycles = wavelet_cycles[ii] # standard error st = cycles / (2 * np.pi * fq) # calculate window size wavelet_win = np.arange(-3 * st, 3 * st, 1/srate) # half of window length w_l_half = (len(wavelet_win) - 1) / 2 # wavelet 1: calc sinus in complex domain tmp_sine = np.exp((1j) * 2 * np.pi * fq / srate * np.arange(-w_l_half,w_l_half+1)) # Gaussian normalization part A = 1/np.sqrt(st*np.sqrt(np.pi)) # wavelet 2: calc gaussian wrappers tmp_gaus_win = A * np.exp(-np.power(wavelet_win, 2)/(2 * np.power(cycles/(2 * np.pi * fq), 2))) # wave kernel tmp_wavelet = tmp_sine * tmp_gaus_win # padding w_l = len(tmp_wavelet) n_pre = int(np.ceil(d_l / 2) - np.floor(w_l/2)) n_post = int(d_l - n_pre - w_l) wvi = np.pad(tmp_wavelet, [n_pre, n_post], 'constant', constant_values = 0) fft_wave = np.conj(fft(wvi)) fft_waves[ii, :] = fft_wave # Convolution Notice that since we don't pad zeros to data # d_l is nrows of wave_spectrum. However, if wave_spectrum is changed # we should not use d_l anymore. instead, use nrow(fft_waves) wave_len = fft_waves.shape[-1] wave_spectrum = ifft(fft_data * fft_waves) # use numpy fancy index cut_off = np.ceil(wave_len / 2) ind = np.concatenate([np.arange(cut_off, wave_len, dtype = np.int), np.arange(cut_off, dtype = np.int)]) wave_spectrum = wave_spectrum[:, ind] / np.sqrt(srate / 2) # returns amplitude and phase data phase = np.angle(wave_spectrum) power = np.power(np.real(wave_spectrum), 2) + np.power(np.imag(wave_spectrum), 2) coef = np.sqrt(power) return { 'power' : power, 'phase' : phase, 'coef' : coef }
def resample(x, num, t=None, axis=0, window=None): """ Resample `x` to `num` samples using Fourier method along the given axis. The resampled signal starts at the same value as `x` but is sampled with a spacing of ``len(x) / num * (spacing of x)``. Because a Fourier method is used, the signal is assumed to be periodic. Parameters ---------- x : array_like The data to be resampled. num : int The number of samples in the resampled signal. t : array_like, optional If `t` is given, it is assumed to be the sample positions associated with the signal data in `x`. axis : int, optional The axis of `x` that is resampled. Default is 0. window : array_like, callable, string, float, or tuple, optional Specifies the window applied to the signal in the Fourier domain. See below for details. Returns ------- resampled_x or (resampled_x, resampled_t) Either the resampled array, or, if `t` was given, a tuple containing the resampled array and the corresponding resampled positions. Notes ----- The argument `window` controls a Fourier-domain window that tapers the Fourier spectrum before zero-padding to alleviate ringing in the resampled values for sampled signals you didn't intend to be interpreted as band-limited. If `window` is a function, then it is called with a vector of inputs indicating the frequency bins (i.e. fftfreq(x.shape[axis]) ). If `window` is an array of the same length as `x.shape[axis]` it is assumed to be the window to be applied directly in the Fourier domain (with dc and low-frequency first). For any other type of `window`, the function `scipy.signal.get_window` is called to generate the window. The first sample of the returned vector is the same as the first sample of the input vector. The spacing between samples is changed from ``dx`` to ``dx * len(x) / num``. If `t` is not None, then it represents the old sample positions, and the new sample positions will be returned as well as the new samples. As noted, `resample` uses FFT transformations, which can be very slow if the number of input samples is large and prime, see `scipy.fftpack.fft`. """ x = np.asarray(x) X = fft(x, axis=axis) Nx = x.shape[axis] if window is not None: if callable(window): W = window(fftfreq(Nx)) elif isinstance(window, np.ndarray): if window.shape != (Nx, ): raise ValueError('window must have the same length as data') W = window else: W = ifftshift(get_window(window, Nx)) newshape = [1] * x.ndim newshape[axis] = len(W) W.shape = newshape X = X * W sl = [slice(None)] * len(x.shape) newshape = list(x.shape) newshape[axis] = num N = int(np.minimum(num, Nx)) Y = np.zeros(newshape, 'D') sl[axis] = slice(0, (N + 1) // 2) Y[sl] = X[sl] sl[axis] = slice(-(N - 1) // 2, None) Y[sl] = X[sl] y = ifft(Y, axis=axis) * (float(num) / float(Nx)) if x.dtype.char not in ['F', 'D']: y = y.real if t is None: return y else: new_t = np.arange(0, num) * (t[1] - t[0]) * Nx / float(num) + t[0] return y, new_t