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: give each electrocardiogram (ECG) reading collected at a planned
time point one record per test, the QT interval as a number and the
overall interpretation as text, with the replicate number (EGREPNUM),
the planned time point (EGTPT, EGTPTNUM) and the result in standard
character form (EGSTRESC) on every record.
Input: long-form Operational Data Model (ODM) rows, one row per collected item. Each planned time point has its own ECG form, and each replicate ECG taken there is one repeat of the ECG item group, numbered in the item-group repeat key. A repeat holds the ECG's date and time, its QT interval in milliseconds, and the reader's interpretation.
Variables:
EGSEQnumbers every record of a subject in one sequence: by time point number, then replicate number, then test code (INTPbeforeQTAG).EGTESTCDandEGTESTname the test:QTAG, "QT Interval, Aggregate", for the interval andINTP, "Interpretation", for the interpretation.EGORRESis the result as collected: the interval in milliseconds, or the interpretation,NORMALorABNORMAL.EGORRESUismsecfor the interval and empty for the interpretation.EGSTRESCis the result in standard form, always as text, for both tests: the collected result, since the interval was collected in standard units and the interpretation in standard terms.EGSTRESNis the interval as a number; empty for the interpretation, which has no numeric result.EGSTRESUismsecfor the interval and empty for the interpretation.EGREPNUMis the item-group repeat key: it numbers the replicate ECGs of one time point, starting again at each time point.EGDTCis the date and time of the ECG, shared by both of its records; empty when the repeat holds none.EGTPTis the planned time point of the form:PRE-DOSEor2 H POST-DOSE.EGTPTNUMis the planned time point number: 1 before the dose and 2 after it.
Note: a result left blank on the form gives no record, while the other result of the same ECG keeps its record.
Inputs
Under review1 source fileinput/odm.csv
Edit36 rows| StudyOID | MetaDataVersionOID | SubjectKey | StudyEventOID | StudyEventRepeatKey | FormOID | FormRepeatKey | ItemGroupOID | ItemGroupRepeatKey | ItemOID | Value |
|---|---|---|---|---|---|---|---|---|---|---|
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 1 | IT.EG.EGDTC | 2026-03-02T07:50:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 1 | IT.EG.QT | 402 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 1 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 2 | IT.EG.EGDTC | 2026-03-02T07:52:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 2 | IT.EG.QT | 398 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 2 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 3 | IT.EG.EGDTC | 2026-03-02T07:54:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 3 | IT.EG.QT | 405 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 3 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 1 | IT.EG.EGDTC | 2026-03-02T10:00:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 1 | IT.EG.QT | 418 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 1 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 2 | IT.EG.EGDTC | 2026-03-02T10:02:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 2 | IT.EG.QT | 424 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 2 | IT.EG.INTP | ABNORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 3 | IT.EG.EGDTC | 2026-03-02T10:04:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 3 | IT.EG.QT | 421 |
| YAMAA-01 | MDV.1 | YAMAA-01-001 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 3 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 1 | IT.EG.EGDTC | 2026-03-03T07:46:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 1 | IT.EG.QT | 386 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 1 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 2 | IT.EG.EGDTC | 2026-03-03T07:48:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 2 | IT.EG.QT | 391 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 2 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 3 | IT.EG.EGDTC | 2026-03-03T07:50:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 3 | IT.EG.QT | 389 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_PREDOSE | 1 | IG.EG | 3 | IT.EG.INTP | NORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 1 | IT.EG.EGDTC | 2026-03-03T10:01:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 1 | IT.EG.QT | 437 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 1 | IT.EG.INTP | ABNORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 2 | IT.EG.EGDTC | 2026-03-03T10:03:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 2 | IT.EG.QT | — |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 2 | IT.EG.INTP | ABNORMAL |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 3 | IT.EG.EGDTC | 2026-03-03T10:05:00 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 3 | IT.EG.QT | 433 |
| YAMAA-01 | MDV.1 | YAMAA-01-002 | SE.DAY1 | 1 | FO.EG_2H | 1 | IG.EG | 3 | IT.EG.INTP | ABNORMAL |
Expected output
Under review23 expected rowsexpected/eg.csv
Edit23 rows| DOMAIN | STUDYID | USUBJID | EGSEQ | EGTESTCD | EGTEST | EGORRES | EGORRESU | EGSTRESC | EGSTRESN | EGSTRESU | EGREPNUM | VISIT | EGDTC | EGTPT | EGTPTNUM |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| EG | YAMAA-01 | 1 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 1 | DAY 1 | 2026-03-02T07:50:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 2 | QTAG | QT Interval, Aggregate | 402 | msec | 402 | 402 | msec | 1 | DAY 1 | 2026-03-02T07:50:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 3 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 2 | DAY 1 | 2026-03-02T07:52:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 4 | QTAG | QT Interval, Aggregate | 398 | msec | 398 | 398 | msec | 2 | DAY 1 | 2026-03-02T07:52:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 5 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 3 | DAY 1 | 2026-03-02T07:54:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 6 | QTAG | QT Interval, Aggregate | 405 | msec | 405 | 405 | msec | 3 | DAY 1 | 2026-03-02T07:54:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 7 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 1 | DAY 1 | 2026-03-02T10:00:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 8 | QTAG | QT Interval, Aggregate | 418 | msec | 418 | 418 | msec | 1 | DAY 1 | 2026-03-02T10:00:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 9 | INTP | Interpretation | ABNORMAL | — | ABNORMAL | — | — | 2 | DAY 1 | 2026-03-02T10:02:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 10 | QTAG | QT Interval, Aggregate | 424 | msec | 424 | 424 | msec | 2 | DAY 1 | 2026-03-02T10:02:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 11 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 3 | DAY 1 | 2026-03-02T10:04:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 12 | QTAG | QT Interval, Aggregate | 421 | msec | 421 | 421 | msec | 3 | DAY 1 | 2026-03-02T10:04:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 1 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 1 | DAY 1 | 2026-03-03T07:46:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 2 | QTAG | QT Interval, Aggregate | 386 | msec | 386 | 386 | msec | 1 | DAY 1 | 2026-03-03T07:46:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 3 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 2 | DAY 1 | 2026-03-03T07:48:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 4 | QTAG | QT Interval, Aggregate | 391 | msec | 391 | 391 | msec | 2 | DAY 1 | 2026-03-03T07:48:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 5 | INTP | Interpretation | NORMAL | — | NORMAL | — | — | 3 | DAY 1 | 2026-03-03T07:50:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 6 | QTAG | QT Interval, Aggregate | 389 | msec | 389 | 389 | msec | 3 | DAY 1 | 2026-03-03T07:50:00 | PRE-DOSE | 1 | |
| EG | YAMAA-01 | 7 | INTP | Interpretation | ABNORMAL | — | ABNORMAL | — | — | 1 | DAY 1 | 2026-03-03T10:01:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 8 | QTAG | QT Interval, Aggregate | 437 | msec | 437 | 437 | msec | 1 | DAY 1 | 2026-03-03T10:01:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 9 | INTP | Interpretation | ABNORMAL | — | ABNORMAL | — | — | 2 | DAY 1 | 2026-03-03T10:03:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 10 | INTP | Interpretation | ABNORMAL | — | ABNORMAL | — | — | 3 | DAY 1 | 2026-03-03T10:05:00 | 2 H POST-DOSE | 2 | |
| EG | YAMAA-01 | 11 | QTAG | QT Interval, Aggregate | 433 | msec | 433 | 433 | msec | 3 | DAY 1 | 2026-03-03T10:05:00 | 2 H POST-DOSE | 2 |
Human review and assessment
| Variable Name | Variable Label | Type | Length | Controlled Terms or Format | Origin | Core | Conversion Definition | Variable Type | Variable Order | Comments for Define |
|---|---|---|---|---|---|---|---|---|---|---|
| DOMAIN | Domain Abbreviation | Char | Assigned | Set constant value "EG". | SDTM | 1 | ||||
| STUDYID | Study Identifier | Char | Collected | Copy value from ODM.StudyOID. | SDTM | 2 | ||||
| USUBJID | Unique Subject Identifier | Char | Collected | Copy value from ODM.SubjectKey. | SDTM | 3 | ||||
| VISIT | Visit Name | Char | Collected | Recode ODM.StudyEventOID: "SE.DAY1" → "DAY 1"; missing values stay missing; unlisted values are errors. | SDTM | 4 | ||||
| EGTPT | Planned Time Point Name | Char | Collected | Recode ODM.FormOID: "FO.EG_PREDOSE" → "PRE-DOSE", "FO.EG_2H" → "2 H POST-DOSE"; missing values stay missing; unlisted values are errors. | SDTM | 5 | ||||
| EGTPTNUM | Planned Time Point Number | Num | Collected | Recode ODM.FormOID: "FO.EG_PREDOSE" → "1", "FO.EG_2H" → "2"; missing values stay missing; unlisted values are errors. | SDTM | 6 | ||||
| EGREPNUM | Repetition Number | Num | Collected | Copy value from ODM.ItemGroupRepeatKey. | SDTM | 7 | ||||
| EGTESTCD | ECG Test or Examination Short Name | Char | QTAG, INTP | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 8 | |||
| EGTEST | ECG Test or Examination Name | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 9 | ||||
| EGORRES | Result or Finding in Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 10 | ||||
| EGORRESU | Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 11 | ||||
| EGSTRESC | Character Result/Finding in Std Format | Char | Derived | Copy value from EGORRES. | SDTM | 12 | ||||
| EGSTRESN | Numeric Result/Finding in Standard Units | Num | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 13 | ||||
| EGSTRESU | Standard Units | Char | Derived | Copy value from EGORRESU. | SDTM | 14 | ||||
| EGDTC | Date/Time of ECG | Char | ISO 8601 | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 15 | |||
| EGSEQ | Sequence Number | Num | Derived | Row number within each group of STUDYID, USUBJID, ordered by EGTPTNUM then EGREPNUM then EGTESTCD. | SDTM | 16 |
| Template | Include when | EGTESTCD | EGTEST | EGORRES | EGORRESU | EGSTRESN | EGDTC |
|---|---|---|---|---|---|---|---|
| qt-interval | EGORRES IS NOT NULL | Constant "QTAG" | Constant "QT Interval, Aggregate" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.EG.QT' | Constant "msec" | Copy EGORRES | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.EG.EGDTC' |
| interpretation | EGORRES IS NOT NULL | Constant "INTP" | Constant "Interpretation" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.EG.INTP' | Constant "None" | Constant "None" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.EG.EGDTC' |
| Variable | Permitted values |
|---|---|
| EGTESTCD | QTAG, INTP |
| 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/sdtm-eg-ecg-findings/full.md
Edit31 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an EG dataset with one record per test of each replicate ECG taken at a planned time point.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, EGSEQ, EGTESTCD, EGTEST, EGORRES, EGORRESU, EGSTRESC, EGSTRESN, EGSTRESU, EGREPNUM, VISIT, EGDTC, EGTPT, EGTPTNUM
DOMAIN is "EG". STUDYID is the ODM study OID and USUBJID is the ODM subject key, each as given. VISIT is "DAY 1", the visit the ECGs were taken at. Each planned time point has its own ECG form: EGTPT is "PRE-DOSE" with EGTPTNUM 1 on the pre-dose form, and "2 H POST-DOSE" with EGTPTNUM 2 on the two-hour post-dose form. Each replicate ECG taken at a time point is one repeat of the ECG item group, and EGREPNUM is that item-group repeat key: it numbers the replicate ECGs of one time point, starting again at each time point. Each ECG gives one record for its QT interval, with EGTESTCD "QTAG" and EGTEST "QT Interval, Aggregate", and one for its overall interpretation, with EGTESTCD "INTP" and EGTEST "Interpretation". A result left blank on the form gives no record, while the other result of the same ECG keeps its record. EGORRES is the result as collected: the interval in milliseconds, or the interpretation. EGSTRESC is the collected result as text for both tests, since the interval was collected in standard units and the interpretation in standard terms. EGSTRESN is the interval as a number and has no value for the interpretation. EGORRESU and EGSTRESU are "msec" for the interval and have no value for the interpretation. EGDTC is the ECG's date and time, from the same repeat, on both of its records. EGSEQ numbers each subject's records from 1 by time point number, then replicate number, then test code (INTP before QTAG).
Read the source datasets from /app/input and save the completed dataset as /app/output/eg.csv.
prompts/sdtm-eg-ecg-findings/conventions.md
Edit15 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an EG dataset with one record per test of each replicate ECG taken at a planned time point.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, EGSEQ, EGTESTCD, EGTEST, EGORRES, EGORRESU, EGSTRESC, EGSTRESN, EGSTRESU, EGREPNUM, VISIT, EGDTC, EGTPT, EGTPTNUM
DOMAIN is "EG". VISIT is "DAY 1". EGTPT is "PRE-DOSE" with EGTPTNUM 1, or "2 H POST-DOSE" with EGTPTNUM 2. EGTESTCD is "QTAG" with EGTEST "QT Interval, Aggregate", or "INTP" with EGTEST "Interpretation". EGORRESU and EGSTRESU are "msec" or have no value.
Read the source datasets from /app/input and save the completed dataset as /app/output/eg.csv.
prompts/sdtm-eg-ecg-findings/brief.md
Edit10 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an EG dataset with one record per test of each replicate ECG taken at a planned time point.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, EGSEQ, EGTESTCD, EGTEST, EGORRES, EGORRESU, EGSTRESC, EGSTRESN, EGSTRESU, EGREPNUM, VISIT, EGDTC, EGTPT, EGTPTNUM
Read the source datasets from /app/input and save the completed dataset as /app/output/eg.csv.
R
Edit3 lineslibrary(yamaa)yamaa_spec("spec.yaml")$run()$outputPython
Edit3 linesimport yamaayamaa.spec("spec.yaml").run().outputsolutions/sdtm-eg-ecg-findings/result.R
Edit67 lines# Reference solution for the yamaa benchmark sdtm-eg-ecg-findings (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)library(tidyr)odm <- read_csv( "/app/input/odm.csv", col_types = cols(.default = col_character()), na = "")# One replicate ECG is one repeat of the ECG item group on a time point's# form; its items become columns.ecgs <- odm |> filter(FormOID %in% c("FO.EG_PREDOSE", "FO.EG_2H")) |> select(StudyOID, SubjectKey, StudyEventOID, FormOID, ItemGroupRepeatKey, ItemOID, Value) |> pivot_wider(names_from = ItemOID, values_from = Value)# One record per test; a blank result gives no record.qt <- ecgs |> transmute(StudyOID, SubjectKey, StudyEventOID, FormOID, ItemGroupRepeatKey, EGDTC = IT.EG.EGDTC, EGTESTCD = "QTAG", EGTEST = "QT Interval, Aggregate", EGORRES = IT.EG.QT, EGORRESU = "msec")interpretation <- ecgs |> transmute(StudyOID, SubjectKey, StudyEventOID, FormOID, ItemGroupRepeatKey, EGDTC = IT.EG.EGDTC, EGTESTCD = "INTP", EGTEST = "Interpretation", EGORRES = IT.EG.INTP, EGORRESU = NA_character_)eg <- bind_rows(qt, interpretation) |> filter(!is.na(EGORRES), trimws(EGORRES) != "") |> mutate( DOMAIN = "EG", STUDYID = StudyOID, USUBJID = SubjectKey, EGSTRESC = EGORRES, EGSTRESN = if_else(EGTESTCD == "QTAG", suppressWarnings(as.numeric(EGORRES)), NA_real_), EGSTRESU = EGORRESU, EGREPNUM = as.integer(ItemGroupRepeatKey), VISIT = case_when(StudyEventOID == "SE.DAY1" ~ "DAY 1"), EGTPT = case_when( FormOID == "FO.EG_PREDOSE" ~ "PRE-DOSE", FormOID == "FO.EG_2H" ~ "2 H POST-DOSE" ), EGTPTNUM = case_when( FormOID == "FO.EG_PREDOSE" ~ 1L, FormOID == "FO.EG_2H" ~ 2L ) ) |> arrange(STUDYID, USUBJID, EGTPTNUM, EGREPNUM, EGTESTCD) |> group_by(STUDYID, USUBJID) |> mutate(EGSEQ = row_number()) |> ungroup() |> select(DOMAIN, STUDYID, USUBJID, EGSEQ, EGTESTCD, EGTEST, EGORRES, EGORRESU, EGSTRESC, EGSTRESN, EGSTRESU, EGREPNUM, VISIT, EGDTC, EGTPT, EGTPTNUM)dir.create("/app/output", showWarnings = FALSE)write_csv(eg, "/app/output/eg.csv", na = "")solutions/sdtm-eg-ecg-findings/result.py
Edit97 lines# Reference solution for the yamaa benchmark sdtm-eg-ecg-findings (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.import csvODM_CSV = "/app/input/odm.csv"OUTPUT_CSV = "/app/output/eg.csv"VISITS = {"SE.DAY1": "DAY 1"}TIME_POINTS = { "FO.EG_PREDOSE": ("PRE-DOSE", 1), "FO.EG_2H": ("2 H POST-DOSE", 2),}# Item OID: test code, test name, unit (None for a character result).TESTS = { "IT.EG.QT": ("QTAG", "QT Interval, Aggregate", "msec"), "IT.EG.INTP": ("INTP", "Interpretation", None),}DATETIME_ITEM = "IT.EG.EGDTC"COLUMNS = [ "DOMAIN", "STUDYID", "USUBJID", "EGSEQ", "EGTESTCD", "EGTEST", "EGORRES", "EGORRESU", "EGSTRESC", "EGSTRESN", "EGSTRESU", "EGREPNUM", "VISIT", "EGDTC", "EGTPT", "EGTPTNUM",]def main(): with open(ODM_CSV, newline="") as f: odm = list(csv.DictReader(f)) # One replicate ECG is one repeat of the ECG item group on a time point's # form; collect its items. ecgs = {} for row in odm: if row["FormOID"] not in TIME_POINTS: continue key = ( row["StudyOID"], row["SubjectKey"], row["StudyEventOID"], row["FormOID"], int(row["ItemGroupRepeatKey"]), ) ecgs.setdefault(key, {})[row["ItemOID"]] = row["Value"].strip() records = [] for (study, subject, event, form, repeat), items in ecgs.items(): tpt, tptnum = TIME_POINTS[form] for item, (testcd, test, unit) in TESTS.items(): result = items.get(item, "") if result == "": continue # a blank result gives no record records.append( { "DOMAIN": "EG", "STUDYID": study, "USUBJID": subject, "EGTESTCD": testcd, "EGTEST": test, "EGORRES": result, "EGORRESU": unit or "", "EGSTRESC": result, "EGSTRESN": result if unit else "", "EGSTRESU": unit or "", "EGREPNUM": repeat, "VISIT": VISITS[event], "EGDTC": items.get(DATETIME_ITEM, ""), "EGTPT": tpt, "EGTPTNUM": tptnum, } ) records.sort( key=lambda r: ( r["STUDYID"], r["USUBJID"], r["EGTPTNUM"], r["EGREPNUM"], r["EGTESTCD"], ) ) sequence = {} for record in records: subject = (record["STUDYID"], record["USUBJID"]) sequence[subject] = sequence.get(subject, 0) + 1 record["EGSEQ"] = sequence[subject] with open(OUTPUT_CSV, "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=COLUMNS) writer.writeheader() writer.writerows(records)if __name__ == "__main__": main()solutions/sdtm-eg-ecg-findings/result.sas
Edit60 lines/* opensas reference solution for the yamaa benchmark sdtm-eg-ecg-findings. Reads the benchmark inputs and derives the requested output. Execute with the existing opensas runtime: opensas /app/output/result.sas. */data odm; length StudyOID MetaDataVersionOID SubjectKey StudyEventOID StudyEventRepeatKey FormOID FormRepeatKey ItemGroupOID ItemGroupRepeatKey ItemOID Value $1024; infile "/app/input/odm.csv" dsd dlm="," firstobs=2 truncover lrecl=32767; input StudyOID : $char1024. MetaDataVersionOID : $char1024. SubjectKey : $char1024. StudyEventOID : $char1024. StudyEventRepeatKey : $char1024. FormOID : $char1024. FormRepeatKey : $char1024. ItemGroupOID : $char1024. ItemGroupRepeatKey : $char1024. ItemOID : $char1024. Value : $char1024.;run;/* One replicate ECG is one repeat of the ECG item group on a time point's form: collect its date and time, QT interval and interpretation. */proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID FormOID ItemGroupRepeatKey; run;data wide; length EGDTC QT INTP $1024; retain EGDTC QT INTP; set ordered; where FormOID in ('FO.EG_PREDOSE','FO.EG_2H'); by StudyOID SubjectKey StudyEventOID FormOID ItemGroupRepeatKey; if first.ItemGroupRepeatKey then do; EGDTC=''; QT=''; INTP=''; end; if ItemOID='IT.EG.EGDTC' then EGDTC=Value; if ItemOID='IT.EG.QT' then QT=Value; if ItemOID='IT.EG.INTP' then INTP=Value; if last.ItemGroupRepeatKey then output;run;/* One record per test; a blank result gives no record. */data prepared; set wide; length DOMAIN $2 STUDYID USUBJID EGTESTCD EGTEST EGORRES EGORRESU EGSTRESC EGSTRESU VISIT EGTPT $1024; DOMAIN='EG'; STUDYID=StudyOID; USUBJID=SubjectKey; VISIT=''; if StudyEventOID='SE.DAY1' then VISIT='DAY 1'; EGTPT=''; EGTPTNUM=.; if FormOID='FO.EG_PREDOSE' then do; EGTPT='PRE-DOSE'; EGTPTNUM=1; end; if FormOID='FO.EG_2H' then do; EGTPT='2 H POST-DOSE'; EGTPTNUM=2; end; EGREPNUM=input(ItemGroupRepeatKey,best32.); if not missing(QT) then do; EGTESTCD='QTAG'; EGTEST='QT Interval, Aggregate'; EGORRES=strip(QT); EGORRESU='msec'; EGSTRESC=EGORRES; EGSTRESN=input(EGORRES,best32.); EGSTRESU='msec'; output; end; if not missing(INTP) then do; EGTESTCD='INTP'; EGTEST='Interpretation'; EGORRES=strip(INTP); EGORRESU=''; EGSTRESC=EGORRES; EGSTRESN=.; EGSTRESU=''; output; end;run;proc sort data=prepared; by STUDYID USUBJID EGTPTNUM EGREPNUM EGTESTCD; run;data result; set prepared; by STUDYID USUBJID; retain EGSEQ; if first.USUBJID then EGSEQ=0; EGSEQ+1;run;proc sql; create table final as select DOMAIN, STUDYID, USUBJID, EGSEQ, EGTESTCD, EGTEST, EGORRES, EGORRESU, EGSTRESC, EGSTRESN, EGSTRESU, EGREPNUM, VISIT, EGDTC, EGTPT, EGTPTNUM from result;quit;proc export data=final outfile="/app/output/eg.csv" dbms=csv replace; run;
yamaa