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: draftEditUnder review
What this benchmark means
README.md

Goal: build the progression-free survival record for each subject under the fixed PFS code (Progression-Free Survival), setting STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, and SRCSEQ.

Input: randomization dates (RANDDT); tumor response assessments for overall response (RSTESTCD of OVRLRESP) with result (RSSTRESC), assessment date (RSDTC), and adequacy flag (ADEQFL of Y); disposition records with outcome (DSDECOD of DEATH) and date (DSDTC); and the start date of new anti-cancer therapy (NTXSTDT) for subjects who began one. Records sharing a date are ordered by their sequence number.

Variables:

  • STARTDT is the randomization date from RANDDT.
  • ADT is the event date, the earlier of the progression and death dates, when the subject has an event; else the last adequate assessment date, or the randomization date when no adequate assessment can supply a censoring date.
  • AVAL is the number of days from STARTDT through ADT, counting the randomization day as day one.
  • CNSR is 0 when an event occurred (a progression or death date is present), 1 otherwise. The event always wins: a death after the last adequate assessment is still an event, unless new anti-cancer therapy started first.
  • EVNTDESC is DISEASE PROGRESSION for a progression event, DEATH for a death event, and CENSORED when both dates are absent. A progression and a death on the same day count as progression.
  • SRCDOM, SRCVAR, and SRCSEQ trace ADT to its source: DS with DSDTC and the disposition sequence number for a death event, or RS with RSDTC and the response sequence number for a progression event or a censored assessment. All three are empty when the subject is censored at randomization.

Note: only assessments flagged adequate can supply a progression event or a censoring date, so an inadequate progression assessment leaves the subject censored at the last adequate dated assessment. An assessment without a date cannot be the last dated assessment. When new anti-cancer therapy started (NTXSTDT present), only dates on or before therapy start are usable: a progression or death dated after therapy start is not an event, and the subject is censored at the last adequate assessment dated on or before therapy start. A subject with no usable adequate assessment at all is censored at randomization, for a 1-day PFS.

Inputs

Under review
3 source files

input/adsl.csv

Edit
12 rows
input/adsl.csv
STUDYIDUSUBJIDRANDDTNTXSTDT
YAMAA-012026-01-01—
YAMAA-012026-01-01—
YAMAA-012026-02-01—
YAMAA-012026-01-15—
YAMAA-012026-03-01—
YAMAA-012026-01-01—
YAMAA-012026-01-01—
YAMAA-012026-02-01—
YAMAA-012026-04-01—
YAMAA-012026-01-012026-03-01
YAMAA-012026-05-01—
YAMAA-012026-01-012026-02-01

input/ds.csv

Edit
6 rows
input/ds.csv
STUDYIDUSUBJIDDSSEQDSDTCDSDECOD
YAMAA-0112026-03-21DEATH
YAMAA-0112026-01-30DEATH
YAMAA-0112026-03-15DEATH
YAMAA-0112026-05-01DEATH
YAMAA-0122026-05-01DEATH
YAMAA-0112026-03-15DEATH

input/rs.csv

Edit
25 rows
input/rs.csv
STUDYIDUSUBJIDRSSEQRSTESTCDRSDTCRSSTRESCADEQFL
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-02-19PDY
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-01-22SDY
YAMAA-011OVRLRESP2026-02-01SDY
YAMAA-012OVRLRESP2026-03-14SDY
YAMAA-011OVRLRESP2026-01-15SDY
YAMAA-012OVRLRESP2026-02-05SDY
YAMAA-013OVRLRESP2026-03-06PDN
YAMAA-011OVRLRESP2026-03-01SDY
YAMAA-012OVRLRESP—SDY
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-03-15PDY
YAMAA-011OVRLRESP2026-01-01SDY
YAMAA-012OVRLRESP2026-04-01PDY
YAMAA-013OVRLRESP2026-04-01PDY
YAMAA-011OVRLRESP2026-02-01SDY
YAMAA-012OVRLRESP2026-03-01SDY
YAMAA-011OVRLRESP2026-04-15SDY
YAMAA-011OVRLRESP2026-01-15SDY
YAMAA-012OVRLRESP2026-03-01SDY
YAMAA-013OVRLRESP2026-04-01PDY
YAMAA-011OVRLRESP—SDY
YAMAA-012OVRLRESP2026-06-01PDN
YAMAA-011OVRLRESP2026-01-10SDY

