Time-to-Event Data, Censoring, and Hazard Functions
Learn how survival analysis handles right-censored durations, defines hazard and survival functions, and builds the censored-data likelihood from scratch.
Machine Learning from ScratchAll chapters Time-to-Event Data, Censoring, and Hazard FunctionsFull chapter · Sections in this chapter1 / 10 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Time-to-Event Data, Censoring, and Hazard Functions
A pump is still running when a maintenance study ends. Its recorded duration is twelve months, but its lifetime is not twelve months: the lifetime is greater than twelve months. Treating that record as a failure moves an event to a time when no event occurred. Dropping it loses twelve months of evidence that the pump kept working. Survival analysis starts by preserving that distinction.
Probability Distributions and Descriptive Statistics provide the tools for describing durations. If every event time were observed, we could fit a distribution or summarize the complete durations directly. With incomplete follow-up, some pumps are still running at the administrative cutoff, and we know only a lower bound on their event times. This is right censoring. Ignoring the distinction between an event and a censoring time can distort estimates of how long things last.
This chapter defines the observed duration and event indicator, then connects the survival function, density, and hazard for a positive, absolutely continuous event time. The identity connects survival probabilities to cumulative hazards. Under independent censoring and the other assumptions stated below, these functions give a likelihood that uses both exact event times and survival up to a censoring time.
By the end, you should be able to interpret right-censored records, construct their parametric likelihood, and explain why ordinary regression on observed durations can target the wrong outcome. The next chapters develop non-parametric and regression estimators:
- Kaplan–Meier and Nelson–Aalen provide the non-parametric view.
- The Cox proportional hazards model provides a semi-parametric regression.
- Accelerated failure-time models provide fully parametric alternatives.
Advantages
The survival framework uses incomplete observations. Under the right censoring assumptions, the likelihood weights an event record by the density and a censored record by the survival function , the probability of surviving past that time. It retains censored follow-up instead of discarding those records.
A second advantage is that the framework accepts data exactly as it is collected. Subjects can have different observed durations. They observe until an administrative cut-off, until a subject drops out, or until an event occurs, whichever comes first. The pair , the observed duration and a binary event indicator, is a faithful representation of what we know, and modern estimators can consume this representation directly.
A third advantage is interpretation. A continuous-time hazard is an event rate per unit time among subjects who are still event-free. Survival probabilities answer finite-horizon questions such as the probability that a functioning pump fails within the next month. These are related quantities, but a rate is not itself a probability. A valid survival model respects the support of durations; unconstrained linear predictions of a raw duration can, for example, become negative.
Survival models also extend to covariates and time-varying effects. When the target distinguishes competing terminal event types, retain an event-type indicator and define the target, such as a cause-specific hazard or cumulative incidence. A prespecified composite first-event target can use a binary indicator; recurrent or nonterminal events may need another formulation. The binary pair alone does not encode those distinctions.
Disadvantages
The main assumption concerns the censoring mechanism. For the likelihood in this chapter, event and censoring times are independent conditional on the covariates used in the analysis, and the censoring law has parameters distinct from the event-time parameters. If unobserved prognosis affects both times, the ordinary likelihood need not estimate the target event-time distribution correctly. Conditional independent censoring resembles a missing-at-random assumption, but it is a statement about the observation process for event times and must be justified for the study.
Interpreting survival output requires choosing the right scale. The coefficient in a Cox model is a log hazard ratio, not a change in expected duration. The same coefficient can be interpreted as a change in the log of the ratio of hazards, and translating that into "how many more months do patients live" requires additional work. If you are used to linear models, you may approach survival output cautiously.
Censoring also reduces information relative to observing the same subjects' complete event times. Precision depends on the number and timing of events, follow-up, risk-set sizes, and model assumptions, rather than on a censoring-percentage threshold alone. A sample with ninety percent censoring may still contain many observed events if the study is large; a small study with fewer censored records can remain imprecise.
Finally, the framework cannot compensate for poor study design. If the observation window is misaligned, if event times are rounded so aggressively that dates become unreliable, or if the time origin is chosen differently across subgroups, estimates can acquire bias or artificial patterns. Fitting a survival model does not automatically repair those data-construction errors. The mathematics assumes we have correctly identified the time origin, the event, the censoring mechanism, and the covariates that matter.
Why ordinary regression can misrepresent event timing
Consider treating every observed duration as an event time. Since , the observed-duration mean cannot exceed the latent event-time mean when those means exist. The inequality is strict if censoring shortens durations with positive probability. An intercept-only least-squares fit to therefore estimates , not . For the exponential example below, counting censorings as events also inflates the fitted rate. These facts do not imply that every misspecified distribution or every regression coefficient has the same bias direction.
Dropping censored records selects subjects with . Under independent censoring, the event-time density among observed events is proportional to , so longer event times receive no more selection weight than shorter ones. This can pull complete-case averages downward; strictness requires a nontrivial change in that weight over event times. It does not mean every censored subject lived longer than every observed event. With covariates, the selection can differ across groups, so coefficient bias needs a separate analysis. Ordinary Least Squares (OLS) residual diagnostics alone do not restore the lost observation mechanism.
If covariates affect both event and censoring times, unconditional independence may fail even when conditional independence holds. For example, groups with different prognoses may also have different follow-up schedules. Conditioning on the relevant measured covariates can make an independent-censoring analysis defensible. Dependence that remains after conditioning needs additional assumptions, models, or sensitivity analysis; the ordinary survival likelihood cannot automatically repair it.
Formula
In this section we build the notation from scratch. The destination is a single likelihood expression that correctly handles censored records, but the route matters. We first describe what a censored observation tells us, then introduce the three functions that describe a time-to-event distribution, then connect those functions through the hazard, and only then write the likelihood. Keeping that order in mind makes the formulas feel inevitable rather than arbitrary.
Event times, observation windows, and censoring
Let be the true time until an event, measured from a defined origin . For a pump, it could be working lifetime measured from installation. We assume here that is finite almost surely and has an absolutely continuous distribution, with and density . This excludes a point mass at zero and a permanently event-free fraction. Discrete event times and cure models require corresponding changes to the formulas.
We do not observe every subject's event time directly. Instead, each subject is followed over an observation window. Denote by the time, measured from the same origin, at which observation of subject ends if the event has not yet occurred. can come from an administrative cutoff ("the study ends in December"), from a dropout ("the patient moves to a different hospital"), or from a loss to follow-up. Whatever the mechanism, the data we record is the pair
where:
- : the true (latent) event time for subject , measured from the time origin
- : the potential censoring time for subject , the time observation would end if no event had occurred
- : the observed time, the smaller of the event and censoring times, so always
- : the event indicator, equal to one when the event is observed and zero when the record is censored
- : the indicator function, which equals one when its argument is true and zero otherwise
Here is the observed time and is the event indicator, equal to one if we saw the event and zero if the observation ended first. This single binary flag encodes a lot of information. means we know the exact event time, and means we only know . The pair is therefore the fundamental data unit of survival analysis. It is not a pair of unrelated numbers. It is a compressed statement about an event that either happened exactly at or had not yet happened by .
Right censoring means the event is later than a known bound. Left censoring means the event has already occurred but its exact time is unknown, so only an upper bound is recorded. Interval censoring gives bounds such as . Under an appropriate independent observation process, these contribute , , and respectively. They are different observation schemes, not interchangeable inputs to a right-censoring estimator. Ordinary missingness, with neither a known event time nor a valid bound, supplies less information than these records.
Delayed entry, or left truncation, is different from left censoring. A subject enters at age only if still event-free then: subjects with are absent from the observed cohort. For example, a registry that enrolls only patients alive several months after diagnosis selects survivors. We observe their entry ages and subsequent follow-up, and must account for the selection. Merely starting otherwise fully observed subjects on different calendar dates is not left truncation when each is followed from its own relevant time origin.
Survival, density, and hazard functions
With the data unit fixed, we need a way to describe how durations are distributed. Three functions do the job, and each answers a different practical question. The first is the survival function
This is the probability that the subject is event-free at time . We have , and as , . The survival function is non-increasing. It is the natural first object in survival analysis because a censored record does not tell us when the event happened. It tells us that the event had not happened before the observation ended. The survival function is exactly the probability of that kind of statement.
The second is the probability density function , which is the familiar object from Probability Distributions. The density is the derivative of the CDF, so integrating over an interval gives the probability that falls in that interval. It answers a different question from the survival function. Rather than "has the event happened yet?" it asks "how much probability mass sits near this particular time?" If every duration were fully observed, the density would be the most direct object to model.
The third is the hazard function
where:
- : the hazard function, the instantaneous event rate at time among those still at risk
- : the event time for a generic subject
- : a small positive time increment taken to zero in the limit
- : the conditional probability that the event occurs in the next increment, given survival to time
- the denominator converts that probability into a rate per unit time
Read this out loud: the hazard is the instantaneous rate at which the event occurs in the next tiny slice of time, given that it has not occurred yet. The conditioning event is what distinguishes the hazard from the density. The density measures the unconditional probability of the event near . The hazard measures the conditional intensity, so it captures how risky the next instant is for a subject who has survived up to . This conditional view is what makes censored data manageable. A censored subject contributes information about surviving up to the censoring time, and the hazard is the function that describes risk among exactly those survivors.
To express the hazard in terms of and , start from the definition of conditional probability. For small positive ,
The numerator is approximately because the density integrates to the probability over a short interval, and the denominator is for a continuous (equality holds because the point mass at is zero). Divide by and take the limit:
These equalities hold where and the required derivatives exist. The hazard is the negative log-derivative of the survival function. Here denotes the derivative of with respect to , and the final equality uses the chain rule for the logarithm, .
Because , integrating both sides from zero to gives
Since , we have and
Define the cumulative hazard , so the identity becomes
This identity connects survival probabilities and cumulative hazards for the continuous-time models considered here. It says that if we know the hazard at every time, we know the survival function by integration and exponentiation, and vice versa. It is also the reason hazard-based modeling is practical. We can write a model in terms of the hazard when event rates are the scientific target, and still evaluate the survival probability that the likelihood needs.
For a right-censored record, the event-time likelihood contribution is for an observed event and for censoring, after removing factors from an independent censoring mechanism that do not depend on . A density is not a point probability: for continuous times, , whereas measures density per unit time. Assuming independent subjects and the censoring conditions detailed next, the event-time likelihood, up to a parameter-free factor, is
Substituting ,
Taking logarithms turns the product into a sum:
where:
- : the log-likelihood as a function of the parameter vector
- : the event contribution, present only when
- : the negative cumulative hazard contribution, paid by every record whether event or censoring
This full log-likelihood is the starting point for the parametric right-censoring fits below; other estimators and observation schemes modify the objective. Every event contributes the log of the hazard at its event time. Every record contributes the negative cumulative hazard at its observed time, whether it was an event or a censoring. The formula is compact because the event indicator does exactly the bookkeeping we need. It switches on the density term for observed events and leaves only the survival term for censored records.
Censoring assumptions and delayed entry
The single likelihood in the previous section relies on one key factorization. If the event and censoring are independent random variables given covariates , that is,
where contains the conditioning covariates. For continuous censoring times, the joint density of , with respect to time and the binary indicator, is
Here and are the censoring density and survival function. If their parameters are distinct from , their factor can be removed when estimating . It depends on the event indicator as well as on time. Fixed administrative censoring has atoms rather than a continuous density, but conditioning on a predetermined independent follow-up schedule gives the same event-density and event-survival contributions. The product likelihood also assumes subjects are independent. Estimation beyond the observed follow-up range remains dependent on the model and on available support.
If conditional independence fails, removing the censoring mechanism is generally unjustified. Selective loss of subjects with poor prognoses can conceal early events, but the size and direction of distortion depend on the joint process and the target. Inverse-probability-of-censoring weights require conditional independence given measured history, positive probabilities of remaining observed over the target horizon, and an adequate censoring model. They do not identify unobserved dependence automatically. Joint models or sensitivity analyses address different additional assumptions. A dropout is not automatically a competing event; a competing event must preclude the event of interest under the chosen definition.
For delayed entry, suppose a subject is included only if , and is observed to . Under non-informative entry and censoring conditional on the relevant covariates, the event-time likelihood is conditioned on survival to entry. Its contribution is
where:
- : the delayed-entry (left-truncation) time at which subject first becomes observable, with
- : the probability that a subject with parameters survives to the entry time, the conditioning term
- the numerator is the ordinary density or survival contribution evaluated at from the original time origin; division by conditions it on survival to entry
The denominator conditions on the selection . Non-parametric methods implement this by admitting subjects to risk sets only after entry. It matters even for an exponential model: , giving exposure and, when and total post-entry exposure is positive, rate estimate , rather than . With no events and positive exposure there is no positive interior rate estimate; zero exposure also needs a separate report. For the six durations below, entry at would change exposure from to and the rate from to . Memorylessness preserves the exponential family for residual lifetime; it does not justify ignoring entry. Independence of entry and event time, possibly conditional on covariates, is an additional assumption and can fail in survivor cohorts.
Mathematical properties
Several properties follow directly from the definitions and are worth pinning down before we start estimating. They are not decoration. Each one will be used either to check an estimate or to simplify a likelihood.
- The survival function is non-increasing, right-continuous, satisfies , and tends to zero as . Accumulated failures can only reduce the probability of being event-free.
- The hazard is non-negative where it is defined, with . At a finite endpoint where survival reaches zero, the ratio is not defined.
- The cumulative hazard is non-decreasing with . As survival approaches zero, diverges; this can occur at a finite upper endpoint as well as in an infinite-time limit.
- The three representations are equivalent. Knowing any one determines the other two via and , or via .
- The expected event time satisfies , provided the integral is finite. This is the "survival-curve area" formula, and it is useful for comparing estimators.
- For ordered discrete event times, define . Then . The log cumulative hazard differs from the sum of discrete hazards ; one cannot substitute the latter into the continuous-time exponential identity exactly.
- If a covariate acts on the hazard as , where is the baseline hazard (the hazard when ) and is a regression coefficient, then is the log hazard ratio and . This is the proportional hazards structure we will develop in Cox proportional hazards.
Visualizing Survival, Censoring, and Hazards
Before fitting anything, it is worth looking at the shapes involved. The three quantities , , and move together, and the way they move is easiest to grasp from a figure. We will use a Weibull family because it conveniently covers the three qualitative hazard shapes: constant (exponential as a special case), increasing, and decreasing.
The Weibull survival function with shape and scale is , and the corresponding hazard is . Here controls the shape of the hazard and is the scale parameter measured in the same time units as . Note that this scale plays a different role from the exponential rate used in the worked example below, where is the constant hazard. When the hazard is constant at . When the hazard increases with time (wear-out). When the hazard decreases with time (infant mortality). Increasing, decreasing, and approximately constant hazards over particular time ranges have been observed in component reliability data. Understanding these survival curves is a first diagnostic tool.
PythonIn [3]Expand
def weibull_survival(t, shape, scale):
return np.exp(-((t / scale) ** shape))
def weibull_hazard(t, shape, scale):
return (shape / scale) * (t / scale) ** (shape - 1.0)
t_grid = np.linspace(0.01, 20.0, 400)
t_grid_survival = np.linspace(0.0, 20.0, 400)
scenarios = [
dict(name="Exponential (shape = 1)", shape=1.0, scale=5.0),
dict(name="Weibull increasing (shape = 2)", shape=2.0, scale=5.0),
dict(name="Weibull decreasing (shape = 0.5)", shape=0.5, scale=5.0),
]
for s in scenarios:
s["S"] = weibull_survival(t_grid_survival, s["shape"], s["scale"])
s["h"] = weibull_hazard(t_grid, s["shape"], s["scale"])The next two figure cells are split purely for presentation. Both read the precomputed grids and the scenario dictionaries, and neither recomputes the data. The first shows the survival functions. The second shows the hazards.
PythonIn [4]Expand
from book_plot_style import PALETTE, polish_axes, use_book_style
def pal(i: int):
keys = ("primary", "amber", "teal", "rose")
return PALETTE[keys[i % len(keys)]]
use_book_style()
fig, ax = plt.subplots(figsize=(6.0, 4.0))
for i, s in enumerate(scenarios):
ax.plot(
t_grid_survival,
s["S"],
color=pal(i),
linewidth=1.1,
linestyle=("-", "--", "-.")[i],
label=s["name"],
)
ax.set_xlabel("Time t")
ax.set_ylabel("Survival probability S(t)")
ax.set_title("Weibull survival at three shapes")
ax.set_ylim(0, 1.02)
ax.legend(loc="upper right")
polish_axes(ax)
plt.tight_layout()
plt.show()VisualizationOut [4]Expand

