///|
/// Data-cleaning report for reliability observations.
pub struct DataQualityReport {
input_count : Int
valid_count : Int
invalid_count : Int
duplicate_count : Int
negative_count : Int
zero_count : Int
warnings : Array[String]
}
///|
pub fn data_quality_report(
input_count~ : Int,
valid_count~ : Int,
invalid_count~ : Int,
duplicate_count~ : Int,
negative_count~ : Int,
zero_count~ : Int,
warnings~ : Array[String],
) -> DataQualityReport {
{
input_count,
valid_count,
invalid_count,
duplicate_count,
negative_count,
zero_count,
warnings,
}
}
///|
pub fn inspect_times(times : Array[Double]) -> DataQualityReport {
let warnings : Array[String] = []
let mut negative = 0
let mut zero = 0
let mut duplicates = 0
let sorted = times.copy()
sorted.sort()
for i in 0.. 0 {
warnings.push("negative times found")
}
if zero > 0 {
warnings.push("zero times require special PDF handling")
}
if duplicates > 0 {
warnings.push("tied event times found; use a tied-risk estimator")
}
data_quality_report(
input_count=times.length(),
valid_count=times.length() - negative,
invalid_count=negative,
duplicate_count=duplicates,
negative_count=negative,
zero_count=zero,
warnings~,
)
}
///|
pub fn winsorize(
values : Array[Double],
lower_probability : Double,
upper_probability : Double,
) -> Array[Double] {
if lower_probability < 0.0 ||
upper_probability > 1.0 ||
lower_probability >= upper_probability {
abort("invalid winsorization probabilities")
}
let lower = quantile(values, lower_probability)
let upper = quantile(values, upper_probability)
values.map(value => value.max(lower).min(upper))
}
///|
pub fn remove_nonpositive(values : Array[Double]) -> Array[Double] {
values.filter(value => value > 0.0)
}
///|
pub fn impute_right_censoring(
times : Array[Double],
censor_time : Double,
) -> Array[LifeObservation] {
times.map(time => {
if time <= censor_time {
failure(time)
} else {
right_censored(censor_time)
}
})
}
///|
pub fn merge_observations(
first : Array[LifeObservation],
second : Array[LifeObservation],
) -> Array[LifeObservation] {
let result = first.copy()
for record in second {
result.push(record)
}
sort_observations(result)
}
///|
pub fn stratify_observations(
records : Array[LifeObservation],
strata : Array[Int],
) -> Map[Int, Array[LifeObservation]] {
if records.length() != strata.length() {
abort("strata length mismatch")
}
let result : Map[Int, Array[LifeObservation]] = Map([])
for i in 0.. [])
bucket.push(records[i])
}
result
}