Skip to main content
Work in Progress

This page is currently under development. Content may be incomplete or contain inaccuracies. If you notice any errors or have suggestions, please contact us.

Use Case 2: Clinical Trial Data Management

Clinical trials generate complex, longitudinal datasets that must comply with regulatory requirements (ICH-GCP, GDPR), follow standardized formats (CDISC), and undergo rigorous quality control. This use case covers the data management lifecycle from study design through analysis-ready datasets.

Workflow Overview

Study design → Data collection → Cleaning & coding → Analysis datasets → Reporting
Protocol/CRF → REDCap/EDC entries → SDTM domains → ADaM datasets → Tables, Figures, Listings

Key Concepts

Electronic Data Capture (EDC)

Modern clinical trials collect data electronically using systems like:

  • REDCap — Free, widely used in academic research
  • OpenClinica — Open-source, GCP-compliant
  • Medidata Rave — Commercial, industry standard
  • Castor EDC — Cloud-based, European-hosted

CDISC Standards

The Clinical Data Interchange Standards Consortium defines how clinical trial data should be structured:

StandardPurposeStage
CDASHStandardized data collection formsData entry
SDTMStudy Data Tabulation Model — standardized domains (DM, AE, LB, VS, etc.)Tabulation
ADaMAnalysis Data Model — analysis-ready datasetsStatistical analysis
Define-XMLMachine-readable data dictionary describing variablesDocumentation

Data Quality

Clinical data undergoes rigorous quality control:

  • Edit checks — Automated validation rules in the EDC system
  • Query management — Data managers raise queries for inconsistent or missing data
  • Source data verification (SDV) — Comparing EDC entries against source documents
  • Medical coding — Standardizing adverse events (MedDRA) and medications (WHO Drug Dictionary)

Regulatory Requirements

  • ICH E6 (R2) / GCP — Good Clinical Practice guidelines for data integrity
  • 21 CFR Part 11 (FDA) — Requirements for electronic records and signatures
  • GDPR — EU data protection for patient data
  • Data retention — Trial data must typically be retained for 15-25 years

Data Capture

  • REDCap — Secure web-based EDC, strong academic community
  • OpenClinica — Open-source, CDISC-aware
  • LimeSurvey — Open-source for surveys and patient-reported outcomes

Data Management and Analysis

  • R (tidyverse, haven, admiral) — CDISC dataset creation and analysis
  • SAS — Traditional pharmaceutical industry standard
  • Python (pandas, lifelines) — Data cleaning and survival analysis
  • Pinnacle 21 — CDISC validation tool

Randomization and Monitoring

  • randomizeR (R) — Randomization procedures
  • DSMB tools — Interim analysis and monitoring

Code Example: REDCap Data Processing with R

Click to expand R clinical data processing script
#!/usr/bin/env Rscript
# Clinical trial data processing: REDCap export to analysis-ready dataset

library(tidyverse)
library(REDCapR)
library(lubridate)
library(survival)
library(survminer)

set.seed(42)

# ============================================================
# Step 1: Import data from REDCap
# ============================================================

# Option A: API access (preferred for reproducibility)
# redcap_data <- redcap_read(
# redcap_uri = "https://redcap.institution.de/api/",
# token = Sys.getenv("REDCAP_TOKEN")
# )$data

# Option B: CSV export (manual download)
raw_data <- read_csv("redcap_export.csv", show_col_types = FALSE)

cat("Imported", nrow(raw_data), "records with", ncol(raw_data), "variables\n")

# ============================================================
# Step 2: Data cleaning and validation
# ============================================================

clean_data <- raw_data %>%
# Remove test/dummy records
filter(!str_detect(record_id, "TEST|DUMMY")) %>%
# Parse dates
mutate(
enrollment_date = ymd(enrollment_date),
birth_date = ymd(birth_date),
age = as.numeric(difftime(enrollment_date, birth_date, units = "days")) / 365.25
) %>%
# Validate ranges
mutate(
age_valid = between(age, 18, 100),
weight_valid = between(weight_kg, 30, 300),
height_valid = between(height_cm, 100, 250)
)

# Report validation issues
validation_issues <- clean_data %>%
filter(!age_valid | !weight_valid | !height_valid) %>%
dplyr::select(record_id, age, age_valid, weight_kg, weight_valid,
height_cm, height_valid)

if (nrow(validation_issues) > 0) {
cat("WARNING:", nrow(validation_issues), "records with validation issues\n")
write_csv(validation_issues, "data_queries.csv")
}

