Review ask

As a clinical trial statistician or programmer, challenge anything in this benchmark: describe a problem in Comments, or propose a fix with the Edit button on any source file.

  1. Task: Would a real study need this derivation, specified this way? Does it follow CDISC ADaM: variable names, controlled terms, record structure? Flag any rule that is ambiguous or that a sponsor would handle differently.
  2. Inputs: Do they look like data collected in a real trial, and do they include the situations you would expect to meet in one?
  3. Expected output: Would independent QC programming reproduce every value from these inputs and rules?

The other tabs beside Comments are background for assessment, not under review.

Summary

Lifecycle: reviewedEditUnder review
What this benchmark means
README.md

Goal: split each adverse event (AE) into continuous analysis periods and mark treatment-emergent periods (TRTEMFL).

Input: AE holds one record per event, with its sequence (AESEQ), term (AEDECOD), overall start/end (AESTDTC, AEENDTC) and maximum severity/grade (AESEV, AETOXGR). Findings About (FA) holds severity (SEV) and toxicity-grade (TOXGR) findings at onset and each collected change time (FADTC). FALNKID matches the event's AELNKID. The subject-level analysis dataset (ADSL) supplies first exposure (TRTSDTM), empty for an untreated subject.

Variables:

  • ASEQ numbers analysis periods within a subject; repeated AESEQ retains the source event identity and its original values.
  • ASTDTM starts a period at a collected change time. AENDTM is the next period's start, or the original end for the last period; an ongoing event's last end stays empty.
  • ASEV and ATOXGRN hold the period's severity and numeric toxicity grade (1 through 5). Each persists until that dimension changes; without an earlier collected value it stays empty.
  • TRTEMFL is Y for events starting at or after first exposure. For an ongoing pre-treatment event, it starts at the first post-exposure increase over the last pre-treatment severity or grade and remains Y on subsequent periods. Earlier periods, improvement alone and untreated subjects' periods stay empty.

Note: findings are collected effective change times, rather than visit assessments from which a change time must be inferred. Simultaneous severity and grade changes create one period. Missing comparison values show no worsening for that dimension. Separate events sharing a term remain separate. Periods are continuous with end equal to next start.

Inputs

Under review
3 source files

input/adsl.csv

Edit
5 rows
input/adsl.csv
STUDYIDUSUBJIDTRTSDTM
PILOT72025-03-04T09:00:00
PILOT72025-03-04T09:00:00
PILOT72025-03-04T09:00:00
PILOT7—
PILOT72025-03-04T09:00:00

input/ae.csv

Edit
6 rows
input/ae.csv
STUDYIDUSUBJIDAESEQAELNKIDAEDECODAESTDTCAEENDTCAESEVAETOXGR
PILOT71AE-1HEADACHE2025-03-04T07:00:002025-03-05T10:00:00SEVERE1
PILOT72AE-2HEADACHE2025-03-06T10:00:002025-03-06T11:00:00MILD1
PILOT71AE-1RASH2025-03-03T09:00:002025-03-07T09:00:00SEVERE2
PILOT71AE-1VOMITING2025-03-03T10:00:002025-03-06T10:00:00MODERATE2
PILOT71AE-1FATIGUE2025-03-03T09:00:002025-03-06T09:00:00SEVERE1
PILOT71AE-1COUGH2025-03-03T09:00:00—SEVERE3

input/fa.csv

