def cwt(data, wavelet, scales, precision): ''' Implementation of the Continuous Wavelet Transform Arguments: data: preprocessed snowradar signal data wavelet: the specific Wavelet to use (currently the Haar wavelet) scales: precision: precision to apply to wavelet operations (default 10) Outputs: out_coefs:(?) ''' out_coefs = np.zeros((np.size(scales), data.size)) int_psi, x = pywt.integrate_wavelet(wavelet, precision=precision) step = x[1] - x[0] x_step = (x[-1] - x[0]) + 1 j_a = [np.arange(scale * x_step) / (scale * step) for scale in scales] j_m = [ np.delete(j, np.where((j >= np.size(int_psi)))[0]) for j in j_a if np.max(j) >= np.size(int_psi) ] coef_a = [ -np.sqrt(scales[i]) * np.diff(np.convolve(data, int_psi[x.astype(np.int)][::-1])) for (i, x) in enumerate(j_m) ] out_coefs = np.asarray([ coef[int(np.floor((coef.size - data.size) / 2)):int(-np.ceil((coef.size - data.size) / 2))] for coef in coef_a ]) return out_coefs
def test_intwave_orthogonal(): w = pywt.Wavelet('db1') int_psi, x = pywt.integrate_wavelet(w, precision=12) ix = x < 0.5 # For x < 0.5, the integral is equal to x assert_allclose(int_psi[ix], x[ix]) # For x > 0.5, the integral is equal to (1 - x) # Ignore last point here, there x > 1 and something goes wrong assert_allclose(int_psi[~ix][:-1], 1 - x[~ix][:-1], atol=1e-10)
def cwt_tf(data, scales, wavelet, batch_size, sampling_period=1.): # accept array_like input; make a copy to ensure a contiguous array dt = tf.float32 dt_cplx = tf.complex64 if not isinstance(wavelet, (pywt.ContinuousWavelet, pywt.Wavelet)): wavelet = pywt.DiscreteContinuousWavelet(wavelet) if np.isscalar(scales): scales = np.array([scales]) dt_out = dt_cplx if wavelet.complex_cwt else dt out = [] precision = 10 int_psi, x = pywt.integrate_wavelet(wavelet, precision=precision) int_psi = np.conj(int_psi) if wavelet.complex_cwt else int_psi # convert int_psi, x to the same precision as the data dt_psi = np.complex64 if int_psi.dtype.kind == 'c' else np.float32 int_psi = np.asarray(int_psi, dtype=dt_psi) x = np.asarray(x, dtype=np.float32) for i, scale in enumerate(scales): step = x[1] - x[0] j = np.arange(scale * (x[-1] - x[0]) + 1) / (scale * step) j = j.astype(int) # floor if j[-1] >= int_psi.size: j = np.extract(j < int_psi.size, j) int_psi_scale = int_psi[j][::-1] int_psi_scale_n = np.zeros( (len(int_psi_scale), data.shape[-1], data.shape[-1]), dtype=int_psi_scale.dtype) for c in range(data.shape[-1]): int_psi_scale_n[:, c, c] = int_psi_scale conv = tf.nn.convolution(data, int_psi_scale_n, 1, 'SAME') coef = -tf.math.sqrt(float(scale)) * tf.experimental.numpy.diff( conv, axis=-2) if dt_out != dt_cplx: coef = tf.math.real(coef) out += [tf.cast(tf.abs(coef), dt_out)] # frequencies = pywt.scale2frequency(wavelet, scales, precision) # if np.isscalar(frequencies): # frequencies = np.array([frequencies]) # frequencies /= sampling_period return tf.transpose(tf.convert_to_tensor(out, dtype=dt_out), (1, 0, 2, 3)) #, frequencies
def Continuous_Wavelet_Transform(X, scales, sample_rate=500, wavelet=None, use_scipy_signal=True, *a, **k): ''' Unforturnately it's a very misleading terminology here to name it by Continuous Wavelet Transform. Actually, in engineering, both cwt and dwt are digital, point-by-point transform algorithums that can easily implemented on a computer. If two mathematicians talk about CWT, it really mean Continuous-WT. But here, CWT just misleading people. A cwt is a discret operation as well as dwt. The difference is how they convolve signal with wavelet. CWT will convolve signal with wavelet moveing foreward point-by-point while DWT moves window-by-window. When decomposition level grows, wavelet need to be expanded in length. CWT wavelet length will be 2, 3, 4, 5, ... and DWT will be 2, 4, 8, ... ''' # check params if np.isscalar(scales): scales = np.arange(1, scales + 1) scales = np.array(scales) assert 0 not in scales if not use_scipy_signal and wavelet not in pywt.wavelist(): wavelet = 'morl' # prepare wavelets if use_scipy_signal: wavelets = [ wavelet(min(10 * scale, X.shape[1]), scale) for scale in scales ] else: int_psi, x = pywt.integrate_wavelet(wavelet, precision=10) wavelets = [] for scale in scales: j = np.arange(scale * (x[-1] - x[0]) + 1) j = np.floor(j / scale / (x[1] - x[0])) wavelets.append(int_psi[np.int32(j[j < len(int_psi)])][::-1]) # convolve coef = np.array([[np.convolve(ch, w, mode='same') for w in wavelets] for ch in X]) if use_scipy_signal: freq = None else: coef = -np.sqrt(scales).reshape(len(scales), 1) * np.diff(coef) freq = (pywt.central_frequency(wavelet, 10) / scales * sample_rate) return coef, freq
def child_wav(wavelet, scale): """Returns an array of complex values with the child wavelet used at the given ``scale``. The ``wavelet`` argument can be either a string like 'cmor1-1.5' or a ``pywt.ContinuousWavelet`` instance """ wavelet = _wavelet_instance(wavelet) # the following code has been extracted from pywt.cwt() 1.0.2 precision = 10 int_psi, x = pywt.integrate_wavelet(wavelet, precision=precision) step = x[1] - x[0] j = np.floor(np.arange(scale * (x[-1] - x[0]) + 1) / (scale * step)) if np.max(j) >= np.size(int_psi): j = np.delete(j, np.where((j >= np.size(int_psi)))[0]) return int_psi[j.astype(np.int)]
# print the range over which the wavelet will be evaluated print("Continuous wavelet will be evaluated over the range [{}, {}]".format( wav.lower_bound, wav.upper_bound)) width = wav.upper_bound - wav.lower_bound scales = [1, 2, 3, 4, 10, 15] max_len = int(np.max(scales) * width + 1) t = np.arange(max_len) fig, axes = plt.subplots(len(scales), 2, figsize=(12, 6)) for n, scale in enumerate(scales): # The following code is adapted from the internals of cwt int_psi, x = pywt.integrate_wavelet(wav, precision=10) step = x[1] - x[0] j = np.floor(np.arange(scale * width + 1) / (scale * step)) if np.max(j) >= np.size(int_psi): j = np.delete(j, np.where((j >= np.size(int_psi)))[0]) j = j.astype(np.int_) # normalize int_psi for easier plotting int_psi /= np.abs(int_psi).max() # discrete samples of the integrated wavelet filt = int_psi[j][::-1] # The CWT consists of convolution of filt with the signal at this scale # Here we plot this discrete convolution kernel at each scale.
def test_continuous_wavelet_dtype(dtype): wavelet = pywt.ContinuousWavelet('cmor1.5-1.0', dtype) int_psi, x = pywt.integrate_wavelet(wavelet) assert int_psi.real.dtype == dtype assert x.dtype == dtype
# print the range over which the wavelet will be evaluated print("Continuous wavelet will be evaluated over the range [{}, {}]".format( wav.lower_bound, wav.upper_bound)) width = wav.upper_bound - wav.lower_bound scales = [1, 2, 3, 4, 10, 15] max_len = int(np.max(scales)*width + 1) t = np.arange(max_len) fig, axes = plt.subplots(len(scales), 2, figsize=(12, 6)) for n, scale in enumerate(scales): # The following code is adapted from the internals of cwt int_psi, x = pywt.integrate_wavelet(wav, precision=10) step = x[1] - x[0] j = np.floor( np.arange(scale * width + 1) / (scale * step)) if np.max(j) >= np.size(int_psi): j = np.delete(j, np.where((j >= np.size(int_psi)))[0]) j = j.astype(np.int) # normalize int_psi for easier plotting int_psi /= np.abs(int_psi).max() # discrete samples of the integrated wavelet filt = int_psi[j][::-1] # The CWT consists of convolution of filt with the signal at this scale # Here we plot this discrete convolution kernel at each scale.
def fastcwt(data, scales, wavelet, sampling_period=1.0, method='auto'): """ Compute the continuous wavelet transform (CWT) and has the same signature as ``pywt.cwt()`` but is faster for large signals length and scales. Parameters ---------- signal : array to compute the CWT on scales: dilatation factors for the CWT wavelet: wavelet name or pywt.ContinuousWavelet method=['auto'] | 'conv' | 'fft' for selecting the convolution method the `'auto'` keyword switch automatically to the best complexity at each scales. While the `'fft'` and `'conv'` uses `numpy.fft` and `numpy.conv` respectively. In practice the `'fft'` method is implemented by using the convolution theorem which states:: convolve(wav,sig) == ifft(fft(wav)*fft(sig)) Zero padding is adjusted to keep at bay circular convolution side effects. Example:: %time (coef1, freq1) = fastcwt(np.arange(140000), np.arange(2,200), 'cmorl1-1') => CPU times: user 12.6 s, sys: 2.2 s, total: 14.8 s => Wall time: 14.9 s %time (coef1, freq1) = pywt.cwt(np.arange(140000), np.arange(2,200), 'cmorl1-1') => CPU times: user 1min 50s, sys: 401 ms, total: 1min 51s => Wall time: 1min 51s """ # accept array_like input; make a copy to ensure a contiguous array data = np.array(data) if not isinstance(wavelet, (pywt.ContinuousWavelet, pywt.Wavelet)): wavelet = pywt.DiscreteContinuousWavelet(wavelet) if np.isscalar(scales): scales = np.array([scales]) dt_out = None # currently keep the 1.0.2 behaviour: TODO fix in/out dtype consistency if data.ndim == 1: if wavelet.complex_cwt: dt_out = complex out = np.zeros((np.size(scales), data.size), dtype=dt_out) precision = 10 int_psi, x = pywt.integrate_wavelet(wavelet, precision=precision) if method in ('auto', 'fft'): # - to be as large as the sum of data length and and maximum wavelet # support to avoid circular convolution effects # - additional padding to reach a power of 2 for CPU-optimal FFT size_pad = lambda s: 2**np.int(np.ceil(np.log2(s[0] + s[1]))) size_scale0 = size_pad( (len(data), np.take(scales, 0) * ((x[-1] - x[0]) + 1))) fft_data = None elif not method == 'conv': raise ValueError("method must be in: 'conv', 'fft' or 'auto'") for i in np.arange(np.size(scales)): step = x[1] - x[0] j = np.floor( np.arange(scales[i] * (x[-1] - x[0]) + 1) / (scales[i] * step)) if np.max(j) >= np.size(int_psi): j = np.delete(j, np.where((j >= np.size(int_psi)))[0]) int_psi_scale = int_psi[j.astype(np.int)][::-1] if method == 'conv': conv = np.convolve(data, int_psi_scale) else: size_scale = size_pad((len(data), len(int_psi_scale))) if size_scale != size_scale0: # the fft of data changes when padding size changes thus # it has to be recomputed fft_data = None size_scale0 = size_scale nops_conv = len(data) * len(int_psi_scale) nops_fft = ( 2 + (fft_data is None)) * size_scale * np.log2(size_scale) if (method == 'fft') or ((method == 'auto') and (nops_fft < nops_conv)): if fft_data is None: fft_data = np.fft.fft(data, size_scale) fft_wav = np.fft.fft(int_psi_scale, size_scale) conv = np.fft.ifft(fft_wav * fft_data) conv = conv[0:len(data) + len(int_psi_scale) - 1] else: conv = np.convolve(data, int_psi_scale) coef = -np.sqrt(scales[i]) * np.diff(conv) if not np.iscomplexobj(out): coef = np.real(coef) d = (coef.size - data.size) / 2. if d > 0: out[i, :] = coef[int(np.floor(d)):int(-np.ceil(d))] elif d == 0.: out[i, :] = coef else: raise ValueError("Selected scale of {} too small.".format( scales[i])) frequencies = pywt.scale2frequency(wavelet, scales, precision) if np.isscalar(frequencies): frequencies = np.array([frequencies]) for i in np.arange(len(frequencies)): frequencies[i] /= sampling_period return out, frequencies else: raise ValueError("Only dim == 1 supported")
The wavelet transform of a time series $y(t)$ is given by: $\begin{align} WT(y(t)) = \frac{1}{\sqrt{|\sigma|}}\int_{-\infty}^{\infty}y(t)\psi \left( \frac{t-h}{\sigma} \right)dt \end{align}$ where $\sigma$ is the scale, $h$ is the time lag/translation, and $\psi$ is the mother wavelet. From the WT, we obtaing sets of coefficients corresponding to different time lags and scales. The greater the magnitude of the coefficients, the greater overlap there is between the scaled, time-shifted mother wavelet and the time series. def rescale(arr, scale=2): n = len(arr) return np.interp(np.linspace(0, n, scale*n+1), np.arange(n), arr) fig, ax = plt.subplots(figsize=(15, 2)) wav1 = pywt.ContinuousWavelet('gaus1') int_psi1, x = pywt.integrate_wavelet(wav1) for s in range(5): ax.plot(rescale(int_psi1, scale=s), label='Scale: '+ str(s)) ax.legend() ax.set_title('Gaussian Mother Wavelet') Suppose we have a signal of repearing Gaussian waves. Applying the WT on this signal using a mother wavelet corresponding to that of a Gaussian wavelet, we can pinpoint where the time series exhibits a Gaussian-like pattern. s1 = list(scp.signal.gaussian(250, std=20)) s1 = np.tile(s1, 4) plt.subplots(figsize=(15, 2)) plt.xlim(left=0, right=1000) plt.ylabel('Value') plt.xlabel('Time (t)') plt.plot(s1) plt.show()