Thinking about your unobserved failures (you can split the dataset in observed and unobserved failures). The likelihood for unobserved failures is something like:
pm.Mixture(
"unobserved_failure",
w=[p_faulty_sensor, 1 - p_faulty_sensor],
dist=[
# If sensor is faulty, we have left censoring
pm.Censored.dist(dist=time_to_event, lower=df.check_day),
# Otherwise, right censoring
pm.Censored.dist(dist=time_to_event, upper=df.check_day),
],
observed=df.check_day
)
For observed failures it’s just left censoring.
The other thing you want to do is to inform p_faulty_sensor by the number of observed_failures as well. That’s an extra likelihood term of the form:
pm.Binomial("observed_failures", n=n_observed_failures, p=1-p_faulty_sensor, observed=n_observed_failures")
I would have to think a bit more whether you can combine the 3 elements into a single “distribution”. If not, a CustomDist with manual logp may be the more clean implementation.