#![allow(clippy::unwrap_used, clippy::expect_used)]
use chronix::chronix_analytics::preprocess::auto_features::auto_features;
use chronix::chronix_analytics::preprocess::{
AutoFeatureConfig, ExponentialSmoother, GapDetector, Interpolator, MovingAverageSmoother,
PreprocessConfig, PreprocessPipeline, ResampleConfig, Resampler, Smoother, StlConfig,
WeightedMovingAverage, detect_period, diff, ewm, lag, pct_change, rolling_std, stl_decompose,
};
fn main() {
println!("=== Chronix Data Preprocessing ===\n");
let interval_ns: i64 = 1_000_000_000;
let mut timestamps: Vec<i64> = Vec::new();
let mut values: Vec<f64> = Vec::new();
let base_ts = 1_700_000_000_000_000_000i64;
for i in 0..200 {
if (50..=54).contains(&i) || (120..=122).contains(&i) {
continue;
}
timestamps.push(base_ts + i * interval_ns);
let t = i as f64;
values.push(50.0 + t * 0.1 + (t * 2.0 * std::f64::consts::PI / 24.0).sin() * 15.0);
}
println!(
"Generated {} points with gaps (expected 200, missing {})\n",
timestamps.len(),
200 - timestamps.len()
);
println!("--- Gap Detection ---");
let detector = GapDetector::new(interval_ns, 1.5).expect("Failed to create GapDetector");
let gaps = detector.detect(×tamps);
println!("Found {} gaps:", gaps.len());
for gap in &gaps {
println!(
" Gap at index {}: {} missing points (ts {} → {})",
gap.before_idx, gap.missing_count, gap.before_ts, gap.after_ts
);
}
println!("\n--- Interpolation ---");
let methods = [
("Linear", Interpolator::Linear),
("Nearest", Interpolator::Nearest),
("CatmullRom", Interpolator::CatmullRom),
("Forward", Interpolator::Forward),
("Zero", Interpolator::Zero),
];
for (name, method) in &methods {
let (filled_ts, filled_vals, gaps_filled, _gaps_skipped) = method
.fill(×tamps, &values, interval_ns)
.expect("interpolation failed");
println!(
" {name:>12}: filled {gaps_filled} gaps → {} total points",
filled_ts.len()
);
let _ = (filled_ts, filled_vals); }
let (full_ts, full_vals, _, _) = Interpolator::Linear
.fill(×tamps, &values, interval_ns)
.expect("interpolation failed");
println!("\n--- Smoothing ---");
let ema = ExponentialSmoother::new(0.3);
let ema_vals = ema.smooth(&full_vals);
println!(
" EMA(α=0.3): first 5 = {:?}",
&ema_vals[..5.min(ema_vals.len())]
);
let ma = MovingAverageSmoother::new(5);
let ma_vals = ma.smooth(&full_vals);
println!(
" MA(w=5): first 5 = {:?}",
&ma_vals[..5.min(ma_vals.len())]
);
let wma = WeightedMovingAverage::new(vec![0.1, 0.2, 0.3, 0.4]);
let wma_vals = wma.smooth(&full_vals);
println!(
" WMA: first 5 = {:?}",
&wma_vals[..5.min(wma_vals.len())]
);
let smoother = Smoother::Exponential { alpha: 0.5 };
let smoothed = smoother.smooth(&full_vals);
println!(
" Enum-EMA: first 5 = {:?}",
&smoothed[..5.min(smoothed.len())]
);
println!("\n--- Resampling ---");
let resample_config = ResampleConfig {
target_interval_ns: 5 * interval_ns, aggregation: chronix::chronix_analytics::preprocess::AggregationFn::Mean,
interpolation: Interpolator::Linear,
};
let (resampled_ts, resampled_vals) =
Resampler::resample(&full_ts, &full_vals, &resample_config);
println!(
" {} points → {} points (5x downsample)",
full_ts.len(),
resampled_ts.len()
);
println!(
" First 5 resampled: {:?}",
&resampled_vals[..5.min(resampled_vals.len())]
);
println!("\n--- STL Decomposition ---");
let period = detect_period(&full_vals, 50);
println!(" Auto-detected period: {:?}", period);
let stl_period = period.unwrap_or(24);
let config = StlConfig::new(stl_period).with_iterations(3);
let decomp = stl_decompose(&full_vals, &config).expect("STL decomposition failed");
println!(
" Trend component: first 5 = {:?}",
&decomp.trend[..5.min(decomp.trend.len())]
);
println!(
" Seasonal component: first 5 = {:?}",
&decomp.seasonal[..5.min(decomp.seasonal.len())]
);
println!(
" Residual component: first 5 = {:?}",
&decomp.residual[..5.min(decomp.residual.len())]
);
println!("\n--- Feature Engineering ---");
let lagged = lag(&full_vals, 3);
println!(" lag(3): len={}", lagged.len());
let diffed = diff(&full_vals, 1);
println!(
" diff(1): first 5 = {:?}",
&diffed[..5.min(diffed.len())]
);
let pct = pct_change(&full_vals);
println!(" pct_change: first 5 = {:?}", &pct[..5.min(pct.len())]);
let w = 10;
let rstd = rolling_std(&full_vals, w);
let settled = &rstd[(w - 1).min(rstd.len())..];
println!(
" rolling_std: {} NaN warm-up, then {:?}",
w - 1,
&settled[..5.min(settled.len())]
);
let ewm_vals = ewm(&full_vals, 0.3);
println!(
" ewm(0.3): first 5 = {:?}",
&ewm_vals[..5.min(ewm_vals.len())]
);
println!("\n--- Auto Feature Extraction ---");
let feature_config = AutoFeatureConfig::default()
.with_period(24)
.with_windows(vec![5, 10, 20]);
let feature_matrix = auto_features(&full_ts, &full_vals, &feature_config);
println!(
" Generated {} features × {} rows",
feature_matrix.n_features(),
feature_matrix.n_rows
);
println!(" Feature names: {:?}", feature_matrix.names);
println!("\n--- Full Preprocessing Pipeline ---");
let pipe_config = PreprocessConfig {
expected_interval_ns: interval_ns,
gap_tolerance: 1.5,
interpolation: Some(Interpolator::Linear),
smoothing: Some(Smoother::MovingAverage { window: 5 }),
resample: None,
..Default::default()
};
let result =
PreprocessPipeline::run(&pipe_config, ×tamps, &values).expect("Pipeline failed");
println!(
" Pipeline output: {} points, {} gaps filled",
result.values.len(),
result.gaps_filled
);
println!(
" First 5 values: {:?}",
&result.values[..5.min(result.values.len())]
);
println!("\n✓ Preprocessing complete");
}