1.1 Introduction

The event functions identify which event occurs first or last within a time window. They can search either a cohort table or a concept set:

Event source Return the date Return days relative to the index date
Cohort table addCohortEventDate() addCohortEventDays()
Concept set addConceptEventDate() addConceptEventDays()

Each function adds two columns for every window:

Unlike the intersection functions, the event functions also return the applicable boundary when no target event occurs in the requested window. This makes it possible to distinguish an observed event, an explicit censoring boundary, and the end of observation.

We will use the GiBleed dataset to illustrate this functionality:

library(PatientProfiles)
library(dplyr)
library(omock)
library(CohortConstructor)
library(CodelistGenerator)

cdm <- mockCdmFromDataset(datasetName = "GiBleed", source = "duckdb")

Let’s create some simple cohorts:

codelist <- getDrugIngredientCodes(
  cdm = cdm,
  name = "acetaminophen",
  nameStyle = "{concept_name}"
)
cdm$my_cohort <- conceptCohort(
  cdm = cdm,
  conceptSet = codelist,
  name = "my_cohort"
) |>
  requireIsFirstEntry()
codelist <- list(osteoarthritis = 80180L, diverticular_disease = 4266809L)
cdm$conditions <- conceptCohort(
  cdm = cdm,
  conceptSet = codelist,
  name = "conditions"
)

1.2 Finding a cohort event

addCohortEventDays() searches the cohorts in targetCohortTable. In this example we find the first event on or after each record’s cohort start date and up to 365 days later.

cohort_event_days <- cdm$my_cohort |>
  addCohortEventDays(
    targetCohortTable = "conditions",
    indexDate = "cohort_start_date",
    order = "first",
    window = list(next_year = c(0, 365))
  )

cohort_event_days |>
  glimpse()
#> Rows: ??
#> Columns: 6
#> Database: DuckDB 1.5.0 [root@Darwin 25.5.0:R 4.4.1//private/var/folders/pl/k11lm9710hlgl02nvzx4z9wr0000gp/T/Rtmp615E3t/fileabee35112b65.duckdb]
#> $ cohort_definition_id <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ subject_id           <int> 6, 16, 42, 18, 35, 40, 53, 9, 2, 49, 11, 61, 32, …
#> $ cohort_start_date    <date> 1969-12-20, 1974-06-11, 1937-09-07, 1970-09-22, …
#> $ cohort_end_date      <date> 1970-01-03, 1974-06-25, 1937-09-14, 1970-10-06, …
#> $ event_next_year      <chr> "censor", "censor", "censor", "censor", "censor",…
#> $ days_next_year       <dbl> 365, 365, 365, 365, 365, 365, 365, 365, 365, 365,…

The days_next_year value is relative to cohort_start_date: positive values are after the index date, zero is the index date, and negative values are before it.

We can obtain a quick summary of the result:

cohort_event_days |>
  group_by(event_next_year) |>
  summarise(
    n = n(),
    median_days = median(days_next_year)
  ) |>
  collect()
#> # A tibble: 4 × 3
#>   event_next_year          n median_days
#>   <chr>                <dbl>       <dbl>
#> 1 end_of_observation       1         321
#> 2 diverticular_disease     1         221
#> 3 censor                2676         365
#> 4 osteoarthritis           1         199

To find the nearest previous event, use a window ending on the index date and order = "last". Here, "last" selects the latest event in the window. Because the complete window is in the past, this is the event nearest the index date.

cohort_event_date <- cdm$my_cohort |>
  addCohortEventDate(
    targetCohortTable = "conditions",
    indexDate = "cohort_start_date",
    order = "last",
    window = list(previous_year = c(-365, 0))
  )

cohort_event_date |>
  glimpse()
#> Rows: ??
#> Columns: 6
#> Database: DuckDB 1.5.0 [root@Darwin 25.5.0:R 4.4.1//private/var/folders/pl/k11lm9710hlgl02nvzx4z9wr0000gp/T/Rtmp615E3t/fileabee35112b65.duckdb]
#> $ cohort_definition_id <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ subject_id           <int> 6, 16, 42, 18, 35, 40, 53, 9, 2, 49, 11, 61, 32, …
#> $ cohort_start_date    <date> 1969-12-20, 1974-06-11, 1937-09-07, 1970-09-22, …
#> $ cohort_end_date      <date> 1970-01-03, 1974-06-25, 1937-09-14, 1970-10-06, …
#> $ event_previous_year  <chr> "censor", "censor", "censor", "censor", "end_of_o…
#> $ date_previous_year   <date> 1968-12-20, 1973-06-11, 1936-09-07, 1969-09-22, …

Use targetCohortId to restrict the search to particular cohorts. targetDate determines which date in the target cohort table represents the event and defaults to cohort_start_date.

1.3 Finding a concept event

The concept event functions search records associated with one or more concept sets across the OMOP clinical tables. The names of the concept sets become the event names.

concept_event_days <- cdm$my_cohort |>
  addConceptEventDays(
    conceptSet = codelist,
    order = "first",
    window = list(next_event = c(0, Inf))
  )

concept_event_days |>
  glimpse()
