Skip to content

Overview

A cohort defines a set of persons and, when dates are present, one or more episodes per person. dsOMOP v2 supports three ways to obtain one:

  1. Pre-existing cohorts stored in the results schema (e.g. generated by ATLAS or CohortGenerator).
  2. Spec-based cohorts created on-the-fly from a structured specification.
  3. Workspace cohorts built from an already-extracted omop.table with ds.omop.cohort.from_table().

All three produce a server-side cohort table (carried client-side as a dsomop_cohort_handle) that can scope exploration queries (the unified cohort = argument) and filter extraction plans and recipes.

Listing and Inspecting Cohorts

library(dsOMOPClient)

cohorts <- ds.omop.cohort.list(symbol = "omop", conns = conns)
# cohort_definition_id, cohort_definition_name, ...

defn <- ds.omop.cohort.definition(42, symbol = "omop", conns = conns)
# Full definition details for cohort 42

Creating a Cohort from a Spec

ds.omop.cohort.create() takes a spec list that must contain a type and a concept_set:

diabetes <- ds.omop.cohort.create(
  spec = list(type = "condition", concept_set = c(201826)),
  cohort_id = 100,
  name = "diabetes_cohort",
  mode = "temporary",
  symbol = "omop",
  conns = conns
)
# Returns a dsomop_cohort_handle pointing at the server-side cohort table

Supported Spec Types

Type Source Table Notes
condition condition_occurrence Filter by condition concepts
drug drug_exposure Filter by drug concepts
measurement measurement Filter by concepts + optional issued numeric bin
observation observation Filter by observation concepts
procedure procedure_occurrence Filter by procedure concepts

Measurement with a Server-Issued Numeric Bin

high_hba1c_bin <- ds.omop.safe.filter.value(
  "measurement", "value_as_number",
  threshold = 6.5, direction = "above",
  concept_id = 3004410, symbol = "omop", conns = conns
)
high_hba1c <- ds.omop.cohort.create(
  spec = list(
    type = "measurement",
    concept_set = c(3004410),
    value_bin = high_hba1c_bin
  ),
  cohort_id = 101,
  name = "high_hba1c",
  mode = "temporary",
  symbol = "omop",
  conns = conns
)

Persistent Cohorts

mode = "persistent" writes to the results/cohort schema so the cohort survives across sessions, instead of the default session-scoped temporary table. This is disabled by default and requires both options(dsomop.allow_persistent_cohorts = TRUE) and a separately reviewed write role/schema. The ordinary analysis identity should otherwise remain read-only apart from supported session-temporary objects.

ds.omop.cohort.create(
  spec = list(type = "condition", concept_set = c(201826)),
  cohort_id = 200,
  name = "diabetes_persistent",
  mode = "persistent",
  symbol = "omop",
  conns = conns
)

Combining Cohorts

ds.omop.cohort.combine() accepts the handles returned by cohort.create() (or cohort table names) and applies a set operation. The combined result is re-gated on its distinct-person count:

both <- ds.omop.cohort.combine(
  op = "intersect",          # "union", "intersect", or "setdiff"
  cohort_a = diabetes,
  cohort_b = high_hba1c,
  new_name = "dm_and_high_hba1c",
  symbol = "omop",
  conns = conns
)

"difference" is accepted as an alias for "setdiff". Every set operation is gated on nfilter.subset; a result describing too few unique persons is blocked.

Building a Cohort from a Workspace Table

Any server-side omop.table you have already produced – the output of ds.omop.plan.execute(), of a recipe, or of a manipulation verb (ds.omop.merge() and friends) – can be promoted to a reusable cohort. Only the symbol name travels to the server; the server reads its distinct person tokens, reverses them to original ids with the per-resource key, gates the count (fail-closed), and materialises a size-checked cohort table. No identifier ever leaves the server.

# Suppose a plan produced a features table under the symbol "F"
ds.omop.plan.execute(plan, out = c(features = "F"),
  symbol = "omop", conns = conns)