# ============================================================
# Step 3: Create SDTM-like domains
# ============================================================

# Demographics (DM) domain
dm <- clean_data %>%
transmute(
STUDYID = "TRIAL-001",
USUBJID = paste0("TRIAL-001-", str_pad(record_id, 4, pad = "0")),
SUBJID = record_id,
AGE = round(age, 1),
AGEU = "YEARS",
SEX = case_when(
sex == 1 ~ "M",
sex == 2 ~ "F",
TRUE ~ "U"
),
RACE = race_label,
ARM = treatment_arm,
ARMCD = case_when(
treatment_arm == "Treatment" ~ "TRT",
treatment_arm == "Placebo" ~ "PBO",
TRUE ~ "UNK"
),
RFSTDTC = format(enrollment_date, "%Y-%m-%d")
)

cat("DM domain:", nrow(dm), "subjects\n")
cat(" Treatment:", sum(dm$ARMCD == "TRT"), "\n")
cat(" Placebo:", sum(dm$ARMCD == "PBO"), "\n")

# ============================================================
# Step 4: Basic analysis — survival analysis example
# ============================================================

# Create analysis dataset
analysis <- dm %>%
left_join(
clean_data %>%
transmute(
USUBJID = paste0("TRIAL-001-", str_pad(record_id, 4, pad = "0")),
event_time_days = as.numeric(event_time),
event_status = as.integer(event_occurred)
),
by = "USUBJID"
) %>%
filter(!is.na(event_time_days))

# Kaplan-Meier survival analysis
surv_obj <- Surv(analysis$event_time_days, analysis$event_status)
km_fit <- survfit(surv_obj ~ ARM, data = analysis)

# Plot
pdf("kaplan_meier.pdf", width = 8, height = 6)
ggsurvplot(km_fit, data = analysis,
pval = TRUE, conf.int = TRUE,
risk.table = TRUE,
xlab = "Time (days)",
ylab = "Survival probability",
title = "TRIAL-001: Kaplan-Meier Survival Curves",
palette = c("#E41A1C", "#377EB8"))
dev.off()

# Log-rank test
logrank <- survdiff(surv_obj ~ ARM, data = analysis)
cat("Log-rank test p-value:", format.pval(1 - pchisq(logrank$chisq, 1)), "\n")

# ============================================================
# Step 5: Export datasets
# ============================================================

write_csv(dm, "output/sdtm_dm.csv")
write_csv(analysis, "output/adam_analysis.csv")

cat("Data processing complete!\n")
sessionInfo()

Expected Outputs

  • SDTM datasets — Standardized tabulation domains (DM, AE, LB, VS, etc.)
  • ADaM datasets — Analysis-ready datasets with derived variables
  • Data quality reports — Validation summaries, query logs
  • Define-XML — Machine-readable data dictionary
  • Tables, Figures, Listings (TFLs) — Statistical analysis outputs
  • Audit trail — Complete log of data modifications

Computational Requirements

TaskCPURAMStorageTime
REDCap data export and cleaning1-22-4 GB<1 GBMinutes
SDTM/ADaM dataset creation2-44-8 GB<1 GBMinutes-hours
Statistical analysis (standard)2-44-8 GB<1 GBMinutes
Complex modeling (Bayesian, ML)8-1616-32 GB1-5 GBHours

Common Issues & Troubleshooting

Common Problems

Missing data

  • Clinical trials always have missing data — document the extent and mechanism (MCAR, MAR, MNAR)
  • Pre-specify imputation strategies in the Statistical Analysis Plan (SAP)
  • Never impute primary endpoint data without strong justification

Date inconsistencies

  • Different sites may use different date formats — standardize early
  • Partial dates (e.g., only month/year) are common — define imputation rules in advance
  • Use ISO 8601 format (YYYY-MM-DD) consistently

Coding inconsistencies

  • Free-text entries for adverse events need medical coding (MedDRA)
  • Medication names vary — map to WHO Drug Dictionary or ATC codes
  • Establish a coding convention document before data collection begins

Key Considerations

  • Plan data management before enrollment — Create a Data Management Plan (DMP) alongside the protocol
  • Use validated systems — EDC systems must comply with 21 CFR Part 11 / GCP requirements
  • Separate roles — Data managers, biostatisticians, and monitors should have distinct responsibilities
  • Lock data before analysis — Freeze the database after cleaning and before unblinding
  • Archive everything — Raw data, analysis code, outputs, and correspondence must be retained per regulatory requirements
  • Pre-register your analysis — Publish the Statistical Analysis Plan before database lock to avoid bias