15  The Analysis Data Reviewer’s Guide

NoteSession at a Glance

Total core time: about 80 minutes.

Section Time Type
When Do You Use This? + Learning Objectives 5 min Reading
Background: the document that says why 20 min Reading
Example 1: The inventory sections 15 min Worked example
Example 2: Treatment and imputation 15 min Worked example
Example 3: The conformance summary 10 min Worked example
The half that cannot be generated 10 min Reading
Exercises + Comprehension Check 15 min Practice / Self-test

Reads after Define-XML and Define-XML from a Specification Workbook, because the ADRG’s whole reason for existing is what define.xml cannot do. Split after Example 2 for self-paced study.

ImportantScope of This Session

This session teaches what the ADRG is, what it must contain, and how to generate its data-driven components from R.

It does not teach submission strategy, agency interaction, or review expectations. Those require experience of running a submission, which the author does not have, and a course that fakes them is worse than a course that omits them. Where a section of the ADRG requires that kind of judgement, this session says so and shows you what a real study would put there, rather than inventing plausible prose.

Every regulatory statement below is cited to a published document you can read yourself: the FDA Study Data Technical Conformance Guide v6.0 (March 2025) and the PhUSE ADRG Completion Guidelines v1.2 (2019-07-12).

TipHow to Use the Code in This Session

Run R from the project root. The tables shown here are generated by R/build_adrg_components.R and committed under data/adrg/, so you can read them without rebuilding.

The PhUSE ADRG Package is free and does not require membership. It is reference material, not redistributable by this course: https://advance.hub.phuse.global/wiki/spaces/WEL/pages/26804660/Analysis+Data+Reviewer+s+Guide+ADRG+Package

15.1 When Do You Use This?

Tip

Your ADaM datasets are built and your define.xml is generated. Then:

  • Four subjects have no AGE because their birth dates are partial. define.xml can say AGE is derived. Where do you say why four subjects do not have one?
  • Your SAP defines baseline as the last value on or before first dose. ABLFL records the result. Where do you record the choice, and the fact that a different defensible choice would have moved the headline number?
  • A reviewer opens your submission with no memory of your study. What single document orients them?

Where do the decisions live, once the data has recorded only their consequences?

15.2 Learning Objectives

After completing this session you will be able to:

  • State what the ADRG is, what file it must be, and where the FDA guidance says it goes
  • List the seven required sections and two optional sections of the PhUSE ADRG template
  • Generate the ADRG’s data-driven tables from analysis datasets and define.xml, rather than maintaining them by hand
  • Distinguish the sections that can be generated from those that require a human, and explain why that boundary is where it is
  • Produce a conformance issues summary from real checks, including when the findings are your own

15.3 Background: The Document That Says Why

Estimated time: ~20 minutes (reading)

Define-XML built a document that records, in machine-readable form, what every variable is and where it came from. That is a great deal of information. It is also, in a specific and important way, silent.

def:Origin Type="Derived" tells a reviewer that AGE was computed. A MethodDef can even tell them the algorithm. Neither tells them that four subjects in GLPX-1 have no age at all, that this is because their birth dates arrived partial, and that the study chose to leave them null rather than impute. That is a decision, with a rationale, and XML has no element for a rationale.

The FDA guidance is unusually direct about the ADRG’s role, including the part that sounds like a flaw:

The ADRG purposefully duplicates limited information found in other submission documentation (e.g., the protocol, statistical analysis plan (SAP), clinical study report, define.xml) in order to provide FDA reviewers with a single point of orientation to the analysis datasets (Study Data Technical Conformance Guide v6.0, §2.3)

Duplication is normally a defect. Here it is the design. And the guidance immediately closes the obvious misreading:

It should be noted that the submission of an ADRG does not eliminate the requirement to submit a complete and informative define.xml file corresponding to the analysis datasets (TCG v6.0, §2.3)

So the ADRG is not a substitute for define.xml, and define.xml is not a substitute for the ADRG. That gives the intuition to carry through the session:

define.xml records what you decided. The ADRG exists to say why. A machine-readable record of a decision is not an explanation of it, and the half of the ADRG that cannot be generated is precisely the half worth reading.

15.3.1 What it is, concretely

The TCG is specific about the artifact, and this much is pure fact rather than expectation:

  • Its preparation is “recommended as an important part of a standards-compliant analysis data submission for clinical trials” (§2.3)
  • It must be a PDF called adrg.pdf (§2.3)
  • It goes with the analysis data in Module 5 of the eCTD, file-tagged analysis-data-reviewers-guide in eCTD v3.2.2, or with document type keyword analysis data reviewers guide in eCTD v4.0 (§2.3)