The survival plot makes two things visible. First, all three start at and eventually decay to zero, as required. Second, the shape parameter changes the shape of the decay. The decreasing-hazard case (infant mortality) drops steeply early and then flattens, so its survival curve has a long tail. The increasing-hazard case (wear-out) stays near one for a while and then falls rapidly. These are two ends of the stated monotone-hazard spectrum. Real data require their own hazard diagnostics.
PythonIn [5]Expand
import matplotlib.pyplot as plt
from book_plot_style import PALETTE, polish_axes, use_book_style
use_book_style()
fig, ax = plt.subplots(figsize=(6.0, 4.0))
for i, s in enumerate(scenarios):
ax.plot(
t_grid,
s["h"],
color=pal(i),
linewidth=1.1,
linestyle=("-", "--", "-.")[i],
label=s["name"],
)
ax.set_xlabel("Time t")
ax.set_ylabel("Hazard h(t)")
ax.set_title("Weibull hazards at three shapes")
ax.set_ylim(0, 2.4)
ax.legend(loc="upper right")
polish_axes(ax)
plt.tight_layout()
plt.show()VisualizationOut [5]Expand

A constant hazard means the same instantaneous failure rate at each age; for an exponential lifetime it also gives a memoryless residual lifetime. A rising hazard can describe wear-out, while a falling hazard can describe early failures followed by lower risk among survivors. A decreasing population hazard alone does not prove that individual devices become stronger. The hazard grid starts at because the shape- hazard diverges as ; the survival grid includes zero.
Cumulative hazard is dimensionless and increases at rate . At a given time, a larger corresponds to a smaller survival probability through . The absolute rate of decline of survival is , so a larger hazard alone does not imply a steeper survival curve when the survival levels differ.
PythonIn [6]Expand
import numpy as np
cumulative_hazards = []
for scenario in scenarios:
cumulative_hazards.append(
dict(name=scenario["name"], H=-np.log(scenario["S"]))
)VisualizationOut [7]Expand