Edit
20 rows
input/fa.csv
STUDYIDUSUBJIDFASEQFALNKIDFATESTCDFAOBJFASTRESCFADTC
PILOT71AE-1SEVHEADACHEMILD2025-03-04T07:00:00
PILOT72AE-1TOXGRHEADACHE12025-03-04T07:00:00
PILOT73AE-2SEVHEADACHEMILD2025-03-06T10:00:00
PILOT74AE-2TOXGRHEADACHE12025-03-06T10:00:00
PILOT71AE-1SEVRASHMODERATE2025-03-03T09:00:00
PILOT72AE-1TOXGRRASH22025-03-03T09:00:00
PILOT71AE-1SEVVOMITINGMODERATE2025-03-03T10:00:00
PILOT72AE-1TOXGRVOMITING12025-03-03T10:00:00
PILOT71AE-1SEVFATIGUEMILD2025-03-03T09:00:00
PILOT72AE-1TOXGRFATIGUE12025-03-03T09:00:00
PILOT71AE-1SEVCOUGHSEVERE2025-03-03T09:00:00
PILOT72AE-1TOXGRCOUGH32025-03-03T09:00:00
PILOT75AE-1SEVHEADACHEMODERATE2025-03-04T11:00:00
PILOT76AE-1SEVHEADACHESEVERE2025-03-04T13:00:00
PILOT77AE-1SEVHEADACHEMILD2025-03-05T06:00:00
PILOT73AE-1SEVRASHMILD2025-03-05T09:00:00
PILOT74AE-1SEVRASHSEVERE2025-03-06T09:00:00
PILOT73AE-1TOXGRVOMITING22025-03-05T10:00:00
PILOT73AE-1SEVFATIGUESEVERE2025-03-05T09:00:00
PILOT73AE-1SEVCOUGHMODERATE2025-03-05T09:00:00

Expected output

Under review
14 expected rows

expected/adae.csv

Edit
14 rows
expected/adae.csv
STUDYIDUSUBJIDASEQAESEQAEDECODAESTDTCAEENDTCASTDTMAENDTMTRTSDTMAESEVAETOXGRASEVATOXGRNTRTEMFL
PILOT711HEADACHE2025-03-04T07:00:002025-03-05T10:00:002025-03-04T07:00:002025-03-04T11:00:002025-03-04T09:00:00SEVERE1MILD1—
PILOT721HEADACHE2025-03-04T07:00:002025-03-05T10:00:002025-03-04T11:00:002025-03-04T13:00:002025-03-04T09:00:00SEVERE1MODERATE1Y
PILOT731HEADACHE2025-03-04T07:00:002025-03-05T10:00:002025-03-04T13:00:002025-03-05T06:00:002025-03-04T09:00:00SEVERE1SEVERE1Y
PILOT741HEADACHE2025-03-04T07:00:002025-03-05T10:00:002025-03-05T06:00:002025-03-05T10:00:002025-03-04T09:00:00SEVERE1MILD1Y
PILOT752HEADACHE2025-03-06T10:00:002025-03-06T11:00:002025-03-06T10:00:002025-03-06T11:00:002025-03-04T09:00:00MILD1MILD1Y
PILOT711RASH2025-03-03T09:00:002025-03-07T09:00:002025-03-03T09:00:002025-03-05T09:00:002025-03-04T09:00:00SEVERE2MODERATE2—
PILOT721RASH2025-03-03T09:00:002025-03-07T09:00:002025-03-05T09:00:002025-03-06T09:00:002025-03-04T09:00:00SEVERE2MILD2—
PILOT731RASH2025-03-03T09:00:002025-03-07T09:00:002025-03-06T09:00:002025-03-07T09:00:002025-03-04T09:00:00SEVERE2SEVERE2Y
PILOT711VOMITING2025-03-03T10:00:002025-03-06T10:00:002025-03-03T10:00:002025-03-05T10:00:002025-03-04T09:00:00MODERATE2MODERATE1—
PILOT721VOMITING2025-03-03T10:00:002025-03-06T10:00:002025-03-05T10:00:002025-03-06T10:00:002025-03-04T09:00:00MODERATE2MODERATE2Y
PILOT711FATIGUE2025-03-03T09:00:002025-03-06T09:00:002025-03-03T09:00:002025-03-05T09:00:00—SEVERE1MILD1—
PILOT721FATIGUE2025-03-03T09:00:002025-03-06T09:00:002025-03-05T09:00:002025-03-06T09:00:00—SEVERE1SEVERE1—
PILOT711COUGH2025-03-03T09:00:00—2025-03-03T09:00:002025-03-05T09:00:002025-03-04T09:00:00SEVERE3SEVERE3—
PILOT721COUGH2025-03-03T09:00:00—2025-03-05T09:00:00—2025-03-04T09:00:00SEVERE3MODERATE3—
Shaded columns are derived— = missing value

Comments and assessment materials

Comments

Signed in with GitHub; saved in repository Discussions

Mapping spec

