{ "cells": [ { "cell_type": "markdown", "id": "79345bf6", "metadata": {}, "source": [ "# Fourier Processor\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 1, "id": "c43db203", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:22:59.859557Z", "iopub.status.busy": "2026-07-22T22:22:59.859312Z", "iopub.status.idle": "2026-07-22T22:22:59.865354Z", "shell.execute_reply": "2026-07-22T22:22:59.864710Z" } }, "outputs": [], "source": [ "# Import to be able to import python package from src\n", "import sys\n", "sys.path.insert(0, '../../src')" ] }, { "cell_type": "code", "execution_count": 2, "id": "0a6f35b1", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:22:59.866710Z", "iopub.status.busy": "2026-07-22T22:22:59.866625Z", "iopub.status.idle": "2026-07-22T22:23:05.782793Z", "shell.execute_reply": "2026-07-22T22:23:05.782394Z" } }, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import ontime as on" ] }, { "cell_type": "markdown", "id": "b491e9fc", "metadata": {}, "source": [ "---\n", "## Generation of a signal with two frequencies\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 3, "id": "44319a61", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:05.784737Z", "iopub.status.busy": "2026-07-22T22:23:05.784570Z", "iopub.status.idle": "2026-07-22T22:23:05.790068Z", "shell.execute_reply": "2026-07-22T22:23:05.789629Z" } }, "outputs": [], "source": [ "n = 512\n", "index = pd.date_range('2022-01-01', periods=n, freq='h')\n", "t = np.arange(n)\n", "\n", "slow = np.sin(2 * np.pi * t / 48)\n", "fast = np.sin(2 * np.pi * t / 6) * (t >= n // 2)\n", "\n", "ts = on.TimeSeries.from_times_and_values(index, slow + fast)" ] }, { "cell_type": "code", "execution_count": 4, "id": "5bdaee4f", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:05.791623Z", "iopub.status.busy": "2026-07-22T22:23:05.791544Z", "iopub.status.idle": "2026-07-22T22:23:08.776986Z", "shell.execute_reply": "2026-07-22T22:23:08.776505Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "" ], "text/plain": [ "alt.LayerChart(...)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ts.plot()" ] }, { "cell_type": "markdown", "id": "ad26ba60", "metadata": {}, "source": [ "---\n", "## Apply the FFT on sliding windows\n", "\n", "The parameters are:\n", "\n", "- `window_size`: number of samples per FFT window\n", "- `step_size`: number of samples the window slides by\n", "- `n_bins`: number of frequency ranges in the output\n", "- `frequency_cap`: optional `(min, max)` tuple to restrict the frequency range\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 5, "id": "66fe2beb", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:08.778484Z", "iopub.status.busy": "2026-07-22T22:23:08.778387Z", "iopub.status.idle": "2026-07-22T22:23:08.793198Z", "shell.execute_reply": "2026-07-22T22:23:08.792821Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<TimeSeries (DataArray) (time: 5, component: 8, sample: 1)> Size: 320B\n",
       "array([[[0.22070334],\n",
       "        [0.03416374],\n",
       "        [0.01765641],\n",
       "        [0.01251935],\n",
       "        [0.01005724],\n",
       "        [0.00870652],\n",
       "        [0.00795725],\n",
       "        [0.00759005]],\n",
       "\n",
       "       [[0.22255655],\n",
       "        [0.02869736],\n",
       "        [0.01428615],\n",
       "        [0.01003409],\n",
       "        [0.0080311 ],\n",
       "        [0.00694045],\n",
       "        [0.00633767],\n",
       "        [0.00604279]],\n",
       "\n",
       "       [[0.18277584],\n",
       "        [0.05702162],\n",
       "...\n",
       "        [0.01428417],\n",
       "        [0.01363151]],\n",
       "\n",
       "       [[0.22070334],\n",
       "        [0.03416374],\n",
       "        [0.01765641],\n",
       "        [0.01251935],\n",
       "        [0.01005724],\n",
       "        [0.00870652],\n",
       "        [0.00795725],\n",
       "        [0.00759005]],\n",
       "\n",
       "       [[0.22255655],\n",
       "        [0.02869736],\n",
       "        [0.01428615],\n",
       "        [0.01003409],\n",
       "        [0.0080311 ],\n",
       "        [0.00694045],\n",
       "        [0.00633767],\n",
       "        [0.00604279]]])\n",
       "Coordinates:\n",
       "  * time       (time) datetime64[ns] 40B 2022-01-03T15:00:00 ... 2022-01-04T2...\n",
       "  * component  (component) object 64B 'freq_0.0000_0.0625' ... 'freq_0.4375_0...\n",
       "Dimensions without coordinates: sample\n",
       "Attributes:\n",
       "    static_covariates:  None\n",
       "    hierarchy:          None
" ], "text/plain": [ " Size: 320B\n", "array([[[0.22070334],\n", " [0.03416374],\n", " [0.01765641],\n", " [0.01251935],\n", " [0.01005724],\n", " [0.00870652],\n", " [0.00795725],\n", " [0.00759005]],\n", "\n", " [[0.22255655],\n", " [0.02869736],\n", " [0.01428615],\n", " [0.01003409],\n", " [0.0080311 ],\n", " [0.00694045],\n", " [0.00633767],\n", " [0.00604279]],\n", "\n", " [[0.18277584],\n", " [0.05702162],\n", "...\n", " [0.01428417],\n", " [0.01363151]],\n", "\n", " [[0.22070334],\n", " [0.03416374],\n", " [0.01765641],\n", " [0.01251935],\n", " [0.01005724],\n", " [0.00870652],\n", " [0.00795725],\n", " [0.00759005]],\n", "\n", " [[0.22255655],\n", " [0.02869736],\n", " [0.01428615],\n", " [0.01003409],\n", " [0.0080311 ],\n", " [0.00694045],\n", " [0.00633767],\n", " [0.00604279]]])\n", "Coordinates:\n", " * time (time) datetime64[ns] 40B 2022-01-03T15:00:00 ... 2022-01-04T2...\n", " * component (component) object 64B 'freq_0.0000_0.0625' ... 'freq_0.4375_0...\n", "Dimensions without coordinates: sample\n", "Attributes:\n", " static_covariates: None\n", " hierarchy: None" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fourier = on.processors.fourier(window_size=64, step_size=8, n_bins=8)\n", "spectrum_ts = fourier.process(ts)\n", "spectrum_ts.head()" ] }, { "cell_type": "markdown", "id": "ca2453fb", "metadata": {}, "source": [ "Each column is a frequency range and each row is a window, timestamped at the window's last sample." ] }, { "cell_type": "code", "execution_count": 6, "id": "3b36cf91", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:08.794432Z", "iopub.status.busy": "2026-07-22T22:23:08.794359Z", "iopub.status.idle": "2026-07-22T22:23:08.796360Z", "shell.execute_reply": "2026-07-22T22:23:08.795982Z" } }, "outputs": [ { "data": { "text/plain": [ "['freq_0.0000_0.0625',\n", " 'freq_0.0625_0.1250',\n", " 'freq_0.1250_0.1875',\n", " 'freq_0.1875_0.2500',\n", " 'freq_0.2500_0.3125',\n", " 'freq_0.3125_0.3750',\n", " 'freq_0.3750_0.4375',\n", " 'freq_0.4375_0.5000']" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spectrum_ts.components.tolist()" ] }, { "cell_type": "markdown", "id": "384aa1e1", "metadata": {}, "source": [ "---\n", "## Visualize the frequency content over time\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 7, "id": "c7e23658", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:08.797635Z", "iopub.status.busy": "2026-07-22T22:23:08.797566Z", "iopub.status.idle": "2026-07-22T22:23:08.816802Z", "shell.execute_reply": "2026-07-22T22:23:08.816454Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "" ], "text/plain": [ "alt.LayerChart(...)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spectrum_ts['freq_0.0000_0.0625'].plot()" ] }, { "cell_type": "code", "execution_count": 8, "id": "f28f58ce", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:08.818278Z", "iopub.status.busy": "2026-07-22T22:23:08.818200Z", "iopub.status.idle": "2026-07-22T22:23:08.837403Z", "shell.execute_reply": "2026-07-22T22:23:08.837049Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "
\n", "" ], "text/plain": [ "alt.LayerChart(...)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "spectrum_ts['freq_0.1250_0.1875'].plot()" ] }, { "cell_type": "markdown", "id": "b3b6d98a", "metadata": {}, "source": [ "---\n", "## Restrict the frequency range\n", "\n", "With `frequency_cap`, the binning is restricted to a given frequency range. Here we zoom on the frequencies around the fast wave." ] }, { "cell_type": "code", "execution_count": 9, "id": "3a855694", "metadata": { "execution": { "iopub.execute_input": "2026-07-22T22:23:08.838874Z", "iopub.status.busy": "2026-07-22T22:23:08.838798Z", "iopub.status.idle": "2026-07-22T22:23:08.847467Z", "shell.execute_reply": "2026-07-22T22:23:08.847003Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<TimeSeries (DataArray) (time: 5, component: 4, sample: 1)> Size: 160B\n",
       "array([[[0.02237228],\n",
       "        [0.01662483],\n",
       "        [0.01334846],\n",
       "        [0.01137745]],\n",
       "\n",
       "       [[0.01826692],\n",
       "        [0.01342067],\n",
       "        [0.01071169],\n",
       "        [0.00910219]],\n",
       "\n",
       "       [[0.03891696],\n",
       "        [0.02935155],\n",
       "        [0.02374411],\n",
       "        [0.02031414]],\n",
       "\n",
       "       [[0.02237228],\n",
       "        [0.01662483],\n",
       "        [0.01334846],\n",
       "        [0.01137745]],\n",
       "\n",
       "       [[0.01826692],\n",
       "        [0.01342067],\n",
       "        [0.01071169],\n",
       "        [0.00910219]]])\n",
       "Coordinates:\n",
       "  * time       (time) datetime64[ns] 40B 2022-01-03T15:00:00 ... 2022-01-04T2...\n",
       "  * component  (component) object 32B 'freq_0.1000_0.1375' ... 'freq_0.2125_0...\n",
       "Dimensions without coordinates: sample\n",
       "Attributes:\n",
       "    static_covariates:  None\n",
       "    hierarchy:          None
" ], "text/plain": [ " Size: 160B\n", "array([[[0.02237228],\n", " [0.01662483],\n", " [0.01334846],\n", " [0.01137745]],\n", "\n", " [[0.01826692],\n", " [0.01342067],\n", " [0.01071169],\n", " [0.00910219]],\n", "\n", " [[0.03891696],\n", " [0.02935155],\n", " [0.02374411],\n", " [0.02031414]],\n", "\n", " [[0.02237228],\n", " [0.01662483],\n", " [0.01334846],\n", " [0.01137745]],\n", "\n", " [[0.01826692],\n", " [0.01342067],\n", " [0.01071169],\n", " [0.00910219]]])\n", "Coordinates:\n", " * time (time) datetime64[ns] 40B 2022-01-03T15:00:00 ... 2022-01-04T2...\n", " * component (component) object 32B 'freq_0.1000_0.1375' ... 'freq_0.2125_0...\n", "Dimensions without coordinates: sample\n", "Attributes:\n", " static_covariates: None\n", " hierarchy: None" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fourier_capped = on.processors.fourier(\n", " window_size=64, step_size=8, n_bins=4, frequency_cap=(0.1, 0.25)\n", ")\n", "capped_ts = fourier_capped.process(ts)\n", "capped_ts.head()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }