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