# Turn the persons in F into a cohort, then reuse it to scope exploration
coh <- ds.omop.cohort.from_table("F", symbol = "omop", conns = conns)
ds.omop.concept.prevalence("condition_occurrence", cohort = coh,
  symbol = "omop", conns = conns)

The Unified cohort = Argument

Exploration aggregates accept a single cohort = argument that resolves any of: a dsomop_cohort_handle, a numeric cohort definition id, or a server-side cohort table name. This lets you run the same profiling within a subpopulation:

ds.omop.column.stats("measurement", "value_as_number", concept_id = 3004410,
  cohort = diabetes, symbol = "omop", conns = conns)
ds.omop.missingness("measurement", cohort = diabetes,
  symbol = "omop", conns = conns)
ds.omop.crosstab("person", "gender_concept_id", "race_concept_id",
  cohort = diabetes, symbol = "omop", conns = conns)
ds.omop.value.counts("condition_occurrence", "condition_concept_id",
  cohort = 42, symbol = "omop", conns = conns)   # by definition id

Using Cohorts in Plans

Reference a cohort in an extraction plan with ds.omop.plan.cohort():

plan <- ds.omop.plan()

# Use an existing cohort by definition id ...
plan <- ds.omop.plan.cohort(plan, cohort_definition_id = 42)

# ... or define one inline with a spec
plan <- ds.omop.plan.cohort(plan,
  spec = list(type = "condition", concept_set = c(201826)))

plan <- ds.omop.plan.baseline(plan,
  columns = c("gender_concept_id", "race_concept_id"),
  derived = c("age_at_index"))

ds.omop.plan.execute(plan, out = c(baseline = "D"),
  symbol = "omop", conns = conns)

ds.omop.cohort.ref(id) builds the same cohort_definition_id reference object if you prefer to assemble the reference separately.

Recurrent Cohorts and Index Events

The longitudinal unit is explicit. Materialized cohort rows receive a stable cohort_row_id; identical people may therefore have several index episodes. For a recipe population anchored on source events, declare the index event and whether to retain the first, last or all candidates:

pop <- omop_population(
  id = "study",
  index_event = omop_index_event(
    table = "condition_occurrence",
    concept_id = 201826,
    primary_limit = "all"
  ),
  filters = list(
    omop_filter_has_concept(
      concept_id = 3004410,
      table = "measurement",
      window = list(start = -365L, end = 0L)
    )
  )
)

Index-event execution currently supports condition, drug, measurement, observation, procedure, device and visit-occurrence tables with a standard person key, event date/end and stable primary key. It implements a deliberate Circe-like subset; it is not arbitrary CirceR/CohortGenerator execution. Primary first/last/all selection occurs before inclusion filters. By default each retained index episode ends at the end of the unique OMOP observation period covering its index, matching Circe. To request a bounded offset, use for example end_strategy = list(DateOffset = list(DateField = "StartDate", Offset = 30L)). DateField = "EndDate", Offset = 0L explicitly requests the source event’s physical end (capped at observation-period end). Missing or overlapping observation periods and unsupported Circe end strategies fail closed.

When an externally generated cohort can be recurrent and a population filter uses its index date, set episode_policy to "any_episode", "all_episodes", "first_episode" or "last_episode". Without one, recurrent index-dependent filtering fails closed instead of silently choosing an episode. These policies decide person membership; per-episode extraction is separately controlled by the output grain described in the Data Extraction vignette.

ds.omop.cohort.from_table() promotes the distinct person set represented by an omop.table; it does not reconstruct arbitrary episode dates from a generic workspace table.

Disclosure Controls

Every cohort operation – creation, combination, and from_table promotion – enforces nfilter.subset on the number of unique persons (counted as COUNT(DISTINCT person_id)). If a cohort would contain fewer than the threshold, the operation fails closed without returning the observed support:

Error: Disclosive: cohort support is below the configured threshold.

See the Security vignette for how an Opal administrator configures the thresholds.