#> Rows: ??
#> Columns: 6
#> Database: DuckDB 1.5.0 [root@Darwin 25.5.0:R 4.4.1//private/var/folders/pl/k11lm9710hlgl02nvzx4z9wr0000gp/T/Rtmp615E3t/fileabee35112b65.duckdb]
#> $ cohort_definition_id <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ subject_id           <int> 18, 43, 12, 74, 111, 67, 86, 187, 158, 135, 194, …
#> $ cohort_start_date    <date> 1970-09-22, 1978-12-23, 1964-08-22, 1973-09-28, …
#> $ cohort_end_date      <date> 1970-10-06, 1979-01-06, 1964-09-05, 1973-10-12, …
#> $ event_next_event     <chr> "osteoarthritis", "osteoarthritis", "diverticular…
#> $ days_next_event      <dbl> 14060, 6664, 15935, 15293, 12680, 14055, 13301, 6…

For concept events, targetDate can be either event_start_date, the default, or event_end_date. The concept set is validated against the vocabulary in the CDM before the clinical records are searched.

1.4 First, last, and censoring

The window defines the period to search, while order determines which event within that period is returned:

The search is always restricted to the observation period containing the index date. For a forward search, a column supplied through censorDate can shorten the available follow-up. If no event occurs before the applicable boundary, the event column contains:

The accompanying date or days value is the applicable boundary.

For a forward search, the boundary is the earliest of the upper window boundary, censorDate, and the observation period end. For a backward search, the boundary is the latest of the lower window boundary and the observation period start.

An event on the boundary is returned as the event itself. It is not combined with "censor" or "end_of_observation". If the index date is outside observation, both added values are missing.

1.5 Events occurring on the same date

Several cohorts or concept sets can occur on the selected date. The multipleEvents argument controls how these ties are represented:

Use NULL to follow the original order of the concept set:

cdm$my_cohort |>
  addConceptEventDays(
    conceptSet = codelist,
    order = "first",
    window = list("next" = c(0, Inf)),
    multipleEvents = NULL
  ) |>
  group_by(event_next) |>
  summarise(n = n()) |>
  collect()
#> # A tibble: 3 × 2
#>   event_next               n
#>   <chr>                <dbl>
#> 1 osteoarthritis        2174
#> 2 end_of_observation     115
#> 3 diverticular_disease   390

Use TRUE to combine all events that occur on the selected date:

cdm$my_cohort |>
  addConceptEventDays(
    conceptSet = codelist,
    order = "first",
    window = list("next" = c(0, Inf)),
    multipleEvents = TRUE
  ) |>
  group_by(event_next) |>
  summarise(n = n()) |>
  collect()
#> # A tibble: 3 × 2
#>   event_next                               n
#>   <chr>                                <dbl>
#> 1 end_of_observation                     115
#> 2 osteoarthritis                        2174
#> 3 diverticular_disease; osteoarthritis   390

Alternatively, provide an explicit priority order. Here, osteoarthritis is selected before diverticular_disease when they occur on the same date:

cdm$my_cohort |>
  addConceptEventDays(
    conceptSet = codelist,
    order = "first",
    window = list("next" = c(0, Inf)),
    multipleEvents = c("osteoarthritis", "diverticular_disease")
  ) |>
  group_by(event_next) |>
  summarise(n = n()) |>
  collect()
#> # A tibble: 2 × 2
#>   event_next             n
#>   <chr>              <dbl>
#> 1 osteoarthritis      2564
#> 2 end_of_observation   115

In this example, 390 cohort records have both osteoarthritis and diverticular disease on the selected date. The three calls above show how multipleEvents can resolve these ties using the original order, a combined label, or an explicit priority.

1.6 Multiple windows and column names

More than one window can be supplied in a named list. The default nameStyle = "{value}_{window_name}" uses {value} for event, date, or days, and {window_name} for the name of the window.

events_in_two_directions <- cdm$my_cohort |>
  addCohortEventDays(
    targetCohortTable = "conditions",
    order = "first",
    window = list(
      before = c(-365, 0),
      after = c(0, 365)
    )
  )

events_in_two_directions |>
  glimpse()
#> Rows: ??
#> Columns: 8
#> Database: DuckDB 1.5.0 [root@Darwin 25.5.0:R 4.4.1//private/var/folders/pl/k11lm9710hlgl02nvzx4z9wr0000gp/T/Rtmp615E3t/fileabee35112b65.duckdb]
#> $ cohort_definition_id <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
#> $ subject_id           <int> 6, 16, 42, 18, 35, 40, 53, 9, 2, 49, 11, 61, 32, …
#> $ cohort_start_date    <date> 1969-12-20, 1974-06-11, 1937-09-07, 1970-09-22, …
#> $ cohort_end_date      <date> 1970-01-03, 1974-06-25, 1937-09-14, 1970-10-06, …
#> $ event_before         <chr> "censor", "censor", "censor", "censor", "censor",…
#> $ event_after          <chr> "censor", "censor", "censor", "censor", "censor",…
#> $ days_before          <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ days_after           <dbl> 365, 365, 365, 365, 365, 365, 365, 365, 365, 365,…

The same value of order applies to every supplied window. If different windows require different values of order, use separate calls.

nameStyle must contain {value} so that the event and its date or days have different names. It must also produce a unique name for every output column.

mirror server hosted at Truenetwork, Russian Federation.