Skip to main content

Section 13.5 Estimating Hazard Functions

In the light bulb example, we know the life spans for all 50 bulbs, so we can compute the survival function directlyโ€”and we can use the survival function to compute the hazard function.
In the marriage example, we know the age at first marriage for some respondents, the ones who had been married before they were interviewed. But for respondents who had never married, we donโ€™t know at what age they would marry in the futureโ€”or if they will.
This kind of missing data is said to be censored. That term might seem odd, because censored information is usually hidden deliberately, but in this case it is hidden just because we donโ€™t know the future.
However, we have partial information we can work with: if someone is unmarried when they are surveyed, we know that the age when they get married (if they do) must be greater than their current age.
We can use this partial information to estimate the hazard function; then we can use the hazard function to compute the survival function. This process is called Kaplan-Meier estimation.
To demonstrate, Iโ€™ll select just one cohort from the resampled data.
Listing 13.5.1. Python Code
resp60 = sample.query("cohort == 1960")
For respondents who were married when they were surveyed, weโ€™ll select their age at first marriage. There are 9921 of them, which weโ€™ll call โ€œcompleteโ€ cases.
Listing 13.5.2. Python Code
complete = resp60.query("evrmarry == 1")["agemarr"]
complete.count()
np.int64(9921)
For respondents who had not married, weโ€™ll select their age when they were surveyed. There are 5468 of them, which weโ€™ll call the โ€œongoingโ€ cases.
Listing 13.5.3. Python Code
ongoing = resp60.query("evrmarry == 0")["age"]
ongoing.count()
np.int64(5468)
Now, to estimate the hazard function, weโ€™ll compute the total number of cases that were โ€œat riskโ€ at each age, including everyone who was unmarried up to that age. It will be convenient to make a FreqTab object that counts the number of complete and ongoing cases at each age.
Listing 13.5.4. Python Code
from empiricaldist import FreqTab

ft_complete = FreqTab.from_seq(complete)
ft_ongoing = FreqTab.from_seq(ongoing)
As an example, there are 58 respondents who reported that they were married for the first time at age 25.
Listing 13.5.5. Python Code
ft_complete[25]
np.int64(58)
And another 5 respondents who were surveyed at age 25 and reported that they had never married.
Listing 13.5.6. Python Code
ft_ongoing[25]
np.int64(5)
From these FreqTab objects, we can compute unnormalized Surv objects that contain the number of complete and ongoing cases that exceed each age.
Listing 13.5.7. Python Code
surv_complete = ft_complete.make_surv()
surv_ongoing = ft_ongoing.make_surv()
For example, there are 2848 people who reported getting married after age 25.
Listing 13.5.8. Python Code
surv_complete[25]
np.int64(2848)
And 2273 people surveyed after age 25 who had never married.
Listing 13.5.9. Python Code
surv_ongoing[25]
np.int64(2273)
The sum of the four numbers we just computed is the number of respondents who were at riskโ€”that is, people who could have married at age 25. The term โ€œat riskโ€ is a legacy of survival analysis in medicine, where it often refers to risk of disease or death. It might seem incongruent in the context of marriage, which is generally considered a positive milestone. That said, hereโ€™s how we compute it.
Listing 13.5.10. Python Code
at_risk = ft_complete[25] + ft_ongoing[25] + surv_complete[25] + surv_ongoing[25]
at_risk
np.int64(5184)
Of those, the number who actually married at age 25 is ft_complete[25]. So we can compute the hazard function at age 25 like this.
Listing 13.5.11. Python Code
hazard = ft_complete[25] / at_risk
hazard
np.float64(0.011188271604938271)
Thatโ€™s how we can compute the hazard function at a single age. Now letโ€™s compute the whole function, for all ages. Weโ€™ll use the union method of the Index class to compute a Pandas Index that contains all of the ages from ft_complete and ft_ongoing, in order.
Listing 13.5.12. Python Code
ts = pd.Index.union(ft_complete.index, ft_ongoing.index)
Now we can compute the number of people at risk at every age, by looking up the ages in ts in each of the FreqTab and Surv objects.
Listing 13.5.13. Python Code
at_risk = ft_complete(ts) + ft_ongoing(ts) + surv_complete(ts) + surv_ongoing(ts)
Finally, we can compute the hazard function at each age, and put the results into a Hazard object.
Listing 13.5.14. Python Code
from empiricaldist import Hazard

hs = ft_complete(ts) / at_risk
hazard = Hazard(hs, ts)
Hereโ€™s what the cumulative hazard function looks like.
Listing 13.5.15. Python Code
hazard.cumsum().plot()

decorate(xlabel="Age", ylabel="Cumulative hazard")
Figure 13.5.16.
It is steepest between ages 20 and 30, which means that an unmarried person is at the greatest โ€œriskโ€ of getting married at these ages. After that, the cumulative hazard levels off, which means that the hazard gradually decreases.