Expected output

Under review
12 expected rows

expected/adtte.csv

Edit
12 rows
expected/adtte.csv
STUDYIDUSUBJIDPARAMCDPARAMSTARTDTADTAVALCNSREVNTDESCSRCDOMSRCVARSRCSEQ
YAMAA-01PFSProgression-Free Survival2026-01-012026-02-19500DISEASE PROGRESSIONRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-01-012026-01-30300DEATHDSDSDTC1
YAMAA-01PFSProgression-Free Survival2026-02-012026-03-14421CENSOREDRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-01-152026-02-05221CENSOREDRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-03-012026-03-0111CENSOREDRSRSDTC1
YAMAA-01PFSProgression-Free Survival2026-01-012026-03-15740DISEASE PROGRESSIONRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-01-012026-04-01910DISEASE PROGRESSIONRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-02-012026-05-01900DEATHDSDSDTC1
YAMAA-01PFSProgression-Free Survival2026-04-012026-04-15151CENSOREDRSRSDTC1
YAMAA-01PFSProgression-Free Survival2026-01-012026-03-01601CENSOREDRSRSDTC2
YAMAA-01PFSProgression-Free Survival2026-05-012026-05-0111CENSORED———
YAMAA-01PFSProgression-Free Survival2026-01-012026-01-10101CENSOREDRSRSDTC1
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
ADTTESTUDYIDStudy IdentifiercharacterCopy value from ADSL.STUDYID.Predecessor
ADTTEUSUBJIDUnique Subject IdentifiercharacterCopy value from ADSL.USUBJID.Predecessor
ADTTEPARAMCDParameter CodecharacterSet constant value "PFS".Assigned
ADTTEPARAMParametercharacterSet constant value "Progression-Free Survival".Assigned
ADTTESTARTDTTime to Event Origin Date for SubjectnumericISO 8601Copy value from ADSL.RANDDT.Predecessor
ADTTECNSRCensornumericIf EVENTDT IS NOT NULL then "0"; otherwise "1".Derived
ADTTEADTAnalysis DatenumericISO 8601First non-missing of EVENTDT and CENSORDT.Derived
ADTTEAVALAnalysis ValuenumericWhole days from STARTDT to ADT, counting both endpoints.Derived
ADTTEEVNTDESCEvent or Censoring DescriptioncharacterIf EVENTDT IS NULL then "CENSORED"; If PDDT IS NOT NULL AND (DEATHDT IS NULL OR PDDT <= DEATHDT) then "DISEASE PROGRESSION"; otherwise "DEATH".Derived
ADTTESRCDOMSource DatacharacterIf EVNTDESC = 'DEATH' then "DS"; If CENSORASSESSDT IS NULL then "None"; otherwise "RS".Derived
ADTTESRCVARSource VariablecharacterIf EVNTDESC = 'DEATH' then "DSDTC"; If CENSORASSESSDT IS NULL then "None"; otherwise "RSDTC".Derived
ADTTESRCSEQSource Sequence NumbernumericIf EVNTDESC = 'DISEASE PROGRESSION' then "PROGRESSION.RSSEQ"; If EVNTDESC = 'DEATH' then "DEATH.DSSEQ"; If CENSORASSESSDT IS NULL then "None"; otherwise "CENSORSEQ".Derived
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-adtte-pfs/full.md

Edit
47 lines

Following CDISC ADaM standards, use the provided ADSL, RS, and DS datasets to create an ADTTE dataset for progression-free survival, with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ

