///|
pub fn weighted_median(data : Array[Double], weights : Array[Double]) -> Double {
weighted_quantile(data, weights, 0.5)
}
///|
pub fn weighted_lower_quartile(
data : Array[Double],
weights : Array[Double],
) -> Double {
weighted_quantile(data, weights, 0.25)
}
///|
pub fn weighted_upper_quartile(
data : Array[Double],
weights : Array[Double],
) -> Double {
weighted_quantile(data, weights, 0.75)
}
///|
pub fn weighted_iqr(data : Array[Double], weights : Array[Double]) -> Double {
weighted_upper_quartile(data, weights) -
weighted_lower_quartile(data, weights)
}
///|
pub fn trimmed_sum(data : Array[Double], trim_percent : Double) -> Double {
validate_trim(trim_percent)
if data.length() == 0 {
return 0.0
}
let sorted = copy_and_sort(data)
let cut = (sorted.length().to_double() * trim_percent).to_int()
let mut total = 0.0
for index = cut; index < sorted.length() - cut; index = index + 1 {
total += sorted[index]
}
total
}
///|
pub fn winsorized_sum(data : Array[Double], trim_percent : Double) -> Double {
sum_values(winsorize(data, trim_percent))
}
///|
pub fn midhinge(data : Array[Double]) -> Double {
(lower_quartile(data) + upper_quartile(data)) / 2.0
}
///|
pub fn trimean(data : Array[Double]) -> Double {
(lower_quartile(data) + 2.0 * median(data) + upper_quartile(data)) / 4.0
}
///|
pub fn quantile_skewness(data : Array[Double]) -> Double {
let denominator = upper_quartile(data) - lower_quartile(data)
if denominator == 0.0 {
0.0
} else {
(upper_quartile(data) + lower_quartile(data) - 2.0 * median(data)) /
denominator
}
}
///|
pub fn bowley_skewness(data : Array[Double]) -> Double {
quantile_skewness(data)
}
///|
pub fn moors_kurtosis(data : Array[Double]) -> Double {
let q1 = quantile(data, 0.125)
let q2 = quantile(data, 0.375)
let q3 = quantile(data, 0.625)
let q4 = quantile(data, 0.875)
let denominator = upper_quartile(data) - lower_quartile(data)
if denominator == 0.0 {
0.0
} else {
(q4 - q3 + q1 - q2) / denominator
}
}
///|
pub fn lower_tail_mean(data : Array[Double], probability : Double) -> Double {
if data.length() == 0 {
return 0.0
}
validate_probability(probability)
let threshold = quantile(data, probability)
let selected = []
for value in data {
if value <= threshold {
selected.push(value)
}
}
mean(selected)
}
///|
pub fn upper_tail_mean(data : Array[Double], probability : Double) -> Double {
if data.length() == 0 {
return 0.0
}
validate_probability(probability)
let threshold = quantile(data, probability)
let selected = []
for value in data {
if value >= threshold {
selected.push(value)
}
}
mean(selected)
}
///|
pub fn expected_shortfall(data : Array[Double], probability : Double) -> Double {
upper_tail_mean(data, probability)
}
///|
pub fn lower_partial_moment(
data : Array[Double],
order : Int,
threshold : Double,
) -> Double {
if order < 0 {
abort("order must be non-negative")
}
let mut total = 0.0
let mut count = 0
for value in data {
if value < threshold {
let mut power = 1.0
let difference = threshold - value
for exponent = 0; exponent < order; exponent = exponent + 1 {
power *= difference
}
total += power
count += 1
}
}
if count == 0 {
0.0
} else {
total / count.to_double()
}
}
///|
pub fn upper_partial_moment(
data : Array[Double],
order : Int,
threshold : Double,
) -> Double {
if order < 0 {
abort("order must be non-negative")
}
let mut total = 0.0
let mut count = 0
for value in data {
if value > threshold {
let mut power = 1.0
let difference = value - threshold
for exponent = 0; exponent < order; exponent = exponent + 1 {
power *= difference
}
total += power
count += 1
}
}
if count == 0 {
0.0
} else {
total / count.to_double()
}
}
///|
pub fn tail_count(
data : Array[Double],
threshold : Double,
upper? : Bool = true,
) -> Int {
let mut count = 0
for value in data {
if upper && value >= threshold {
count += 1
} else if !upper && value <= threshold {
count += 1
}
}
count
}
///|
pub fn tail_fraction(
data : Array[Double],
threshold : Double,
upper? : Bool = true,
) -> Double {
if data.length() == 0 {
0.0
} else {
tail_count(data, threshold, upper~).to_double() / data.length().to_double()
}
}
///|
pub fn rank_sum(data : Array[Double]) -> Double {
sum_values(ranks(data))
}
///|
pub fn rank_distance(left : Array[Double], right : Array[Double]) -> Double {
if left.length() != right.length() {
return 0.0
}
let left_ranks = ranks(left)
let right_ranks = ranks(right)
let mut total = 0.0
for index = 0; index < left.length(); index = index + 1 {
total += abs_double(left_ranks[index] - right_ranks[index])
}
total
}
///|
pub fn pairwise_median_difference(data : Array[Double]) -> Double {
if data.length() == 0 {
return 0.0
}
let differences = []
for left in data {
for right in data {
differences.push(left - right)
}
}
median(differences)
}
///|
pub fn pairwise_absolute_difference_median(data : Array[Double]) -> Double {
if data.length() == 0 {
return 0.0
}
let differences = []
for left = 0; left < data.length(); left = left + 1 {
for right = left + 1; right < data.length(); right = right + 1 {
differences.push(abs_double(data[left] - data[right]))
}
}
median(differences)
}
///|
pub fn median_ratio(data : Array[Double], baseline : Double) -> Double {
if baseline == 0.0 {
abort("baseline must not be zero")
}
median(data) / baseline
}
///|
pub fn relative_iqr(data : Array[Double]) -> Double {
let center = abs_double(median(data))
if center == 0.0 {
0.0
} else {
interquartile_range(data) / center
}
}