///|
pub fn summary(values : ArrayView[Double]) -> Summary {
if values.length() == 0 {
return {
count: 0,
missing: 0,
minimum: 0.0,
maximum: 0.0,
mean: 0.0,
variance: 0.0,
sum: 0.0,
}
}
let mut count = 0
let mut missing = 0
let mut sum = 0.0
let mut minimum = 1.0e308
let mut maximum = -1.0e308
for value in values {
if is_finite(value) {
count += 1
sum += value
if value < minimum {
minimum = value
}
if value > maximum {
maximum = value
}
} else {
missing += 1
}
}
if count == 0 {
{
count: 0,
missing,
minimum: 0.0,
maximum: 0.0,
mean: 0.0,
variance: 0.0,
sum: 0.0,
}
} else {
let mean = sum / Double::from_int(count)
let mut squared = 0.0
for value in values {
if is_finite(value) {
squared += (value - mean) * (value - mean)
}
}
{
count,
missing,
minimum,
maximum,
mean,
variance: squared / Double::from_int(count),
sum,
}
}
}
///|
pub fn values_from_samples(
samples : ArrayView[FieldSample],
policy? : MissingPolicy = SkipMissing,
) -> Array[Double] {
let values : Array[Double] = []
for sample in samples {
if is_finite(sample.value) {
values.push(sample.value)
} else {
match policy {
SkipMissing => ()
PreserveMissing => values.push(0.0 / 0.0)
ReplaceWith(value~) => values.push(value)
}
}
}
values
}
///|
pub fn sample_summary(samples : ArrayView[FieldSample]) -> Summary {
summary(values_from_samples(samples))
}
///|
pub fn z_score(value : Double, center : Double, scale : Double) -> Double {
if scale == 0.0 {
0.0
} else {
(value - center) / scale
}
}
///|
pub fn rms(values : ArrayView[Double]) -> Double {
let mut total = 0.0
let mut count = 0
for value in values {
if is_finite(value) {
total += value * value
count += 1
}
}
if count == 0 {
0.0
} else {
(total / Double::from_int(count)).sqrt()
}
}
///|
pub fn weighted_mean(
values : ArrayView[Double],
weights : ArrayView[Double],
) -> Double {
let mut total = 0.0
let mut weight_total = 0.0
let limit = Int::min(values.length(), weights.length())
for i in 0..= 0.0 {
total += values[i] * weights[i]
weight_total += weights[i]
}
}
if weight_total == 0.0 {
0.0
} else {
total / weight_total
}
}
///|
pub fn percentile(values : ArrayView[Double], fraction : Double) -> Double {
let clean : Array[Double] = []
for value in values {
if is_finite(value) {
clean.push(value)
}
}
clean.sort()
if clean.length() == 0 {
0.0
} else {
let index = (fraction.clamp(min=0.0, max=1.0) *
Double::from_int(clean.length() - 1))
.round()
.to_int()
clean[index]
}
}
///|
pub fn median(values : ArrayView[Double]) -> Double {
percentile(values, 0.5)
}
///|
pub fn interquartile_range(values : ArrayView[Double]) -> Double {
percentile(values, 0.75) - percentile(values, 0.25)
}