One detail is worth noticing because it is easy to overstate the ADRG’s status. For the Study Data Reviewer’s Guide, the TCG’s footnotes record that the PhUSE templates “have been reviewed by FDA and published in the Federal Register” and that “FDA prefers but does not require” their use. The ADRG footnote carries no equivalent language. It says only:

A specific template for an Analysis Data Reviewer’s Guide is not specified. However, an example can be found at https://phuse.global/Deliverables (TCG v6.0, §2.3, footnote 16)

No template is mandated. The PhUSE template is what the industry converged on, not what the agency imposed.

15.3.2 The seven required sections

The PhUSE ADRG Completion Guidelines are explicit about structure:

The ADRG has seven required sections: Introduction; Protocol Description; Analysis Considerations Related to Multiple Analysis Datasets; Analysis Data Creation and Processing Issues; Analysis Dataset Descriptions; Data Conformance Summary; Submissions of Programs (ADRG Completion Guidelines v1.2, ADRG Overview)

Plus two optional ones: an Appendix, and a Legacy Data Conversion Plan & Report Appendix for sponsors who converted non-SDTM data to ADaM.

WarningMind the Version

This session uses ADRG Completion Guidelines v1.2 (2019-07-12), the current version. It matters, because v1.2 renumbered section 3:

Topic Earlier template v1.2
SDTM/ADaM content comparison 3.1 5.1
Core Variables 3.2 3.1
Treatment Variables 3.3 3.2
Visit windowing 3.5 3.4
Imputation/Derivation 3.6 3.5
Variable Conventions 4.4 folded into 3.4/3.5

An ADRG citing “section 3.2, Core Variables” is working from a superseded template. This is the same lesson Define-XML taught about enumerations: check the current publication, do not trust a section number you remember.

15.4 Example 1: The Inventory Sections

Estimated time: ~15 minutes (worked example)

Sections 1.3 and 3.1 are tables. Neither should ever be typed.

15.4.1 1.3 Study Data Standards and Dictionary Inventory

The guidelines ask for the ADaM, SDTM and Define versions used. Those facts already exist in the define.xml, so read them from there, if the ADRG and the define.xml disagree about which version of Define-XML the study used, that is a finding, and it should be impossible rather than merely unlikely.

read_csv("data/adrg/01_standards_inventory.csv", show_col_types = FALSE) |>
  kable()
Standard or Dictionary Versions Used
SDTM SDTM v2.0 / SDTMIG v3.4
ADaM ADaM v2.1 / ADaMIG v1.3; OCCDS v1.1 for ADAE
Controlled Terminology CDISC/NCI SDTM CT
Data Definitions Define-XML 2.0.0
Dictionaries (MedDRA, WHODrug) Not applicable - see section 3.5

R/build_adrg_components.R pulls the Define-XML version out of the MetaDataVersion element rather than restating it.

15.4.2 3.1 Core Variables

Core variables are those that are represented across all/most analysis datasets (ADRG Completion Guidelines v1.2, §3.1)

“Represented across all analysis datasets” is a computation, not an opinion. It is the intersection of the variable names:

read_csv("data/adrg/02_core_variables.csv", show_col_types = FALSE) |>
  kable()
Variable Name Variable Description
STUDYID Study identifier used for this protocol
USUBJID Unique subject identifier
SUBJID Subject identifier within the study
SITEID Study site identifier
TRTSDT Date of first exposure to study treatment
SAFFL Safety population flag

15.4.3 Run It Yourself

ads <- list(
  ADSL = read_csv("data/adam/adsl.csv", show_col_types = FALSE),
  ADAE = read_csv("data/adam/adae.csv", show_col_types = FALSE),
  ADLB = read_csv("data/adam/adlb.csv", show_col_types = FALSE)
)

reduce(map(ads, names), intersect)
[1] "STUDYID" "USUBJID" "SUBJID"  "SITEID"  "TRTSDT"  "SAFFL"  

15.4.4 Reading the Output Line by Line

Six variables appear in all three analysis datasets. Two of them, STUDYID and USUBJID, are there because ADaM requires them: the guidelines note that as the minimum this table would contain. The other four are this study’s own shape: SUBJID and SITEID carried through for traceability, TRTSDT because every dataset needs the treatment start date to compute study day, and SAFFL because every analysis in this study is run on the safety population.

That last one is the kind of thing worth a reviewer’s attention, and it is visible only because the table was computed rather than copied from the last study’s ADRG.

15.5 Example 2: Treatment and Imputation

Estimated time: ~15 minutes (worked example)

15.5.1 3.2 Treatment Variables