Generated from the benchmark YAML; do not edit by hand

Dataset NameVariable NameVariable LabelTypeCodelist/Controlled TermsCoreComputational MethodOrigin
ADAESTUDYIDStudy IdentifiercharacterSet per constructed row; see the Row construction sheet.Derived
ADAEUSUBJIDUnique Subject IdentifiercharacterSet per constructed row; see the Row construction sheet.Derived
ADAEAESEQSource Adverse Event Sequence NumbernumericTake AESEQ from the AE record matching on STUDYID, USUBJID and AELNKID, and a row with no match is an error.Derived
ADAEAEDECODDictionary-Derived TermcharacterTake AEDECOD from the AE record matching on STUDYID, USUBJID and AELNKID, and a row with no match is an error.Derived
ADAEAESTDTCSource Event Start Date/TimenumericISO 8601Take AESTDTC from the AE record matching on STUDYID, USUBJID and AELNKID, and a row with no match is an error.Derived
ADAEAEENDTCSource Event End Date/TimenumericISO 8601Take AEENDTC from the AE record matching on STUDYID, USUBJID and AELNKID, and a row with no match is an error.Derived
ADAEASTDTMAnalysis Segment Start Date/TimenumericISO 8601Set per constructed row; see the Row construction sheet.Derived
ADAEAENDTMAnalysis Segment End Date/TimenumericISO 8601First non-missing of NEXTSTART and AEENDTC.Derived
ADAETRTSDTMDatetime of First Exposure to TreatmentnumericISO 8601Copy value from ADSL.TRTSDTM.Predecessor
ADAEAESEVSource Event Severity/IntensitycharacterTake AESEV from the AE record matching on STUDYID, USUBJID and AELNKID, and a row with no match is an error.Derived
ADAEAETOXGRSource Event Standard Toxicity GradecharacterTake AETOXGR from the AE record matching on STUDYID, USUBJID and AELNKID, and a row with no match is an error.Derived
ADAEASEVAnalysis Severity/IntensitycharacterMILD, MODERATE, SEVERELast observation carried forward from SEVCOL using window EVENT_ORDER.Derived
ADAEATOXGRNAnalysis Toxicity Grade (N)numeric1, 2, 3, 4, 5Last observation carried forward from GRDCOL using window EVENT_ORDER.Derived
ADAETRTEMFLTreatment Emergent Analysis FlagcharacterY"Y" when AESTDTC IS NOT NULL AND TRTSDTM IS NOT NULL AND (AESTDTC >= TRTSDTM OR ASTDTM >= FIRSTWRS); blank otherwise.Derived
ADAEASEQAnalysis Sequence NumbernumericRow number within each group of STUDYID, USUBJID, ordered by AESEQ then ASTDTM.Derived
TemplateInclude whenSTUDYIDUSUBJIDAELNKIDASTDTMSEVCOLGRDCOL
severity_periodsAggregate MAX(FA.STUDYID)Aggregate MAX(FA.USUBJID)Aggregate MAX(FA.FALNKID)Aggregate MAX(FA.FADTC)Aggregate ONLY(FA.FASTRESC) over records matching on the shared output keys, where FA.FATESTCD = 'SEV'Aggregate ONLY(FA.FASTRESC) over records matching on the shared output keys, where FA.FATESTCD = 'TOXGR'
VariablePermitted values
ASEVMILD, MODERATE, SEVERE
ATOXGRN1, 2, 3, 4, 5
TRTEMFLY
VersionDateAuthorDescriptionReviewerSign-off
1.32026-09-29yamaa docs buildShow study codelist names, external dictionaries, and ISO 8601 formats in variable rows; list the document's codelist values and metadata
1.22026-09-29yamaa docs buildClassify ADaM copies from a declared input as Predecessor and recodes as Derived, and SDTM recodes of locally derived columns as Derived
1.12026-09-29yamaa docs buildResolve lookups into the donor record they select, and give every derivation shape a plain-language rule instead of its raw YAML
1.0yamaa docs buildInitial generation from spec.yaml

Agent prompt

The request an agent receives with the input files; each tier states no more than the one above

prompts/adam-adae-worsening-emergence/full.md

Edit
42 lines