Use PARAMCD "PFS" and PARAM "Progression-Free Survival". STARTDT is the randomization date. ADSL also carries NTXSTDT, the start date of new anti-cancer therapy, for subjects who began one.

The event is disease progression (the first adequate overall-response assessment with a PD result) or death (the first disposition record with a DEATH outcome), whichever comes first. Only an assessment flagged adequate can supply a progression event or a censoring date, and an assessment without a date never counts. Records sharing a date are ordered by their sequence number.

When new anti-cancer therapy started, only dates on or before the therapy start are usable: a progression or death dated after therapy start is not an event, and the subject is censored at the last adequate assessment dated on or before therapy start.

A subject with an event has ADT at the earlier of the progression and death dates; a subject without one is censored at the last adequate assessment date, or at the randomization date when no adequate assessment can supply a censoring date. The event always wins, even when it comes after the last adequate assessment, unless new anti-cancer therapy started first.

AVAL is the number of days from STARTDT to ADT, counting the randomization day as day one. CNSR is 0 for an event and 1 for a censored record.

Describe each record with EVNTDESC DISEASE PROGRESSION, DEATH, or CENSORED. A progression and a death on the same day count as progression.

Trace ADT with SRCDOM DS, SRCVAR DSDTC, and the disposition sequence for a death; otherwise with SRCDOM RS, SRCVAR RSDTC, and the response sequence of the progression event or the censored assessment. Leave SRCDOM, SRCVAR, and SRCSEQ empty when the subject is censored at randomization.

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

prompts/adam-adtte-pfs/conventions.md

Edit
19 lines

Following CDISC ADaM standards, use the provided ADSL, RS, and DS datasets to create an ADTTE dataset for progression-free survival, with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ

PARAMCD is "PFS" and PARAM "Progression-Free Survival". AVAL counts STARTDT as day one. CNSR is 0 for an event and 1 for a censored record. EVNTDESC is DISEASE PROGRESSION, DEATH, or CENSORED. A progression or death dated after the ADSL new-therapy start date (NTXSTDT) is not an event; the subject is then censored at the last adequate assessment dated on or before NTXSTDT, or at STARTDT when no adequate assessment is usable. SRCDOM, SRCVAR, and SRCSEQ are DS, DSDTC, and the disposition sequence; or RS, RSDTC, and the response sequence; empty when censored at randomization.

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

prompts/adam-adtte-pfs/brief.md

Edit
10 lines

Following CDISC ADaM standards, use the provided ADSL, RS, and DS datasets to create an ADTTE dataset for progression-free survival, with one record per subject.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ

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

yamaa code

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

run.R

Edit
4 lines
library(yamaa)adtte <- yamaa_domain("spec.yaml")$outputadtte

run.py

Edit
4 lines
import yamaaadtte = yamaa.yamaa_domain("spec.yaml").outputadtte

Solution without yamaa

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

solutions/adam-adtte-pfs/result.R

