///|
pub struct ResidualMetrics {
count : Int
mean_absolute_error : Double
rmse : Double
max_abs_error : Double
max_relative_error : Double
signed_bias : Double
} derive(Debug)
///|
pub struct GridSpacing {
count : Int
minimum : Double
maximum : Double
mean : Double
} derive(Debug)
///|
fn positive_or_zero(value : Double) -> Double {
if value < 0.0 {
0.0 - value
} else {
value
}
}
///|
fn max_or_zero(first : Double, second : Double) -> Double {
if first > second {
first
} else {
second
}
}
///|
fn finite_relative_error(reference : Double, predicted : Double) -> Double {
let scale = max_or_zero(positive_or_zero(reference), 1.0E-12)
positive_or_zero(predicted - reference) / scale
}
///|
pub fn residual_metrics(
references : Array[Double],
predicted : Array[Double],
) -> ResidualMetrics {
if references.length() != predicted.length() || references.length() == 0 {
ResidualMetrics::{
count: 0,
mean_absolute_error: 0.0,
rmse: 0.0,
max_abs_error: 0.0,
max_relative_error: 0.0,
signed_bias: 0.0,
}
} else {
let (absolute, squared, maximum, relative, bias) = for i = 0, absolute = 0.0, squared = 0.0, maximum = 0.0, relative = 0.0, bias = 0.0; i <
references.length(); {
let error = predicted[i] - references[i]
let absolute_error = positive_or_zero(error)
let relative_error = finite_relative_error(references[i], predicted[i])
continue i + 1,
absolute + absolute_error,
squared + error * error,
max_or_zero(maximum, absolute_error),
max_or_zero(relative, relative_error),
bias + error
} nobreak {
(absolute, squared, maximum, relative, bias)
}
let count = references.length().to_double()
ResidualMetrics::{
count: references.length(),
mean_absolute_error: absolute / count,
rmse: @math.pow(squared / count, 0.5),
max_abs_error: maximum,
max_relative_error: relative,
signed_bias: bias / count,
}
}
}
///|
pub fn quality_gate(
metrics : ResidualMetrics,
max_rmse~ : Double,
max_relative_error~ : Double,
) -> Bool {
metrics.count > 0 &&
max_rmse >= 0.0 &&
max_relative_error >= 0.0 &&
metrics.rmse <= max_rmse &&
metrics.max_relative_error <= max_relative_error
}
///|
pub fn residual_vector(
references : Array[Double],
predicted : Array[Double],
) -> Array[Double] {
if references.length() != predicted.length() {
[]
} else {
[
for i in 0.. predicted[i] - references[i]
]
}
}
///|
pub fn max_abs_value(values : Array[Double]) -> Double {
for i = 0, maximum = 0.0; i < values.length(); {
continue i + 1, max_or_zero(maximum, positive_or_zero(values[i]))
} nobreak {
maximum
}
}
///|
pub fn monotonicity_violations(
values : Array[Double],
increasing~ : Bool,
) -> Int {
if values.length() < 2 {
0
} else {
for i = 1, count = 0; i < values.length(); {
let violates = if increasing {
values[i] < values[i - 1]
} else {
values[i] > values[i - 1]
}
continue i + 1, if violates { count + 1 } else { count }
} nobreak {
count
}
}
}
///|
pub fn grid_spacing(values : Array[Double]) -> GridSpacing {
if values.length() < 2 {
GridSpacing::{ count: 0, minimum: 0.0, maximum: 0.0, mean: 0.0 }
} else {
let (minimum, maximum, total) = for i = 1, minimum = 1.0E300, maximum = 0.0, total = 0.0; i <
values.length(); {
let spacing = positive_or_zero(values[i] - values[i - 1])
continue i + 1,
if spacing < minimum {
spacing
} else {
minimum
},
max_or_zero(maximum, spacing),
total + spacing
} nobreak {
(minimum, maximum, total)
}
let count = values.length() - 1
GridSpacing::{ count, minimum, maximum, mean: total / count.to_double() }
}
}
///|
pub fn grid_is_well_formed(
values : Array[Double],
minimum_spacing~ : Double,
) -> Bool {
values.length() >= 2 &&
minimum_spacing >= 0.0 &&
monotonicity_violations(values, increasing=true) == 0 &&
grid_spacing(values).minimum >= minimum_spacing
}
///|
pub fn normalized_bias(metrics : ResidualMetrics) -> Double {
if metrics.count == 0 {
0.0
} else {
metrics.signed_bias / max_or_zero(metrics.max_abs_error, 1.0E-12)
}
}
///|
pub fn metrics_summary(metrics : ResidualMetrics) -> String {
"n=" +
metrics.count.to_string() +
", mae=" +
metrics.mean_absolute_error.to_string() +
", rmse=" +
metrics.rmse.to_string() +
", max_abs=" +
metrics.max_abs_error.to_string() +
", max_rel=" +
metrics.max_relative_error.to_string()
}