fsfa: feature-based slow feature analysis
About
Feature-based slow feature analysis (f-SFA) is an approach to parameter inference from a non-stationary unknown process (PINUP) comprising sliding-window computation of multivariate time series of canonical time-series features followed by dimension reduction using slow feature analysis.
Given a time series measured from a non-stationary process for which variation in dynamical properties is governed by some time-varying parameter (TVP), f-SFA is an unsupervised method for inferring the TVP directly from the time series.
This package provides a python implementation licensed under an MIT license.
Acknowledgement :
If you use this software, please read and cite this open-access article:
- 📗 Owens et al. Parameter inference from a non-stationary unknown process using statistical feature-based slow feature analysis, arXiv (In Preparation).
For details on the parameter inference from a non-stationary unknown process (PINUP) problem see:
- 📗 Owens et al. Parameter inference from a non-stationary unknown process, Chaos: An Interdisciplinary Journal of Nonlinear Science (2024).
Installation
Using pip for fsfa:
pip install fsfa
Usage
import fsfa
# dataset for analysis
X = np.random.random((10000, 2)) # (or more interesting data)
# option 1: compute a time series of time-series features
df = fsfa.feature_embedding(X, window=1000, stride=100)
# option 2: f-SFA
fsfa1 = fsfa.fSFA(X, window=1000, stride=100, n_components=1)
Example: non-stationary logistic map
Here we use the chaotic logistic map to simulate a non-stationary process:
import fsfa
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# logistic map function
def logistic_map(x, r):
return r*x*(1-x)
# time varying parameter (tvp)
T = 10**5
time = np.linspace(0, 1, T, endpoint=0)
tvp = np.sin(2*np.pi*5*time) +\
np.sin(2*np.pi*11*time) +\
np.sin(2*np.pi*13*time)
# non-stationary time series with initial condition x=0.6
X = np.zeros((T, 1), 'd')
X[0] = 0.6
for i in range(1,T):
X[i] = logistic_map(X[i-1],3.6+0.13*tvp[i])
Plot the time-varying parameter and non-stationary logistic map process:
# plot tvp and X
fig = plt.figure(layout="constrained", figsize=(8, 4))
mosaic = """
A
B
"""
ax = fig.subplot_mosaic(mosaic)
ax["A"].plot(range(T), tvp, linewidth=1)
ax["A"].set_ylabel('r')
ax["B"].scatter(range(T), X, s=0.01, c='black')
ax["B"].set_ylabel('x')
ax["B"].set_xlabel('Timestep')
for loc in "AB":
ax[loc].spines['top'].set_visible(False)
ax[loc].spines['right'].set_visible(False)
Use f-SFA to infer a time varying parameter directly from the time-series data:
# single-component f-SFA
window, stride = 1000, 100
fsfa1 = fsfa.fSFA(X, window=window, stride=stride, n_components=1)
Compute mean values of the time-varying parameter over the same windows as f-SFA and use linear regression to align the f-SFA and TVP for plotting:
# time points at which to evaluate window features
time_points = np.arange(0, T - window + 1, stride)
window_indices = np.array([np.arange(t, t + window) for t in time_points])
tvp_means = [np.mean(tvp[indices]) for indices in window_indices]
# linear projection to align the time series for plotting
model = LinearRegression().fit(X=fsfa1, y=tvp_means)
projection = model.predict(fsfa1)
# plot the TVP and the f-SFA component
fig, ax = plt.subplots(figsize=(8,2))
ax.plot(tvp_means, c='black', lw=2, label='r')
ax.plot(projection, label='f-SFA', lw=2, linestyle='--', c='#3fadaf')
ax.set_xlabel('Window')
ax.legend(frameon=False)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# compute Pearson R^2 correlation
R2 = round(np.corrcoef(tvp_means, fsfa1.flatten())[0,1]**2, 3)
ax.text(800, -2.3, r'$R^2=$' + f'{R2}')
Usage notes
- The default time-series featureset used in f-SFA is catch24, but there are other keyword options ('mean', 'meanvar', 'quantiles', 'stft', 'catch22', 'catch24'). Alternatively, a list of time-series statistic functions can be passed as a list.
fsfauses thesklearn-sfapackage to perform SFA. Keyword arguments to the underlying SFA function can be passed to fSFA using a dictionary. Important SFA arguments can be found in thesklearn-sfadocumentation, including fill_mode, svd_solver, and robustness_cutoff.
Examples of feature embedding using different feature sets:
# custom features
df = fsfa.feature_embedding(X, window=1000, stride=100, featureset=[np.mean, np.var, np.median])
# mean and variance
df = fsfa.feature_embedding(X, window=1000, stride=100, featureset='meanvar')
# quantiles
df = fsfa.feature_embedding(X, window=1000, stride=100, featureset='quantiles', nb_quantiles=20)
# stft bandpower
df = fsfa.feature_embedding(X, window=1000, stride=100, featureset='stft', stft_fs_window=20)
# catch22
df = fsfa.feature_embedding(X, window=1000, stride=100, featureset='catch22')
# catch24
df = fsfa.feature_embedding(X, window=1000, stride=100, featureset='catch24')
Metadata
Release files for fsfa 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fsfa-0.2.3.tar.gz | 7.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fsfa-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.1 kB
Release files / fsfa-0.2.3.tar.gz
| Download URL | fsfa-0.2.3.tar.gz |
|---|---|
| Size | 7.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ea7d0db151ba668fdf5a3a884a8845b8507594e563d2788b353842253ffb351c
|
|
BLAKE2b-256 checksum How to use checksums |
8eb3953a7798b88fae8a28ad985847cd6c002a349fd0c6baf1e330ed03237a55
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.7
|
Release files / fsfa-0.2.3-py3-none-any.whl
| Download URL | fsfa-0.2.3-py3-none-any.whl |
|---|---|
| Size | 8.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0f2e8be6ff222712cb040219b5a8e0933a704b927b1aacd83539bef1a5383612
|
|
BLAKE2b-256 checksum How to use checksums |
b3d4a2818d683b990267b15db815b605890b251846314c885c746a53a5815e9a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.7
|