///|
fn observation_values(observations : Array[Observation]) -> Array[Double] {
let values = []
for observation in observations {
values.push(observation.value)
}
values
}
///|
/// Summarize observed values and verdicts for one feature.
pub fn summarize_batch(
feature~ : Feature,
observations~ : Array[Observation],
) -> BatchSummary {
let values = observation_values(observations)
let mut pass_count = 0
let mut fail_count = 0
let mut unmeasured_count = 0
for observation in observations {
match classify(feature, Some(observation)) {
Pass => pass_count += 1
Fail(_) => fail_count += 1
Unmeasured => unmeasured_count += 1
}
}
let count = observations.length()
let defect_rate = if count == 0 {
0.0
} else {
fail_count.to_double() / count.to_double()
}
{
feature_id: feature.id,
count,
pass_count,
fail_count,
unmeasured_count,
mean: mean(values),
minimum: minimum(values),
maximum: maximum(values),
range: range(values),
standard_deviation: sample_stddev(values),
defect_rate,
capability: capability_indices(feature~, values~, uncertainty=0.0),
}
}
///|
/// Return the pass rate for a summary.
pub fn pass_rate(summary : BatchSummary) -> Double {
if summary.count == 0 {
0.0
} else {
summary.pass_count.to_double() / summary.count.to_double()
}
}
///|
/// Return whether all observed values passed.
pub fn batch_is_clean(summary : BatchSummary) -> Bool {
summary.count > 0 && summary.fail_count == 0 && summary.unmeasured_count == 0
}
///|
/// Return a simple trend label based on the sequence of observed values.
pub fn batch_trend(values : Array[Double]) -> String {
if values.length() < 2 {
"stable"
} else if is_non_decreasing(values) && values[values.length() - 1] > values[0] {
"increasing"
} else if is_non_increasing(values) && values[values.length() - 1] < values[0] {
"decreasing"
} else {
"mixed"
}
}