DCC treats data cleaning as an auditable pipeline. Raw data is immutable, every correction is driven by a declarative and versioned rule, and each change is recorded at the cell level so the whole run can be reproduced from a manifest. This vignette walks one small dataset through the full Detect -> Execute -> Report workflow.
We start from a tiny response file. S2 has an
out-of-range score and S3 is missing an item.
csv <- tempfile(fileext = ".csv")
writeLines(c(
"sid,score,q1,q2,q3",
"S1,90,1,2,3",
"S2,150,2,2,2",
"S3,70,1,,3"
), csv)Rules live outside the code as declarative YAML. Here we flag scores
outside [0, 100] and respondents missing more than a third
of their items.
dcc_read() loads the file (with encoding detection and
structural diagnostics) and dcc_rules() parses the rule
set, recording its hash for the audit trail. dcc_detect()
then returns a structured dcc_findings object.
dcc_execute() applies declarative actions mapped to rule
IDs. Input data is never mutated, the whole plan is validated before any
change, and findings without an explicit action are returned
unhandled rather than silently flagged or dropped. Below we
blank the out-of-range score and flag the incomplete respondent.
res <- dcc_execute(
x, found,
actions = list(R001 = "set_na", Q_MISSING_ITEMS = "flag"),
id_var = "sid"
)
#> Warning: Action ID 'Q_MISSING_ITEMS' is deprecated; use 'D001'.
dcc_cleaned(res)
#> <dcc_data>
#> rows: 3 cols: 5
#> source: /private/var/folders/f2/vt0pwn11523g8jj9yryz403h0000gn/T/RtmpW6lgf6/file7733490cfb1.csv (csv, latin1)
#> provenance: read -> execute
#> L0 findings: 0
#> data preview:
#> sid score q1 q2 q3
#> <char> <int> <int> <int> <int>
#> 1: S1 90 1 2 3
#> 2: S2 NA 2 2 2
#> 3: S3 70 1 NA 3Every change is captured in the cell-level audit log, with the old and new value, the triggering rule, and the method.
dcc_audit_log(res)
#> finding_id
#> <char>
#> 1: 72:detect-1cbd78178e1a99729a1442154703c8cc-284e81edb08f1365d32a8e68d2c18231|4:R001|2:S2|5:score|0
#> 2: 72:detect-1cbd78178e1a99729a1442154703c8cc-284e81edb08f1365d32a8e68d2c18231|4:D001|2:S3|8:<record>|0
#> record_id variable old_value new_value action check_id
#> <char> <char> <char> <char> <char> <char>
#> 1: S2 score 150 <NA> set_na R001
#> 2: S3 <NA> <NA> <NA> flag D001
#> method timestamp dcc_version
#> <char> <char> <char>
#> 1: cell set to NA 2026-09-02T19:30:23.494+0800 1.2.1
#> 2: reviewed and kept (no data change) 2026-09-02T19:30:23.494+0800 1.2.1
#> ruleset_hash keyfile_hash
#> <char> <char>
#> 1: 1cbd78178e1a99729a1442154703c8cc <NA>
#> 2: 1cbd78178e1a99729a1442154703c8cc <NA>dcc_report() writes a self-contained HTML report – a
management summary plus an audit layer that reconciles findings against
changes – with no external rendering dependency.
dcc_trace() returns the full history of any cell in the
cleaned data.
dcc_trace(res, "S2", "score")
#> <dcc_trace> record 'S2', variable 'score'
#> findings: 1
#> check_id variable evidence severity
#> <char> <char> <char> <char>
#> 1: R001 score value 150 outside range [0, 100] fail
#> changes: 1
#> variable old_value new_value action check_id timestamp
#> <char> <char> <char> <char> <char> <char>
#> 1: score 150 <NA> set_na R001 2026-09-02T19:30:23.494+0800Finally, dcc_manifest() captures the input and rule
hashes, the actions, and the output hashes; dcc_rerun()
re-executes the whole pipeline and verifies the result is byte-identical
(timestamps excluded).
For files that do not fit in memory,
dcc_detect_chunked() streams the input with an adaptive
backend – data.table for delimited text, arrow
for Parquet/Feather – producing findings identical to the in-memory path
for record-local checks.