Source code for neuralfieldmanifold.geometry.disparity
"""Pairwise Procrustes comparison of delay-coordinate trajectories."""
from dataclasses import dataclass
import numpy as np
from scipy.spatial import procrustes
from ..embedding import delay
from .._validation import positive_integer
[docs]
@dataclass(frozen=True)
class Disparity:
mean: float
standard_deviation: float
pair_indices: np.ndarray
values: np.ndarray
[docs]
def pairwise_disparity(signals, *, dimension, tau_samples, target_points=2000):
"""Compare delay embeddings of time-aligned signals using Procrustes analysis.
Parameters
----------
signals : array_like, shape (n_signals, n_samples)
At least two equal-length signals with corresponding time samples.
dimension : int
Number of delay coordinates.
tau_samples : int
Delay between coordinates, in samples.
target_points : int
Approximate number of points per embedding. Subsampling uses stride
max(1, n_points // target_points), so this is not a strict upper bound.
Returns
-------
Disparity
Pair indices, individual disparities, their mean, and population
standard deviation.
"""
signals = np.asarray(signals)
target_points = positive_integer(target_points, "target_points")
if signals.ndim != 2 or len(signals) < 2:
raise ValueError("Expected at least two equal-length 1D signals")
clouds = [delay(x, dimension=dimension, tau_samples=tau_samples) for x in signals]
clouds = [x[:: max(1, len(x) // target_points)] for x in clouds]
size = min(map(len, clouds))
pairs, values = [], []
for i in range(len(clouds)):
for j in range(i + 1, len(clouds)):
_, _, value = procrustes(clouds[i][:size], clouds[j][:size])
pairs.append((i, j))
values.append(value)
values = np.asarray(values)
return Disparity(
float(values.mean()), float(values.std()), np.asarray(pairs), values
)