Edit
116 lines
# Reference solution for the yamaa benchmark adam-adtte-pfs (R track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1.library(dplyr, warn.conflicts = FALSE)library(readr)adsl <- read_csv(  "/app/input/adsl.csv",  col_types = cols(    RANDDT = col_date(), NTXSTDT = col_date(), .default = col_character()  ))rs <- read_csv(  "/app/input/rs.csv",  col_types = cols(RSSEQ = col_integer(), RSDTC = col_date(), .default = col_character()))ds <- read_csv(  "/app/input/ds.csv",  col_types = cols(DSSEQ = col_integer(), DSDTC = col_date(), .default = col_character()))# The first adequate progression, the first death, and the last adequate# assessment. Records sharing a date are ordered by sequence number.progression <- rs |>  filter(RSTESTCD %in% "OVRLRESP", RSSTRESC %in% "PD", ADEQFL %in% "Y", !is.na(RSDTC)) |>  arrange(USUBJID, RSDTC, RSSEQ) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, PDDT = RSDTC, PDSEQ = RSSEQ)death <- ds |>  filter(DSDECOD %in% "DEATH", !is.na(DSDTC)) |>  arrange(USUBJID, DSDTC, DSSEQ) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, DTHDT = DSDTC, DTHSEQ = DSSEQ)last_assessment <- rs |>  filter(RSTESTCD %in% "OVRLRESP", ADEQFL %in% "Y", !is.na(RSDTC)) |>  arrange(USUBJID, desc(RSDTC), desc(RSSEQ)) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, CENSORDT = RSDTC, LASTSEQ = RSSEQ)# The last adequate assessment dated on or before new-therapy start, for# subjects who began one.pre_therapy <- rs |>  inner_join(select(adsl, USUBJID, NTXSTDT), by = "USUBJID") |>  filter(    RSTESTCD %in% "OVRLRESP", ADEQFL %in% "Y", !is.na(RSDTC),    !is.na(NTXSTDT), RSDTC <= NTXSTDT  ) |>  arrange(USUBJID, desc(RSDTC), desc(RSSEQ)) |>  distinct(USUBJID, .keep_all = TRUE) |>  select(USUBJID, PRECENSORDT = RSDTC, PRESEQ = RSSEQ)adtte <- adsl |>  left_join(progression, by = "USUBJID") |>  left_join(death, by = "USUBJID") |>  left_join(last_assessment, by = "USUBJID") |>  left_join(pre_therapy, by = "USUBJID") |>  mutate(    PARAMCD = "PFS",    PARAM = "Progression-Free Survival",    STARTDT = RANDDT,    # A progression or death dated after new-therapy start is not an event.    PDDT = if_else(!is.na(NTXSTDT) & PDDT > NTXSTDT, as.Date(NA), PDDT),    DTHDT = if_else(!is.na(NTXSTDT) & DTHDT > NTXSTDT, as.Date(NA), DTHDT),    # The censoring assessment: the last adequate one overall, or the last    # one dated on or before therapy start when therapy began.    CENSORASSESSDT = if_else(is.na(NTXSTDT), CENSORDT, PRECENSORDT),    CENSORSEQ = if_else(is.na(NTXSTDT), LASTSEQ, PRESEQ),    # With no usable adequate assessment the subject is censored at    # randomization, for a 1-day PFS.    CENSORDT = coalesce(CENSORASSESSDT, STARTDT),    EVENTDT = case_when(      !is.na(PDDT) & !is.na(DTHDT) ~ pmin(PDDT, DTHDT),      !is.na(PDDT) ~ PDDT,      !is.na(DTHDT) ~ DTHDT    ),    # The event always wins, even after the last adequate assessment,    # unless new anti-cancer therapy started first.    CNSR = if_else(!is.na(EVENTDT), 0L, 1L),    ADT = coalesce(EVENTDT, CENSORDT),    AVAL = as.integer(ADT - STARTDT) + 1L,    # A progression and a death on the same day count as progression.    EVNTDESC = case_when(      is.na(EVENTDT) ~ "CENSORED",      !is.na(PDDT) & (is.na(DTHDT) | PDDT <= DTHDT) ~ "DISEASE PROGRESSION",      .default = "DEATH"    ),    SRCDOM = case_when(      EVNTDESC == "DEATH" ~ "DS",      is.na(CENSORASSESSDT) ~ NA_character_,      .default = "RS"    ),    SRCVAR = case_when(      EVNTDESC == "DEATH" ~ "DSDTC",      is.na(CENSORASSESSDT) ~ NA_character_,      .default = "RSDTC"    ),    SRCSEQ = case_when(      EVNTDESC == "DISEASE PROGRESSION" ~ PDSEQ,      EVNTDESC == "DEATH" ~ DTHSEQ,      is.na(CENSORASSESSDT) ~ NA_integer_,      .default = CENSORSEQ    )  ) |>  arrange(USUBJID) |>  select(    STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC,    SRCDOM, SRCVAR, SRCSEQ  )dir.create("/app/output", showWarnings = FALSE)write_csv(adtte, "/app/output/adtte.csv", na = "")

