///|
pub struct BenchmarkCase {
name : String
sample_size : Int
mean : Double
median : Double
mad : Double
trimmed_mean : Double
winsorized_mean : Double
huber_location : Double
outlier_count : Int
covariance : Double
stream_mean : Double
}
///|
pub fn benchmark_data(size : Int) -> Array[Double] {
if size < 0 {
abort("size must not be negative")
}
let result = []
for index = 0; index < size; index = index + 1 {
let cycle = index % 17
let baseline = 100.0 + cycle.to_double() * 0.25
let spike = if index % 97 == 0 {
250.0
} else if index % 131 == 0 {
-180.0
} else {
0.0
}
result.push(baseline + spike)
}
result
}
///|
pub fn benchmark_case(name : String, data : Array[Double]) -> BenchmarkCase {
let stream = StreamingRobustStats::new(5.0)
stream.push_many(data)
let x = []
let y = []
for index = 0; index < data.length(); index = index + 1 {
x.push(index.to_double())
y.push(data[index] * 0.5)
}
{
name,
sample_size: data.length(),
mean: mean(data),
median: median(data),
mad: mad(data),
trimmed_mean: trimmed_mean(data, 0.1),
winsorized_mean: winsorized_mean(data, 0.1),
huber_location: huber_location(data),
outlier_count: outlier_indices_iqr(data).length(),
covariance: robust_covariance(x, y, 0.1),
stream_mean: stream.mean(),
}
}
///|
pub fn benchmark_cases(sizes : Array[Int]) -> Array[BenchmarkCase] {
let result = []
for size in sizes {
result.push(benchmark_case("synthetic-contaminated", benchmark_data(size)))
}
result
}
///|
pub fn benchmark_checksum(case : BenchmarkCase) -> Double {
case.mean +
case.median +
case.mad +
case.trimmed_mean +
case.winsorized_mean +
case.huber_location +
case.covariance +
case.stream_mean +
case.outlier_count.to_double()
}
///|
pub fn benchmark_checksums(sizes : Array[Int]) -> Array[Double] {
let result = []
for case in benchmark_cases(sizes) {
result.push(benchmark_checksum(case))
}
result
}
///|
pub fn benchmark_summary(case : BenchmarkCase) -> Array[String] {
[
"name=" + case.name,
"sample_size=" + case.sample_size.to_string(),
"mean=" + case.mean.to_string(),
"median=" + case.median.to_string(),
"mad=" + case.mad.to_string(),
"trimmed_mean=" + case.trimmed_mean.to_string(),
"winsorized_mean=" + case.winsorized_mean.to_string(),
"huber_location=" + case.huber_location.to_string(),
"outlier_count=" + case.outlier_count.to_string(),
"covariance=" + case.covariance.to_string(),
"stream_mean=" + case.stream_mean.to_string(),
]
}
///|
pub fn benchmark_report(sizes : Array[Int]) -> String {
let lines = []
for case in benchmark_cases(sizes) {
for line in benchmark_summary(case) {
lines.push(line)
}
lines.push("checksum=" + benchmark_checksum(case).to_string())
lines.push("")
}
lines.join("\n")
}
///|
pub fn benchmark_scaling_score(
small : BenchmarkCase,
large : BenchmarkCase,
) -> Double {
if small.sample_size <= 0 || large.sample_size <= 0 {
0.0
} else {
large.sample_size.to_double() / small.sample_size.to_double()
}
}
///|
pub fn benchmark_reproducible(sizes : Array[Int]) -> Bool {
benchmark_checksums(sizes) == benchmark_checksums(sizes)
}
///|
pub fn benchmark_contamination_fraction(data : Array[Double]) -> Double {
outlier_fraction(outlier_indices_iqr(data), data.length())
}
///|
pub fn benchmark_robust_gain(case : BenchmarkCase) -> Double {
abs_double(case.mean - case.median) / (1.0 + case.mad)
}