File size: 1,236 Bytes
d03866e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
"""Spectral Residual
"""
# Author: Andreas Mueller <andreas.mueller@microsoft.com>
import numpy as np

def SR(X, window_size):
    X = (X - X.min()) / (X.max() - X.min())
    X = X.ravel()
    fft = np.fft.fft(X)

    amp = np.abs(fft)
    log_amp = np.log(amp)
    phase = np.angle(fft)
    # split spectrum into bias term and symmetric frequencies
    bias, sym_freq = log_amp[:1], log_amp[1:]
    # select just the first half of the sym_freq
    freq = sym_freq[:(len(sym_freq) + 1) // 2]
    window_amp = 100

    pad_left = (window_amp - 1) // 2
    padded_freq = np.concatenate([np.tile(X[0], pad_left), freq, np.tile(X[-1], window_amp - pad_left - 1)])
    conv_amp = np.ones(window_amp) / window_amp
    ma_freq = np.convolve(padded_freq, conv_amp, 'valid')
    # construct moving average log amplitude spectrum
    ma_log_amp = np.concatenate([
        bias,
        ma_freq,
        (ma_freq[:-1] if len(sym_freq) % 2 == 1 else ma_freq)[::-1]
    ])
    assert ma_log_amp.shape[0] == log_amp.shape[0], "`ma_log_amp` size does not match `log_amp` size."
    # compute residual spectrum and transform back to time domain
    res_amp = log_amp - ma_log_amp
    sr = np.abs(np.fft.ifft(np.exp(res_amp + 1j * phase)))
    return sr