Following CDISC ADaM standards, use the provided AE, FA and ADSL datasets to create an ADAE dataset with one record per analysis severity period.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, ASEQ, AESEQ, AEDECOD, AESTDTC, AEENDTC, ASTDTM, AENDTM, TRTSDTM, AESEV, AETOXGR, ASEV, ATOXGRN, TRTEMFL

AE contains one record per source event with its overall start, end and maximum severity/grade. Copy AESEQ, AEDECOD, AESTDTC, AEENDTC, AESEV and AETOXGR unchanged to every period for that event. Match FA to AE by STUDYID, USUBJID and FALNKID = AELNKID. FA contains a SEV and TOXGR finding at onset, then findings at each collected effective change time FADTC. All change times are within the source event. There is at most one finding per event, time and FATESTCD. These times are actual changes, rather than visit assessments requiring an inferred onset.

Create one period per distinct event and FADTC, including onset. Simultaneous severity and grade findings create one period. ASTDTM is FADTC. AENDTM is the next ASTDTM for the same source event, or AEENDTC for the last period; a missing original end means the last period is ongoing. Periods share exactly equal end/start boundaries. ASEV takes FASTRESC for SEV and persists until the next severity finding. ATOXGRN similarly takes the numeric TOXGR finding, from 1 through 5. A dimension with no earlier collected value stays empty; missing findings do not supply a new value. AESEQ remains the source event identifier. ASEQ numbers periods from 1 within each STUDYID and USUBJID, ordered by numeric AESEQ and ASTDTM.

TRTSDTM is first exposure from ADSL, matched by STUDYID and USUBJID. For an event starting at or after exposure, TRTEMFL is Y on every period. For an event starting before exposure, compare each period at or after exposure with the last severity and grade strictly before exposure. Severity order is MILD < MODERATE < SEVERE. TRTEMFL first becomes Y when either known dimension increases over its known pre-treatment value; it stays Y on subsequent periods, even if intensity later decreases. Earlier periods and improvement without worsening have no flag. A missing comparison value shows no worsening for that dimension. When original onset or exposure is missing, TRTEMFL has no value. Separate source events with the same term never share treatment-emergent status.

Read the source datasets from /app/input and save the completed dataset as /app/output/adae.csv.

prompts/adam-adae-worsening-emergence/conventions.md

Edit
12 lines

Following CDISC ADaM standards, use the provided AE, FA and ADSL datasets to create an ADAE dataset with one record per analysis severity period.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, ASEQ, AESEQ, AEDECOD, AESTDTC, AEENDTC, ASTDTM, AENDTM, TRTSDTM, AESEV, AETOXGR, ASEV, ATOXGRN, TRTEMFL

ASEV is MILD, MODERATE or SEVERE. ATOXGRN holds a toxicity grade from 1 through 5, or is empty. TRTEMFL is Y or has no value.

Read the source datasets from /app/input and save the completed dataset as /app/output/adae.csv.

prompts/adam-adae-worsening-emergence/brief.md

Edit
9 lines

Following CDISC ADaM standards, use the provided AE, FA and ADSL datasets to create an ADAE dataset with one record per analysis severity period.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, ASEQ, AESEQ, AEDECOD, AESTDTC, AEENDTC, ASTDTM, AENDTM, TRTSDTM, AESEV, AETOXGR, ASEV, ATOXGRN, TRTEMFL

Read the source datasets from /app/input and save the completed dataset as /app/output/adae.csv.

yamaa code

Runs the yamaa specification; its result must match the expected output

run.R

Edit
4 lines
library(yamaa)adae <- yamaa_domain("spec.yaml")$outputadae

run.py

Edit
4 lines
import yamaaadae = yamaa.yamaa_domain("spec.yaml").outputadae

Solution without yamaa

Written from the full prompt and the inputs alone; CI checks it reproduces the expected output

solutions/adam-adae-worsening-emergence/result.R

