Skip to contents

Introduction

The basic concept of preparing data for time-to-event analyses is quite simple:

  • we need to know whether the event occurred or not
  • if it did, the first time when it occurred is set as the event time
  • if it did not, the last time when it is known that the subject didn’t have an event is set as the censoring time.

Depending on the definition of the event and the collection of the data, the creation of a time-to-event ADaM dataset can be more or less complex. In this vignette, we will discuss different scenarios and how to derive the essential variables CNSR and ADT. For a complete programming workflow see the Creating a BDS Time-to-Event ADaM vignette.

Observation Period

The observation period is the time during which the subjects are at risk of experiencing the event. Usually it starts at the beginning of the treatment. The end of the observation period is study- or analysis-specific. It may be derived from more than one date, e.g., the end of the treatment plus a fixed time, the end of the study, death, the start of an alternative treatment, etc.

The records of the input datasets need to be restricted to the observation period. This can be done by deriving ANLzzFL variables in the input datasets and then filtering the records based on these flags.

Another option is to use the end_dates argument of the derive_param_tte() function to specify the dates which restrict the observation period. The input records are then automatically restricted to records before the specified end dates.

Scenarios

There are three main scenarios to consider when deriving time-to-event datasets:

Continuous Assessments

The simplest case are events which are assessed continuously, e.g., death or adverse events. These can occur at any time within the observation period. It is assumed that the event didn’t occur if no event is recorded. In this case, the event time is the time of the first occurrence of the event and the censoring time is the end of the observation period.

Discrete Assessments, Negative Event

Many events require dedicated assessments to determine whether the event occurred or not, e.g., lab assessments, tumor assessments, questionnaires, etc. That is, information about the event is available only at these time points and it may happen that for some assessments it is unknown if the event occurred or not, e.g., tumor scans were not readable or the score couldn’t be calculated because too few questions were answered. In this case, the event time is the time of the first assessment where the event is recorded. For the censoring time it depends on the type of the event. If the event is a negative event like death, worsening, adverse event, etc., the most conservative approach is to ignore time points where it is not known if the event occurred, i.e., the censoring time is set to the time of the last assessment where it is known that the event didn’t occur 1.

For the examples, we will use the following questionnaire ADaM dataset. The variable CHGCAT1 indicates whether the subject worsened, improved, or was unchanged compared to baseline. For some records it is unknown whether the subject worsened or not.

adqs_a dataset (click to expand/collapse)

A “time to worsening” parameter is derived by using derive_param_tte(). By default a negative event is assumed, thus the event_type argument doesn’t need to be specified.

trt_end <- censor_source(
  dataset_name = "adsl",
  date = TRTEDT
)

worsening <- event_source(
  dataset_name = "adqs",
  filter = CHGCAT1 == "WORSENED",
  date = ADT
)

valid_assessment <- censor_source(
  dataset_name = "adqs",
  filter = !is.na(CHGCAT1),
  date = ADT,
  order = exprs(PARAMCD)
)

adtte <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl, adqs = adqs_a),
  end_dates = list(trt_end),
  event_conditions = list(worsening),
  censor_conditions = list(valid_assessment),
  set_values_to = exprs(
    PARAMCD = "TTWORS",
    PARAM = "Time to worsening"
  )
)
adtte dataset (click to expand/collapse)

Discrete Assessments, Positive Event

For positive events like improvement, response, etc., the most conservative approach is to set the censoring time to the end of the observation period, even if it is not known whether the event occurred or not at this time point.

For positive events, the event_type argument of the derive_param_tte() function can be set to "positive".

improvement <- event_source(
  dataset_name = "adqs",
  filter = CHGCAT1 == "IMPROVED",
  date = ADT
)

adtte <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl, adqs = adqs_a),
  end_dates = list(trt_end),
  event_type = "positive",
  event_conditions = list(improvement),
  censor_conditions = list(valid_assessment),
  set_values_to = exprs(
    PARAMCD = "TTIMPR",
    PARAM = "Time to improvement"
  )
)
adtte dataset (click to expand/collapse)

Events Considering more than one Assessment

For some events it is necessary to consider more than one assessment. For example, improvement or worsening could require a confirmation at a subsequent assessment.

derive_param_tte() doesn’t allow to consider subsequent records to decide whether an event occurred or not. Thus the confirmation information needs to be derived in the input dataset, e.g., by deriving a variable or a parameter which indicates whether the event is confirmed or not. Then this variable or parameter can be used in the derive_param_tte() function to select the event records.