solutions/adam-adtte-pfs/result.py

Edit
174 lines
# Reference solution for the yamaa benchmark adam-adtte-pfs (Python track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1.from pathlib import Pathimport polars as pldef read(name: str) -> pl.DataFrame:    """Read a benchmark input CSV with every column as text."""    return pl.read_csv(f"/app/input/{name}", infer_schema=False)def first_per_subject(records: pl.DataFrame, by: list[str]) -> pl.DataFrame:    """Keep the earliest record per subject, breaking ties by sequence."""    return records.sort(by).unique("USUBJID", keep="first", maintain_order=True)def last_per_subject(records: pl.DataFrame, by: list[str]) -> pl.DataFrame:    """Keep the latest record per subject, breaking ties by sequence."""    return records.sort(by, descending=True).unique(        "USUBJID", keep="first", maintain_order=True    )adsl = read("adsl.csv").with_columns(    pl.col("RANDDT").str.to_date(strict=False),    pl.col("NTXSTDT").str.to_date(strict=False),)rs = read("rs.csv").with_columns(    pl.col("RSDTC").str.to_date(strict=False),    pl.col("RSSEQ").cast(pl.Int64, strict=False),)ds = read("ds.csv").with_columns(    pl.col("DSDTC").str.to_date(strict=False),    pl.col("DSSEQ").cast(pl.Int64, strict=False),)# The first adequate progression, the first death, and the last adequate# assessment. Records sharing a date are ordered by sequence number.progression = first_per_subject(    rs.filter(        (pl.col("RSTESTCD") == "OVRLRESP")        & (pl.col("RSSTRESC") == "PD")        & (pl.col("ADEQFL") == "Y")        & pl.col("RSDTC").is_not_null()    ),    ["RSDTC", "RSSEQ"],).select("USUBJID", PDDT="RSDTC", PDSEQ="RSSEQ")death = first_per_subject(    ds.filter(        (pl.col("DSDECOD") == "DEATH") & pl.col("DSDTC").is_not_null()    ),    ["DSDTC", "DSSEQ"],).select("USUBJID", DTHDT="DSDTC", DTHSEQ="DSSEQ")last_assessment = last_per_subject(    rs.filter(        (pl.col("RSTESTCD") == "OVRLRESP")        & (pl.col("ADEQFL") == "Y")        & pl.col("RSDTC").is_not_null()    ),    ["RSDTC", "RSSEQ"],).select("USUBJID", CENSORDT="RSDTC", LASTSEQ="RSSEQ")# The last adequate assessment dated on or before new-therapy start, for# subjects who began one.pre_therapy = last_per_subject(    rs.join(adsl.select("USUBJID", "NTXSTDT"), on="USUBJID", how="left").filter(        (pl.col("RSTESTCD") == "OVRLRESP")        & (pl.col("ADEQFL") == "Y")        & pl.col("RSDTC").is_not_null()        & pl.col("NTXSTDT").is_not_null()        & (pl.col("RSDTC") <= pl.col("NTXSTDT"))    ),    ["RSDTC", "RSSEQ"],).select("USUBJID", PRECENSORDT="RSDTC", PRESEQ="RSSEQ")PDDT, DTHDT = pl.col("PDDT"), pl.col("DTHDT")adtte = (    adsl.join(progression, on="USUBJID", how="left")    .join(death, on="USUBJID", how="left")    .join(last_assessment, on="USUBJID", how="left")    .join(pre_therapy, on="USUBJID", how="left")    # A progression or death dated after new-therapy start is not an event.    .with_columns(        PDDT=pl.when(            pl.col("NTXSTDT").is_not_null() & (pl.col("PDDT") > pl.col("NTXSTDT"))        )        .then(None)        .otherwise("PDDT"),        DTHDT=pl.when(            pl.col("NTXSTDT").is_not_null() & (pl.col("DTHDT") > pl.col("NTXSTDT"))        )        .then(None)        .otherwise("DTHDT"),    )    .with_columns(        EVENTDT=pl.min_horizontal("PDDT", "DTHDT"),        STARTDT=pl.col("RANDDT"),        PARAMCD=pl.lit("PFS"),        PARAM=pl.lit("Progression-Free Survival"),        # The censoring assessment: the last adequate one overall, or the        # last one dated on or before therapy start when therapy began.        CENSORASSESSDT=pl.when(pl.col("NTXSTDT").is_null())        .then(pl.col("CENSORDT"))        .otherwise(pl.col("PRECENSORDT")),        CENSORSEQ=pl.when(pl.col("NTXSTDT").is_null())        .then(pl.col("LASTSEQ"))        .otherwise(pl.col("PRESEQ")),    )    # With no usable adequate assessment the subject is censored at    # randomization, for a 1-day PFS.    .with_columns(        CENSORDT=pl.coalesce("CENSORASSESSDT", "STARTDT"),    )    # The event always wins, even after the last adequate assessment,    # unless new anti-cancer therapy started first.    .with_columns(        CNSR=pl.when(pl.col("EVENTDT").is_not_null()).then(0).otherwise(1),        ADT=pl.coalesce("EVENTDT", "CENSORDT"),    )    .with_columns(        AVAL=(pl.col("ADT") - pl.col("STARTDT")).dt.total_days() + 1,        # A progression and a death on the same day count as progression.        EVNTDESC=pl.when(pl.col("EVENTDT").is_null())        .then(pl.lit("CENSORED"))        .when(PDDT.is_not_null() & (DTHDT.is_null() | (PDDT <= DTHDT)))        .then(pl.lit("DISEASE PROGRESSION"))        .otherwise(pl.lit("DEATH")),    )    .with_columns(        SRCDOM=pl.when(pl.col("EVNTDESC") == "DEATH")        .then(pl.lit("DS"))        .when(pl.col("CENSORASSESSDT").is_null())        .then(None)        .otherwise(pl.lit("RS")),        SRCVAR=pl.when(pl.col("EVNTDESC") == "DEATH")        .then(pl.lit("DSDTC"))        .when(pl.col("CENSORASSESSDT").is_null())        .then(None)        .otherwise(pl.lit("RSDTC")),        SRCSEQ=pl.when(pl.col("EVNTDESC") == "DISEASE PROGRESSION")        .then(pl.col("PDSEQ"))        .when(pl.col("EVNTDESC") == "DEATH")        .then(pl.col("DTHSEQ"))        .when(pl.col("CENSORASSESSDT").is_null())        .then(None)        .otherwise(pl.col("CENSORSEQ")),    )    .sort("USUBJID")    .select(        "STUDYID",        "USUBJID",        "PARAMCD",        "PARAM",        "STARTDT",        "ADT",        "AVAL",        "CNSR",        "EVNTDESC",        "SRCDOM",        "SRCVAR",        "SRCSEQ",    ))Path("/app/output").mkdir(parents=True, exist_ok=True)adtte.write_csv("/app/output/adtte.csv")

