use std::{cmp, mem};
use cast;
use float::Float;
use num_cpus;
use thread_scoped as thread;
use tuple::{Tuple, TupledDistributionsBuilder};
use univariate::Percentiles;
use univariate::resamples::Resamples;
pub struct Sample<A>([A]);
impl<A> Sample<A> {
pub fn as_slice(&self) -> &[A] {
unsafe { mem::transmute(self) }
}
}
impl<A> Sample<A>
where
A: Float,
{
pub fn new(slice: &[A]) -> &Sample<A> {
assert!(slice.len() > 1 && slice.iter().all(|x| !x.is_nan()));
unsafe { mem::transmute(slice) }
}
pub fn max(&self) -> A {
let mut elems = self.as_slice().iter();
match elems.next() {
Some(&head) => elems.fold(head, |a, &b| a.max(b)),
None => unreachable!(),
}
}
pub fn mean(&self) -> A {
let n = self.as_slice().len();
self.sum() / A::cast(n)
}
pub fn median_abs_dev(&self, median: Option<A>) -> A
where
usize: cast::From<A, Output = Result<usize, cast::Error>>,
{
let median = median.unwrap_or_else(|| self.percentiles().median());
let abs_devs = self.as_slice()
.iter()
.map(|&x| (x - median).abs())
.collect::<Vec<_>>();
let abs_devs: &Sample<A> = unsafe { mem::transmute(&*abs_devs) };
abs_devs.percentiles().median() * A::cast(1.4826)
}
pub fn median_abs_dev_pct(&self) -> A
where
usize: cast::From<A, Output = Result<usize, cast::Error>>,
{
let _100 = A::cast(100);
let median = self.percentiles().median();
let mad = self.median_abs_dev(Some(median));
(mad / median) * _100
}
pub fn min(&self) -> A {
let mut elems = self.as_slice().iter();
match elems.next() {
Some(&elem) => elems.fold(elem, |a, &b| a.min(b)),
None => unreachable!(),
}
}
pub fn percentiles(&self) -> Percentiles<A>
where
usize: cast::From<A, Output = Result<usize, cast::Error>>,
{
use std::cmp::Ordering;
fn cmp<T>(a: &T, b: &T) -> Ordering
where
T: PartialOrd,
{
if a < b {
Ordering::Less
} else if a == b {
Ordering::Equal
} else {
Ordering::Greater
}
}
let mut v = self.as_slice().to_vec().into_boxed_slice();
v.sort_by(cmp);
unsafe { mem::transmute(v) }
}
pub fn std_dev(&self, mean: Option<A>) -> A {
self.var(mean).sqrt()
}
pub fn std_dev_pct(&self) -> A {
let _100 = A::cast(100);
let mean = self.mean();
let std_dev = self.std_dev(Some(mean));
(std_dev / mean) * _100
}
pub fn sum(&self) -> A {
::sum(self.as_slice())
}
pub fn t(&self, other: &Sample<A>) -> A {
let (x_bar, y_bar) = (self.mean(), other.mean());
let (s2_x, s2_y) = (self.var(Some(x_bar)), other.var(Some(y_bar)));
let n_x = A::cast(self.as_slice().len());
let n_y = A::cast(other.as_slice().len());
let num = x_bar - y_bar;
let den = (s2_x / n_x + s2_y / n_y).sqrt();
num / den
}
pub fn var(&self, mean: Option<A>) -> A {
use std::ops::Add;
let mean = mean.unwrap_or_else(|| self.mean());
let slice = self.as_slice();
let sum = slice
.iter()
.map(|&x| (x - mean).powi(2))
.fold(A::cast(0), Add::add);
sum / A::cast(slice.len() - 1)
}
pub fn bootstrap<T, S>(&self, nresamples: usize, statistic: S) -> T::Distributions
where
S: Fn(&Sample<A>) -> T,
S: Sync,
T: Tuple,
T: Send,
T::Distributions: Send,
T::Builder: Send,
{
let ncpus = num_cpus::get();
unsafe {
if ncpus > 1 && nresamples > self.as_slice().len() {
let granularity = nresamples / ncpus + 1;
let statistic = &statistic;
let chunks = (0..ncpus)
.map(|i| {
let mut sub_distributions: T::Builder =
TupledDistributionsBuilder::new(granularity);
let mut resamples = Resamples::new(self);
let offset = i * granularity;
thread::scoped(move || {
for _ in offset..cmp::min(offset + granularity, nresamples) {
sub_distributions.push(statistic(resamples.next()))
}
sub_distributions
})
})
.collect::<Vec<_>>();
let mut builder: T::Builder = TupledDistributionsBuilder::new(nresamples);
for chunk in chunks {
builder.extend(&mut (chunk.join()));
}
builder.complete()
} else {
let mut builder: T::Builder = TupledDistributionsBuilder::new(nresamples);
let mut resamples = Resamples::new(self);
for _ in 0..nresamples {
builder.push(statistic(resamples.next()));
}
builder.complete()
}
}
}
#[cfg(test)]
pub fn iqr(&self) -> A
where
usize: cast::From<A, Output = Result<usize, cast::Error>>,
{
self.percentiles().iqr()
}
#[cfg(test)]
pub fn median(&self) -> A
where
usize: cast::From<A, Output = Result<usize, cast::Error>>,
{
self.percentiles().median()
}
}