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 windowstep_size: number of samples the window slides byn_bins: number of frequency ranges in the outputfrequency_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: NoneEach 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