solutions/adam-adtte-pfs/result.sas

Edit
52 lines
/* opensas reference solution for the yamaa benchmark adam-adtte-pfs.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data adsl;  length STUDYID USUBJID RANDDT NTXSTDT $1024;  infile "/app/input/adsl.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. RANDDT : $char1024. NTXSTDT : $char1024.;run;data ds;  length STUDYID USUBJID DSDTC DSDECOD $1024;  infile "/app/input/ds.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. DSSEQ DSDTC : $char1024. DSDECOD : $char1024.;run;data rs;  length STUDYID USUBJID RSTESTCD RSDTC RSSTRESC ADEQFL $1024;  infile "/app/input/rs.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. RSSEQ RSTESTCD : $char1024. RSDTC : $char1024. RSSTRESC : $char1024. ADEQFL : $char1024.;run;proc sql;  create table usable as select a.* from rs as a inner join adsl as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where a.RSDTC ne '' and a.ADEQFL='Y' and a.RSTESTCD='OVRLRESP' and (missing(b.NTXSTDT) or a.RSDTC<=b.NTXSTDT);  create table progression as select a.* from rs as a inner join adsl as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where a.RSDTC ne '' and a.RSSTRESC='PD' and a.ADEQFL='Y' and a.RSTESTCD='OVRLRESP' and (missing(b.NTXSTDT) or a.RSDTC<=b.NTXSTDT);  create table deaths as select a.* from ds as a inner join adsl as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where a.DSDECOD='DEATH' and a.DSDTC ne '' and (missing(b.NTXSTDT) or a.DSDTC<=b.NTXSTDT);quit;proc sort data=progression; by STUDYID USUBJID RSDTC RSSEQ; run;data pd; set progression; by STUDYID USUBJID; if first.USUBJID; keep STUDYID USUBJID RSDTC RSSEQ; rename RSDTC=PDDT RSSEQ=PDSEQ; run;proc sort data=deaths; by STUDYID USUBJID DSDTC DSSEQ; run;data death; set deaths; by STUDYID USUBJID; if first.USUBJID; keep STUDYID USUBJID DSDTC DSSEQ; rename DSDTC=DEATHDT DSSEQ=DEATHSEQ; run;proc sort data=usable; by STUDYID USUBJID descending RSDTC descending RSSEQ; run;data lastassessment; set usable; by STUDYID USUBJID; if first.USUBJID; keep STUDYID USUBJID RSDTC RSSEQ; rename RSDTC=LASTDT RSSEQ=LASTSEQ; run;proc sql; create table joined as select a.*,b.PDDT,b.PDSEQ,c.DEATHDT,c.DEATHSEQ,d.LASTDT,d.LASTSEQ from adsl as a  left join pd as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID left join death as c on a.STUDYID=c.STUDYID and a.USUBJID=c.USUBJID left join lastassessment as d on a.STUDYID=d.STUDYID and a.USUBJID=d.USUBJID; quit;data result;  length PARAMCD $8 PARAM $40 STARTDT ADT $10 EVNTDESC CNSDTDSC $40 SRCDOM $4 SRCVAR $16;  set joined;  PARAMCD='PFS'; PARAM='Progression-Free Survival'; STARTDT=RANDDT;  if not missing(PDDT) and (missing(DEATHDT) or PDDT<=DEATHDT) then do; ADT=PDDT; CNSR=0; EVNTDESC='DISEASE PROGRESSION'; SRCDOM='RS'; SRCVAR='RSDTC'; SRCSEQ=PDSEQ; end;  else if not missing(DEATHDT) then do; ADT=DEATHDT; CNSR=0; EVNTDESC='DEATH'; SRCDOM='DS'; SRCVAR='DSDTC'; SRCSEQ=DEATHSEQ; end;  else do;    CNSR=1; EVNTDESC='CENSORED';    if not missing(LASTDT) then do; ADT=LASTDT; SRCDOM='RS'; SRCVAR='RSDTC'; SRCSEQ=LASTSEQ; end; else ADT=RANDDT;  end;  if not missing(ADT) and not missing(STARTDT) then AVAL=input(ADT,yymmdd10.)-input(STARTDT,yymmdd10.)+1;run;proc sql;  create table final as select STUDYID, USUBJID, PARAMCD, PARAM, STARTDT, ADT, AVAL, CNSR, EVNTDESC, SRCDOM, SRCVAR, SRCSEQ from result;quit;proc export data=final outfile="/app/output/adtte.csv" dbms=csv replace; run;