Fourier Processor#

The Fourier processor performs a sliding-window FFT (Short-Time Fourier Transform) on a time series. It slides a window over the series, computes an FFT per window and aggregates the amplitude spectrum into frequency ranges. The result is a multivariate time series where each column is a frequency range.

[1]:
# Import to be able to import python package from src
import sys
sys.path.insert(0, '../../src')
[2]:
import pandas as pd
import numpy as np
import ontime as on

Generation of a signal with two frequencies#

We build a signal made of a slow sine wave (period of 48 samples) that is present all along, plus a fast sine wave (period of 6 samples) that only appears in the second half of the series.

[3]:
n = 512
index = pd.date_range('2022-01-01', periods=n, freq='h')
t = np.arange(n)

slow = np.sin(2 * np.pi * t / 48)
fast = np.sin(2 * np.pi * t / 6) * (t >= n // 2)

ts = on.TimeSeries.from_times_and_values(index, slow + fast)
[4]:
ts.plot()
[4]:

Apply the FFT on sliding windows#

The parameters are:

  • window_size: number of samples per FFT window

  • step_size: number of samples the window slides by

  • n_bins: number of frequency ranges in the output

  • frequency_cap: optional (min, max) tuple to restrict the frequency range

Frequencies are expressed in cycles per sample, from 0 to 0.5 (the Nyquist frequency). Here, the slow wave has a frequency of 1/48 ≈ 0.02 and the fast wave 1/6 ≈ 0.17 cycles per sample.

[5]:
fourier = on.processors.fourier(window_size=64, step_size=8, n_bins=8)
spectrum_ts = fourier.process(ts)
spectrum_ts.head()
[5]:
<TimeSeries (DataArray) (time: 5, component: 8, sample: 1)> Size: 320B
array([[[0.22070334],
        [0.03416374],
        [0.01765641],
        [0.01251935],
        [0.01005724],
        [0.00870652],
        [0.00795725],
        [0.00759005]],

       [[0.22255655],
        [0.02869736],
        [0.01428615],
        [0.01003409],
        [0.0080311 ],
        [0.00694045],
        [0.00633767],
        [0.00604279]],

       [[0.18277584],
        [0.05702162],
...
        [0.01428417],
        [0.01363151]],

       [[0.22070334],
        [0.03416374],
        [0.01765641],
        [0.01251935],
        [0.01005724],
        [0.00870652],
        [0.00795725],
        [0.00759005]],

       [[0.22255655],
        [0.02869736],
        [0.01428615],
        [0.01003409],
        [0.0080311 ],
        [0.00694045],
        [0.00633767],
        [0.00604279]]])
Coordinates:
  * time       (time) datetime64[ns] 40B 2022-01-03T15:00:00 ... 2022-01-04T2...
  * component  (component) object 64B 'freq_0.0000_0.0625' ... 'freq_0.4375_0...
Dimensions without coordinates: sample
Attributes:
    static_covariates:  None
    hierarchy:          None

Each column is a frequency range and each row is a window, timestamped at the window’s last sample.

[6]:
spectrum_ts.components.tolist()
[6]:
['freq_0.0000_0.0625',
 'freq_0.0625_0.1250',
 'freq_0.1250_0.1875',
 'freq_0.1875_0.2500',
 'freq_0.2500_0.3125',
 'freq_0.3125_0.3750',
 'freq_0.3750_0.4375',
 'freq_0.4375_0.5000']

Visualize the frequency content over time#

The first bin (lowest frequencies) is active all along, while the third bin (which contains the fast wave’s frequency) only becomes active in the second half of the series.

[7]:
spectrum_ts['freq_0.0000_0.0625'].plot()
[7]:
[8]:
spectrum_ts['freq_0.1250_0.1875'].plot()
[8]:

Restrict the frequency range#

With frequency_cap, the binning is restricted to a given frequency range. Here we zoom on the frequencies around the fast wave.

[9]:
fourier_capped = on.processors.fourier(
    window_size=64, step_size=8, n_bins=4, frequency_cap=(0.1, 0.25)
)
capped_ts = fourier_capped.process(ts)
capped_ts.head()
[9]:
<TimeSeries (DataArray) (time: 5, component: 4, sample: 1)> Size: 160B
array([[[0.02237228],
        [0.01662483],
        [0.01334846],
        [0.01137745]],

       [[0.01826692],
        [0.01342067],
        [0.01071169],
        [0.00910219]],

       [[0.03891696],
        [0.02935155],
        [0.02374411],
        [0.02031414]],

       [[0.02237228],
        [0.01662483],
        [0.01334846],
        [0.01137745]],

       [[0.01826692],
        [0.01342067],
        [0.01071169],
        [0.00910219]]])
Coordinates:
  * time       (time) datetime64[ns] 40B 2022-01-03T15:00:00 ... 2022-01-04T2...
  * component  (component) object 32B 'freq_0.1000_0.1375' ... 'freq_0.2125_0...
Dimensions without coordinates: sample
Attributes:
    static_covariates:  None
    hierarchy:          None