Whether to derive a variable or a parameter depends mainly on the users preference. If the confirmed change is considered as a separate endpoint, CDISC recommends to derive a separate parameter. If the confirmation is considered only as a selection criterion for the time-to-event analysis, both options are possible.

In the following sections three options are presented how to derive a “confirmed worsening” parameter in ADTTE. The dataset and plot displayed at the end of each section is the same but the code to derive it is different.

Adding a Confirmation Flag to the Source Dataset

To derive a variable (CONFFL) which indicates whether the worsening or improvement is confirmed or not, the derive_var_joined_exist_flag() function can be used. Here an assessment is considered as confirmed if it doesn’t change at the next visit. Only assessments within the observation period (ANL01FL == "Y") are considered for the confirmation.

adqs_ext <- adqs_a %>%
  derive_var_joined_exist_flag(
    dataset_add = adqs_a,
    filter_add = ANL01FL == "Y",
    by_vars = exprs(USUBJID),
    new_var = CONFFL,
    tmp_obs_nr_var = tmp_obs_nr,
    join_vars = exprs(CHGCAT1),
    join_type = "after",
    order = exprs(AVISITN),
    filter_join = CHGCAT1 %in% c("IMPROVED", "WORSENED") & CHGCAT1 == CHGCAT1.join &
      tmp_obs_nr + 1 == tmp_obs_nr.join
  )
adqs_ext dataset (click to expand/collapse)

The new variable CONFFL is then used in the definition of the event for confirmed worsening.

confirmed_worsening <- event_source(
  dataset_name = "adqs",
  filter = CHGCAT1 == "WORSENED" & CONFFL == "Y",
  date = ADT
)

adtte <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl, adqs = adqs_ext),
  end_dates = list(trt_end),
  event_conditions = list(confirmed_worsening),
  censor_conditions = list(valid_assessment),
  set_values_to = exprs(
    PARAMCD = "TTCWORS",
    PARAM = "Time to confirmed worsening"
  )
)
adtte dataset (click to expand/collapse)

Adding a Confirmation Parameter to the Source Dataset

For adding a new parameter instead of a new variable the derive_extreme_event() function can be used. The filter_source argument of the event_joined() object is used to restrict the records to observation period (ANL01FL == "Y").

adqs_ext <- adqs_a %>%
  derive_extreme_event(
    by_vars = exprs(USUBJID, AVISITN),
    source_datasets = list(adqs = adqs_a),
    tmp_event_nr_var = event_nr,
    order = exprs(event_nr),
    mode = "first",
    events = list(
      event_joined(
        dataset_name = "adqs",
        filter_source = ANL01FL == "Y",
        by_vars = exprs(USUBJID),
        order = exprs(AVISITN),
        join_vars = exprs(CHGCAT1),
        join_type = "after",
        tmp_obs_nr_var = tmp_obs_nr,
        condition = CHGCAT1 %in% c("IMPROVED", "WORSENED") & CHGCAT1 == CHGCAT1.join &
          tmp_obs_nr.join == tmp_obs_nr + 1,
        set_values_to = exprs(
          AVALC = "Y"
        )
      ),
      event(
        dataset_name = "adqs",
        set_values_to = exprs(
          AVALC = "N"
        )
      )
    ),
    set_values_to = exprs(
      PARAMCD = "CONFCHGA",
      PARAM = "Confirmed change of score A",
      AVAL = yn_to_numeric(AVALC)
    )
  )
adqs_ext dataset (click to expand/collapse)

The new parameter CONFCHGA is then used in the definition of the event for confirmed worsening.

confirmed_worsening <- event_source(
  dataset_name = "adqs",
  filter = PARAMCD == "CONFCHGA" & CHGCAT1 == "WORSENED" & AVALC == "Y",
  date = ADT
)

adtte <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl, adqs = adqs_ext),
  end_dates = list(trt_end),
  event_conditions = list(confirmed_worsening),
  censor_conditions = list(valid_assessment),
  set_values_to = exprs(
    PARAMCD = "TTCWORS",
    PARAM = "Time to confirmed worsening"
  )
)
adtte dataset (click to expand/collapse)

Using derive_extreme_event() to Derive Confirmation and Time-to-Event in One Step

Another option is to use derive_extreme_event() with event_joined() objects to derive the time-to-event parameter. This has the advantage that the input dataset doesn’t need to be modified but it has the disadvantage that the results are harder to review and trace back. E.g., in the example below, the assessments which are considered confirmed are derived within the derive_extreme_event() function, i.e., they are not accessible for reviewers or for debugging.