Edit
86 lines
# R reference for the yamaa benchmark adam-adae-worsening-emergence.# Uses the collected event records and dated findings to construct periods.library(readr)read_input <- function(name) {  read_csv(paste0('/app/input/', name),           col_types = cols(.default = col_character()),           show_col_types = FALSE)}normalize <- function(x) {  short <- !is.na(x) & nchar(x) == 16L  x[short] <- paste0(x[short], ':00')  x}ae <- read_input('ae.csv')fa <- read_input('fa.csv')adsl <- read_input('adsl.csv')fa$FADTC <- normalize(fa$FADTC)ae <- ae[order(ae$STUDYID, ae$USUBJID, as.integer(ae$AESEQ)), ]ranks <- c(MILD = 1L, MODERATE = 2L, SEVERE = 3L)output <- list()previous_study <- previous_subject <- NA_character_aseq <- 0Lfor (i in seq_len(nrow(ae))) {  event <- ae[i, ]  subject <- event$USUBJID  study <- event$STUDYID  if (!identical(study, previous_study) ||      !identical(subject, previous_subject)) aseq <- 0L  previous_study <- study  previous_subject <- subject  trt <- normalize(adsl$TRTSDTM[    adsl$STUDYID == study & adsl$USUBJID == subject  ])[1]  onset <- normalize(event$AESTDTC)  ending <- normalize(event$AEENDTC)  findings <- fa[fa$STUDYID == study & fa$USUBJID == subject &                   fa$FALNKID == event$AELNKID, ]  times <- sort(unique(findings$FADTC))  sevs <- rep(NA_character_, length(times))  grades <- rep(NA_integer_, length(times))  sev <- NA_character_  grade <- NA_integer_  for (j in seq_along(times)) {    updates <- findings[findings$FADTC == times[j], ]    if (anyDuplicated(updates$FATESTCD)) stop('Duplicate finding at event/time')    new_sev <- updates$FASTRESC[updates$FATESTCD == 'SEV']    new_grade <- updates$FASTRESC[updates$FATESTCD == 'TOXGR']    if (length(new_sev) && !is.na(new_sev)) sev <- new_sev    if (length(new_grade) && !is.na(new_grade)) grade <- as.integer(new_grade)    sevs[j] <- sev    grades[j] <- grade  }  before <- if (is.na(trt)) integer() else which(times < trt)  baseline_sev <- baseline_grade <- NA_integer_  if (length(before)) {    last <- tail(before, 1)    baseline_sev <- unname(ranks[sevs[last]])    baseline_grade <- grades[last]  }  emergent <- FALSE  for (j in seq_along(times)) {    if (!is.na(onset) && !is.na(trt)) {      if (onset >= trt) emergent <- TRUE      else if (times[j] >= trt) {        rank <- unname(ranks[sevs[j]])        sev_worse <- !is.na(rank) && !is.na(baseline_sev) && rank > baseline_sev        grade_worse <- !is.na(grades[j]) && !is.na(baseline_grade) &&          grades[j] > baseline_grade        emergent <- emergent || sev_worse || grade_worse      }    }    aseq <- aseq + 1L    output[[length(output) + 1L]] <- data.frame(      STUDYID = study, USUBJID = subject, ASEQ = aseq,      AESEQ = as.integer(event$AESEQ), AEDECOD = event$AEDECOD,      AESTDTC = onset, AEENDTC = ending, ASTDTM = times[j],      AENDTM = if (j < length(times)) times[j + 1L] else ending,      TRTSDTM = trt, AESEV = event$AESEV, AETOXGR = event$AETOXGR,      ASEV = sevs[j], ATOXGRN = grades[j],      TRTEMFL = if (emergent) 'Y' else NA_character_    )  }}dir.create('/app/output', showWarnings = FALSE)write_csv(do.call(rbind, output), '/app/output/adae.csv', na = '')

solutions/adam-adae-worsening-emergence/result.py