For the censoring plot, imagine devices installed at independent, uniformly distributed calendar dates during a fifteen-unit recruitment period, with follow-up ending at a common administrative cutoff. Each device is followed from installation, so its potential follow-up duration is uniform from zero to fifteen and independent of its simulated lifetime . This is staggered recruitment without left truncation. Horizontal segments end at ; diamonds mark failures and squares mark censorings. A censoring marker locates the end of observation, not an unobserved failure.
PythonIn [8]Expand
rng = np.random.default_rng(0)
n_swim = 40
T_swim = rng.exponential(scale=5.0, size=n_swim)
C_swim = rng.uniform(0, 15.0, size=n_swim)
Y_swim = np.minimum(T_swim, C_swim)
delta_swim = (T_swim <= C_swim).astype(int)
order = np.argsort(Y_swim)
Y_swim = Y_swim[order]
delta_swim = delta_swim[order]PythonIn [9]Expand
import matplotlib.pyplot as plt
from book_plot_style import PALETTE, polish_axes, use_book_style
use_book_style()
fig, ax = plt.subplots(figsize=(6.0, 4.5))
for i, (y, d) in enumerate(zip(Y_swim, delta_swim)):
color = pal(0) if d == 1 else pal(1)
ax.plot([0.0, y], [i, i], color=color, linewidth=1.1)
marker = "D" if d == 1 else "s"
ax.plot(y, i, marker=marker, color=color, markersize=5)
ax.set_xlabel("Observed time Y (simulated units)")
ax.set_ylabel("Subject index (sorted by Y)")
ax.set_title("Observed failures and censoring")
ax.legend(
handles=[
plt.Line2D(
[],
[],
color=pal(0),
marker="D",
linestyle="-",
label="Observed event",
),
plt.Line2D(
[],
[],
color=pal(1),
marker="s",
linestyle="-",
label="Right-censored",
),
],
loc="lower right",
)
polish_axes(ax)
plt.tight_layout()
plt.show()VisualizationOut [9]Expand