adtte <- derive_extreme_event(
  by_vars = exprs(USUBJID),
  source_datasets = list(adqs = filter(adqs_a, ANL01FL == "Y")),
  tmp_event_nr_var = tmp_event_nr,
  order = exprs(tmp_event_nr, ADT),
  mode = "first",
  events = list(
    event_joined(
      description = "Confirmed worsening event",
      dataset_name = "adqs",
      order = exprs(AVISITN),
      join_vars = exprs(CHGCAT1),
      join_type = "after",
      condition = CHGCAT1 == "WORSENED" & CHGCAT1.join == "WORSENED",
      keep_source_vars = exprs(ADT, TRTSDT, TRTEDT, STUDYID),
      set_values_to = exprs(CNSR = 0)
    ),
    event(
      description = "Censoring at last valid assessment",
      dataset_name = "adqs",
      condition = !is.na(CHGCAT1),
      keep_source_vars = exprs(ADT, TRTSDT, TRTEDT, STUDYID),
      order = exprs(ADT),
      mode = "last",
      set_values_to = exprs(CNSR = 1)
    )
  ),
  set_values_to = exprs(
    PARAMCD = "TTCWORS",
    PARAM = "Time to confirmed worsening",
    STARTDT = TRTSDT
  )
)
adtte dataset (click to expand/collapse)

Combined Events

Some events are defined as a combination of multiple events. For example, progression-free survival is defined as progression or death. For events combined by “or”, you can create separate event_source() objects for each event and pass them to the event_conditions argument of derive_param_tte(). For events combined by “and” or more complex combinations, you must first derive a variable or parameter in the input dataset that indicates whether the combined event occurred.

Events Combined by “or”

Assume we want to derive a time to improvement parameter which requires improvement in score A or score B.

adqs_all dataset (click to expand/collapse)

We define the events for improvement in score A and score B separately and then specify both events for the event_conditions argument of derive_param_tte().

improvement_a <- event_source(
  dataset_name = "adqs",
  filter = CHGCAT1 == "IMPROVED" & PARAMCD == "A",
  date = ADT
)

improvement_b <- event_source(
  dataset_name = "adqs",
  filter = CHGCAT1 == "IMPROVED" & PARAMCD == "B",
  date = ADT
)

adtte <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl, adqs = adqs_all),
  end_dates = list(trt_end),
  event_type = "positive",
  event_conditions = list(improvement_a, improvement_b),
  censor_conditions = list(valid_assessment),
  set_values_to = exprs(
    PARAMCD = "TTIMPR",
    PARAM = "Time to improvement"
  )
)
adtte dataset (click to expand/collapse)

Events Combined by “and”

If events are combined by “and”, e.g., score A improved and score B didn’t worsen, a variable or parameter needs to be derived in the input dataset which indicates whether the combined event occurred or not. To derive a new parameter the derive_param_computed() function can be used (use derive_var_computed() for deriving a new variable):

adqs_all_ext <- adqs_all %>%
  derive_param_computed(
    by_vars = exprs(STUDYID, USUBJID, AVISIT, AVISITN, ADT, ADY, TRTSDT, TRTEDT),
    parameters = c("A", "B"),
    set_values_to = exprs(
      AVALC = if_else(
        CHGCAT1.A == "IMPROVED" & CHGCAT1.B %in% c("IMPROVED", "UNCHANGED"),
        "Y",
        NA_character_
      ),
      PARAMCD = "IMPROVE",
      PARAM = "Improvement in score A and stable score B"
    )
  )
adqs_all_ext dataset (click to expand/collapse)

Now the new parameter can be used in the derive_param_tte() function to select the event records.

improvement_ab <- event_source(
  dataset_name = "adqs",
  filter = AVALC == "Y" & PARAMCD == "IMPROVE",
  date = ADT
)

adtte <- derive_param_tte(
  dataset_adsl = adsl,
  source_datasets = list(adsl = adsl, adqs = adqs_all_ext),
  end_dates = list(trt_end),
  event_type = "positive",
  event_conditions = list(improvement_ab),
  censor_conditions = list(valid_assessment),
  set_values_to = exprs(
    PARAMCD = "TTIMPRAB",
    PARAM = "Time to improvement in score A and stable score B"
  )
)
adtte dataset (click to expand/collapse)

Summary

The preparation of time-to-event datasets can be more or less complex depending on the definition of the event and the collection of the data. The derive_param_tte() function provides a flexible framework to derive time-to-event parameters for a wide range of scenarios. For more guidance and examples see the documentation of derive_param_tte() and the Creating a BDS Time-to-Event ADaM vignette.

If your use case is not covered or you are unsure how to implement it using admiral, please create an issue in the admiral GitHub repository. Any feedback helps us improving admiral!