◆ ClinCoder

ADaM & traceability

The PFS censoring decision table you can paste into a SAP

· Bhanoji D

Most PFS disagreements are not statistical. They are two people reading the same protocol sentence and producing different CNSR values, three months apart, with nothing written down in between.

The fix is unglamorous: one decision table, agreed before anyone writes code, that says exactly what each patient situation produces. It goes in the SAP, the same wording goes in the ADRG, and the derivation comments point back at it. What follows is that table, plus the ADaM details that reliably survive first review and then fail second review.

The table

Each row is a patient situation. Each row produces exactly one CNSR value and one event date. If a situation is not on this list, it is a protocol question, not a programming decision.

#SituationCNSREvent / censor dateEVNTDESC
1Documented progression0Date of the assessment showing progressionPROGRESSION
2Death, no prior progression0Date of deathDEATH
3No baseline assessment1Randomisation dateNO BASELINE ASSESSMENT
4Baseline only, no post-baseline assessment1Randomisation dateNO POST-BASELINE ASSESSMENT
5Progression after two or more missed assessments1Last adequate assessment before the missed windowPROGRESSION AFTER MISSED VISITS
6New anticancer therapy started, no prior progression1Last adequate assessment before therapyNEW ANTICANCER THERAPY
7Withdrew consent1Last adequate assessmentWITHDRAWAL
8Lost to follow-up1Last adequate assessmentLOST TO FOLLOW-UP
9Alive and progression-free at cut-off1Last adequate assessmentONGOING

Two definitions have to be pinned in the SAP or rows 5–9 are ambiguous:

  • Adequate assessment. An assessment that could have detected progression — meaning the required lesions were actually imaged, not merely that a visit record exists. A visit with a missing scan is not an adequate assessment.
  • The missed-visit window. “Two or more missed assessments” only means something once the protocol’s scheduled interval and its visit window are stated. Write the interval into the row.

Where teams disagree, and what to do about it

Row 6 is a sensitivity analysis, not a fact. Censoring at new anticancer therapy is one convention; treating it as an event is another; ignoring it is a third. Regulatory reviewers routinely ask for the alternative. Decide the primary in the SAP and pre-specify the sensitivity — retrofitting it later means re-opening the define.xml.

Row 5 has a quiet failure mode. If you censor at the last adequate assessment, and that assessment is months before an obvious progression, you have discarded a real event. That is the intended conservative behaviour, but it needs saying in the ADRG, because a reviewer comparing the listing to the KM curve will otherwise ask why.

Rows 3 and 4 are different rows on purpose. Collapsing them loses the distinction between a patient who never had a baseline and one who had a baseline and then vanished. Different EVNTDESC, same CNSR, and the listing tells the story.

The ADaM details that fail second review

CNSR is inverted from what most people expect. In ADaM, CNSR = 0 is the event and CNSR = 1 is the censor. Several SAS survival conventions use the opposite. This single flip has produced more silently-wrong KM curves than any other ADTTE mistake, and it is invisible in a summary table — the curve just bends the wrong way. Assert it in QC rather than trusting it.

Time is a derived variable, and the +1 is a decision. AVAL is normally

AVAL = ADT - STARTDT + 1

in days, because the randomisation day counts as day 1. If your SAP says months, state the divisor explicitly (30.4375 is common and is a choice, not a constant). Put the unit in AVALU and do not leave it implied by the parameter name.

STARTDT is not always randomisation. For a single-arm study it is often first dose. Whatever it is, it comes from ADSL, not from re-deriving it in ADTTE, or the two datasets will drift.

Traceability is three variables, not a comment. SRCDOM, SRCVAR and SRCSEQ should point at the exact record in ADRS or RS that produced the event date. A reviewer who cannot get from a KM point back to the source record in two steps will ask you to do it for them, by email, in a month.

The QC that actually catches things

Comparing your output to a second programmer’s output finds disagreements but does not tell you who is right. These checks tell you something is wrong regardless of who wrote them:

  • Every CNSR = 0 record has an EVNTDESC from rows 1–2, and every CNSR = 1 has one from rows 3–9. No blanks, no values outside the table.
  • ADT >= STARTDT for every record, with no exceptions and no silent drops.
  • Every subject in the analysis population appears exactly once per PARAMCD.
  • The count of CNSR = 0 matches the number of events printed on the KM output.
  • Every EVNTDESC value that appears in the data also appears in the table above — a value that appears in neither the SAP nor the table is a defect, even if it looks reasonable.

That last check is the one people skip, and it is the one that finds the category somebody invented at 11pm.


Provenance: this post makes no measured claims. The structure and variable definitions follow the CDISC ADaMIG time-to-event structure; the censoring rows follow conventions in common use for oncology PFS. It is a template to be adapted against your own protocol and SAP, not a substitute for either. No sponsor data, study, or compound is referenced — all examples are generic.