Source code for att.embedding.dimension
"""Embedding dimension estimation via False Nearest Neighbors."""
import numpy as np
from sklearn.neighbors import KDTree
[docs]
def estimate_dimension(
X: np.ndarray,
delay: int,
method: str = "fnn",
max_dim: int = 10,
threshold: float = 0.01,
rtol: float = 15.0,
atol: float = 2.0,
) -> int:
"""Estimate minimal embedding dimension via False Nearest Neighbors.
Parameters
----------
X : (n_samples,) 1D time series
delay : time delay for embedding
method : "fnn" (only option currently)
max_dim : maximum dimension to test
threshold : FNN fraction below which to stop (default 0.01 = 1%)
rtol : relative distance threshold for FNN criterion 1
atol : absolute distance threshold for FNN criterion 2
Returns
-------
int : estimated embedding dimension
"""
if method != "fnn":
raise ValueError(f"Unknown dimension method: {method}")
n = len(X)
for d in range(1, max_dim + 1):
n_embed = n - d * delay
n_embed_next = n - (d + 1) * delay
if n_embed_next < 10:
return d
# Build embedding at dimension d
cloud = np.zeros((n_embed, d))
for i in range(d):
cloud[:, i] = X[i * delay: i * delay + n_embed]
# Build embedding at dimension d+1 (for the same points)
cloud_next = np.zeros((n_embed_next, d + 1))
for i in range(d + 1):
cloud_next[:, i] = X[i * delay: i * delay + n_embed_next]
# Find nearest neighbors in d-dimensional space
tree = KDTree(cloud[:n_embed_next])
dists, inds = tree.query(cloud[:n_embed_next], k=2)
nn_dists = dists[:, 1]
nn_inds = inds[:, 1]
# Check for false nearest neighbors
n_fnn = 0
n_valid = 0
for i in range(n_embed_next):
if nn_dists[i] < 1e-10:
continue
n_valid += 1
j = nn_inds[i]
# Extra distance in the (d+1)th dimension
extra_dist = abs(cloud_next[i, d] - cloud_next[j, d])
# Criterion 1: relative distance increase
if extra_dist / nn_dists[i] > rtol:
n_fnn += 1
continue
# Criterion 2: absolute distance relative to attractor size
if extra_dist > atol * np.std(X):
n_fnn += 1
fnn_fraction = n_fnn / max(n_valid, 1)
if fnn_fraction < threshold:
return d
return max_dim