Feature extraction toolbox for gait dynamics analysis.
Extracts nonlinear dynamical and gait-specific features from gait time series. Includes Recurrence Quantification Analysis (RQA) validated against PyRQA, plus gait-specific measures not found in standard libraries.
- RQA - Recurrence Quantification Analysis (9 measures: RR, DET, L, Lmax, ENTR, LAM, TT, Vmax, TND)
- CRQA - Cross-Recurrence Quantification Analysis for two coupled time series
- Embedding - Automatic estimation of embedding parameters (AMI + FNN)
- Visualization - Recurrence plots, cross-recurrence plots, distance matrices, comparison charts
- Poincare Plot (SD1/SD2) - Short/long-term variability for arbitrary stride interval series
- Harmonic Ratio - Step-to-step symmetry from trunk acceleration (AP/VT/ML axes)
- Attractor Complexity Index (ACI) - Long-term divergence exponent (Terrier, 2018)
- Stride Variability - CV, MAD, IQR, asymmetry index
- GaitAnalysisPipeline - Single interface for all features (internal + external)
- Automatic integration with
nolds,antropy,neurokit2(all optional) - Entropy measures: Sample, Permutation, SVD, Approximate, Multiscale
- Fractal/scaling: DFA, Lyapunov, Hurst, Correlation Dimension, Higuchi FD, Katz FD
- Produces up to 30 features from a single stride interval series
- Bilateral analysis via Cross-Recurrence for left/right limb coupling
- scikit-learn compatible -
fit()/transform()interface for ML pipelines - Complements existing libraries: use
noldsfor DFA/Lyapunov,antropyfor entropy measures, and this toolbox for RQA + gait-specific features
git clone https://github.com/Taiyou/gait-dynamics-features.git
cd gait-dynamics-features
pip install -e .For development (includes test dependencies):
pip install -e ".[dev]"import numpy as np
from src import RecurrenceAnalyzer, RecurrencePlotter
# Generate a gait-like signal
t = np.linspace(0, 10 * np.pi, 500)
signal = np.sin(t) + 0.3 * np.sin(2 * t)
# Create analyzer
analyzer = RecurrenceAnalyzer(
embedding_dim=3,
time_delay=10,
theiler_window=1,
)
# Auto-select threshold for 5% recurrence rate
eps = analyzer.auto_threshold(signal, target_rr=0.05)
# Compute RQA measures
measures = analyzer.compute_rqa_measures(signal, threshold=eps)
print(measures)
# {'RR': 0.051, 'DET': 0.89, 'L': 7.1, 'Lmax': 160, ...}
# Visualize
plotter = RecurrencePlotter()
rm = analyzer.recurrence_matrix_
fig = plotter.plot_recurrence(rm, title="Gait Recurrence Plot")
fig.savefig("recurrence_plot.png")from src import CrossRecurrenceAnalyzer
# Left and right leg signals
left = np.sin(t) + 0.2 * np.sin(2 * t)
right = np.sin(t + np.pi) + 0.2 * np.sin(2 * (t + np.pi))
crqa = CrossRecurrenceAnalyzer(
embedding_dim=3,
time_delay=10,
threshold_ratio=0.15,
)
measures = crqa.compute_crqa_measures(left, right)
# Detect time lag via diagonal profile
profile = crqa.compute_diagonal_profile(left, right)from src import PoincareAnalyzer, HarmonicRatioAnalyzer, StrideVariability, GaitFeatureExtractor
# Stride interval analysis
stride_intervals = np.array([1.12, 1.08, 1.15, 1.10, ...]) # seconds
# Poincare plot
poincare = PoincareAnalyzer(lag=1)
result = poincare.compute(stride_intervals)
# {'SD1': 0.023, 'SD2': 0.041, 'SD_ratio': 0.56, 'S': 0.003, 'GI': 2.1}
# Stride variability
variability = StrideVariability.compute(stride_intervals)
# {'mean': 1.11, 'std': 0.033, 'cv': 3.0, 'range': 0.12, ...}
# All gait features at once (sklearn-compatible)
extractor = GaitFeatureExtractor(embedding_dim=3, time_delay=10)
features = extractor.fit_transform(stride_intervals) # 15-element vector
# Harmonic ratio from trunk acceleration
hra = HarmonicRatioAnalyzer(n_harmonics=10)
hr = hra.compute_single(stride_acceleration, axis_type='ap')from src import GaitAnalysisPipeline
# Full analysis with a single call
pipeline = GaitAnalysisPipeline(
embedding_dim=3,
time_delay=5,
target_rr=0.05,
)
stride_intervals = 1.1 + 0.03 * np.random.randn(300)
results = pipeline.analyze(stride_intervals)
# Access feature groups
print(results['variability']['cv']) # Coefficient of variation
print(results['poincare']['SD1']) # Short-term variability
print(results['rqa']['DET']) # Determinism
print(results['entropy']) # Sample/Permutation/SVD entropy (if nolds/antropy installed)
print(results['fractal']) # DFA/Lyapunov/Hurst (if nolds/antropy installed)
# Human-readable report
print(pipeline.summary())
# Export as flat dict or feature vector for ML
flat = pipeline.to_flat_dict() # {'VAR_cv': 2.8, 'RQA_DET': 0.95, ...}
vec = pipeline.to_feature_vector() # numpy array (up to 30 features)
# Bilateral coupling analysis
results = pipeline.analyze_bilateral(left_strides, right_strides)
print(results['crqa']['DET']) # Cross-determinism
print(results['coupling_lag']) # Estimated coupling laganalyzer = RecurrenceAnalyzer()
dim, delay = analyzer.estimate_embedding_parameters(signal)
print(f"Optimal dimension: {dim}, delay: {delay}")| Measure | Symbol | Description |
|---|---|---|
| Recurrence Rate | RR | Density of recurrence points |
| Determinism | DET | Fraction of recurrence points forming diagonal lines |
| Average Diagonal Length | L | Mean length of diagonal line structures |
| Max Diagonal Length | Lmax | Longest diagonal line |
| Entropy | ENTR | Shannon entropy of diagonal line length distribution |
| Laminarity | LAM | Fraction of recurrence points forming vertical lines |
| Trapping Time | TT | Mean length of vertical line structures |
| Max Vertical Length | Vmax | Longest vertical line |
| Trend | TND | Drift of recurrence rate away from main diagonal |
| Measure | Class | Description |
|---|---|---|
| SD1 | PoincareAnalyzer |
Short-term stride variability |
| SD2 | PoincareAnalyzer |
Long-term stride variability |
| GI | PoincareAnalyzer |
Gait irregularity index |
| HR | HarmonicRatioAnalyzer |
Harmonic ratio (gait symmetry) |
| ACI | AttractorComplexityIndex |
Attractor complexity index |
| CV | StrideVariability |
Coefficient of variation |
| MAD | StrideVariability |
Mean absolute deviation |
gait-dynamics-features/
├── src/
│ ├── __init__.py
│ ├── rqa_analysis.py # Core RQA (RecurrenceAnalyzer)
│ ├── crqa_analysis.py # Cross-RQA (CrossRecurrenceAnalyzer)
│ ├── rqa_visualization.py # Plotting (RecurrencePlotter)
│ ├── gait_features.py # Gait-specific features
│ └── gait_analysis.py # Unified analysis pipeline
├── tests/
│ ├── conftest.py # Test fixtures
│ ├── test_rqa_analysis.py # RQA tests (34 tests)
│ ├── test_crqa_analysis.py # CRQA tests (13 tests)
│ ├── test_gait_features.py # Gait feature tests (40 tests)
│ └── test_gait_analysis.py # Pipeline tests (21 tests)
├── examples/
│ ├── demo_rqa.py # RQA usage demos
│ ├── demo_gait_analysis.py # Pipeline usage demos
│ ├── validate_against_pyrqa.py # PyRQA comparison
│ └── validate_gait_features.py # Gait feature validation
├── docs/
│ ├── api_reference.md # Full API documentation
│ ├── tutorial.md # Step-by-step tutorial
│ └── pyrqa_comparison.md # Validation against PyRQA
├── setup.py
└── pytest.ini
All RQA measures have been validated against PyRQA across 4 signal types (sine, harmonics, Lorenz chaotic, white noise). See docs/pyrqa_comparison.md for full results.
| Signal | RR | DET | L | Lmax | ENTR | LAM | TT | Vmax |
|---|---|---|---|---|---|---|---|---|
| sine | = | = | = | = | = | = | = | = |
| harmonics | = | = | = | = | = | = | = | = |
| Lorenz | = | = | = | = | = | = | * | * |
| noise | = | = | = | = | = | = | = | = |
= exact match, * edge-case difference (see docs for details)
Performance: 3-14x faster than PyRQA for N=100-1000.
pytest tests/ -vWith coverage:
pytest tests/ -v --cov=src --cov-report=term-missingRequired:
- Python >= 3.9
- NumPy >= 1.24
- SciPy >= 1.10
- matplotlib >= 3.7
- scikit-learn >= 1.3
Optional (for additional entropy/fractal features via GaitAnalysisPipeline):
nolds- Sample Entropy, DFA, Lyapunov, Hurst, Correlation Dimensionantropy- Permutation/SVD/Approximate Entropy, Higuchi/Katz FD, DFAneurokit2- Multiscale Entropy
pip install nolds antropy neurokit2 # optional, any subset is fineGaitAnalysisPipeline automatically detects and uses these libraries when installed:
| Measure | Library | Pipeline Key |
|---|---|---|
| Sample Entropy | nolds or antropy |
ENT_sample_entropy |
| Permutation Entropy | antropy |
ENT_permutation_entropy |
| SVD Entropy | antropy |
ENT_svd_entropy |
| Approximate Entropy | antropy |
ENT_approximate_entropy |
| Multiscale Entropy | neurokit2 |
ENT_multiscale_entropy |
| DFA | nolds or antropy |
FRC_dfa_alpha |
| Lyapunov Exponent | nolds |
FRC_lyapunov_r |
| Hurst Exponent | nolds |
FRC_hurst_exponent |
| Correlation Dimension | nolds |
FRC_correlation_dim |
| Higuchi FD | antropy |
FRC_higuchi_fd |
| Katz FD | antropy |
FRC_katz_fd |
These measures are well-implemented in the above libraries and are wrapped (not reimplemented) by the pipeline. If no external libraries are installed, internal features (RQA, Poincare, ACI, Variability) are still computed.
- Marwan, N., Romano, M.C., Thiel, M., & Kurths, J. (2007). Recurrence plots for the analysis of complex systems. Physics Reports, 438(5-6), 237-329.
- Zbilut, J.P., Giuliani, A., & Webber, C.L. (1998). Detecting deterministic signals in exceptionally noisy environments using cross-recurrence quantification. Physics Letters A, 246(1-2), 122-128.
- Kennel, M.B., Brown, R., & Abarbanel, H.D.I. (1992). Determining embedding dimension for phase-space reconstruction. Physical Review A, 45(6), 3403.
- Fraser, A.M. & Swinney, H.L. (1986). Independent coordinates for strange attractors from mutual information. Physical Review A, 33(2), 1134.
- Brennan, M., Palaniswami, M., & Kamen, P. (2001). Do existing measures of Poincare plot geometry reflect nonlinear features of heart rate variability? IEEE Trans Biomed Eng, 48(11), 1342-1347.
- Bellanca, J.L., Lowry, K.A., VanSwearingen, J.M., Brach, J.S., & Redfern, M.S. (2013). Harmonic ratios: A quantification of step to step symmetry. J Biomech, 46, 828-834.
- Terrier, P. (2018). Complexity of human walking: the attractor complexity index. Gait & Posture, 61, 378-384.
- Rosenstein, M.T., Collins, J.J., & De Luca, C.J. (1993). A practical method for calculating largest Lyapunov exponents from small data sets. Physica D, 65, 117-134.
MIT