The guidelines ask a yes/no question: are the values of planned treatment equivalent in meaning to actual treatment? A cross-tabulation answers it and shows the working:

read_csv("data/adrg/03_treatment_agreement.csv", show_col_types = FALSE) |>
  kable()
TRT01P TRT01A Subjects Agree
GLPX 10 mg GLPX 10 mg 200 Yes
Placebo Placebo 200 Yes

Zero discrepancies across 400 subjects. In GLPX-1 nobody received a treatment other than the one they were randomised to, which is a simulation convenience, and an ADRG for a real study would very probably have a non-empty “No” row and a paragraph explaining it.

15.5.2 3.5 Imputation/Derivation Methods

This is where the course’s deliberate data defects finally get written down where a reviewer would see them:

read_csv("data/adrg/04_imputation_summary.csv", show_col_types = FALSE) |>
  kable()
Dataset Variable Situation Records Affected Convention
ADSL AGE Partial birth date; age not computable 4 Left null. No imputation performed.
ADAE ASTDT Adverse event start date not recorded 1 Left null; TRTEMFL also null, never ‘N’.
ADLB BASE No pre-dose value on or before first dose 0 Left null. Baseline is the last value on or before first dose.

Read the Convention column, because each entry is a decision that ADSL, ADAE and ADLB argued through at length:

  • Four subjects with no AGE, partial birth dates, the D1 defect from The DM Domain. Left null, not imputed. define.xml can say AGE is derived; only this table says four people have none.
  • One adverse event with no ASTDT, and the convention matters more than the count. TRTEMFL is left null, never "N", because “we do not know whether this event was treatment-emergent” is a different claim from “this event was not treatment-emergent”. That distinction is invisible in the dataset and invisible in define.xml.
  • Zero records with no BASE: a real number, not an absence of effort. It is worth stating precisely because a reader would otherwise wonder.

15.5.3 5.2 Analysis Datasets

read_csv("data/adrg/05_dataset_inventory.csv", show_col_types = FALSE) |>
  kable()
Dataset Label Class Structure Records Variables Subjects
ADSL Subject Level Analysis Dataset SUBJECT LEVEL ANALYSIS DATASET One record per subject 400 28 400
ADAE Adverse Events Analysis Dataset OCCURRENCE DATA STRUCTURE One record per subject per adverse event 389 26 262
ADLB Laboratory Analysis Dataset BASIC DATA STRUCTURE One record per subject per parameter per analysis visit 12388 29 400

The Class column is the same vocabulary Define-XML found in the ItemGroupClass enumeration - including OCCURRENCE DATA STRUCTURE for ADAE, the decision ADAE made and defended.

15.6 Example 3: The Conformance Summary

Estimated time: ~10 minutes (worked example)

Section 6 is required, and section 6.2 asks for an issues summary. Define-XML built the machinery: two Specification-sourced conformance rules that no schema check will catch. Run them against this study’s own define.xml and report what comes back.

read_csv("data/adrg/06_conformance_issues.csv", show_col_types = FALSE) |>
  select(Rule, `Diagnostic Message`, Records) |>
  kable()
Rule Diagnostic Message Records
Define-XML 65 ItemRef/(ItemOID?) must reference an existing ItemDef 0
Define-XML 73 MethodOID required when Origin Type is ‘Derived’ 17

Rule 65 is clean. Rule 73 is not, and the finding is ours:

read_csv("data/adrg/06_conformance_issues.csv", show_col_types = FALSE) |>
  filter(Records > 0) |>
  pull(Explanation) |>
  str_wrap(70) |>
  cat()
Missing MethodOID: IT.DM.RFSTDTC, IT.DM.RFENDTC, IT.DM.RFXSTDTC,
IT.DM.RFXENDTC, IT.DM.RFPENDTC, IT.DM.DTHFL, IT.LB.LBSEQ,
IT.LB.LBSTRESC, IT.LB.LBSTRESN, IT.LB.LBSTRESN.ALT.ALT,
IT.LB.LBSTRESN.CREAT.CREAT, IT.LB.LBSTRESN.GLUC.GLUC,
IT.LB.LBSTRESN.HBA1C.HBA1C, IT.LB.LBSTRESU, IT.LB.LBSTNRLO,
IT.LB.LBSTNRHI, IT.LB.LBDRVFL

15.6.1 Reading the Output Line by Line

Seventeen variables in GLPX-1’s define.xml have Origin Type="Derived" and no MethodOID: nothing states how they were derived. The cause is upstream, in the specification workbook: COMPUTATION_METHOD defines five methods, but twenty-three variables are marked derived.