Edit
88 lines
# Python reference for the yamaa benchmark adam-adae-worsening-emergence.# Uses the collected event records and dated findings to construct periods.from pathlib import Pathimport polars as pldef normalize(value):    return value + ":00" if value and len(value) == 16 else valueae = pl.read_csv("/app/input/ae.csv", infer_schema=False).to_dicts()fa = pl.read_csv("/app/input/fa.csv", infer_schema=False).to_dicts()exposure = {    (row["STUDYID"], row["USUBJID"]): normalize(row["TRTSDTM"])    for row in pl.read_csv("/app/input/adsl.csv", infer_schema=False).to_dicts()}severity_rank = {"MILD": 1, "MODERATE": 2, "SEVERE": 3}output = []sequences = {}for event in sorted(ae, key=lambda r: (r["STUDYID"], r["USUBJID"], int(r["AESEQ"]))):    subject = event["STUDYID"], event["USUBJID"]    trt = exposure.get(subject)    onset, end = normalize(event["AESTDTC"]), normalize(event["AEENDTC"])    changes = {}    for finding in fa:        if (finding["STUDYID"], finding["USUBJID"]) != subject:            continue        if finding["FALNKID"] != event["AELNKID"]:            continue        time = normalize(finding["FADTC"])        values = changes.setdefault(time, {})        test = finding["FATESTCD"]        if test in values:            raise ValueError("Duplicate finding at the same event and time")        values[test] = finding["FASTRESC"]    states = []    sev, grade = None, None    for time, values in sorted(changes.items()):        if values.get("SEV") is not None:            sev = values["SEV"]        if values.get("TOXGR") is not None:            grade = int(values["TOXGR"])        states.append((time, sev, grade))    before = [state for state in states if trt and state[0] < trt]    baseline_sev = severity_rank.get(before[-1][1]) if before else None    baseline_grade = before[-1][2] if before else None    emergent = False    for i, (time, sev, grade) in enumerate(states):        if onset and trt:            if onset >= trt:                emergent = True            elif time >= trt:                rank = severity_rank.get(sev)                sev_worse = (                    rank is not None                    and baseline_sev is not None                    and rank > baseline_sev                )                grade_worse = (                    grade is not None                    and baseline_grade is not None                    and grade > baseline_grade                )                emergent = emergent or sev_worse or grade_worse        sequences[subject] = sequences.get(subject, 0) + 1        output.append(            {                "STUDYID": event["STUDYID"],                "USUBJID": event["USUBJID"],                "ASEQ": sequences[subject],                "AESEQ": int(event["AESEQ"]),                "AEDECOD": event["AEDECOD"],                "AESTDTC": onset,                "AEENDTC": end,                "ASTDTM": time,                "AENDTM": states[i + 1][0] if i + 1 < len(states) else end,                "TRTSDTM": trt,                "AESEV": event["AESEV"],                "AETOXGR": event["AETOXGR"],                "ASEV": sev,                "ATOXGRN": grade,                "TRTEMFL": "Y" if emergent else None,            }        )Path("/app/output").mkdir(exist_ok=True)pl.DataFrame(output).write_csv("/app/output/adae.csv")

solutions/adam-adae-worsening-emergence/result.sas