This is the visual form of the representation. Read each line as "device was observed from 0 to ; the marker tells us whether the device failed or was still running." The censored lines are not wrong or missing. They carry important information, namely that the true failure time is greater than the endpoint we see. A correct survival likelihood gives those records weight for the region to the right of the marker, which is exactly the probability that the event had not yet occurred at the moment we stopped observing.
Example
To see the likelihood in action, let us work through a small hand-computable example. Suppose we observe six subjects, each of whose observed times and event indicators are as follows.
| Subject | Observed time | Event |
|---|---|---|
| 1 | 2 | 1 |
| 2 | 3 | 1 |
| 3 | 5 | 0 |
| 4 | 1 | 1 |
| 5 | 4 | 1 |
| 6 | 6 | 0 |
Four of the six records are observed events, and two are right-censored. We do not know the underlying or . All we know is that and . Those two censored records are the entire reason this example is worth doing. If we knew all six event times, the exercise would be an ordinary exponential fit. Because we do not, the example shows how the likelihood turns partial information into a usable estimate. Suppose we adopt an exponential model, for all , so that , , and . The log-likelihood becomes
where is the number of events and the sum inside the second term is over all records, not just the events.
The two sufficient statistics are
To maximize , take the derivative with respect to :
which is the standard one-variable calculus problem from Maximum Likelihood Estimation. Setting it to zero gives . The second derivative is , so this is a maximum. Notice what the estimator is doing. It divides the number of observed events by the total time that subjects were under observation. Censored records still contribute exposure time to the denominator, which is exactly how they inform the hazard without pretending that their events were observed.
Two alternative estimates reveal what the correct likelihood is protecting us from.
If we treat every observed record as an event, that is, we ignore censoring completely, we would maximize the product of densities over all six records, , whose logarithm is , since . The maximizer is , which is fifty percent higher than the honest estimate. That corresponds to an expected lifetime of time units, versus for the correct fit.
If instead we drop the two censored records, the "complete cases" approach, we maximize , where the total observed time only includes the four observed events. The maximizer is , which is times the censoring-aware estimate, or more than double it. That corresponds to an expected lifetime of time units.
PythonIn [10]Expand
Y_ex = np.array([2.0, 3.0, 5.0, 1.0, 4.0, 6.0])
d_ex = np.array([1, 1, 0, 1, 1, 0])
D_ex = int(d_ex.sum())
sumY_ex = float(Y_ex.sum())
lam_hat = D_ex / sumY_ex
lam_naive = len(Y_ex) / sumY_ex
sumY_event = float(Y_ex[d_ex == 1].sum())
lam_drop = D_ex / sumY_event
# Independent library comparison: an exponential distribution with fixed location.
reference_data = CensoredData.right_censored(Y_ex, d_ex == 0)
reference_loc, reference_scale = expon.fit(reference_data, floc=0.0)
lam_reference = 1.0 / reference_scale
np.testing.assert_allclose(lam_reference, lam_hat, rtol=1e-10, atol=1e-12)
ll_hat = D_ex * np.log(lam_hat) - lam_hat * sumY_ex
ll_naive = len(Y_ex) * np.log(lam_naive) - lam_naive * sumY_ex
ll_drop = D_ex * np.log(lam_drop) - lam_drop * sumY_event| estimate | lambda_hat | expected_lifetime | |
|---|---|---|---|
| 0 | correct MLE | 0.1905 | 5.25 |
| 1 | treat censored as events | 0.2857 | 3.50 |
| 2 | drop censored | 0.4000 | 2.50 |
SciPy's CensoredData.right_censored marks the two censored observations, and expon.fit(..., floc=0) estimates the exponential scale while holding the location at zero. Its reciprocal agrees with to the tolerance asserted in the executed cell. This checks the six-record exponential case against an established censored-distribution implementation; it is not a check of covariate regression or delayed entry.
On these six records, treating censorings as events increases the rate from to , and dropping them gives . The censored durations contribute eleven units of known event-free exposure. Dropping them removes that exposure without changing the event count. This calculation demonstrates the effect on this dataset; it is not proof of the direction of every regression bias. The three fits use different observation rules, so their maximized objective values would not be valid likelihood comparisons on a common dataset.
To see the maximum visually, plot the log-likelihood as a function of .
PythonIn [12]Expand
lam_grid = np.linspace(0.05, 0.45, 200)
ll_grid = D_ex * np.log(lam_grid) - lam_grid * sumY_exPythonIn [13]Expand
import matplotlib.pyplot as plt
from book_plot_style import PALETTE, polish_axes, use_book_style
use_book_style()
fig, ax = plt.subplots(figsize=(6.0, 4.0))
ax.plot(lam_grid, ll_grid, color=pal(0), linewidth=1.1, label="log-likelihood")
ax.axvline(
lam_hat,
color=pal(1),
linestyle="--",
linewidth=1.0,
label=f"MLE $\\hat\\lambda = {lam_hat:.4f}$",
)
ax.axvline(
lam_naive,
color=pal(2),
linestyle=":",
linewidth=1.0,
label=f"naive $\\hat\\lambda = {lam_naive:.4f}$",
)
ax.scatter([lam_hat], [ll_hat], color=pal(1), zorder=5)
ax.set_xlabel("$\\lambda$")
ax.set_ylabel("log-likelihood $\\ell(\\lambda)$")
ax.set_title("Exponential rate: six-record likelihood")
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2))
polish_axes(ax)
plt.tight_layout()
plt.show()VisualizationOut [13]Expand

