Why Data Encoding Matters
A competing-risk analysis begins with a record for each individual: a follow-up time and a description of whether follow-up ended in censoring or in one of the event types of interest. A package cannot infer the intended meaning of a numeric code. If a recorded cause is accidentally treated as censoring, or a censoring code is treated as an event, the software may run while answering the wrong statistical question.
The goal here is a reliable data representation, not a model fit. The same records can be represented in more than one way: a single status code is useful for cumulative incidence calculations, while a binary event indicator is needed for a cause-specific survival response. These representations are related, but they are not interchangeable.
The time must be measured from a specified origin to the recorded endpoint: either an event time or the last time known to be event-free. State the time unit, such as days or years, and use it consistently. For the basic right-censored setup here, follow-up times must be numeric and finite; negative values are invalid. A time of zero may be legitimate if the study design allows an event or censoring at the origin, but it should be checked against the protocol and the requirements of the intended analysis.
A practical raw-data schema has three fields: follow-up time, an any-event indicator, and cause. Under one common convention, the cause is missing for censored records and is a positive integer for event records. Some source files instead use a special value such as zero for censored cause. Either convention can be used in the raw file, but it must be documented and converted consistently before analysis.
Constructing the Status and Cause-Specific Indicators
The competing-risk status \(S\) preserves the distinction between censoring and each event type. For cause \(k\), a separate binary indicator is \(D_k=1\) when \(\Delta=1\) and \(J=k\), and \(D_k=0\) otherwise. This cause-specific indicator is formed at the original follow-up time. If another cause occurred, that individual is coded as not having the event of cause \(k\) at that time; the individual is not removed from the data before that time.
The distinction follows directly from the endpoint being encoded. For the cause-\(k\) endpoint, the event indicator equals one precisely for a cause-\(k\) event. Therefore, for a competing event, \(D_k=0\), while the observed time remains the time of that competing event. In contrast, a status code for competing-risk methods must preserve which competing event occurred, since treating all such events as ordinary censoring would discard the cause information.
Worked Example: Recode Raw Records Into Competing-Risk Status
Suppose a hypothetical study records follow-up in days, an any-event indicator \(\Delta\), and a cause that is missing for censored participants. The five records are: A, 12 days, \(\Delta=1\), cause 1; B, 8 days, \(\Delta=0\), cause missing; C, 15 days, \(\Delta=1\), cause 2; D, 5 days, \(\Delta=1\), cause 1; E, 9 days, \(\Delta=0\), cause missing. There are two event causes.
For A, C, and D, an event occurred, so the status is the recorded cause. For B and E, follow-up was censored, so the status is zero. The resulting status sequence is \(1,0,2,1,0\). For cause 1, the binary event indicator is \(1,0,0,1,0\); for cause 2, it is \(0,0,1,0,0\). Each indicator uses the same recorded time as the original record.
In R, assuming these columns are named time, delta, and cause, the status can be built after validation with: `status <- ifelse(delta == 0, 0L, as.integer(cause))`. Cause-specific indicators can be made with `event1 <- as.integer(status == 1L)` and `event2 <- as.integer(status == 2L)`. The integer suffix in `0L` and `1L` makes the intended integer coding explicit.
The encoding is internally consistent because every \(\Delta=0\) record receives status zero, and every \(\Delta=1\) record receives one of the allowed positive cause codes. For cause 1, participant C has a competing event at day 15 and therefore has `event1 = 0` at that observed time; that record must not be changed to day 15 censoring in the original status variable.
Validation Before Analysis
Validation should precede recoding. At minimum, check that time is numeric, finite, and nonnegative; that the event indicator contains only the intended values; and that each observed event has exactly one recognized cause. Also check the reverse condition: censored records must not carry a positive event cause under the chosen raw-data convention. Missingness is not itself a censoring code. A missing event indicator or an event record with a missing cause is an unresolved data problem, not evidence of censoring.
An audit can be implemented with basic R checks. For example: `stopifnot(is.numeric(dat$time))`; `sum(!is.finite(dat$time))` counts nonfinite times; `sum(dat$time < 0, na.rm = TRUE)` counts negative times; and `table(dat$delta, useNA = "ifany")` displays event-indicator values and missingness. If zero times are not allowed by the protocol, check them separately with `sum(dat$time == 0, na.rm = TRUE)`.
For a raw cause field whose censored values are missing, a useful consistency check is to count `delta == 1 & is.na(cause)` and `delta == 0 & !is.na(cause)`. The first count should be zero because every event needs a cause; the second should be zero if the raw-data convention requires missing causes on censored records. Check unknown codes with a condition such as `delta == 1 & !(cause %in% c(1L, 2L))`, adapting the allowed values to the study. Missing values in `delta` must be checked separately because logical comparisons involving `NA` can themselves return `NA`.
In real data, a single `stopifnot` statement can stop a script as soon as a condition fails, but counts and row identifiers are often more useful during cleaning. Preserve the original fields, report which records fail, and resolve problems against source documentation rather than silently converting them. Only after the audit should the analysis variables be generated.
Worked Example: Find and Resolve Coding Inconsistencies
Consider a hypothetical five-row extract with fields time in months, delta, and cause: (1) 4, 1, 1; (2) 7, 0, missing; (3) -2, 1, 2; (4) 6, 1, missing; (5) 9, 0, 2. Assume the study has causes 1 and 2 and uses missing cause values for censoring.
Row 3 has a negative follow-up time, so it fails the time check even though its event code and cause are otherwise valid. Row 4 has an observed event but no cause. Row 5 is marked censored but carries a cause code, contradicting the stated raw-data convention. Rows 1 and 2 pass these checks. There are three problematic rows, and assigning status codes before investigating them would conceal rather than repair the inconsistencies.
The corresponding audit counts are: one negative time; one event with a missing cause; and one censored record with a nonmissing cause. The first should be checked against the source to determine whether the time was entered with the wrong sign or whether the time origin was miscoded. The missing cause and the censoring mismatch also require source review. If row 5 is confirmed to be censored, its cause should be set to missing under this convention; if it is confirmed to be an event, the delta field needs correction. These decisions cannot be made from the inconsistent row alone.
Preparing Inputs for Common R Packages
For a right-censored response, `survival::Surv(time, event)` uses a time and a binary event indicator. To represent a cause-\(k\) cause-specific endpoint, supply the original time and \(D_k\), not the full multilevel status as though it were a binary indicator. For example, with cause 1, `survival::Surv(dat$time, dat$event1)` defines an event at the observed time only when cause 1 occurred. The competing causes have indicator zero for this endpoint.
For competing-risk functions in `cmprsk`, including `cuminc` and `crr`, the usual input convention is a follow-up time and a numeric status: zero for censoring and a distinct positive code for each cause. Thus, `cmprsk::cuminc(ftime = dat$time, fstatus = dat$status)` retains the cause labels. This example illustrates data input only; it does not specify or interpret a fitted model.
For `riskRegression`, a competing-risk response can be created with `Hist(time, status, cens.code = 0)` from the `prodlim` package, which is used by `riskRegression`. Here the status must again distinguish censoring from the event causes. An explicit censoring code helps make the intended convention visible. Check the documentation for the specific function and version being used, particularly if the response is represented as a factor or if a different censoring convention is required.
These interfaces differ because they request different outcome representations. The binary `Surv` response describes one event indicator at a time. The competing-risk status used by `cmprsk` and `Hist` preserves all event causes together. Keep a single validated source status and derive other variables from it; do not independently retype cause codes in multiple parts of an analysis script.
Worked Example: Create Package-Ready Variables From Validated Data
Use the valid five records from the first example. The times in days are \(12,8,15,5,9\), and the status codes are \(1,0,2,1,0\). The counts are three observed events and two censored records. The cause-specific event indicator for cause 1 is \(1,0,0,1,0\).
In R, after confirming that the status values are exactly zero, one, or two, use `dat$status <- as.integer(dat$status)`. For the cause-1 survival response, use `y1 <- survival::Surv(dat$time, as.integer(dat$status == 1L))`. This produces two cause-1 events; records with status 0 or 2 have a zero event indicator for this endpoint.
For a competing-risk input, retain the full codes: `ci_input <- with(dat, cmprsk::cuminc(ftime = time, fstatus = status))`. For a `Hist` response, use `h <- prodlim::Hist(dat$time, dat$status, cens.code = 0)`. The same time and status values are passed to the competing-risk representations, so cause 2 remains distinguishable from censoring. The code does not establish that any statistical model is appropriate; it only prepares the outcome fields in the stated format.
Diagnostics, Alternatives, and Limitations
A successful run of a package function is not a data validation check. Before analysis, inspect the frequency table of status, verify that every positive code is an allowed cause, compare the counts of `delta == 1` and `status != 0`, and confirm that the time unit and origin are documented. If status counts disagree with the raw event indicator, the recoding or source data are inconsistent. If a cause appears with an unexpected code, confirm whether it represents a new cause, a typo, or a special missing-value code.
The main alternative to storing one multilevel status is to keep separate cause-specific binary indicators. That can be convenient when an analysis repeatedly uses a particular cause as its endpoint, but it is easier for the indicators to become inconsistent with one another. Deriving each indicator from a validated status avoids that risk. Conversely, a single binary event indicator alone cannot distinguish event causes, so it is insufficient when the analysis needs competing-risk outcomes.
This basic setup assumes one right-censored follow-up time per individual and a mutually exclusive observed outcome at that time: censoring or exactly one event cause. It does not encode recurrent events, multiple simultaneous causes, delayed entry, or a sequence of state transitions. Those situations require a representation that matches their observation structure; do not force them into one time/status row without checking the relevant method's data requirements.
Document time origin and units, the meaning of the event indicator, and how censored causes are recorded.
Check missingness, finite nonnegative times, allowed indicator values, recognized causes, and consistency between event and cause.
Use zero for censoring and a distinct positive integer for every observed cause.
Retain multilevel status for competing-risk inputs; create a binary cause-specific event indicator for a `Surv` response.
Check Your Understanding
Answer each question, then compare with the solutions below.
- A record has time 10, delta 0, and cause missing. What is its competing-risk status?
- An individual experiences cause 2 at day 7. What event indicator is used for a cause-1 `Surv` response, and what time is retained?
- Why is a missing cause on a record with delta 1 not automatically treated as censoring?
- A status field contains codes 0, 1, 2, and 9, while the study defines only causes 1 and 2. What should be done before fitting an analysis?
- Which representation preserves the distinction between censoring and two competing causes: a binary event indicator alone, or a status coded 0, 1, and 2?
Solutions. (1) Status 0, because delta indicates right censoring. (2) The cause-1 indicator is 0 and the time remains 7 days; the observed competing event is not erased or moved to an earlier time. (3) Delta says an event was observed, so a missing cause is an unresolved inconsistency; it does not establish censoring. Review the source record. (4) Investigate code 9 against the data dictionary and source. Do not silently map it to censoring or a known cause. (5) The multilevel status preserves all three outcomes; a binary indicator alone cannot identify which competing cause occurred.