///|
pub fn is_empty(data : Array[Double]) -> Bool {
data.length() == 0
}
///|
pub fn is_constant(data : Array[Double], tolerance? : Double = 0.0) -> Bool {
if data.length() <= 1 {
true
} else {
let first = data[0]
for value in data {
if abs_double(value - first) > tolerance {
return false
}
}
true
}
}
///|
pub fn is_sorted_non_decreasing(data : Array[Double]) -> Bool {
for index = 1; index < data.length(); index = index + 1 {
if data[index] < data[index - 1] {
return false
}
}
true
}
///|
pub fn is_sorted_non_increasing(data : Array[Double]) -> Bool {
for index = 1; index < data.length(); index = index + 1 {
if data[index] > data[index - 1] {
return false
}
}
true
}
///|
pub fn unique_value_count(data : Array[Double]) -> Int {
let sorted = copy_and_sort(data)
if sorted.length() == 0 {
return 0
}
let mut count = 1
for index = 1; index < sorted.length(); index = index + 1 {
if sorted[index] != sorted[index - 1] {
count += 1
}
}
count
}
///|
pub fn duplicate_fraction(data : Array[Double]) -> Double {
if data.length() == 0 {
0.0
} else {
1.0 - unique_value_count(data).to_double() / data.length().to_double()
}
}
///|
pub fn value_frequency(data : Array[Double], value : Double) -> Int {
let mut count = 0
for item in data {
if item == value {
count += 1
}
}
count
}
///|
pub fn value_frequency_fraction(data : Array[Double], value : Double) -> Double {
if data.length() == 0 {
0.0
} else {
value_frequency(data, value).to_double() / data.length().to_double()
}
}
///|
pub fn duplicate_indices(data : Array[Double]) -> Array[Int] {
let result = []
for index = 0; index < data.length(); index = index + 1 {
if value_frequency(data, data[index]) > 1 {
result.push(index)
}
}
result
}
///|
pub fn monotonicity_score(data : Array[Double]) -> Double {
if data.length() <= 1 {
return 1.0
}
let mut increasing = 0
let mut decreasing = 0
for index = 1; index < data.length(); index = index + 1 {
if data[index] > data[index - 1] {
increasing += 1
} else if data[index] < data[index - 1] {
decreasing += 1
}
}
let direction = if increasing >= decreasing { increasing } else { decreasing }
direction.to_double() / (data.length() - 1).to_double()
}
///|
pub fn sign_change_count(data : Array[Double]) -> Int {
let mut count = 0
for index = 1; index < data.length(); index = index + 1 {
if sign_double(data[index]) != sign_double(data[index - 1]) {
count += 1
}
}
count
}
///|
pub fn zero_fraction(data : Array[Double]) -> Double {
if data.length() == 0 {
return 0.0
}
let mut count = 0
for value in data {
if value == 0.0 {
count += 1
}
}
count.to_double() / data.length().to_double()
}
///|
pub fn range_ratio(data : Array[Double]) -> Double {
let minimum = abs_double(min_value(data))
let maximum = abs_double(max_value(data))
if minimum == 0.0 {
0.0
} else {
maximum / minimum
}
}
///|
pub fn boundary_distance(
data : Array[Double],
lower : Double,
upper : Double,
) -> Array[Double] {
if lower > upper {
abort("lower must not exceed upper")
}
let result = []
for value in data {
result.push(
if value < lower {
lower - value
} else if value > upper {
value - upper
} else {
0.0
},
)
}
result
}
///|
pub fn boundary_violation_count(
data : Array[Double],
lower : Double,
upper : Double,
) -> Int {
let mut count = 0
for distance in boundary_distance(data, lower, upper) {
if distance > 0.0 {
count += 1
}
}
count
}
///|
pub fn boundary_violation_fraction(
data : Array[Double],
lower : Double,
upper : Double,
) -> Double {
if data.length() == 0 {
0.0
} else {
boundary_violation_count(data, lower, upper).to_double() /
data.length().to_double()
}
}
///|
pub fn robust_range_quality(data : Array[Double]) -> Double {
if data.length() == 0 {
0.0
} else {
1.0 /
(
1.0 +
duplicate_fraction(data) +
outlier_fraction(outlier_indices_z(data), data.length())
)
}
}
///|
pub fn compare_quality(first : Array[Double], second : Array[Double]) -> Double {
robust_range_quality(first) - robust_range_quality(second)
}
///|
pub fn split_by_threshold(
data : Array[Double],
threshold : Double,
) -> Array[Array[Double]] {
let lower = []
let upper = []
for value in data {
if value <= threshold {
lower.push(value)
} else {
upper.push(value)
}
}
[lower, upper]
}
///|
pub fn split_by_median(data : Array[Double]) -> Array[Array[Double]] {
split_by_threshold(data, median(data))
}
///|
pub fn central_fraction(
data : Array[Double],
probability : Double,
) -> Array[Double] {
central_interval(data, probability)
}
///|
pub fn boundary_trimmed_data(
data : Array[Double],
lower : Double,
upper : Double,
) -> Array[Double] {
if lower > upper {
abort("lower must not exceed upper")
}
let result = []
for value in data {
if value >= lower && value <= upper {
result.push(value)
}
}
result
}