Edit
124 lines
/* opensas reference for yamaa's adam-adae-worsening-emergence benchmark.   Construct periods from one AE and its collected, dated FA findings. */data ae;  length STUDYID USUBJID AELNKID AEDECOD AESTDTC AEENDTC AESEV AETOXGR $1024;  infile '/app/input/ae.csv' dsd dlm=',' firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. AESEQ    AELNKID : $char1024. AEDECOD : $char1024. AESTDTC : $char1024.    AEENDTC : $char1024. AESEV : $char1024. AETOXGR : $char1024.;  if lengthn(AESTDTC)=16 then AESTDTC=cats(AESTDTC,':00');  if lengthn(AEENDTC)=16 then AEENDTC=cats(AEENDTC,':00');run;data fa;  length STUDYID USUBJID FALNKID FATESTCD FAOBJ FASTRESC FADTC $1024;  infile '/app/input/fa.csv' dsd dlm=',' firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. FASEQ    FALNKID : $char1024. FATESTCD : $char1024. FAOBJ : $char1024.    FASTRESC : $char1024. FADTC : $char1024.;  if lengthn(FADTC)=16 then FADTC=cats(FADTC,':00');  SEVRANK=.;  if FATESTCD='SEV' then do;    select(FASTRESC);      when('MILD') SEVRANK=1; when('MODERATE') SEVRANK=2;      when('SEVERE') SEVRANK=3; otherwise SEVRANK=.;    end;  end;run;data adsl;  length STUDYID USUBJID TRTSDTM $1024;  infile '/app/input/adsl.csv' dsd dlm=',' firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. TRTSDTM : $char1024.;  if lengthn(TRTSDTM)=16 then TRTSDTM=cats(TRTSDTM,':00');run;proc sql;  create table changes as    select STUDYID,USUBJID,FALNKID,FADTC as ASTDTM,      max(SEVRANK) as SEVCOLN,      max(case when FATESTCD='TOXGR' then input(FASTRESC,best32.)        else . end) as GRDCOL    from fa group by STUDYID,USUBJID,FALNKID,FADTC;  create table joined as    select a.*,c.ASTDTM,c.SEVCOLN,c.GRDCOL,b.TRTSDTM    from changes as c inner join ae as a      on c.STUDYID=a.STUDYID and c.USUBJID=a.USUBJID        and c.FALNKID=a.AELNKID    left join adsl as b      on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID;quit;proc sort data=joined;  by STUDYID USUBJID AESEQ ASTDTM;run;data states;  length ASEV $1024;  set joined;  by STUDYID USUBJID AESEQ;  retain ASEVN ATOXGRN;  if first.AESEQ then do; ASEVN=.; ATOXGRN=.; end;  if not missing(SEVCOLN) then ASEVN=SEVCOLN;  if not missing(GRDCOL) then ATOXGRN=GRDCOL;  select(ASEVN);    when(1) ASEV='MILD'; when(2) ASEV='MODERATE';    when(3) ASEV='SEVERE'; otherwise ASEV='';  end;run;/* Read the next start by scanning periods in reverse chronological order. */proc sort data=states;  by STUDYID USUBJID AESEQ descending ASTDTM;run;data periods;  length AENDTM NEXTSTART $1024;  set states;  by STUDYID USUBJID AESEQ;  retain NEXTSTART;  if first.AESEQ then NEXTSTART='';  if not missing(NEXTSTART) then AENDTM=NEXTSTART;  else AENDTM=AEENDTC;  NEXTSTART=ASTDTM;run;proc sql;  create table pretreatment as    select STUDYID,USUBJID,AESEQ,max(ASTDTM) as BASEDTM    from periods where not missing(TRTSDTM) and ASTDTM<TRTSDTM    group by STUDYID,USUBJID,AESEQ;  create table baseline as    select a.STUDYID,a.USUBJID,a.AESEQ,      a.ASEVN as PRESEV,a.ATOXGRN as PREGRD    from periods as a inner join pretreatment as b      on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID        and a.AESEQ=b.AESEQ and a.ASTDTM=b.BASEDTM;  create table compared as    select a.*,b.PRESEV,b.PREGRD    from periods as a left join baseline as b      on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID and a.AESEQ=b.AESEQ;  create table worsening as    select STUDYID,USUBJID,AESEQ,min(ASTDTM) as FIRSTWRS    from compared where not missing(TRTSDTM) and ASTDTM>=TRTSDTM and      ((not missing(ASEVN) and not missing(PRESEV) and ASEVN>PRESEV) or       (not missing(ATOXGRN) and not missing(PREGRD) and ATOXGRN>PREGRD))    group by STUDYID,USUBJID,AESEQ;  create table flagged as    select a.*,b.FIRSTWRS from periods as a left join worsening as b      on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID and a.AESEQ=b.AESEQ;quit;proc sort data=flagged;  by STUDYID USUBJID AESEQ ASTDTM;run;data result;  length TRTEMFL $1;  set flagged;  by STUDYID USUBJID;  retain ASEQ;  if first.USUBJID then ASEQ=0;  ASEQ=ASEQ+1;  TRTEMFL='';  if not missing(AESTDTC) and not missing(TRTSDTM) then do;    if AESTDTC>=TRTSDTM or      (not missing(FIRSTWRS) and ASTDTM>=FIRSTWRS) then TRTEMFL='Y';  end;run;proc sql;  create table final as    select STUDYID,USUBJID,ASEQ,AESEQ,AEDECOD,AESTDTC,AEENDTC,      ASTDTM,AENDTM,TRTSDTM,AESEV,AETOXGR,ASEV,ATOXGRN,TRTEMFL from result;quit;proc export data=final outfile='/app/output/adae.csv' dbms=csv replace; run;