Struktura
Time-series anomaly detection with no training data.
Struktura is a Rust crate and CLI. It measures the correlation structure of a time series with detrended fluctuation analysis (DFA) and runs a streaming monitor (struktura guard) that calibrates itself on the first rows of your data: a CSV goes in, an exit code comes out.
Why Struktura?
Most monitoring checks whether a value leaves a band (“is the temperature above 80 C?”). That is fast when a fault makes values bigger. It misses faults that change how values follow each other while their spread stays the same, and it false-alarms on healthy signals that wander slowly. Struktura’s monitor looks at structure as well as level. Use it next to limit checks, not instead of them.
Measured results, with the command behind each number, are in the README and in docs/claims.tsv, which CI re-runs.
Install
cargo add struktura
Quick Start
Analyze any signal
#![allow(unused)]
fn main() {
use struktura::{analyze, health_check, HealthVerdict};
// Load your time series data
let data: Vec<f64> = load_csv("vibration.csv");
// Analyze structural health
let law = analyze(&data);
println!("DFA alpha: {:.3}", law.dfa.alpha);
println!("R-squared: {:.4}", law.dfa.r_squared);
println!("Quality: {:?}", law.quality);
// Compare against a known healthy baseline
let verdict = health_check(&law, 0.389); // baseline from calibration
match verdict {
HealthVerdict::Healthy => println!("System is healthy"),
HealthVerdict::Watch => println!("Minor structural shift detected"),
HealthVerdict::Warning => println!("Significant structural change"),
HealthVerdict::Critical => println!("CRITICAL: Major structural departure"),
}
}
What the numbers mean
- DFA alpha near 0.5: uncorrelated noise — no exploitable structure
- DFA alpha 0.5-1.0: long-range correlated — healthy complex system
- Alpha shift > 0.08 from baseline: something is changing
- R-squared > 0.7: the measurement is reliable
- R-squared < 0.7: ABSTAIN — not enough structure to diagnose
How DFA Works
Detrended Fluctuation Analysis measures long-range correlation in a time series.
The algorithm in 4 steps
- Profile: compute the cumulative sum of deviations from the mean
- Box: divide the profile into non-overlapping boxes of size s
- Detrend: fit and subtract a linear trend within each box
- Scale: measure the root-mean-square residual F(s) at each box size
The scaling exponent alpha is the slope of log F(s) vs log s.
What alpha means
| Alpha range | Interpretation |
|---|---|
| ~0.5 | White noise (uncorrelated) |
| 0.5 - 1.0 | Long-range correlated (healthy complexity) |
| ~1.0 | 1/f noise (pink noise) |
| > 1.0 | Non-stationary / trend-dominated |
When it helps anomaly detection
Many healthy systems keep a characteristic alpha, and some faults change it. Whether alpha moves before an amplitude monitor fires depends on the fault. On the bundled CWRU bearing excerpts alpha drops from 0.689 to 0.183 while RMS amplitude rises 11% (struktura demo). On the IMS run-to-failure bearing a plain RMS threshold trips earlier than the alpha alarm (see REPRODUCIBILITY.md). So alpha complements amplitude checks; it does not replace them.
References
- Peng et al., “Mosaic organization of DNA nucleotide sequences,” Physical Review E 49(2), 1994.
- Peng et al., “Quantification of scaling exponents,” Chaos 5(1), 1995.
- Goldberger et al., “Fractal dynamics in physiology,” PNAS 99(suppl 1), 2002.
Bearing Fault Detection
On 12 kHz vibration data from the Case Western Reserve University Bearing Data Center, DFA α separates a normal bearing from one with an inner-race fault:
| Condition | DFA α |
|---|---|
| Normal | 0.689 |
| Inner-race fault | 0.183 |
Reproduce with struktura demo (the data is embedded). CI re-checks these two numbers (docs/claims.tsv, row bearing-alpha). Other fault types (outer race, ball) are not part of the checked set.
How to use it
#![allow(unused)]
fn main() {
use struktura::{analyze, health_check};
let normal = analyze(&normal_vibration);
let baseline = normal.dfa.alpha; // establish during healthy operation
// Later, during monitoring:
let current = analyze(¤t_vibration);
let verdict = health_check(¤t, baseline);
}
health_check compares α against the baseline with fixed thresholds (0.03 / 0.08 / 0.15). They are defaults, not significance tests; decide what shift matters for your machine.
What DFA measures
DFA measures how the fluctuations of a signal scale with the window size, which reflects its correlation structure. A fault can change that structure without adding a new frequency peak, so DFA complements spectral (FFT) and amplitude checks. On the NASA IMS run-to-failure bearing, a plain RMS threshold alarmed earlier than struktura, so do not treat DFA as an early-warning method.
Cross-Domain Results
The same DFA runs on very different signals. These are α values measured on bundled data; only the bearing pair is a normal-vs-fault comparison.
| Domain | Signal | DFA α | R² |
|---|---|---|---|
| Bearings | CWRU 12 kHz vibration, normal | 0.689 | |
| Bearings | CWRU 12 kHz vibration, inner-race fault | 0.183 | |
| Genome | Human chr1 GC% in 1 kb windows (struktura genome) | 0.967 | 0.980 |
Shuffle control
To check that a measured structure is real and not an artifact, permute the signal and run DFA again. If shuffling moves α toward 0.5, the order of the values carried the structure. struktura prove <file> runs this test with a bootstrap interval on α.
The full list of checked numbers, with commands and controls, is docs/claims.tsv.
API Reference
Core functions
dfa(values: &[f64]) -> DfaResult
Compute the DFA scaling exponent. Returns alpha and R-squared.
acr(values: &[f64]) -> DfaResult
Compute autocorrelation decay exponent.
analyze(values: &[f64]) -> StructuralLaw
Full structural analysis: DFA + ACR + statistics.
health_check(law: &StructuralLaw, baseline: f64) -> HealthVerdict
Compare current DFA alpha against a baseline.
Types
DfaResult { alpha: f64, r_squared: f64 }
StructuralLaw { hurst, dfa, acr, mean, std_dev, kurtosis, p99, max, n, quality }
LawQuality — Exact, Strong, Good, Approx, Abstain, Insufficient
HealthVerdict — Healthy, Watch, Warning, Critical
Thresholds
| Shift from baseline | Verdict |
|---|---|
| < 0.03 | Healthy |
| 0.03 - 0.08 | Watch |
| 0.08 - 0.15 | Warning |
| > 0.15 | Critical |