Records a QC decision against a cohort: either annotate matching rows
with a status and reason (action = "flag"), or remove them
(action = "drop"). Every call is recorded in the cohort's QC log (see
qc_log()), which is not cleared by cohort_filter(), so the record of
why something was dropped survives later structural changes.
Arguments
- cohort
A Cohort object.
- ids
Character vector of ids to act on. At least one, no
NA. Duplicates are removed. Their meaning depends onscope.- scope
One of
"sample"or"subject": whetheridsare sample ids (matched againstcohort@sample_map$sample_id) or subject ids (matched againstcohort@subject_tbl$subject_id). No default.- action
One of
"flag"or"drop"."flag"setsqc_status/qc_reasonon the matching rows and keeps them."drop"removes the matching rows entirely. No default.- reason
A single, non-empty string explaining the decision.
Value
A new Cohort.
Details
An id in ids that does not exist for the given scope is an error; it
is never silently ignored.
action = "flag", scope = "sample" sets qc_status/qc_reason on
sample_map, creating the columns if they are absent. scope = "subject"
sets the same two column names on subject_tbl instead; these are a
separate pair of columns from the sample-level ones, and both can be set
on the same cohort for different reasons. Flagging an id that already has
a qc_reason appends the new reason rather than replacing it.
action = "drop" delegates to cohort_filter(): scope = "sample" uses
its drop_sample_ids argument, with drop_empty = FALSE so a subject
left with no samples is not also removed; scope = "subject" uses its
subject_ids argument to keep every other subject. Either way, the
resulting cache reset is the same one a direct cohort_filter() call
would produce.
sample_id is normally unique, so qc_log()'s previous_status reflects
the one matching row. If sample_map holds duplicate sample_ids (built
with allow_duplicates = TRUE), every matching row is still flagged or
dropped correctly, but the logged previous_status reflects only one of
them.
Examples
data(example_cohort)
# Flag one sample, keeping it
bad_id <- samples(example_cohort)$sample_id[[1]]
flagged <- cohort_qc(
example_cohort, bad_id,
scope = "sample", action = "flag", reason = "failed QC review"
)
samples(flagged)
#> # A tibble: 12 × 8
#> subject_id assay sample_id role fastq_1 fastq_2 qc_status qc_reason
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 RAT001 wes WES_R001_T tumor wes_r001_t_R1… wes_r0… flagged failed Q…
#> 2 RAT001 wes WES_R001_N normal wes_r001_n_R1… wes_r0… NA NA
#> 3 RAT001 scrna SNRNA_R001 tumor snrna_r001_R1… snrna_… NA NA
#> 4 RAT002 wes WES_R002_T tumor wes_r002_t_R1… wes_r0… NA NA
#> 5 RAT002 wes WES_R002_N normal wes_r002_n_R1… wes_r0… NA NA
#> 6 RAT002 scrna SNRNA_R002 tumor snrna_r002_R1… snrna_… NA NA
#> 7 MOUSE001 wes WES_M001_T tumor wes_m001_t_R1… wes_m0… NA NA
#> 8 MOUSE001 wes WES_M001_N normal wes_m001_n_R1… wes_m0… NA NA
#> 9 MOUSE001 scrna SNRNA_M001 tumor snrna_m001_R1… snrna_… NA NA
#> 10 MOUSE002 wes WES_M002_T tumor wes_m002_t_R1… wes_m0… NA NA
#> 11 MOUSE002 wes WES_M002_N normal wes_m002_n_R1… wes_m0… NA NA
#> 12 MOUSE002 scrna SNRNA_M002 tumor snrna_m002_R1… snrna_… NA NA
qc_log(flagged)
#> # A tibble: 1 × 6
#> scope id action reason previous_status timestamp
#> <chr> <chr> <chr> <chr> <chr> <dttm>
#> 1 sample WES_R001_T flag failed QC review NA 2026-09-16 14:56:11
# Drop the same sample instead
dropped <- cohort_qc(
example_cohort, bad_id,
scope = "sample", action = "drop", reason = "failed QC review"
)
nrow(samples(dropped)) < nrow(samples(example_cohort))
#> [1] TRUE