For this exponential rate objective, and positive total exposure imply a strictly negative second derivative and a unique positive maximizer. The claim is about this parameterization and dataset, not a general concavity guarantee for every survival family or optimizer. The dashed line marks the maximum; the dotted naive estimate sits to its right. Next we compare the analytic estimate, a primitive numerical objective, and a library fit that directly accepts censored observations.
Implementation in NumPy and SciPy
For larger datasets, code can accumulate the event count and exposure while retaining the same closed-form exponential estimator. We now build the log-likelihood from primitives, fit it with SciPy, and compare with the analytic estimator. This is a reusable pattern for continuous parametric survival models fitted by full right-censoring likelihood; other estimators use different objectives or risk-set calculations. We encode the hazard, encode the cumulative hazard, sum the log-likelihood contributions, and minimize the negative. The hand calculation gives us a known answer to check against. The implementation gives us a procedure that scales.
Our simulation returns with exponential latent lifetimes and independent uniform follow-up windows. As in the swimlane example, varying administrative follow-up can represent uniformly staggered installations with a common calendar cutoff. Latent event times are retained only as a teaching diagnostic; fitting uses the observed pair.
PythonIn [14]Expand
def simulate_exponential_censored(n, lam_true, censoring_window, rng):
"""Simulate right-censored exponential event times and a binary event indicator."""
T_true = rng.exponential(scale=1.0 / lam_true, size=n)
C_time = rng.uniform(0.0, censoring_window, size=n)
Y_obs = np.minimum(T_true, C_time)
delta = (T_true <= C_time).astype(int)
return Y_obs, delta, T_true
lam_true = 0.15
censoring_window = 12.0
rng = np.random.default_rng(7)
Y_train, d_train, T_train = simulate_exponential_censored(
500, lam_true, censoring_window, rng
)
Y_test, d_test, _ = simulate_exponential_censored(
200, lam_true, censoring_window, rng
)We now derive the log-likelihood from scratch. For the exponential model with rate , and , so the negative log-likelihood is
where:
- : the exponential rate (constant hazard), with
- : the observed time for record
- : the event indicator for record
- : total time under observation, including censored records
- : total number of observed events
We minimize this with SciPy's minimize. A small guard rejects any non-positive , because is undefined there.
PythonIn [15]Expand
def negative_log_likelihood_exponential(lam, Y, delta):
lam = float(np.asarray(lam).reshape(-1)[0])
if not np.isfinite(lam) or lam <= 0:
return np.inf
return -(delta.sum() * np.log(lam) - lam * Y.sum())
fit_result = minimize(
fun=negative_log_likelihood_exponential,
x0=np.array([0.10]),
args=(Y_train, d_train),
method="Nelder-Mead",
options=dict(xatol=1e-10, fatol=1e-10),
)
if not fit_result.success:
raise RuntimeError(f"Exponential optimizer failed: {fit_result.message}")
lam_mle_numeric = float(fit_result.x[0])
lam_mle_analytic = float(d_train.sum() / Y_train.sum())
np.testing.assert_allclose(
lam_mle_numeric, lam_mle_analytic, rtol=1e-7, atol=1e-10
)ConsoleOut [16]Expand
events in training set: 250 / 500 total observed time: 1743.3 analytic MLE lambda: 0.143406 numeric MLE lambda: 0.143406 true lambda: 0.150000
The numerical estimate agrees with to the asserted relative tolerance of ; this is a numerical check, not a machine-precision guarantee. The optimizer also reports successful termination. Agreement with the analytic solution checks this objective and fitting call. The estimate is near the generating rate in this sample, but sampling variability prevents exact recovery of a population parameter.
To complete the workflow, we compute the negative log-likelihood on the held-out test set at the fitted . This tells us how the model generalizes to new records, not just how well it fit the training sample.
PythonIn [17]Expand
nll_train = float(
negative_log_likelihood_exponential(lam_mle_analytic, Y_train, d_train)
)
nll_test = float(
negative_log_likelihood_exponential(lam_mle_analytic, Y_test, d_test)
)
per_record_train = nll_train / len(Y_train)
per_record_test = nll_test / len(Y_test)| metric | train | test | |
|---|---|---|---|
| 0 | negative log-likelihood | 735.5183 | 310.7176 |
| 1 | per-record NLL | 1.4710 | 1.5536 |
The per-record negative log-likelihood is similar across these training and test samples. This is one held-out check under the same simulated data-generating and censoring processes. It does not prove calibration or absence of overfitting. Likelihood comparisons between event-time models should use the same observed records and observation mechanism. The chapter on evaluating survival predictions adds censoring-aware discrimination and probability-error measures.
We also fit the two naive estimators to the same observed training records. This measures their error in this simulated realization; repeated samples would be needed to estimate their sampling bias empirically.
PythonIn [19]Expand
lam_naive_events = len(Y_train) / Y_train.sum()
lam_naive_drop = d_train.sum() / Y_train[d_train == 1].sum()| estimator | lambda_hat | expected_lifetime | |
|---|---|---|---|
| 0 | correct MLE | 0.1434 | 6.9732 |
| 1 | treat censored as events | 0.2868 | 3.4866 |
| 2 | drop censored | 0.3237 | 3.0889 |
Both naive rates exceed the generating rate in this run: about when censorings are counted as events and after dropping them, versus . Their ordering follows from the retained exposure in this sample; closeness to the generating parameter is not guaranteed by that ordering in every sample. The figure compares the resulting survival curves.
PythonIn [21]Expand
t_grid_fit = np.linspace(0.01, 25.0, 400)
survival_true = np.exp(-lam_true * t_grid_fit)
survival_mle = np.exp(-lam_mle_analytic * t_grid_fit)
survival_naive_events = np.exp(-lam_naive_events * t_grid_fit)
survival_naive_drop = np.exp(-lam_naive_drop * t_grid_fit)PythonIn [22]Expand
import matplotlib.pyplot as plt
from book_plot_style import PALETTE, polish_axes, use_book_style
use_book_style()
fig, ax = plt.subplots(figsize=(6.0, 4.0))
ax.plot(
t_grid_fit,
survival_true,
color=pal(0),
linewidth=1.1,
label=f"true $\\lambda = {lam_true:.3f}$",
)
ax.plot(
t_grid_fit,
survival_mle,
color=pal(1),
linewidth=1.1,
linestyle="--",
label=f"correct MLE $\\hat\\lambda = {lam_mle_analytic:.3f}$",
)
ax.plot(
t_grid_fit,
survival_naive_events,
color=pal(2),
linewidth=1.1,
linestyle=":",
label=f"treat censored as events $\\hat\\lambda = {lam_naive_events:.3f}$",
)
ax.plot(
t_grid_fit,
survival_naive_drop,
color=pal(3),
linewidth=1.1,
linestyle="-.",
label=f"drop censored $\\hat\\lambda = {lam_naive_drop:.3f}$",
)
ax.set_xlabel("Time t")
ax.set_ylabel("Survival probability S(t)")
ax.set_title("Survival fits with and without censoring")
ax.set_ylim(0, 1.02)
ax.legend(loc="upper center", bbox_to_anchor=(0.5, -0.2))
polish_axes(ax)
plt.tight_layout()
plt.show()VisualizationOut [22]Expand

