As a clinical trial statistician or programmer, answer three short questions in Your review: the task, the inputs and outputs, and the AI-assisted review. Agreeing with all three approves this benchmark; disagreeing with any needs a comment. Propose a fix with the Edit button on any source file, or add it to your comments.
We suggest spending no more than 5 minutes on a review. The other tabs beside Your review are background for assessment, not under review.
Summary
README.mdPublic entries: Python and R. Passing coverage requires an installed inventory.
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:
STARTDTis the randomization date fromRANDDT.ADTis 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.AVALis the number of days fromSTARTDTthroughADT, counting the randomization day as day one.CNSRis0when an event occurred (a progression or death date is present),1otherwise. The event always wins: a death after the last adequate assessment is still an event, unless new anti-cancer therapy started first.EVNTDESCisDISEASE PROGRESSIONfor a progression event,DEATHfor a death event, andCENSOREDwhen both dates are absent. A progression and a death on the same day count as progression.SRCDOM,SRCVAR, andSRCSEQtraceADTto its source:DSwithDSDTCand the disposition sequence number for a death event, orRSwithRSDTCand 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 review3 source filesinput/adsl.csv
Edit12 rows| STUDYID | USUBJID | RANDDT | NTXSTDT |
|---|---|---|---|
| YAMAA-01 | 2026-01-01 | — | |
| YAMAA-01 | 2026-01-01 | — | |
| YAMAA-01 | 2026-02-01 | — | |
| YAMAA-01 | 2026-01-15 | — | |
| YAMAA-01 | 2026-03-01 | — | |
| YAMAA-01 | 2026-01-01 | — | |
| YAMAA-01 | 2026-01-01 | — | |
| YAMAA-01 | 2026-02-01 | — | |
| YAMAA-01 | 2026-04-01 | — | |
| YAMAA-01 | 2026-01-01 | 2026-03-01 | |
| YAMAA-01 | 2026-05-01 | — | |
| YAMAA-01 | 2026-01-01 | 2026-02-01 |
input/ds.csv
Edit6 rows| STUDYID | USUBJID | DSSEQ | DSDTC | DSDECOD |
|---|---|---|---|---|
| YAMAA-01 | 1 | 2026-03-21 | DEATH | |
| YAMAA-01 | 1 | 2026-01-30 | DEATH | |
| YAMAA-01 | 1 | 2026-03-15 | DEATH | |
| YAMAA-01 | 1 | 2026-05-01 | DEATH | |
| YAMAA-01 | 2 | 2026-05-01 | DEATH | |
| YAMAA-01 | 1 | 2026-03-15 | DEATH |
input/rs.csv
Edit25 rows| STUDYID | USUBJID | RSSEQ | RSTESTCD | RSDTC | RSSTRESC | ADEQFL |
|---|---|---|---|---|---|---|
| YAMAA-01 | 1 | OVRLRESP | 2026-01-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-02-19 | PD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-01-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-01-22 | SD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-02-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-03-14 | SD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-01-15 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-02-05 | SD | Y | |
| YAMAA-01 | 3 | OVRLRESP | 2026-03-06 | PD | N | |
| YAMAA-01 | 1 | OVRLRESP | 2026-03-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | — | SD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-01-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-03-15 | PD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-01-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-04-01 | PD | Y | |
| YAMAA-01 | 3 | OVRLRESP | 2026-04-01 | PD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-02-01 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-03-01 | SD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-04-15 | SD | Y | |
| YAMAA-01 | 1 | OVRLRESP | 2026-01-15 | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-03-01 | SD | Y | |
| YAMAA-01 | 3 | OVRLRESP | 2026-04-01 | PD | Y | |
| YAMAA-01 | 1 | OVRLRESP | — | SD | Y | |
| YAMAA-01 | 2 | OVRLRESP | 2026-06-01 | PD | N | |
| YAMAA-01 | 1 | OVRLRESP | 2026-01-10 | SD | Y |
Expected output
Under review12 expected rowsexpected/adtte.csv
Edit12 rows| STUDYID | USUBJID | PARAMCD | PARAM | STARTDT | ADT | AVAL | CNSR | EVNTDESC | SRCDOM | SRCVAR | SRCSEQ |
|---|---|---|---|---|---|---|---|---|---|---|---|
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-01 | 2026-02-19 | 50 | 0 | DISEASE PROGRESSION | RS | RSDTC | 2 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-01 | 2026-01-30 | 30 | 0 | DEATH | DS | DSDTC | 1 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-02-01 | 2026-03-14 | 42 | 1 | CENSORED | RS | RSDTC | 2 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-15 | 2026-02-05 | 22 | 1 | CENSORED | RS | RSDTC | 2 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-03-01 | 2026-03-01 | 1 | 1 | CENSORED | RS | RSDTC | 1 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-01 | 2026-03-15 | 74 | 0 | DISEASE PROGRESSION | RS | RSDTC | 2 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-01 | 2026-04-01 | 91 | 0 | DISEASE PROGRESSION | RS | RSDTC | 2 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-02-01 | 2026-05-01 | 90 | 0 | DEATH | DS | DSDTC | 1 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-04-01 | 2026-04-15 | 15 | 1 | CENSORED | RS | RSDTC | 1 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-01 | 2026-03-01 | 60 | 1 | CENSORED | RS | RSDTC | 2 | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-05-01 | 2026-05-01 | 1 | 1 | CENSORED | — | — | — | |
| YAMAA-01 | PFS | Progression-Free Survival | 2026-01-01 | 2026-01-10 | 10 | 1 | CENSORED | RS | RSDTC | 1 |
Human review and assessment
| Dataset Name | Variable Name | Variable Label | Type | Codelist/Controlled Terms | Core | Computational Method | Origin |
|---|---|---|---|---|---|---|---|
| ADTTE | STUDYID | Study Identifier | character | Copy value from ADSL.STUDYID. | Predecessor | ||
| ADTTE | USUBJID | Unique Subject Identifier | character | Copy value from ADSL.USUBJID. | Predecessor | ||
| ADTTE | PARAMCD | Parameter Code | character | Set constant value "PFS". | Assigned | ||
| ADTTE | PARAM | Parameter | character | Set constant value "Progression-Free Survival". | Assigned | ||
| ADTTE | STARTDT | Time to Event Origin Date for Subject | numeric | ISO 8601 | Copy value from ADSL.RANDDT. | Predecessor | |
| ADTTE | CNSR | Censor | numeric | If EVENTDT IS NOT NULL then "0"; otherwise "1". | Derived | ||
| ADTTE | ADT | Analysis Date | numeric | ISO 8601 | First non-missing of EVENTDT and CENSORDT. | Derived | |
| ADTTE | AVAL | Analysis Value | numeric | Whole days from STARTDT to ADT, counting both endpoints. | Derived | ||
| ADTTE | EVNTDESC | Event or Censoring Description | character | If EVENTDT IS NULL then "CENSORED"; If PDDT IS NOT NULL AND (DEATHDT IS NULL OR PDDT <= DEATHDT) then "DISEASE PROGRESSION"; otherwise "DEATH". | Derived | ||
| ADTTE | SRCDOM | Source Data | character | If EVNTDESC = 'DEATH' then "DS"; If CENSORASSESSDT IS NULL then "None"; otherwise "RS". | Derived | ||
| ADTTE | SRCVAR | Source Variable | character | If EVNTDESC = 'DEATH' then "DSDTC"; If CENSORASSESSDT IS NULL then "None"; otherwise "RSDTC". | Derived | ||
| ADTTE | SRCSEQ | Source Sequence Number | numeric | If EVNTDESC = 'DISEASE PROGRESSION' then "PROGRESSION.RSSEQ"; If EVNTDESC = 'DEATH' then "DEATH.DSSEQ"; If CENSORASSESSDT IS NULL then "None"; otherwise "CENSORSEQ". | Derived |
| Version | Date | Author | Description | Reviewer | Sign-off |
|---|---|---|---|---|---|
| 1.7 | 2026-10-20 | yamaa docs build | Read a specification in the 1.0 surface as its 0.5 counterpart; state that missing values stay missing where a mapping declares no missing handler, and leave out a declared missing answer that restates a handler's default | ||
| 1.6 | 2026-10-17 | yamaa docs build | Describe collected date parts written as ISO 8601 date text and times of day counted in seconds since midnight | ||
| 1.5 | 2026-10-17 | yamaa docs build | Name collected precision in words instead of letter codes; state imputation fallbacks, minimum source precision, bounds ignored outside the collected period, and invalid text with no handler as an error; describe date and datetime shifts, imputation flag codes, and partial text giving no date part | ||
| 1.4 | 2026-10-15 | yamaa docs build | State datetime precision codes through the hour and minute, and complete only the time parts a collected datetime lacks | ||
| 1.3 | 2026-09-29 | yamaa docs build | Show study codelist names, external dictionaries, and ISO 8601 formats in variable rows; list the document's codelist values and metadata | ||
| 1.2 | 2026-09-29 | yamaa docs build | Classify ADaM copies from a declared input as Predecessor and recodes as Derived, and SDTM recodes of locally derived columns as Derived | ||
| 1.1 | 2026-09-29 | yamaa docs build | Resolve lookups into the donor record they select, and give every derivation shape a plain-language rule instead of its raw YAML | ||
| 1.0 | yamaa docs build | Initial generation from spec.yaml |
prompts/adam-adtte-pfs/full.md
Edit39 linesFollowing CDISC ADaM standards, use the provided ADSL, DS, and RS datasets to create an ADTTE dataset 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 is "Progression-Free Survival" for every record. STARTDT is the subject's randomization date, RANDDT in ADSL. The progression date is the earliest date of an overall response assessment (RSTESTCD "OVRLRESP") with a result of "PD" (RSSTRESC) that is flagged adequate (ADEQFL "Y") and has a date; when assessments share that date, the one with the lowest sequence number is the progression record. The death date is the earliest date of a disposition record with an outcome of "DEATH" (DSDECOD), the record with the lowest sequence number supplying it when records share that date. Only assessments flagged adequate can supply a progression event or a censoring date, and an assessment without a date can supply neither, so an inadequate progression assessment leaves the subject censored at the last adequate dated assessment. When the subject started new anti-cancer therapy (NTXSTDT in ADSL), only dates on or before the therapy start date are usable: a progression or death dated after the therapy start date is not an event, and censoring uses only adequate assessments dated on or before the therapy start date. ADT is the earlier of the progression and death dates when either is present, and CNSR is 0 then; a death after the last adequate assessment is still an event. When neither date is present, CNSR is 1 and ADT is the date of the subject's last adequate dated overall response assessment. A subject with no usable adequate assessment at all is censored at the randomization date: ADT is STARTDT, AVAL is 1, and SRCDOM, SRCVAR, and SRCSEQ have no value. AVAL is the number of days from STARTDT through ADT, counting the randomization day as day one. 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", "DSDTC", and the disposition sequence number for a death event, or "RS", "RSDTC", and the response sequence number for a progression event or a censored assessment.
Read the source datasets from /app/input and save the completed dataset as /app/output/adtte.csv.
prompts/adam-adtte-pfs/conventions.md
Edit16 linesFollowing CDISC ADaM standards, use the provided ADSL, DS, and RS datasets to create an ADTTE dataset 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 is "Progression-Free Survival" for every record. AVAL is the number of days from STARTDT through ADT, counting the randomization day as day one. CNSR is 0 or 1. EVNTDESC is "DISEASE PROGRESSION", "DEATH", or "CENSORED". SRCDOM is "DS" or "RS", or has no value. SRCVAR is "DSDTC" when SRCDOM is "DS" and "RSDTC" when SRCDOM is "RS", or has no value.
Read the source datasets from /app/input and save the completed dataset as /app/output/adtte.csv.
prompts/adam-adtte-pfs/brief.md
Edit9 linesFollowing CDISC ADaM standards, use the provided ADSL, DS, and RS datasets to create an ADTTE dataset 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.
R
Edit3 lineslibrary(yamaa)yamaa_spec("spec.yaml")$run()$outputPython
Edit3 linesimport yamaayamaa.spec("spec.yaml").run().outputsolutions/adam-adtte-pfs/result.R
Edit116 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
Edit155 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."""Build ADTTE (PFS) from ADSL, DS, and RS per the task instruction.Reads /app/input/{adsl,ds,rs}.csv and writes /app/output/adtte.csv withone record per subject."""import csvfrom datetime import dateINPUT_DIR = "/app/input"OUTPUT_PATH = "/app/output/adtte.csv"def parse_date(text): """Return a date for an ISO date string, or None when blank.""" text = (text or "").strip() if not text: return None return date.fromisoformat(text)def read_rows(name): with open(f"{INPUT_DIR}/{name}", newline="", encoding="utf-8") as handle: return list(csv.DictReader(handle))def main(): adsl = read_rows("adsl.csv") ds = read_rows("ds.csv") rs = read_rows("rs.csv") # Per-subject new anti-cancer therapy start: only dates on or before # it are usable for events and censoring. ntx_by_subject = {} for subj in adsl: key = (subj["STUDYID"], subj["USUBJID"]) ntx_by_subject[key] = parse_date(subj["NTXSTDT"]) def usable(key, day): ntx = ntx_by_subject.get(key) return ntx is None or day <= ntx deaths = {} for row in ds: if row["DSDECOD"] != "DEATH": continue dthdt = parse_date(row["DSDTC"]) if dthdt is None: continue key = (row["STUDYID"], row["USUBJID"]) if not usable(key, dthdt): continue candidate = (dthdt, int(row["DSSEQ"])) if key not in deaths or candidate < deaths[key]: deaths[key] = candidate progressions = {} last_adequate = {} for row in rs: if row["RSTESTCD"] != "OVRLRESP" or row["ADEQFL"] != "Y": continue adt = parse_date(row["RSDTC"]) if adt is None: continue key = (row["STUDYID"], row["USUBJID"]) if not usable(key, adt): continue seq = int(row["RSSEQ"]) if row["RSSTRESC"] == "PD": candidate = (adt, seq) if key not in progressions or candidate < progressions[key]: progressions[key] = candidate latest = last_adequate.get(key) if latest is None or (adt, seq) > latest: last_adequate[key] = (adt, seq) out_rows = [] for subj in adsl: studyid = subj["STUDYID"] usubjid = subj["USUBJID"] key = (studyid, usubjid) startdt = parse_date(subj["RANDDT"]) prog = progressions.get(key) death = deaths.get(key) srcdom = srcvar = srcseq = "" if prog is not None and (death is None or prog[0] <= death[0]): adt = prog[0] cnsr = 0 evntdesc = "DISEASE PROGRESSION" srcdom, srcvar, srcseq = "RS", "RSDTC", prog[1] elif death is not None: adt = death[0] cnsr = 0 evntdesc = "DEATH" srcdom, srcvar, srcseq = "DS", "DSDTC", death[1] else: cnsr = 1 evntdesc = "CENSORED" last = last_adequate.get(key) if last is None: # No usable adequate assessment: censored at randomization. adt = startdt else: adt = last[0] srcdom, srcvar, srcseq = "RS", "RSDTC", last[1] aval = (adt - startdt).days + 1 out_rows.append( { "STUDYID": studyid, "USUBJID": usubjid, "PARAMCD": "PFS", "PARAM": "Progression-Free Survival", "STARTDT": startdt.isoformat(), "ADT": adt.isoformat(), "AVAL": aval, "CNSR": cnsr, "EVNTDESC": evntdesc, "SRCDOM": srcdom, "SRCVAR": srcvar, "SRCSEQ": srcseq, } ) fieldnames = [ "STUDYID", "USUBJID", "PARAMCD", "PARAM", "STARTDT", "ADT", "AVAL", "CNSR", "EVNTDESC", "SRCDOM", "SRCVAR", "SRCSEQ", ] with open(OUTPUT_PATH, "w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter(handle, fieldnames=fieldnames) writer.writeheader() writer.writerows(out_rows)if __name__ == "__main__": main()solutions/adam-adtte-pfs/result.sas
Edit52 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;
yamaa