- by Handson
- August 17, 2026
Clinical SAS Technical Case Study: From Raw Clinical Trial Data to Regulatory-Ready Analysis
Company: NovaCure Pharmaceuticals Ltd.
Study: NC-201-301
Phase: Phase III
Therapeutic Area: Oncology
Indication: Advanced Metastatic Colorectal Cancer
Treatment: NC-201 + Standard of Care vs Placebo + Standard of Care
Programming Environment: SAS 9.4
Data Standards: CDISC SDTM and ADaM
Primary Analysis: Efficacy and Safety
Case Study Type: Fictionalized technical project using simulated clinical-trial data
The Business Problem
At 6:30 PM on a Thursday, the Clinical Data Management team at NovaCure Pharmaceuticals Ltd. released the final database snapshot for one of the company's most important clinical programs.
The study was NC-201-301, a randomized, double-blind, placebo-controlled Phase III trial evaluating NC-201 in patients with advanced metastatic colorectal cancer.
The database was locked.
The clinical team was waiting.
The biostatisticians were preparing the statistical analysis.
Regulatory submission activities were approaching.
And the Clinical Programming team had one critical responsibility:
Convert the locked clinical data into standardized, analysis-ready datasets and produce the statistical outputs required for the primary study report.
The raw data looked straightforward.
It wasn't.
The programming team discovered inconsistencies in treatment assignments, partial dates, duplicate laboratory records, adverse-event terminology and several derivation requirements that could materially affect the analysis.
The project became a practical demonstration of why clinical programming is not simply about writing SAS code.
It is about understanding data, clinical meaning, statistical requirements, CDISC standards and validation at the same time.
1. The Clinical Trial
NovaCure Pharmaceuticals designed NC-201-301 to evaluate whether NC-201 could improve progression-free survival compared with placebo when administered alongside standard-of-care therapy.
The study randomized 612 subjects across 86 clinical sites.
Study Design
| Parameter | Study Detail |
|---|---|
| Study | NC-201-301 |
| Phase | III |
| Design | Randomized, double-blind, placebo-controlled |
| Subjects randomized | 612 |
| Treatment arms | NC-201 + SoC / Placebo + SoC |
| Primary endpoint | Progression-Free Survival |
| Secondary endpoint | Overall Survival |
| Safety assessment | Adverse Events, Laboratory Data, Vital Signs |
| Database status | Locked |
| Programming standard | SAS 9.4 |
| Data standards | SDTM / ADaM |
The programming team was responsible for transforming the source data into the required standardized and analysis datasets and producing the planned TLFs.
2. The Raw Data Arrives
The first programming package contained raw datasets extracted from the clinical database.
The initial directory looked something like this:
/raw_data
dm_raw.sas7bdat
ae_raw.sas7bdat
cm_raw.sas7bdat
ex_raw.sas7bdat
lb_raw.sas7bdat
vs_raw.sas7bdat
ds_raw.sas7bdat
tumor_raw.sas7bdat
The team also received:
/specifications
SDTM_mapping_spec.xlsx
ADaM_spec.xlsx
TLF_shells.xlsx
SAP.pdf
CRF.pdf
This is an important distinction.
The programmer does not simply open the raw dataset and start coding.
Before programming begins, the team needs to understand:
What was collected?
What does each variable mean?
What is the target standard?
What does the Statistical Analysis Plan require?
What population should be analyzed?
What derivations are required?
The documentation drives the programming.
3. Step One: Understanding the Raw Data
The programming team began with a basic SAS assessment.
proc contents data=raw.dm_raw;
run;
proc contents data=raw.ae_raw;
run;
proc contents data=raw.lb_raw;
run;
They then reviewed frequencies and missing values.
proc freq data=raw.dm_raw;
tables sex race country / missing;
run;
proc freq data=raw.ae_raw;
tables aeterm aeser aeout / missing;
run;
For continuous laboratory variables:
proc means data=raw.lb_raw n nmiss min max mean std;
var lbvalue;
run;
This first profiling exercise immediately uncovered several issues.
Finding 1: Treatment Assignment
The raw treatment variable contained:
NC-201
NC-201
NC201
Placebo
PLACEBO
The values were clinically equivalent in meaning but technically inconsistent.
Finding 2: Partial Dates
Some adverse-event dates were recorded as:
2025-06
2025
rather than complete ISO dates.
Finding 3: Duplicate Laboratory Records
Several subjects had duplicate laboratory observations for the same visit and test.
Finding 4: Missing Baseline Indicators
The raw data did not contain a ready-made baseline flag.
The team therefore had to derive it according to the statistical analysis specifications.
4. SDTM Programming Begins
The first major deliverable was the SDTM layer.
The programming team started with the DM domain.
The raw demographic data contained variables such as:
SUBJECT_ID
SITE
SEX
RACE
COUNTRY
BRTHDAT
RANDOMIZED_TREATMENT
The SDTM mapping specification required standardized variables including:
STUDYID
DOMAIN
USUBJID
SUBJID
SITEID
SEX
RACE
COUNTRY
ARM
ACTARM
RFSTDTC
RFENDTC
5. Creating the DM Domain
A simplified example of the SAS programming logic was:
data sdtm.dm;
set raw.dm_raw;
length STUDYID $20 DOMAIN $2 USUBJID $30;
STUDYID = "NC-201-301";
DOMAIN = "DM";
SUBJID = strip(subject_id);
SITEID = strip(site);
USUBJID = cats(STUDYID, "-", SUBJID);
SEX = upcase(strip(sex));
RACE = strip(race);
COUNTRY = upcase(strip(country));
ARM = case
when upcase(randomized_treatment) = "NC-201"
then "NC-201 + Standard of Care"
when upcase(randomized_treatment) = "PLACEBO"
then "Placebo + Standard of Care"
else ""
end;
keep STUDYID DOMAIN USUBJID SUBJID SITEID
SEX RACE COUNTRY ARM;
run;
The actual implementation would follow the study-specific mapping specifications and applicable SDTM implementation guidance.
The important point is that the programmer wasn't merely renaming variables.
The programmer was applying business rules and clinical specifications.
6. The First Major Programming Challenge
During validation, the team discovered that 11 subjects had a mismatch between randomized treatment and actual treatment received.
For example:
| USUBJID | Randomized | Actual Treatment |
|---|---|---|
| NC-201-301-00123 | NC-201 | NC-201 |
| NC-201-301-00187 | NC-201 | Placebo |
| NC-201-301-00341 | Placebo | NC-201 |
This created an important distinction.
The analysis could require:
-
Planned treatment
-
Randomized treatment
-
Actual treatment
These concepts are not interchangeable.
The team went back to the Statistical Analysis Plan and dataset specifications before making any programming decision.
This is where clinical programming differs from ordinary data manipulation.
The programmer cannot simply decide which variable "looks right."
The specification and statistical methodology determine the correct derivation.
7. Handling Partial Dates
Another challenge involved adverse-event dates.
Suppose the raw data contained:
AE Start Date = 2025-07
The programmer cannot simply assume:
2025-07-01
because that would create information that was never actually collected.
Instead, partial dates need to be represented appropriately according to the study's specifications and data standards.
A simplified ISO conversion might preserve the known precision:
if length(aestdt_raw)=10 then
aestdtc = aestdt_raw;
else if length(aestdt_raw)=7 then
aestdtc = aestdt_raw;
else if length(aestdt_raw)=4 then
aestdtc = aestdt_raw;
The exact treatment of partial dates for analysis variables would then follow the SAP and ADaM specifications.
This distinction is critical:
SDTM representation and statistical analysis derivation are not necessarily the same thing.
8. Building the AE Domain
The Adverse Events domain was another major component.
The raw dataset contained:
SUBJECT_ID
AE_TERM
START_DATE
END_DATE
SERIOUS
SEVERITY
OUTCOME
RELATIONSHIP
The target SDTM domain required standardized variables.
A simplified programming structure could look like:
data sdtm.ae;
set raw.ae_raw;
length STUDYID $20 DOMAIN $2 USUBJID $30;
STUDYID = "NC-201-301";
DOMAIN = "AE";
USUBJID = cats(STUDYID, "-", strip(subject_id));
AETERM = strip(ae_term);
AESER = upcase(strip(serious));
AESEV = strip(severity);
AEOUT = strip(outcome);
AESTDTC = start_date;
AEENDTC = end_date;
keep STUDYID DOMAIN USUBJID
AETERM AESER AESEV AEOUT
AESTDTC AEENDTC;
run;
Again, the production program would be driven by the study's approved mapping specifications, controlled terminology and validation requirements.
9. From SDTM to ADaM
Once the SDTM datasets were completed and validated, the project moved into the analysis layer.
The first major analysis dataset was:
ADSL — Subject-Level Analysis Dataset
The objective was to create one record per subject containing the key variables required for analysis.
The ADSL specification included variables such as:
STUDYID
USUBJID
SUBJID
SITEID
AGE
SEX
RACE
TRT01P
TRT01A
SAFFL
ITTFL
FASFL
RANDDT
TRTSDT
TRTEDT
The programmer created ADSL by integrating information from multiple SDTM domains and other approved source datasets.
10. Deriving the Safety Population
The Statistical Analysis Plan defined the Safety Population as subjects who received at least one dose of study treatment.
The programming team therefore needed to determine treatment exposure from the EX domain.
Conceptually:
proc sql;
create table exposure as
select usubjid,
min(input(exstdtc,yymmdd10.)) as first_dose format=date9.
from sdtm.ex
where not missing(exstdtc)
group by usubjid;
quit;
The ADSL derivation could then use the exposure information to assign the safety flag.
if not missing(first_dose) then SAFFL="Y";
else SAFFL="N";
But even this apparently simple derivation requires careful consideration.
What constitutes exposure?
What if the dose was recorded but subsequently invalidated?
What if the subject received placebo?
What if treatment administration was partially recorded?
The answers come from the study specifications and SAP—not from the programmer's assumptions.
11. The Baseline Problem
One of the most common real-world programming challenges is determining the correct baseline value.
Suppose a subject has laboratory results:
| Date | Hemoglobin |
|---|---|
| Day -14 | 10.8 |
| Day -7 | 11.1 |
| Day 1 | 10.9 |
| Day 8 | 11.5 |
Which value is baseline?
The answer isn't automatically "the first value."
The study specification may define baseline as:
The last non-missing assessment collected before the first dose of study treatment.
The programmer therefore needed to derive baseline according to the protocol-defined rule.
A simplified approach might involve sorting by subject, test and date and selecting the appropriate observation.
proc sort data=sdtm.lb out=lb_sorted;
by usubjid lbtestcd lbdt;
run;
The final derivation would incorporate the study-specific treatment start date and baseline window.
This is where SAS programming meets statistical methodology.
12. Creating ADAE
The next major analysis dataset was ADAE, the analysis dataset for adverse events.
ADAE needed to support questions such as:
-
How many subjects experienced at least one AE?
-
How many experienced serious AEs?
-
How many experienced Grade 3 or higher AEs?
-
How many experienced treatment-related AEs?
-
Which preferred terms were most common?
-
Which system organ classes were most affected?
The dataset therefore incorporated analysis variables derived from SDTM AE data and treatment dates.
For example:
USUBJID
TRT01A
SAFFL
AEREL
AESER
AESEV
AEBODSYS
AEDECOD
ASTDT
AENDT
ASTDY
AENDY
13. The Treatment-Emergent AE Derivation
One of the most important derivations was the treatment-emergent adverse event flag.
The study specification defined a treatment-emergent AE according to the protocol/SAP-defined relationship between the AE start date and treatment exposure.
A simplified conceptual derivation might be:
if not missing(ASTDT) and not missing(TRTSDT) then do;
if ASTDT >= TRTSDT then
TRTEMFL = "Y";
end;
However, real-world studies may have additional rules involving treatment periods, partial dates, prior events and post-treatment windows.
Therefore, the production derivation must always follow the approved specification.
14. Creating ADLB
The laboratory analysis dataset required another layer of derivations.
The raw laboratory data contained:
SUBJECT
TEST
RESULT
UNIT
REFERENCE_LOW
REFERENCE_HIGH
DATE
The analysis dataset needed to support:
-
Baseline values
-
Post-baseline values
-
Change from baseline
-
Percent change
-
Normal/abnormal classification
-
Shift tables
-
Potentially clinically significant abnormalities
A simplified derivation of change might be:
CHG = AVAL - BASE;
and percent change:
if BASE ne 0 then
PCHG = ((AVAL - BASE) / BASE) * 100;
The programmer then validated these derivations against the approved specifications.
15. From Analysis Datasets to TLFs
Now came the deliverables the clinical team was waiting for.
Tables, Listings and Figures.
The programming team had to reproduce the outputs defined in the Statistical Analysis Plan.
Examples included:
Table 14.1.1
Subject Disposition
Table 14.2.1
Demographic and Baseline Characteristics
Table 14.3.1
Overall Summary of Adverse Events
Table 14.3.2
Treatment-Emergent Adverse Events by System Organ Class and Preferred Term
Table 14.3.3
Serious Adverse Events
Figure 14.2.1
Kaplan-Meier Curve for Progression-Free Survival
Listing 16.2.7
Serious Adverse Events
This is where the programming team transformed datasets into information that clinicians and statisticians could interpret.
16. Example: Safety Table Programming
Suppose the safety population consisted of:
| Treatment | Subjects |
|---|---|
| NC-201 | 306 |
| Placebo | 298 |
The programmer might create an AE summary using:
proc freq data=adam.adae;
tables TRT01A*AEDECOD / norow nocol nopercent;
where SAFFL="Y" and TRTEMFL="Y";
run;
But a production-quality table requires much more than a PROC FREQ.
The programmer must account for:
-
Subject-level counting
-
Multiple occurrences of the same AE
-
Severity
-
Seriousness
-
Treatment relationship
-
System Organ Class
-
Preferred Term
-
Treatment groups
-
Denominators
-
Missing values
-
Table formatting
For example, if one patient experienced the same adverse event five times, the table may need to count that patient once, depending on the table's statistical rules.
That requires careful programming.
17. The TLF Quality-Control Problem
The first production run generated the following result:
NC-201 treatment arm: 184 subjects with at least one TEAE
But the independent QC program produced:
NC-201 treatment arm: 186 subjects with at least one TEAE
Two subjects were missing.
The team stopped the production process.
They did not simply change the number.
They investigated.
18. Root-Cause Analysis
The programmers compared:
Production Dataset
versus
Independent QC Dataset
They used SAS comparison procedures.
proc compare
base=prod.adae
compare=qc.adae
listall;
run;
The difference came from two subjects with partially recorded AE start dates.
The production program had excluded the records because the date could not be completely derived.
The QC program had retained them based on the study-specific partial-date rules.
The team reviewed the specifications.
The QC interpretation was correct.
The production derivation was updated.
The datasets were regenerated.
The table was rerun.
The counts matched.
This was a small programming discrepancy.
But in a regulated environment, it was a serious quality issue.
19. Why Independent Programming Matters
This is one of the most important lessons from the project.
Clinical programming cannot rely on:
"The program ran without errors."
A SAS log can be clean while the result is wrong.
Therefore, the project used independent QC.
The workflow was:
Specification
↓
Production Programming
↓
Analysis Dataset
↓
Independent QC Programming
↓
Comparison
↓
Discrepancy Investigation
↓
Resolution
↓
Final Dataset
↓
Final TLF
This creates an additional layer of confidence in the results.
20. The Kaplan-Meier Challenge
The primary endpoint was progression-free survival.
The statistical team required a Kaplan-Meier analysis comparing the two treatment arms.
The analysis dataset needed variables such as:
USUBJID
TRT01A
AVAL
CNSR
where:
-
AVALrepresented analysis time -
CNSRrepresented the censoring indicator
A simplified SAS implementation could use:
proc lifetest data=adam.adtte
plots=survival;
time AVAL*CNSR(1);
strata TRT01A;
run;
The actual production analysis would follow the exact statistical methodology defined in the SAP, including time origin, event definition, censoring rules, analysis populations and other study-specific requirements.
This illustrates another important point:
The Clinical SAS Programmer is implementing statistical methodology—not inventing it.
The Statistical Analysis Plan provides the analytical framework.
The programmer translates that framework into reproducible code.
21. The Final Data Flow
After several rounds of programming, QC and review, the final architecture looked like this:
RAW CLINICAL DATA
|
v
+-------------------+
| Data Review & |
| Profiling |
+-------------------+
|
v
SDTM DATASETS
|
+-------------+-------------+
| | |
v v v
DM AE LB
| | |
+-------------+-------------+
|
v
ADaM DATASETS
|
+-------------+-------------+
| | |
v v v
ADSL ADAE ADLB
|
v
TLF PROGRAMS
|
+-------------+-------------+
| | |
v v v
Tables Listings Figures
|
v
QC & VALIDATION
|
v
FINAL STUDY OUTPUT
This is the basic journey from collected clinical data to statistical evidence.
22. What the Project Delivered
At the end of the programming cycle, the team delivered:
SDTM
-
DM
-
AE
-
CM
-
EX
-
LB
-
VS
-
DS
-
Other study-required domains
ADaM
-
ADSL
-
ADAE
-
ADLB
-
ADTTE
-
Additional analysis datasets required by the SAP
TLFs
-
Demographic tables
-
Disposition tables
-
Efficacy tables
-
Safety tables
-
Laboratory shift tables
-
AE listings
-
Kaplan-Meier figures
-
Other study-specific outputs
Quality Deliverables
-
Production programs
-
QC programs
-
Validation reports
-
Dataset specifications
-
Traceability documentation
-
Programming documentation
23. The Most Important Technical Lessons
The NC-201-301 project demonstrated several realities of Clinical SAS programming.
Lesson 1: SAS Is Only One Part of the Job
Knowing SAS syntax is important.
But it isn't sufficient.
A programmer must understand the clinical context.
Lesson 2: Specifications Drive Programming
The programmer should not make assumptions about:
-
Population definitions
-
Baseline
-
Treatment
-
Missing data
-
Censoring
-
Analysis windows
The SAP and approved specifications are the source of truth.
Lesson 3: SDTM and ADaM Require Different Thinking
SDTM focuses on standardized representation of collected clinical data.
ADaM focuses on analysis-ready datasets.
Understanding this distinction is fundamental.
Lesson 4: Data Quality Is a Programming Responsibility
A successful program is not simply one that executes.
The output must be:
Correct + Reproducible + Traceable + Validated
Lesson 5: Clinical Knowledge Adds Value
A programmer who understands the meaning of an adverse event, laboratory test or treatment exposure can ask better questions.
That is why professionals from pharmacy, biotechnology, life sciences, statistics and related disciplines can potentially build strong clinical programming profiles.
24. Where AI Fits Into This Workflow
Now imagine the same project in 2026.
The programming team has access to AI-assisted development tools.
AI can potentially help programmers:
-
Generate SAS code templates
-
Explain legacy programs
-
Suggest SQL logic
-
Identify potential coding errors
-
Draft documentation
-
Create test cases
-
Automate repetitive programming
-
Help analyze logs
-
Assist with data-quality investigations
But the core workflow remains:
Specification → Programming → Review → Validation → QC → Approval
AI doesn't remove the requirement for professional judgment.
If an AI tool generates a technically valid SAS program that implements the wrong clinical rule, the program is still wrong.
This is why the future Clinical SAS professional needs two capabilities:
Clinical programming expertise
and
AI literacy.
25. What Skills Would a Programmer Need to Work on This Project?
A programmer joining the NC-201-301 project would need a combination of technical and clinical skills.
SAS Programming
-
DATA step
-
PROC SQL
-
PROC SORT
-
PROC TRANSPOSE
-
Macro programming
-
Functions
-
Arrays
-
Formats
-
Debugging
Clinical Programming
-
Clinical trial terminology
-
Study design
-
Safety and efficacy concepts
-
Treatment exposure
-
Analysis populations
CDISC
-
SDTM
-
SDTMIG
-
ADaM
-
ADaMIG
-
Controlled Terminology
-
Define-XML concepts
Statistics
-
Descriptive statistics
-
Change from baseline
-
Frequency analysis
-
Time-to-event analysis
-
Kaplan-Meier concepts
Reporting
-
Tables
-
Listings
-
Figures
-
ODS
-
Output formatting
Quality
-
QC programming
-
PROC COMPARE
-
Log review
-
Traceability
-
Documentation
Modern Technology
-
Python fundamentals
-
Automation
-
Generative AI
-
AI-assisted programming
26. The Career Lesson Hidden Inside the Technical Project
There is an important career lesson behind this fictional project.
A company doesn't hire a Clinical SAS Programmer because the candidate knows how to write:
proc sort;
run;
It hires the person because that programmer can understand the larger problem:
How do we transform clinical trial data into reliable, standardized and analysis-ready information?
That is a much more valuable skill.
The syntax is simply the mechanism.
The real expertise lies in understanding the clinical data lifecycle.
Conclusion
At NovaCure Pharmaceuticals, the NC-201-301 project began with thousands of rows of raw clinical data.
It ended with standardized datasets, analysis-ready data and validated statistical outputs.
Between those two points was an enormous amount of reasoning.
The programmers had to understand:
Clinical research.
Data standards.
SAS programming.
Statistics.
Derivations.
Validation.
Quality control.
And increasingly:
Artificial intelligence.
That is what makes Clinical SAS programming a specialized profession.
It isn't simply about writing code.
It is about transforming clinical data into trustworthy evidence.
And as clinical trials become more data-intensive and technology continues to evolve, the professionals who understand both the science behind the data and the technology used to process it are likely to remain valuable.
The future Clinical SAS Programmer may not write every line of code manually.
They may use AI to generate, review and automate much of the routine work.
But they will still need to answer the most important question:
"Does this program correctly represent the clinical and statistical requirement?"
That is where technical skill becomes professional expertise.
Technical Case Study Summary
| Project Component | Implementation |
|---|---|
| Company | NovaCure Pharmaceuticals Ltd. |
| Study | NC-201-301 |
| Phase | III |
| Therapeutic Area | Oncology |
| Subjects | 612 |
| Primary Endpoint | Progression-Free Survival |
| Programming | SAS 9.4 |
| SDTM Domains | DM, AE, CM, EX, LB, VS, DS, etc. |
| ADaM | ADSL, ADAE, ADLB, ADTTE |
| Reporting | Tables, Listings, Figures |
| QC | Independent Programming + PROC COMPARE |
| Key Challenges | Partial dates, duplicate records, treatment discrepancies, baseline derivation |
| Modern Enhancement | AI-assisted programming and automation |
| Final Objective | Validated, analysis-ready clinical trial data and statistical outputs |
Important Disclaimer
NovaCure Pharmaceuticals Ltd., NC-201, NC-201-301, the clinical trial data, subject counts, study results, programming outputs and project events in this case study are entirely fictional and created for educational purposes. The technical workflow is designed to resemble a realistic Clinical SAS project, but it is not based on an actual pharmaceutical company's clinical trial or confidential industry data. Production implementations must follow the applicable study protocol, Statistical Analysis Plan, CDISC standards, programming specifications and organizational SOPs.


