///|
pub fn segment_bounds(length : Int, segments : Int) -> Array[Array[Int]] {
if segments <= 0 {
abort("segments must be positive")
}
let result = []
for segment = 0; segment < segments; segment = segment + 1 {
result.push([segment * length / segments, (segment + 1) * length / segments])
}
result
}
///|
pub fn segment_values(
data : Array[Double],
segments : Int,
) -> Array[Array[Double]] {
let result = []
for bounds in segment_bounds(data.length(), segments) {
let values = []
for index = bounds[0]; index < bounds[1]; index = index + 1 {
values.push(data[index])
}
result.push(values)
}
result
}
///|
pub fn segment_means(data : Array[Double], segments : Int) -> Array[Double] {
let result = []
for values in segment_values(data, segments) {
result.push(mean(values))
}
result
}
///|
pub fn segment_medians(data : Array[Double], segments : Int) -> Array[Double] {
let result = []
for values in segment_values(data, segments) {
result.push(median(values))
}
result
}
///|
pub fn segment_mads(data : Array[Double], segments : Int) -> Array[Double] {
let result = []
for values in segment_values(data, segments) {
result.push(mad(values))
}
result
}
///|
pub fn segment_trimmed_means(
data : Array[Double],
segments : Int,
trim_percent : Double,
) -> Array[Double] {
let result = []
for values in segment_values(data, segments) {
result.push(trimmed_mean(values, trim_percent))
}
result
}
///|
pub fn cumulative_mean(data : Array[Double]) -> Array[Double] {
let result = []
let mut total = 0.0
for index = 0; index < data.length(); index = index + 1 {
total += data[index]
result.push(total / (index + 1).to_double())
}
result
}
///|
pub fn cumulative_weighted_mean(
data : Array[Double],
weights : Array[Double],
) -> Array[Double] {
if data.length() != weights.length() {
return []
}
let result = []
let mut numerator = 0.0
let mut denominator = 0.0
for index = 0; index < data.length(); index = index + 1 {
if weights[index] < 0.0 {
abort("weights must be non-negative")
}
numerator += data[index] * weights[index]
denominator += weights[index]
result.push(if denominator == 0.0 { 0.0 } else { numerator / denominator })
}
result
}
///|
pub fn cumulative_quantile(
data : Array[Double],
probability : Double,
) -> Array[Double] {
let result = []
let prefix = []
for value in data {
prefix.push(value)
result.push(quantile(prefix, probability))
}
result
}
///|
pub fn cumulative_trimmed_mean(
data : Array[Double],
trim_percent : Double,
) -> Array[Double] {
let result = []
let prefix = []
for value in data {
prefix.push(value)
result.push(trimmed_mean(prefix, trim_percent))
}
result
}
///|
pub fn cumulative_winsorized_mean(
data : Array[Double],
trim_percent : Double,
) -> Array[Double] {
let result = []
let prefix = []
for value in data {
prefix.push(value)
result.push(winsorized_mean(prefix, trim_percent))
}
result
}
///|
pub fn running_minimum(data : Array[Double]) -> Array[Double] {
let result = []
if data.length() == 0 {
return result
}
let mut current = data[0]
for value in data {
if value < current {
current = value
}
result.push(current)
}
result
}
///|
pub fn running_maximum(data : Array[Double]) -> Array[Double] {
let result = []
if data.length() == 0 {
return result
}
let mut current = data[0]
for value in data {
if value > current {
current = value
}
result.push(current)
}
result
}
///|
pub fn running_range(data : Array[Double]) -> Array[Double] {
let minimum = running_minimum(data)
let maximum = running_maximum(data)
let result = []
for index = 0; index < minimum.length(); index = index + 1 {
result.push(maximum[index] - minimum[index])
}
result
}
///|
pub fn aggregate_by_quantile(
data : Array[Double],
groups : Int,
) -> Array[Array[Double]] {
if groups <= 0 {
abort("groups must be positive")
}
let result = []
for group = 0; group < groups; group = group + 1 {
let lower = group.to_double() / groups.to_double()
let upper = (group + 1).to_double() / groups.to_double()
let values = []
for value in data {
let probability = empirical_cdf(data, value)
if probability > lower && probability <= upper {
values.push(value)
}
}
result.push(values)
}
result
}
///|
pub fn quantile_group_means(
data : Array[Double],
groups : Int,
) -> Array[Double] {
let result = []
for values in aggregate_by_quantile(data, groups) {
result.push(mean(values))
}
result
}
///|
pub fn quantile_group_mads(data : Array[Double], groups : Int) -> Array[Double] {
let result = []
for values in aggregate_by_quantile(data, groups) {
result.push(mad(values))
}
result
}
///|
pub fn aggregate_error_by_segment(
actual : Array[Double],
predicted : Array[Double],
segments : Int,
) -> Array[Double] {
if actual.length() != predicted.length() {
return []
}
let errors = []
for index = 0; index < actual.length(); index = index + 1 {
errors.push(actual[index] - predicted[index])
}
segment_mads(errors, segments)
}
///|
pub fn segment_outlier_counts(
data : Array[Double],
segments : Int,
threshold? : Double = 3.5,
) -> Array[Int] {
let result = []
for values in segment_values(data, segments) {
result.push(outlier_indices_z(values, threshold~).length())
}
result
}
///|
pub fn segment_quality_scores(
data : Array[Double],
segments : Int,
) -> Array[Double] {
let result = []
for values in segment_values(data, segments) {
result.push(robust_signal_quality(values))
}
result
}