///|
/// One point of a cumulative-incidence curve.
pub struct CumulativeIncidencePoint {
time : Double
at_risk : Int
events_by_cause : Array[Int]
survival : Double
incidence_by_cause : Array[Double]
}
///|
pub fn cumulative_incidence_point(
time~ : Double,
at_risk~ : Int,
events_by_cause~ : Array[Int],
survival~ : Double,
incidence_by_cause~ : Array[Double],
) -> CumulativeIncidencePoint {
{ time, at_risk, events_by_cause, survival, incidence_by_cause }
}
///|
pub struct CompetingRiskCurve {
points : Array[CumulativeIncidencePoint]
causes : Int
final_incidence : Array[Double]
}
///|
pub fn competing_risk_curve(
points~ : Array[CumulativeIncidencePoint],
causes~ : Int,
final_incidence~ : Array[Double],
) -> CompetingRiskCurve {
{ points, causes, final_incidence }
}
///|
/// Cause values are stored in the observation's `cause` field; cause zero is
/// treated as ordinary failure and causes start at one for competing events.
pub fn cumulative_incidence(
records : Array[LifeObservation],
causes : Int,
) -> CompetingRiskCurve {
if records.is_empty() || causes <= 0 {
abort("competing risks requires records and causes")
}
let sorted = sort_observations(records)
let points : Array[CumulativeIncidencePoint] = []
let incidence = Array::make(causes, 0.0)
let mut survival = 1.0
let mut i = 0
while i < sorted.length() {
let time = sorted[i].time
let events = Array::make(causes, 0)
let mut censored = 0
let mut j = i
while j < sorted.length() && sorted[j].time == time {
if sorted[j].is_failure() {
let cause = sorted[j].cause.max(1).min(causes)
events[cause - 1] += 1
} else {
censored += 1
}
j += 1
}
let mut risk = 0
for record in sorted {
if record.time >= time {
risk += 1
}
}
let total_events = events.fold(init=0, (s, value) => s + value)
if risk > 0 && total_events > 0 {
for cause in 0.. Double {
if cause < 1 || cause > self.causes {
abort("cause outside curve")
}
let mut result = 0.0
for point in self.points {
if point.time <= time {
result = point.incidence_by_cause[cause - 1]
}
}
result
}
///|
pub fn cause_specific_hazard(
records : Array[LifeObservation],
cause : Int,
) -> Array[MetricEstimate] {
if cause <= 0 {
abort("cause must be positive")
}
let sorted = sort_observations(records)
let result : Array[MetricEstimate] = []
let mut i = 0
while i < sorted.length() {
let time = sorted[i].time
let mut events = 0
while i < sorted.length() && sorted[i].time == time {
if sorted[i].is_failure() && sorted[i].cause == cause {
events += 1
}
i += 1
}
let mut risk = 0
for record in sorted {
if record.time >= time {
risk += 1
}
}
if events > 0 && risk > 0 {
let value = events.to_double() / risk.to_double()
result.push(
metric_estimate(
estimate=value,
lower=value,
upper=value,
confidence_level=1.0,
),
)
}
}
result
}