///|
pub struct Statistics {
count : Int
minimum : Float
maximum : Float
mean : Float
variance : Float
standard_deviation : Float
sum : Float
} derive(Debug, Eq)
///|
pub fn mean(values : Array[Float]) -> Float? {
if values.length() == 0 {
None
} else {
let mut total : Float = 0.0
for value in values {
total = total + value
}
Some(total / Float::from_int(values.length()))
}
}
///|
pub fn summarize(values : Array[Float]) -> Statistics {
if values.length() == 0 {
{
count: 0,
minimum: 0.0,
maximum: 0.0,
mean: 0.0,
variance: 0.0,
standard_deviation: 0.0,
sum: 0.0,
}
} else {
let mut total : Float = 0.0
let mut minimum = values[0]
let mut maximum = values[0]
for value in values {
total = total + value
if value < minimum {
minimum = value
}
if value > maximum {
maximum = value
}
}
let average : Float = total / Float::from_int(values.length())
let mut squared : Float = 0.0
for value in values {
let delta = value - average
squared = squared + delta * delta
}
let variance : Float = squared / Float::from_int(values.length())
{
count: values.length(),
minimum,
maximum,
mean: average,
variance,
standard_deviation: variance.sqrt(),
sum: total,
}
}
}
///|
pub fn percentile(values : Array[Float], percentile : Float) -> Float? {
if values.length() == 0 || percentile < 0.0 || percentile > 100.0 {
return None
}
let sorted = values.copy()
sorted.sort()
let position : Float = percentile /
100.0 *
Float::from_int(sorted.length() - 1)
let lower = position.floor().to_int()
let upper = position.ceil().to_int()
if lower == upper {
Some(sorted[lower])
} else {
let fraction = position - Float::from_int(lower)
Some(sorted[lower] + (sorted[upper] - sorted[lower]) * fraction)
}
}
///|
pub fn moving_average(values : Array[Float], window : Int) -> Array[Float] {
let result : Array[Float] = []
if window <= 0 {
return result
}
for index in 0.. ObservationFlag {
NormalObservation
}
///|
pub fn above_control_limit() -> ObservationFlag {
AboveControlLimit
}
///|
pub fn below_control_limit() -> ObservationFlag {
BelowControlLimit
}
///|
pub struct ControlLimits {
center : Float
upper : Float
lower : Float
} derive(Debug, Eq)
///|
pub fn control_limits(values : Array[Float]) -> ControlLimits {
let summary = summarize(values)
let span : Float = 3.0 * summary.standard_deviation
{
center: summary.mean,
upper: summary.mean + span,
lower: summary.mean - span,
}
}
///|
pub fn ControlLimits::flag(
self : ControlLimits,
values : Array[Float],
) -> Array[ObservationFlag] {
let result : Array[ObservationFlag] = []
for value in values {
if value > self.upper {
result.push(AboveControlLimit)
} else if value < self.lower {
result.push(BelowControlLimit)
} else {
result.push(NormalObservation)
}
}
result
}
///|
pub fn ControlLimits::contains(self : ControlLimits, value : Float) -> Bool {
value >= self.lower && value <= self.upper
}
///|
pub fn weighted_mean(values : Array[Float], weights : Array[Float]) -> Float? {
if values.length() == 0 || values.length() != weights.length() {
return None
}
let mut numerator : Float = 0.0
let mut denominator : Float = 0.0
for index, value in values {
numerator = numerator + value * weights[index]
denominator = denominator + weights[index]
}
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
///|
pub fn coefficient_of_variation(values : Array[Float]) -> Float? {
let summary = summarize(values)
if summary.count == 0 || summary.mean == 0.0 {
None
} else {
Some(summary.standard_deviation / summary.mean)
}
}
///|
pub fn clamp(value : Float, minimum : Float, maximum : Float) -> Float {
if value < minimum {
minimum
} else if value > maximum {
maximum
} else {
value
}
}