anomalyzer-ts 0.1.0

Probabilistic anomaly detection for time-series data
Documentation
//! examples/async_multi_sensor.rs
//!
//! Demonstrates running multiple independent `AsyncAnomalyzer` detectors
//! concurrently — one per sensor stream — using `tokio::join!`.
//!
//! Each sensor has its own baseline and anomaly characteristics:
//!   - temperature: subtle drift then a sharp spike
//!   - pressure:    stable, then a gradual climb
//!   - vibration:   noisy but bounded, then a violent burst
//!
//! Run with:
//!   cargo run --example async_multi_sensor --features async

use anomalyzer_ts::{AnomalyzerConf, async_anomalyzer::AsyncAnomalyzer};
use std::sync::Arc;

/// Feed a sequence of values into a detector and print results.
async fn run_sensor(name: &str, detector: Arc<AsyncAnomalyzer>, readings: Vec<f64>) {
    println!("\n[{name}]");
    println!("{:<6}  {:>8}  {:>10}", "step", "value", "prob");
    println!("{}", "-".repeat(30));

    for (i, v) in readings.into_iter().enumerate() {
        let prob = detector.push(v).await;
        let flag = if prob > 0.75 { " ⚠️" } else { "" };
        println!("{:<6}  {:>8.2}  {:>10.3}{}", i + 1, v, prob, flag);
    }
}

#[tokio::main]
async fn main() {
    // ── Temperature sensor ────────────────────────────────────────────────
    let temp_detector = Arc::new(
        AsyncAnomalyzer::new(
            AnomalyzerConf {
                active_size: 1,
                n_seasons: 4,
                methods: vec!["magnitude".into(), "highrank".into()],
                ..Default::default()
            },
            Some(vec![22.0, 22.1, 21.9, 22.2, 22.0]),
        )
        .await
        .unwrap(),
    );

    // ── Pressure sensor ───────────────────────────────────────────────────
    let pressure_detector = Arc::new(
        AsyncAnomalyzer::new(
            AnomalyzerConf {
                active_size: 2,
                n_seasons: 4,
                methods: vec!["magnitude".into(), "cdf".into()],
                ..Default::default()
            },
            Some(vec![1013.0, 1013.2, 1012.8, 1013.1, 1013.0,
                      1013.3, 1012.9, 1013.2]),
        )
        .await
        .unwrap(),
    );

    // ── Vibration sensor ──────────────────────────────────────────────────
    let vib_detector = Arc::new(
        AsyncAnomalyzer::new(
            AnomalyzerConf {
                active_size: 3,
                n_seasons: 3,
                perm_count: 300,
                methods: vec!["ks".into(), "diff".into()],
                ..Default::default()
            },
            Some(vec![0.5, 0.6, 0.55, 0.58, 0.52, 0.57, 0.53, 0.59,
                      0.56, 0.54, 0.58, 0.55]),
        )
        .await
        .unwrap(),
    );

    // ── Feed values concurrently ───────────────────────────────────────────
    let temp_readings     = vec![22.1, 22.3, 22.0, 35.0, 22.2]; // spike at step 4
    let pressure_readings = vec![1013.1, 1013.5, 1014.2, 1015.8, 1018.0]; // gradual climb
    let vib_readings      = vec![0.57, 0.54, 0.56,   // normal
                                  4.2,  3.8,  5.1];   // burst

    tokio::join!(
        run_sensor("temperature °C", Arc::clone(&temp_detector),     temp_readings),
        run_sensor("pressure hPa",   Arc::clone(&pressure_detector), pressure_readings),
        run_sensor("vibration g",    Arc::clone(&vib_detector),      vib_readings),
    );

    println!("\nAll sensors done.");
}