Skip to main content

fdars_core/depth/
band.rs

1//! Band depth and related measures (BD, MBD, MEI).
2
3use crate::iter_maybe_parallel;
4use crate::matrix::FdMatrix;
5use crate::streaming_depth::{
6    FullReferenceState, SortedReferenceState, StreamingBd, StreamingDepth, StreamingMbd,
7};
8#[cfg(feature = "parallel")]
9use rayon::iter::ParallelIterator;
10
11/// Compute Band Depth (BD) for 1D functional data.
12///
13/// BD(x) = proportion of pairs (i,j) where x lies within the band formed by curves i and j.
14///
15/// # Examples
16///
17/// ```
18/// use fdars_core::matrix::FdMatrix;
19/// use fdars_core::depth::band_1d;
20///
21/// let data = FdMatrix::from_column_major(
22///     (0..50).map(|i| (i as f64 * 0.1).sin()).collect(),
23///     5, 10,
24/// ).unwrap();
25/// let depths = band_1d(&data, &data);
26/// assert_eq!(depths.len(), 5);
27/// assert!(depths.iter().all(|&d| d >= 0.0 && d <= 1.0 + 1e-10));
28/// ```
29#[must_use = "expensive computation whose result should not be discarded"]
30pub fn band_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
31    if data_obj.nrows() == 0 || data_ori.nrows() < 2 || data_obj.ncols() == 0 {
32        return Vec::new();
33    }
34    let state = FullReferenceState::from_reference(data_ori);
35    let streaming = StreamingBd::new(state);
36    streaming.depth_batch(data_obj)
37}
38
39/// Compute Modified Band Depth (MBD) for 1D functional data.
40///
41/// MBD(x) = average over pairs of the proportion of the domain where x is inside the band.
42#[must_use = "expensive computation whose result should not be discarded"]
43pub fn modified_band_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
44    if data_obj.nrows() == 0 || data_ori.nrows() < 2 || data_obj.ncols() == 0 {
45        return Vec::new();
46    }
47    let state = SortedReferenceState::from_reference(data_ori);
48    let streaming = StreamingMbd::new(state);
49    streaming.depth_batch(data_obj)
50}
51
52/// Compute Modified Epigraph Index (MEI) for 1D functional data.
53///
54/// MEI measures the proportion of time a curve is below other curves.
55/// Matches R's `roahd::MEI()`: uses `<=` comparison with 0.5 adjustment for ties.
56#[must_use = "expensive computation whose result should not be discarded"]
57pub fn modified_epigraph_index_1d(data_obj: &FdMatrix, data_ori: &FdMatrix) -> Vec<f64> {
58    let nobj = data_obj.nrows();
59    let nori = data_ori.nrows();
60    let n_points = data_obj.ncols();
61
62    if nobj == 0 || nori == 0 || n_points == 0 {
63        return Vec::new();
64    }
65
66    iter_maybe_parallel!(0..nobj)
67        .map(|i| {
68            let mut total = 0.0;
69
70            for j in 0..nori {
71                let mut count = 0.0;
72
73                for t in 0..n_points {
74                    let xi = data_obj[(i, t)];
75                    let xj = data_ori[(j, t)];
76
77                    // R's roahd::MEI uses <= for the epigraph condition
78                    if xi <= xj {
79                        count += 1.0;
80                    }
81                }
82
83                total += count / n_points as f64;
84            }
85
86            total / nori as f64
87        })
88        .collect()
89}