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.
- 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.
- Inputs: Do they look like data collected in a real trial, and do they include the situations you would expect to meet in one?
- 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
README.mdGoal: 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 |
Comments and assessment materials
| 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.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
Edit47 linesFollowing 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
Edit19 linesFollowing 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
Edit10 linesFollowing 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.
run.R
Edit4 lineslibrary(yamaa)adtte <- yamaa_domain("spec.yaml")$outputadtterun.py
Edit4 linesimport yamaaadtte = yamaa.yamaa_domain("spec.yaml").outputadttesolutions/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
Edit174 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
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
Comments
Signed in with GitHub; saved in repository Discussions