The two naive curves lie below the generating survival curve on the positive plotted times, while the censoring-aware estimate is close in this realization. The simulation makes the consequence of the event indicator visible. It does not establish exact recovery or validate the exponential assumption for a real equipment dataset.
Key parameters
The exponential has a single parameter, the rate , and its interpretation is worth spelling out because the same parameterization recurs throughout this part of the book. Its survival function is , its density is , and its hazard is constant at .
- is the mean event time.
- is the median event time.
- , and the coefficient of variation (the standard deviation divided by the mean, ) is 1.
- The cumulative hazard is , linear in time.
These identities make the exponential a convenient baseline but a restrictive one. A constant hazard is restrictive when risk changes with age. The Weibull family we used in the visualization section has two parameters (shape and scale) and lets the hazard rise or fall. A later chapter will fit those parameters with the same style of likelihood.
Key methods
For independent right censoring without delayed entry, the same likelihood construction supports other absolutely continuous parametric event-time models:
- Write the hazard and the cumulative hazard in terms of the model's parameters .
- Form the negative log-likelihood .
- Optimize with SciPy, guarding against invalid parameter regions (negative rates, positive-shape violations, etc.).
- Validate on a held-out sample with the same per-record likelihood or a suitable survival metric.
- Compare against the naive estimators to see how much censoring matters in this particular dataset.
Other families require their own parameter constraints, stable density or survival calculations, diagnostics, and checks that the fitted optimum is meaningful. Delayed entry, other censoring schemes, clustered observations, and competing events change the likelihood or data representation; they are not handled by substituting a new hazard alone.
Practical Implications
Time-to-event methods are most useful when the estimand is a duration and the observation process is explicit. The subsections below cover suitable applications, data preparation, common pitfalls, and computational and deployment considerations.
Appropriate Problems and Domains
Time-to-event methods apply when the target is a duration and the observation process supplies event times or valid censoring bounds. Equipment failure, subscription churn, credit default, and relapse can have that structure, provided the event, origin, population, and follow-up rule are defined. Event times can also be fully observed: censoring is an important reason for using survival methods, not a requirement for them.
When all durations are observed, an ordinary complete-data distributional fit may be sufficient. Light censoring is not, by itself, permission to ignore censored records. For a fixed-horizon question, such as relapse within twelve months, a binary classifier can be appropriate when status at that horizon is known for each subject. Censoring before the horizon leaves that label unknown and needs an explicit treatment.
A third consideration is the audience and the estimand. Hazard ratios describe relative rates of failure among those still at risk, survival probabilities describe the fraction event-free at a given time, and expected or median durations describe typical lifetimes. These answer different questions, and a model that reports one of them does not automatically answer the others without additional computation. Choosing the estimand before fitting is more efficient than fitting first and then searching for a summary that stakeholders can interpret.
Best Practices
Before analysis, record the time origin, event definition, observation-ending rule, and cohort inclusion or entry rule. Retain the observed duration, event indicator, entry time when relevant, and covariate measurement times. These decisions determine the target population and the information each record provides; model fitting cannot resolve an ambiguous event or an incorrectly defined origin.
A Kaplan–Meier survival estimate or Nelson–Aalen cumulative-hazard estimate can help assess a parametric model under their censoring and entry assumptions. A nearly linear cumulative hazard is consistent with an exponential model; marked curvature suggests a changing hazard. Thin risk sets limit interpretation in the tail. These are diagnostics, not proofs of a model or censoring assumption.
For iterative fits, inspect optimizer status, enforce parameter constraints, and compare sensible starting values. Compare information criteria using the same fitted records and observation scheme. Compare held-out likelihood by scoring candidate models on the same held-out records and observation scheme, and describe any differences in training data. Report event counts, follow-up, parameter count, and uncertainty alongside sample size. There is no universal events-per-parameter cutoff guaranteeing stability; the information also depends on effect sizes, covariate distributions, and censoring. Regularization can stabilize estimation but does not add information to the observed-data likelihood.
Data Requirements and Preprocessing
Durations need consistent units and a documented origin; hazards have inverse-time units. Zero durations can reflect rounding, simultaneous entry and event, or data errors, so investigate their meaning before fitting a model that assumes strictly positive times. Replacing them by an arbitrary epsilon changes the data. Ties from rounded measurement may be compatible with a chosen approximation, but genuine interval observation calls for interval-censoring methods rather than pretending the bounds are exact event times.
A baseline prediction model can use only covariates available at its prediction time. A measurement obtained three months later cannot be used as if available at baseline. Time-varying models can instead use information available just before each risk interval, with a suitable data layout and target. Standardizing a continuous covariate changes the unit represented by its coefficient: a hazard ratio per standard deviation differs from a ratio per original unit. With an unpenalized model and an equivalent parameter transformation, predictions and comparisons at the same original covariate values remain unchanged; regularization requires additional care.
A duration-and-indicator row supports basic right-censored models without delayed entry or time-varying covariates. Entry requires an additional field, and changing covariates commonly require start–stop risk intervals. When the target distinguishes multiple event types, retain an event-type field. A prespecified composite first-event target can instead use a binary event indicator. Different calendar recruitment dates alone do not imply left truncation when each subject is observed from its own defined origin.
Common Pitfalls
An analysis should explain why censoring is plausibly independent given the variables it uses. Related prognosis and observation-ending mechanisms can invalidate that assumption, but the direction of bias is not universal. Where dependence is plausible, choose a sensitivity analysis or a model whose additional assumptions address the mechanism. Censoring weights help only under their own conditional-independence, positivity, and model requirements.
A hazard is a rate and can exceed one. For an event-free subject at time , the probability of an event by is , when . For a constant rate , this is , whereas is only a small-interval approximation. Increasing the product does not make that linear approximation a valid probability.
Two further pitfalls concern the timing of the analysis rather than the algebra. When only a handful of subjects remain at risk, a single event can remove a large fraction of the estimated remaining survival probability. Such an unstable tail can look like a dramatic late effect when it is only sampling noise. Plots should display the number at risk beneath the curve, and statements about late survival should be qualified when that number is small. Separately, aligning subjects on different time origins, or allowing a covariate to be defined only for subjects who survived long enough to have it recorded, creates apparent effects that are artifacts of the study design. A fitted summary alone cannot establish that the data construction avoided these problems; inspect how the records were assembled.
Computational Considerations
The exponential rate estimate needs a single pass to accumulate events and exposure, followed by a division. Fits with unknown shape or regression coefficients commonly require iteration. A Weibull shape fit generally uses numerical root finding or optimization; a Cox fit evaluates a partial likelihood over risk sets. Iteration cost depends on the number of parameters and implementation, so an ordinary-regression runtime analogy is not a reliable complexity bound.
Non-parametric estimation has its own cost profile. Computing a Kaplan–Meier curve requires sorting the observed times, which is the dominant expense at , after which the cumulative product is a single linear pass. Nelson–Aalen is similar. Risk-set-based quantities that are recomputed from scratch for each event time can degrade toward quadratic behavior, which can become expensive as the sample grows. It is usually better to accumulate counts through the sorted event times than to filter the full sample at each step.
Use log-density and log-survival calculations to avoid multiplying probabilities that underflow. For the exponential model here, zero observed events and positive exposure give , whose supremum is at the boundary ; there is no positive interior rate estimate. Report that case explicitly rather than an arbitrary optimizer result. Other survival families need their own existence checks. Constraints and multiple starting points can help numerical fitting, but do not prove a global optimum.
Performance and Deployment Considerations
Mean squared error against censored durations evaluates the wrong event-time target. Censoring-aware concordance measures assess risk ordering among suitably comparable pairs; a chance ordering has expected concordance under the usual tie conventions. What constitutes useful discrimination depends on the application, so there is no universal threshold. Censoring-adjusted Brier scores assess probability error across supported horizons and reflect both calibration and discrimination. Held-out event-time likelihood remains useful for comparing parametric specifications on the same observations and observation process.
Deployment adds concerns that do not appear in a single fitted analysis. The survival function is time-dependent, so a deployed model must store its time origin and be queried with an elapsed time, not a calendar date. Mismatches here produce silently wrong predictions. Predictions should be reported with the horizon attached, since a survival probability without a time is meaningless, and predicted curves should be assessed against matured holdout outcomes at supported horizons to check calibration.
Monitor follow-up and censoring patterns as well as covariates and matured outcomes. A changing censoring fraction can reflect a newer cohort with less follow-up rather than a changed event process. Compare like horizons and cohort ages before interpreting drift, and reconsider censoring assumptions if the observation mechanism changes. Refitting and evaluation schedules should follow outcome availability and the application rather than a fixed generic cadence.
Summary
Time-to-event data is the record of a duration combined with a censoring indicator, and . The indicator is not a nuisance. It separates the two kinds of information a survival dataset contains, namely "the event happened at this time" and "the event had not happened by this time." The likelihood that consumes this pair is , or equivalently after rewriting in terms of the hazard and the cumulative hazard.
Three functions describe the same distribution. The survival function is the probability of remaining event-free. The density is the familiar probability density. The hazard is the instantaneous rate of the event given survival so far. The bridge between the survival and cumulative-hazard representations is , which is what makes hazard-based models algebraically convenient. You should be comfortable with all three and be able to translate among them at will.
Independent censoring with distinct nuisance parameters permits removal of censoring factors from the event-time likelihood. Unmeasured dependence can invalidate that factorization, and additional models, weighting assumptions, or sensitivity analysis may be needed. Competing-event analyses need a declared target. When that target distinguishes event types, retain an event-type indicator; a prespecified composite first-event target can use a binary indicator. Non-informative delayed entry conditions on survival to through , and changes exposure even for an exponential lifetime.
The exponential rate estimate is when there is at least one event and positive exposure. In the six-record example it is , while counting censorings as events gives and dropping them gives . The primitive optimizer agrees with the analytic estimate, and SciPy's censored exponential fit checks the hand example independently. In the larger seeded sample the censoring-aware estimate is near the generating rate; the naive estimates are substantially larger. The next chapters extend the estimators while retaining explicit attention to what each observed record tells us.
Quiz
Ready to test your understanding? Take this quick quiz to reinforce what you've learned about right censoring, hazard functions, and the survival likelihood.
Time-to-Event Data, Censoring, and Hazards
Join the discussionAsk a question or share what you learned.
Keep exploring
Reference
Citation details
Cite or share this article.

Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!