criterion/stats/bivariate/
mod.rs

1//! Bivariate analysis
2
3mod bootstrap;
4mod resamples;
5
6pub mod regression;
7
8use rayon::prelude::*;
9use stats::float::Float;
10
11use stats::bivariate::resamples::Resamples;
12use stats::tuple::{Tuple, TupledDistributionsBuilder};
13use stats::univariate::Sample;
14
15/// Bivariate `(X, Y)` data
16///
17/// Invariants:
18///
19/// - No `NaN`s in the data
20/// - At least two data points in the set
21pub struct Data<'a, X, Y>(&'a [X], &'a [Y])
22where
23    X: 'a,
24    Y: 'a;
25
26impl<'a, X, Y> Copy for Data<'a, X, Y> {}
27
28#[cfg_attr(feature = "cargo-clippy", allow(clippy::expl_impl_clone_on_copy))]
29impl<'a, X, Y> Clone for Data<'a, X, Y> {
30    fn clone(&self) -> Data<'a, X, Y> {
31        *self
32    }
33}
34
35impl<'a, X, Y> Data<'a, X, Y> {
36    /// Returns the length of the data set
37    pub fn len(&self) -> usize {
38        self.0.len()
39    }
40
41    /// Iterate over the data set
42    pub fn iter(&self) -> Pairs<'a, X, Y> {
43        Pairs {
44            data: *self,
45            state: 0,
46        }
47    }
48}
49
50impl<'a, X, Y> Data<'a, X, Y>
51where
52    X: Float,
53    Y: Float,
54{
55    /// Creates a new data set from two existing slices
56    pub fn new(xs: &'a [X], ys: &'a [Y]) -> Data<'a, X, Y> {
57        assert!(
58            xs.len() == ys.len()
59                && xs.len() > 1
60                && xs.iter().all(|x| !x.is_nan())
61                && ys.iter().all(|y| !y.is_nan())
62        );
63
64        Data(xs, ys)
65    }
66
67    // TODO Remove the `T` parameter in favor of `S::Output`
68    /// Returns the bootstrap distributions of the parameters estimated by the `statistic`
69    ///
70    /// - Multi-threaded
71    /// - Time: `O(nresamples)`
72    /// - Memory: `O(nresamples)`
73    pub fn bootstrap<T, S>(&self, nresamples: usize, statistic: S) -> T::Distributions
74    where
75        S: Fn(Data<X, Y>) -> T,
76        S: Sync,
77        T: Tuple + Send,
78        T::Distributions: Send,
79        T::Builder: Send,
80    {
81        (0..nresamples)
82            .into_par_iter()
83            .map_init(
84                || Resamples::new(*self),
85                |resamples, _| statistic(resamples.next()),
86            )
87            .fold(
88                || T::Builder::new(0),
89                |mut sub_distributions, sample| {
90                    sub_distributions.push(sample);
91                    sub_distributions
92                },
93            )
94            .reduce(
95                || T::Builder::new(0),
96                |mut a, mut b| {
97                    a.extend(&mut b);
98                    a
99                },
100            )
101            .complete()
102    }
103
104    /// Returns a view into the `X` data
105    pub fn x(&self) -> &'a Sample<X> {
106        Sample::new(&self.0)
107    }
108
109    /// Returns a view into the `Y` data
110    pub fn y(&self) -> &'a Sample<Y> {
111        Sample::new(&self.1)
112    }
113}
114
115/// Iterator over `Data`
116pub struct Pairs<'a, X: 'a, Y: 'a> {
117    data: Data<'a, X, Y>,
118    state: usize,
119}
120
121impl<'a, X, Y> Iterator for Pairs<'a, X, Y> {
122    type Item = (&'a X, &'a Y);
123
124    fn next(&mut self) -> Option<(&'a X, &'a Y)> {
125        if self.state < self.data.len() {
126            let i = self.state;
127            self.state += 1;
128
129            // This is safe because i will always be < self.data.{0,1}.len()
130            debug_assert!(i < self.data.0.len());
131            debug_assert!(i < self.data.1.len());
132            unsafe { Some((self.data.0.get_unchecked(i), self.data.1.get_unchecked(i))) }
133        } else {
134            None
135        }
136    }
137}