Data ScienceRoles and job titles explained

Statistical programmer in pharma: SDTM, ADaM and TLFs explained

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 27 September 2026 · 5 min read
In this article (9 sections)

Related programme: the Data Science course (6 months; ₹1,30,000 (inclusive of GST) · EMI from ₹5,000/month).

The short answer

A statistical programmer in pharma turns locked clinical trial data into standardised datasets and the tables, listings and figures (TLFs) that go into a clinical study report and a regulatory submission. The work follows written specifications closely: map raw data to CDISC SDTM, derive analysis datasets in ADaM, then produce outputs that match the statistical analysis plan, usually with a second programmer independently reproducing each key result.

The pipeline you work inside

text
EDC raw data  ->  SDTM domains (DM, AE, EX, LB, VS ...)  ->  ADaM (ADSL, ADAE, ADLB ...)  ->  TLFs
  collected         standardised tabulation                  analysis-ready                in the report

SDTM organises what was collected into standard domains. ADaM adds what the analysis needs: derived flags, analysis periods, baseline values, treatment variables. CDISC's standards page is the primary reference for what each standard covers and notes that regulators such as the US FDA and Japan's PMDA require SDTM, ADaM and Define-XML for submissions. Define-XML documents the datasets for reviewers.

The artefact: an ADaM derivation spec and its code

Programmers do not decide derivations; the specification does. A few rows for an adverse events analysis dataset might read:

VariableLabelDerivation (from spec)
TRTSDTDate of first exposureFrom ADSL; first dose date from EX
TRTEDTDate of last exposureFrom ADSL; last dose date from EX
ASTDTAnalysis start dateAESTDTC converted to a date
TRTEMFLTreatment-emergent flag"Y" if ASTDT is on or after TRTSDT and no later than TRTEDT + 30 days; blank otherwise
AOCCFLFirst occurrence flag"Y" on the first treatment-emergent record per subject

The 30-day window is study-specific and hypothetical here. The code below applies it to synthetic records, in pandas rather than SAS so it runs anywhere.

python
import pandas as pd

# Synthetic ADSL (one row per subject) and AE records
adsl = pd.DataFrame({
    "USUBJID": ["S-001", "S-002", "S-003"],
    "TRTSDT": pd.to_datetime(["2026-01-10", "2026-01-12", "2026-01-15"]),
    "TRTEDT": pd.to_datetime(["2026-03-10", "2026-02-20", "2026-03-15"]),
})
ae = pd.DataFrame({
    "USUBJID": ["S-001", "S-001", "S-002", "S-002", "S-003"],
    "AETERM": ["Headache", "Nausea", "Rash", "Fatigue", "Dizziness"],
    "AESTDTC": ["2026-01-05", "2026-02-01", "2026-03-15", "2026-03-30", "2026-01-15"],
})

adae = ae.merge(adsl, on="USUBJID", how="left")
adae["ASTDT"] = pd.to_datetime(adae["AESTDTC"])
window_end = adae["TRTEDT"] + pd.Timedelta(days=30)
emergent = (adae["ASTDT"] >= adae["TRTSDT"]) & (adae["ASTDT"] <= window_end)
adae["TRTEMFL"] = emergent.map({True: "Y", False: ""})

adae = adae.sort_values(["USUBJID", "ASTDT"])
first = adae[adae["TRTEMFL"] == "Y"].groupby("USUBJID").head(1).index
adae["AOCCFL"] = ""
adae.loc[first, "AOCCFL"] = "Y"

print(adae[["USUBJID", "AETERM", "ASTDT", "TRTEMFL", "AOCCFL"]].to_string(index=False))
print("Subjects with a TEAE:", adae.loc[adae["TRTEMFL"] == "Y", "USUBJID"].nunique())

Walk the records by hand. S-001's headache started before first dose, so it is not treatment-emergent; the nausea is. S-002's rash on 15 March falls within 30 days of the last dose on 20 February (the window ends 22 March), but the fatigue on 30 March falls outside. S-003's dizziness began on the day of first dose, which the "on or after" condition counts. So three subjects have a treatment-emergent event.

Real derivations add the awkward parts: partial dates like "2026-02" that need imputation rules, events that start before dosing but worsen after, and missing last-dose dates for ongoing subjects. Each is answered in the spec, not improvised by the programmer.

Double programming and why it exists

For key outputs, a second programmer writes independent code from the same specification without seeing the first programmer's work. The two results are compared electronically, and any difference is investigated until both agree or the spec is clarified. It is slow by design. A difference often means the specification was ambiguous, and catching that before submission is the whole point.

A sample fortnight

  • Build ADAE for a phase II study from a spec that arrived yesterday.
  • Validate a colleague's demographics table by independent programming; raise two discrepancies, one of which turns out to be a spec gap.
  • Update outputs after the statistician changes a population definition in the SAP.
  • Respond to a reviewer's comment that a listing's sort order does not match the mock shell.
  • Run the team's CDISC conformance checks and document explanations for any accepted issues.

Clean validation, CDISC conformance and study timelines

Correctness at first validation, turnaround against the study timeline, conformance of datasets to CDISC rules, quality of documentation and, later, how well you lead a study's programming.

Biostatisticians, data managers and medical writers

A programmer reports to a lead statistical programmer or programming manager. You work most closely with biostatisticians (who own the SAP and table shells), clinical data managers upstream (see the clinical data manager guide), medical writers who use your outputs and, at CROs, the sponsor's programming team.

Confused with biostatistician

The biostatistician decides what analysis to run and interprets it. The programmer implements it exactly as specified. The boundary blurs at senior levels, but a statistical programmer is not usually expected to choose methods.

SAS macros, Define-XML and R in pharma postings

  • "Base SAS, SAS macros, PROC REPORT" — a SAS-first team.
  • "SDTM/ADaM mapping, Define-XML" — dataset work, core to the role.
  • "TLF generation, mock shells" — output programming.
  • "R/Shiny, open-source submissions" — a team moving part of its work to R.
  • "Lead programmer, study lead" — responsibility for timelines and a small team.

Listings, then SDTM, then ADaM

Month one: SOPs, standards training and validating simple listings. Month two: program SDTM domains under review. Month three: produce ADaM datasets or tables independently. Next come senior programmer, lead programmer, programming manager, or a move toward statistical or clinical data science. Derivations like the TEAE flag above reward exactly the pandas fluency and statistical grounding taught in the Data Science programme; CDISC specifics are then learned on the job.

Frequently asked questions

Is SAS still required for statistical programming jobs?

Many postings still ask for SAS because established submission workflows were built on it. R, and less often Python, appears increasingly in postings. Read each posting; if it asks for SAS certification, that team works in SAS today.

Can a statistics graduate become a statistical programmer?

Yes, it is one of the most common routes. Programming skill and attention to specification detail matter more than advanced theory, and many CROs train new joiners on CDISC standards.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Science programme — 6 months. From data foundations to machine learning, deep learning and deployment.

Explore Data Science
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.