This finding is left in deliberately, and reported rather than quietly fixed. Two reasons, and both are the point of section 6:

  1. It is what a real conformance summary looks like. A submission with zero findings is rarer than people expect, and the section exists because findings are normal. What matters is that each one is known, stated, and explained, not that the list is empty.
  2. Fixing it belongs in the workbook, not in the XML and not in the ADRG, exactly as the previous session argued. The ADRG reports the state of the submission; it does not launder it.

15.6.2 The Same Rule, Two XPaths, Two Answers

Before trusting that seventeen, look at how it was counted. Rule 73 says a Derived origin needs a MethodOID on the ItemRef that points at it. So you find the derived ItemDefs and check their ItemRefs. The obvious way to write that is wrong.

define <- read_xml("data/define/define.sdtm.xml")

derived <- xml_attr(
  xml_find_all(define, "//d1:ItemDef[def:Origin/@Type='Derived']"), "OID")

count_missing <- function(path) {
  sum(map_lgl(derived, function(oid) {
    ref <- xml_find_first(define, sprintf("%s[@ItemOID='%s']", path, oid))
    !is.na(ref) && is.na(xml_attr(ref, "MethodOID"))
  }))
}

c(narrow = count_missing("//d1:ItemGroupDef/d1:ItemRef"),
  wide   = count_missing("//d1:ItemRef"))
narrow   wide 
    13     17 

Same rule, same document, same day. Four findings appear or disappear depending on one path fragment.

The narrow version looks reasonable: variables live in datasets, so their ItemRefs live in ItemGroupDef. But value-level definitions are referenced from def:ValueListDef, not from ItemGroupDef. For those four, xml_find_first() returns a missing node, !is.na(ref) is FALSE, and they are dropped.

Not skipped with a warning. Dropped.

setdiff(
  keep(derived, function(oid) {
    ref <- xml_find_first(define, sprintf("//d1:ItemRef[@ItemOID='%s']", oid))
    !is.na(ref) && is.na(xml_attr(ref, "MethodOID"))
  }),
  keep(derived, function(oid) {
    ref <- xml_find_first(
      define, sprintf("//d1:ItemGroupDef/d1:ItemRef[@ItemOID='%s']", oid))
    !is.na(ref) && is.na(xml_attr(ref, "MethodOID"))
  })
)
[1] "IT.LB.LBSTRESN.ALT.ALT"     "IT.LB.LBSTRESN.CREAT.CREAT"
[3] "IT.LB.LBSTRESN.GLUC.GLUC"   "IT.LB.LBSTRESN.HBA1C.HBA1C"

The four value-level LBSTRESN definitions, the ones Define-XML built to give each lab parameter its own units and ranges. They are derived, they have no method, and the narrow check never looked at them.

ImportantA check that reports zero is saying one of two things

There are no findings, or I did not look there. These are not the same claim and the output is identical.

You cannot tell them apart by reading the number. You can only tell them apart by reading the check, or by running it against a document you have broken on purpose and confirming it complains.

This matters more than it sounds. Conformance tooling is how a sponsor convinces itself a submission is sound. A check with an unadvertised blind spot does not fail loudly, it reassures quietly, which is the one behaviour you cannot afford in an instrument you are using to decide whether you are done.

A real ADRG’s issues summary would carry, for each finding, an explanation of why it is acceptable or what was done about it. Writing that explanation is a judgement call, which brings us to the rest of the document.

15.7 The Half That Cannot Be Generated

Estimated time: ~10 minutes (reading)

Everything above was a table computed from data. Now count what is left. Of the seven required sections, these cannot be generated at all:

Section What it needs Why no script can write it
2.1 Protocol Number and Title The protocol Not in the data
2.2 Protocol Design in Relation to ADaM Concepts Study design judgement Relates the design to the variables; requires knowing the intent
3.3 Subject Issues Requiring Special Analysis Rules Study conduct knowledge Which subjects, and why they are special, is a clinical matter
3.4 Visit Windowing and Record Selection The SAP The rule is a choice, not a property of the data
4.1–4.3 Split Datasets, Dependencies, Intermediate Datasets Pipeline knowledge Describes how the programming was organised
5.1 Overview SAP objectives Asks whether the datasets support every protocol objective
7 Submission of Programs The submission package Itemises what is being shipped
NoteVERIFY, sections this course does not write

Sections 2.2, 3.3, 4.1–4.3, 5.1 and 7 require submission experience and study conduct knowledge that this course does not have and will not fake.

