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:
- Pre-existing cohorts stored in the results schema (e.g. generated by ATLAS or CohortGenerator).
- Spec-based cohorts created on-the-fly from a structured specification.
-
Workspace cohorts built from an already-extracted
omop.tablewithds.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 42Creating 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 tableSupported 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 idUsing 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.