A vehicle sale or permanent withdrawal can prevent the target replacement; treating it as ordinary independent censoring can misstate the replacement probability.
Competing events and service-policy review
Classify end states
From an inspection, a vehicle may receive battery replacement, be sold, leave the fleet for another reason, or remain under observation. Sale prevents a fleet replacement under the current policy; it is a competing event, not a hidden future replacement. Distinguish it from an administrative export cutoff. Competing-risk incidence supplies the probability target.
Match probability to the decision
A mechanic deciding whether to pre-order parts needs the chance of replacement while the vehicle remains in the fleet. A hypothetical failure risk in a world where no vehicle is sold is a different question. The model output label must say which probability it estimates; otherwise two correct methods can appear to disagree.
Track policy changes
Preventive maintenance may replace batteries earlier or avoid emergency replacement. If a new policy begins midway through the dataset, event incidence changes because actions changed, not necessarily because battery condition improved. Evaluate by calendar period and policy version. A risk model trained on old policy may not forecast the new one.
Do not collapse event types
A generic “closed episode” label merges replacement and sale, teaching the model to predict fleet turnover. The code retains explicit end states and computes crude counts; those counts are not cumulative-incidence estimates because follow-up differs. Censor-aware evaluation is still required.
Write a release gate
Set acceptable calibration for replacement incidence at a supported horizon and track sale incidence as a separate context measure. Hold deployment if a depot’s sale process is not recorded or if the maintenance policy changed without a compatible validation window. The release project joins these checks.
Implementation
vehicle_end_states = [
{"vehicle": "fleet-47", "day": 57, "state": "replacement"},
{"vehicle": "fleet-62", "day": 71, "state": "administrative_cutoff"},
{"vehicle": "fleet-83", "day": 28, "state": "sale"},
{"vehicle": "fleet-94", "day": 64, "state": "replacement"},
]
def end_state_counts(records):
allowed = {"replacement", "sale", "administrative_cutoff"}
counts = {state: 0 for state in allowed}
for record in records:
if record["state"] not in allowed:
raise ValueError("unclassified end state")
counts[record["state"]] += 1
return counts
assert end_state_counts(vehicle_end_states) == {
"replacement": 2, "sale": 1, "administrative_cutoff": 1}Performance and operating cost
Classifying N episode endpoints costs O(N) time and O(1) counters for a fixed event vocabulary. Estimating cause-specific or cumulative-incidence curves needs event-time risk sets and sufficient follow-up. Policy-stratified validation reduces sample size, so show support before drawing conclusions.
Common Mistakes
- Do not code sale as a battery replacement.
- Do not interpret crude event fractions as horizon probabilities.
- Do not ignore preventive-maintenance policy changes in model validation.