For GLPX-1 they would say roughly this, and a real study would say it with authority this course cannot claim:

  • 2.2 would relate TRT01P/TRT01A and the analysis periods to the trial’s two-arm parallel design with eight visits over 26 weeks.
  • 3.4 would state the visit-windowing rule from the SAP. GLPX-1 uses nominal visits with no windowing, which is unusually simple and would itself be worth stating.
  • 4.2 would describe that ADSL is built first and ADAE and ADLB both read it: the dependency R/build_adam.R encodes.
  • 5.1 would confirm whether the datasets support every objective in the protocol and SAP. GLPX-1 has no formal SAP, so this cannot be answered honestly here at all.
  • 7 would itemise the programs shipped. This course ships R/build_sdtm.R, R/build_adam.R, R/build_define.R and R/build_adrg_components.R, but a submission’s program inventory has requirements this course has not verified.

If you are writing a real ADRG, read the Completion Guidelines section by section. They are 33 pages and they answer these questions properly.

That table is the session’s real content. Roughly half the ADRG is a report and half is an explanation, and the boundary is not arbitrary: everything that is a property of the data can be generated, and everything that is a reason for a choice cannot. A script knows that TRTEMFL is null on one record. Only a person knows that null was chosen over "N" because the two mean different things.

15.7.1 A note on ts.xpt

Three earlier sessions promised you would meet ts.xpt again here. Why Standards taught the chain by which the FDA’s technical rejection criteria check a submission’s study tagging file before any human opens it.

The connection to this session is narrow and worth stating plainly: ts.xpt is where the machine learns which standards a study used; ADRG section 1.3 is where a person does. The same facts, twice, for two different readers, which is the “purposefully duplicates” principle again. GLPX-1 has no TS domain, so this course cannot build one, and how ts.xpt is populated for a real submission is outside what this session teaches.

15.8 What Can Go Wrong

  • Hand-maintaining the generated tables. The core-variables table copied from the last study’s ADRG will be wrong in a way nobody notices, because it looks right. Compute it.
  • An empty issues summary. If section 6.2 is empty, the likeliest explanation is that nothing was checked, not that nothing was found.
  • Citing a superseded section number. v1.2 renumbered section 3. “See ADRG section 3.2 for core variables” is a v1.1 sentence.
  • Treating the ADRG as a substitute for define.xml. The TCG closes this off explicitly (§2.3), and it is worth knowing the sentence exists.
  • Writing the ADRG last. Every table above was derivable the moment the datasets existed. The sections that need a human are the ones whose reasoning is freshest while the work is being done, and hardest to reconstruct six months later.

15.9 Exercises

Exercise 10: The Analysis Data Reviewer’s Guide asks you to close the rule-73 finding at its source, and to draft the one narrative section this course can honestly ask you to write.

15.10 Comprehension Check

  1. The TCG says the ADRG “purposefully duplicates” information found in the protocol, SAP and define.xml. Why is that not a design flaw?
  2. GLPX-1’s define.xml records that AGE has Origin Type="Derived". What can it not record, and which ADRG section carries it?
  3. Your ADRG’s section 6.2 lists seventeen rule-73 findings. Where do you fix them, and what do you do about the ADRG in the meantime?
  4. Which of the seven required sections could be regenerated automatically after a data refresh, and which would need rewriting by hand?
  5. A colleague’s ADRG has an empty Data Conformance Summary. What is the most likely explanation?
  1. Because the reviewer needs a single point of orientation. The information exists elsewhere, but scattered across documents in different formats written for different purposes. The ADRG’s job is to be the one place someone with no memory of the study can start, and that job requires repeating things (TCG v6.0, §2.3).

  2. It cannot record that four subjects have no AGE, why, or what was decided about it. Origin describes the variable; it says nothing about the records where the derivation could not be performed. That belongs in section 3.5, Imputation/Derivation Methods, where GLPX-1 states the partial birth dates, the count, and the decision to leave the values null rather than impute.

  3. Fix them in the specification workbook, add the missing COMPUTATION_METHOD entries and the COMPUTATIONMETHODOID references, then regenerate. Not in the XML, which would be overwritten, and not in the ADRG, which reports the state of the submission rather than improving it. In the meantime the ADRG reports the findings honestly, with an explanation, because that is what section 6.2 is for.

  4. Regenerable: 1.3, 3.1, 3.2, 3.5, 5.2, and the findings half of 6. By hand: 2.1, 2.2, 3.3, 3.4, 4.1–4.3, 5.1, 7, and the explanation half of 6.2. The boundary is properties of the data versus reasons for a choice.

  5. That nothing was checked. Section 6.1 asks which conformance software was used and against which version of the checks; an empty 6.2 with a filled 6.1 would be a strong claim, and an empty 6.2 with an empty 6.1 is not a claim at all. Findings are normal; the section exists because they are.