A transparent, rule-based framework for detecting changes between
successive data cuts of clinical trial datasets, classifying those changes
into clinically meaningful categories, tracing user-defined data lineage,
and assessing which downstream analyses and outputs may be affected.
The package is designed to complement existing low-level data frame
comparison tools by adding clinical-trial-specific classification, lineage
and impact-assessment layers on top of deterministic comparison. The
rule-based classification is similar in spirit to the data validation
infrastructure of van der Loo and de Jonge (2021)
trialdiff is a clinical-trial data-cut change detection and downstream
impact assessment framework for R, designed for the pharmaverse ecosystem.
Existing tools tell you that two datasets differ. trialdiff answers the
clinical-programming question that follows:
What changed, what does the change represent, and which downstream analyses or outputs might be affected?
It does this in five transparent, composable layers:
| Layer | Function | Purpose |
|---|---|---|
| Compare | compare_cut() |
Added/removed/modified observations and schema changes |
| Classify | classify_changes() |
Rule-based clinical change taxonomy |
| Trace | define_lineage(), trace_dependencies(), lineage_from_metadata(), output_registry() |
Explicit data lineage graph |
| Assess | assess_impact() |
Definitely / potentially / unlikely impact |
| Report | report_diff() |
HTML, Quarto and machine-readable JSON |
Everything is deterministic. There is no machine learning, and no statistical impact is ever claimed - analyses are flagged for review and rerun.
# From r-universe (includes Windows/macOS binaries)
install.packages(
"trialdiff",
repos = c(
hirujan = "https://hirujan-r.r-universe.dev",
CRAN = "https://cloud.r-project.org"
)
)
# Or from GitHub
# install.packages("remotes")
remotes::install_github("Hirujan-R/trialdiff")
library(trialdiff)
diff <- compare_cut(
old = adsl_cut1,
new = adsl_cut2,
by = "USUBJID",
dataset = "ADSL"
)
classified <- classify_changes(diff)
impact <- assess_impact(classified, adsl_adlb_lineage)
report_diff(diff, impact = impact, output = "report.html")
A treatment assignment change (TRT01P = "Placebo" -> "Drug A") is detected,
classified as a treatment-assignment change, traced through
ADSL.TRT01P -> ADLB.TRT01P -> lab summary -> MMRM -> efficacy table, and every
downstream object is flagged for review with a rationale.
diffdf?diffdf (and waldo) are excellent low-level comparison tools, and
trialdiff deliberately does not reinvent them. trialdiff adds the layers
that are specific to clinical programming:
It can even delegate the low-level comparison itself: compare_cut(..., backend = "diffdf") uses diffdf to detect differences and translates the
result into the same tdiff object.
vignette("trialdiff") - getting started.vignette("change-classification") - the rule system.vignette("lineage-and-impact") - lineage and impact.vignette("ecosystem") - relationship to diffdf, admiral, metacore,
cards, tern, SAS PROC COMPARE and others.vignette("case-study") - end-to-end walkthrough on public pharmaverseadam
data.A full proposal covering the problem statement, ecosystem/gap analysis, novelty assessment, architecture, testing and roadmap is available in the project proposal.
Please note that the trialdiff project is released with a
Contributor Code of Conduct.
By contributing you agree to abide by its terms.
MIT (c) Hirujan Rangaraj. No proprietary or real patient data is included; all example datasets are synthetic.