use core::ops::{Deref, Index};
use core::slice;
use super::super::super::float::Float;
use super::super::Sample;
use self::Label::*;
#[derive(Clone, Copy)]
pub struct LabeledSample<'a, A>
where
A: Float,
{
fences: (A, A, A, A),
sample: &'a Sample<A>,
}
impl<'a, A> LabeledSample<'a, A>
where
A: Float,
{
#[allow(clippy::similar_names)]
pub fn count(&self) -> (usize, usize, usize, usize, usize) {
let (mut los, mut lom, mut noa, mut him, mut his) = (0, 0, 0, 0, 0);
for (_, label) in self {
match label {
LowSevere => {
los += 1;
}
LowMild => {
lom += 1;
}
NotAnOutlier => {
noa += 1;
}
HighMild => {
him += 1;
}
HighSevere => {
his += 1;
}
}
}
(los, lom, noa, him, his)
}
pub fn fences(&self) -> (A, A, A, A) {
self.fences
}
pub fn iter(&self) -> Iter<'a, A> {
Iter {
fences: self.fences,
iter: self.sample.iter(),
}
}
}
impl<'a, A> Deref for LabeledSample<'a, A>
where
A: Float,
{
type Target = Sample<A>;
fn deref(&self) -> &Sample<A> {
self.sample
}
}
impl<'a, A> Index<usize> for LabeledSample<'a, A>
where
A: Float,
{
type Output = Label;
#[allow(clippy::similar_names)]
fn index(&self, i: usize) -> &Label {
static LOW_SEVERE: Label = LowSevere;
static LOW_MILD: Label = LowMild;
static HIGH_MILD: Label = HighMild;
static HIGH_SEVERE: Label = HighSevere;
static NOT_AN_OUTLIER: Label = NotAnOutlier;
let x = self.sample[i];
let (lost, lomt, himt, hist) = self.fences;
if x < lost {
&LOW_SEVERE
} else if x > hist {
&HIGH_SEVERE
} else if x < lomt {
&LOW_MILD
} else if x > himt {
&HIGH_MILD
} else {
&NOT_AN_OUTLIER
}
}
}
impl<'a, A> IntoIterator for &LabeledSample<'a, A>
where
A: Float,
{
type Item = (A, Label);
type IntoIter = Iter<'a, A>;
fn into_iter(self) -> Iter<'a, A> {
self.iter()
}
}
pub struct Iter<'a, A>
where
A: Float,
{
fences: (A, A, A, A),
iter: slice::Iter<'a, A>,
}
impl<'a, A> Iterator for Iter<'a, A>
where
A: Float,
{
type Item = (A, Label);
#[allow(clippy::similar_names)]
fn next(&mut self) -> Option<(A, Label)> {
self.iter.next().map(|&x| {
let (lost, lomt, himt, hist) = self.fences;
let label = if x < lost {
LowSevere
} else if x > hist {
HighSevere
} else if x < lomt {
LowMild
} else if x > himt {
HighMild
} else {
NotAnOutlier
};
(x, label)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub enum Label {
HighMild,
HighSevere,
LowMild,
LowSevere,
NotAnOutlier,
}
impl Label {
pub fn is_high(&self) -> bool {
matches!(*self, HighMild | HighSevere)
}
pub fn is_mild(&self) -> bool {
matches!(*self, HighMild | LowMild)
}
pub fn is_low(&self) -> bool {
matches!(*self, LowMild | LowSevere)
}
pub fn is_outlier(&self) -> bool {
!matches!(*self, NotAnOutlier)
}
pub fn is_severe(&self) -> bool {
matches!(*self, HighSevere | LowSevere)
}
}
pub fn classify<A>(sample: &Sample<A>) -> LabeledSample<'_, A>
where
A: Float,
usize: cast::From<A, Output = Result<usize, cast::Error>>,
{
let (q1, _, q3) = sample.percentiles().quartiles();
let iqr = q3 - q1;
let k_m = A::cast(1.5_f32);
let k_s = A::cast(3);
LabeledSample {
fences: (
q1 - k_s * iqr,
q1 - k_m * iqr,
q3 + k_m * iqr,
q3 + k_s * iqr,
),
sample,
}
}