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 SDTM: 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 one record per test from the wide vital signs
form collected at each visit, carrying VSTESTCD,
VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC,
VSSTRESU, VSPOS, VSMETHOD, VSSTAT, and VSREASND, plus
the visit name in VISIT and the measurement date in VSDTC.
Input: long-form Operational Data Model (ODM) rows, one row per collected form item, with the collected entry in the value field. Each visit contributes one vitals form group: the visit date, body position, temperature method, each measurement with its unit, and the blood pressure not-done flag with its reason, each as its own item.
Variables:
VSTESTCDis the test code:SYSBP,DIABP,PULSE,RESP,TEMP, orWEIGHT.VSTESTis the test name:Systolic Blood Pressure,Diastolic Blood Pressure,Pulse Rate,Respiratory Rate,Temperature, orWeight.VSORRESis the result exactly as collected on the form; blank when the measurement was not done.VSORRESUis the unit exactly as collected:mmHg,beats/min,breaths/min,C, orkg; blank when no result was collected.VSSTRESNis the numeric result in the standard unit; the form already collects standard units, so it equals the collected result; missing when no result was collected.VSSTRESCis the same standardized value written as text, so it always agrees with the numeric result; blank when no result was collected.VSSTRESUis the standard unit, equal to the collected unit; blank when no result was collected.VSPOSis the body position as collected on the form (SITTING) on blood pressure and pulse records with a collected result; blank otherwise.VSMETHODis the measurement method as collected on the form (ORAL) on temperature records with a collected result; blank otherwise.VSSTATisNOT DONEwhen the form flags blood pressure as not done; blank otherwise.VSREASNDis the reason the form gives for blood pressure not done; blank otherwise.
Note: each visit's form group fans out into one record per test.
VSSEQ numbers them per subject by visit date, then in test order
within each visit. A not-done blood pressure still produces its two records
so the reason is kept, but they carry no result, unit, position,
or standardized value.
Inputs
Under review2 source filesinput/odm.csv
Edit58 rows| StudyOID | MetaDataVersionOID | SubjectKey | StudyEventOID | StudyEventRepeatKey | FormOID | FormRepeatKey | ItemGroupOID | ItemGroupRepeatKey | ItemOID | Value |
|---|---|---|---|---|---|---|---|---|---|---|
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-01-05 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPMETHOD | ORAL |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBP | 118 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABP | 76 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 68 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 14 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.6 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 70.2 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-02-02 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPMETHOD | ORAL |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 72 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 16 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.8 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 70.5 |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.BPNOTDONE | Y |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.BPREASND | SUBJECT DECLINED |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-01-06 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPMETHOD | ORAL |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBP | 124 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABP | 80 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 71 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 15 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.9 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 82.4 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.VSDTC | 2026-02-03 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.POSITION | SITTING |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPMETHOD | ORAL |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBP | 120 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.SYSBPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABP | 78 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.DIABPU | mmHg |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSE | 69 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.PULSEU | beats/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESP | 14 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.RESPU | breaths/min |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMP | 36.7 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.TEMPU | C |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHT | 81.6 |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | WEEK 4 | 1 | FO.VS | 1 | IG.VS | 1 | IT.VS.WEIGHTU | kg |
input/vs_tests.csv
Edit6 rows| VSTESTCD | VSTEST | TESTORD | HAS_POSITION | HAS_METHOD | HAS_NOTDONE |
|---|---|---|---|---|---|
| SYSBP | Systolic Blood Pressure | 1 | 1 | 0 | 1 |
| DIABP | Diastolic Blood Pressure | 2 | 1 | 0 | 1 |
| PULSE | Pulse Rate | 3 | 1 | 0 | 0 |
| RESP | Respiratory Rate | 4 | 0 | 0 | 0 |
| TEMP | Temperature | 5 | 0 | 1 | 0 |
| WEIGHT | Weight | 6 | 0 | 0 | 0 |
Expected output
Under review24 expected rowsexpected/vs.csv
Edit24 rows| DOMAIN | STUDYID | USUBJID | VSSEQ | VISIT | VSTESTCD | VSTEST | VSORRES | VSORRESU | VSSTRESN | VSSTRESC | VSSTRESU | VSPOS | VSMETHOD | VSSTAT | VSREASND | VSDTC |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| VS | YAMAA-01 | 1 | SCREENING | SYSBP | Systolic Blood Pressure | 118 | mmHg | 118 | 118 | mmHg | SITTING | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 2 | SCREENING | DIABP | Diastolic Blood Pressure | 76 | mmHg | 76 | 76 | mmHg | SITTING | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 3 | SCREENING | PULSE | Pulse Rate | 68 | beats/min | 68 | 68 | beats/min | SITTING | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 4 | SCREENING | RESP | Respiratory Rate | 14 | breaths/min | 14 | 14 | breaths/min | — | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 5 | SCREENING | TEMP | Temperature | 36.6 | C | 36.6 | 36.6 | C | — | ORAL | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 6 | SCREENING | WEIGHT | Weight | 70.2 | kg | 70.2 | 70.2 | kg | — | — | — | — | 2026-01-05 | |
| VS | YAMAA-01 | 7 | WEEK 4 | SYSBP | Systolic Blood Pressure | — | — | — | — | — | — | — | NOT DONE | SUBJECT DECLINED | 2026-02-02 | |
| VS | YAMAA-01 | 8 | WEEK 4 | DIABP | Diastolic Blood Pressure | — | — | — | — | — | — | — | NOT DONE | SUBJECT DECLINED | 2026-02-02 | |
| VS | YAMAA-01 | 9 | WEEK 4 | PULSE | Pulse Rate | 72 | beats/min | 72 | 72 | beats/min | SITTING | — | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 10 | WEEK 4 | RESP | Respiratory Rate | 16 | breaths/min | 16 | 16 | breaths/min | — | — | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 11 | WEEK 4 | TEMP | Temperature | 36.8 | C | 36.8 | 36.8 | C | — | ORAL | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 12 | WEEK 4 | WEIGHT | Weight | 70.5 | kg | 70.5 | 70.5 | kg | — | — | — | — | 2026-02-02 | |
| VS | YAMAA-01 | 1 | SCREENING | SYSBP | Systolic Blood Pressure | 124 | mmHg | 124 | 124 | mmHg | SITTING | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 2 | SCREENING | DIABP | Diastolic Blood Pressure | 80 | mmHg | 80 | 80 | mmHg | SITTING | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 3 | SCREENING | PULSE | Pulse Rate | 71 | beats/min | 71 | 71 | beats/min | SITTING | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 4 | SCREENING | RESP | Respiratory Rate | 15 | breaths/min | 15 | 15 | breaths/min | — | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 5 | SCREENING | TEMP | Temperature | 36.9 | C | 36.9 | 36.9 | C | — | ORAL | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 6 | SCREENING | WEIGHT | Weight | 82.4 | kg | 82.4 | 82.4 | kg | — | — | — | — | 2026-01-06 | |
| VS | YAMAA-01 | 7 | WEEK 4 | SYSBP | Systolic Blood Pressure | 120 | mmHg | 120 | 120 | mmHg | SITTING | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 8 | WEEK 4 | DIABP | Diastolic Blood Pressure | 78 | mmHg | 78 | 78 | mmHg | SITTING | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 9 | WEEK 4 | PULSE | Pulse Rate | 69 | beats/min | 69 | 69 | beats/min | SITTING | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 10 | WEEK 4 | RESP | Respiratory Rate | 14 | breaths/min | 14 | 14 | breaths/min | — | — | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 11 | WEEK 4 | TEMP | Temperature | 36.7 | C | 36.7 | 36.7 | C | — | ORAL | — | — | 2026-02-03 | |
| VS | YAMAA-01 | 12 | WEEK 4 | WEIGHT | Weight | 81.6 | kg | 81.6 | 81.6 | kg | — | — | — | — | 2026-02-03 |
Comments and assessment materials
| 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 "VS". | 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 | Copy value from ODM.StudyEventOID. | SDTM | 4 | ||||
| VSDTC | Date/Time of Measurements | Char | ISO 8601 | Collected | Read collected ODM item IT.VS.VSDTC; no identified record yields blank. | SDTM | 5 | |||
| VSTESTCD | Vital Signs Test Short Name | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 6 | ||||
| VSTEST | Vital Signs Test Name | Char | Derived | Take VSTEST from the TESTS record matching on VSTESTCD, and a row with no match is an error. | SDTM | 7 | ||||
| VSORRES | Result or Finding in Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 8 | ||||
| VSORRESU | Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 9 | ||||
| VSSTRESN | Numeric Result/Finding in Standard Units | Num | Derived | Copy value from VSORRES; a value that cannot be converted → blank. | SDTM | 10 | ||||
| VSSTRESC | Character Result/Finding in Std Format | Char | Derived | Copy value from VSSTRESN. | SDTM | 11 | ||||
| VSSTRESU | Standard Units | Char | Derived | Copy value from VSORRESU. | SDTM | 12 | ||||
| VSPOS | Position of Subject | Char | Derived | If HAS_POSITION = 1 AND VSORRES IS NOT NULL then "POSITION"; otherwise blank. | SDTM | 13 | ||||
| VSMETHOD | Method of Test or Examination | Char | Derived | If HAS_METHOD = 1 AND VSORRES IS NOT NULL then "TEMPMETHOD"; otherwise blank. | SDTM | 14 | ||||
| VSSTAT | Completion Status | Char | Derived | If HAS_NOTDONE = 1 AND BPNOTDONE = 'Y' then "NOT DONE"; otherwise blank. | SDTM | 15 | ||||
| VSREASND | Reason Not Done | Char | Derived | If HAS_NOTDONE = 1 AND BPNOTDONE = 'Y' then "BPREASND"; otherwise blank. | SDTM | 16 | ||||
| VSSEQ | Sequence Number | Num | Derived | Row number within each group of STUDYID, USUBJID, ordered by VSDTC then TESTORD. | SDTM | 17 |
| Template | Include when | VSTESTCD | VSORRES | VSORRESU |
|---|---|---|---|---|
| sysbp | Constant "SYSBP" | Read collected ODM item IT.VS.SYSBP; no identified record yields blank | Read collected ODM item IT.VS.SYSBPU; no identified record yields blank | |
| diabp | Constant "DIABP" | Read collected ODM item IT.VS.DIABP; no identified record yields blank | Read collected ODM item IT.VS.DIABPU; no identified record yields blank | |
| pulse | Constant "PULSE" | Read collected ODM item IT.VS.PULSE; no identified record yields blank | Read collected ODM item IT.VS.PULSEU; no identified record yields blank | |
| resp | Constant "RESP" | Read collected ODM item IT.VS.RESP; no identified record yields blank | Read collected ODM item IT.VS.RESPU; no identified record yields blank | |
| temp | Constant "TEMP" | Read collected ODM item IT.VS.TEMP; no identified record yields blank | Read collected ODM item IT.VS.TEMPU; no identified record yields blank | |
| weight | Constant "WEIGHT" | Read collected ODM item IT.VS.WEIGHT; no identified record yields blank | Read collected ODM item IT.VS.WEIGHTU; no identified record yields blank |
| 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/sdtm-vs-collected-form/full.md
Edit38 linesFollowing CDISC SDTM standards, use the provided ODM and TESTS datasets to create a VS dataset with one record per test from the wide vital signs form collected at each visit.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSMETHOD, VSSTAT, VSREASND, VSDTC
STUDYID is the ODM StudyOID and USUBJID is the SubjectKey, each as given.
VSSEQ numbers each subject's records by visit date, then in test order within each visit. VISIT is the visit name and VSDTC the measurement date. VSTESTCD is SYSBP, DIABP, PULSE, RESP, TEMP, or WEIGHT; VSTEST is Systolic Blood Pressure, Diastolic Blood Pressure, Pulse Rate, Respiratory Rate, Temperature, or Weight.
VSORRES is the result exactly as collected on the form, and has no value when the measurement was not done. VSORRESU is the unit exactly as collected (mmHg, beats/min, breaths/min, C, or kg), and has no value when no result was collected. VSSTRESN is the numeric result in the standard unit; the form already collects standard units, so it equals the collected result, and has no value when no result was collected. VSSTRESC is the same standardized value written as text, so it always agrees with the numeric result; a whole number is written without a decimal point. VSSTRESU is the standard unit, equal to the collected unit, and has no value when no result was collected. VSPOS is the body position as collected (SITTING) on blood pressure and pulse records with a collected result, and has no value otherwise. VSMETHOD is the measurement method as collected (ORAL) on temperature records with a collected result, and has no value otherwise. VSSTAT is NOT DONE when the form flags blood pressure as not done, and has no value otherwise; VSREASND is the reason the form gives, and has no value otherwise.
A not-done blood pressure still produces its two records so the reason is kept, but they carry no result, unit, position, or standardized value.
Read the source datasets from /app/input and save the completed dataset as /app/output/vs.csv.
prompts/sdtm-vs-collected-form/conventions.md
Edit18 linesFollowing CDISC SDTM standards, use the provided ODM and TESTS datasets to create a VS dataset with one record per test from the wide vital signs form collected at each visit.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSMETHOD, VSSTAT, VSREASND, VSDTC
VSTESTCD is SYSBP, DIABP, PULSE, RESP, TEMP, or WEIGHT; VSTEST is Systolic Blood Pressure, Diastolic Blood Pressure, Pulse Rate, Respiratory Rate, Temperature, or Weight.
In VSSTRESC a whole number is written without a decimal point. VSPOS is SITTING or has no value. VSMETHOD is ORAL or has no value. VSSTAT is NOT DONE or has no value.
Read the source datasets from /app/input and save the completed dataset as /app/output/vs.csv.
prompts/sdtm-vs-collected-form/brief.md
Edit10 linesFollowing CDISC SDTM standards, use the provided ODM and TESTS datasets to create a VS dataset with one record per test from the wide vital signs form collected at each visit.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSMETHOD, VSSTAT, VSREASND, VSDTC
Read the source datasets from /app/input and save the completed dataset as /app/output/vs.csv.
run.R
Edit4 lineslibrary(yamaa)vs <- yamaa_domain("spec.yaml")$outputvsrun.py
Edit4 linesimport yamaavs = yamaa.yamaa_domain("spec.yaml").outputvssolutions/sdtm-vs-collected-form/result.R
Edit113 lines# Reference solution for the yamaa benchmark sdtm-vs-collected-form (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(purrr)library(readr)library(tidyr)fmt_text <- function(x) { if (is.na(x)) { return(NA_character_) } if (x == floor(x)) { formatC(x, format = "f", digits = 0) } else { format(x, scientific = FALSE, trim = TRUE, digits = 15) }}odm <- read_csv( "/app/input/odm.csv", col_types = cols(.default = col_character()))tests <- read_csv( "/app/input/vs_tests.csv", col_types = cols(TESTORD = col_integer(), .default = col_character()))wide <- odm |> select(StudyOID, SubjectKey, StudyEventOID, ItemOID, Value) |> pivot_wider(names_from = ItemOID, values_from = Value, values_fn = first)rows <- list()for (i in seq_len(nrow(wide))) { visit <- wide[i, ] vsdtc <- visit$IT.VS.VSDTC position <- visit$IT.VS.POSITION method <- visit$IT.VS.TEMPMETHOD not_done <- !is.na(visit$IT.VS.BPNOTDONE) && visit$IT.VS.BPNOTDONE == "Y" reason <- visit$IT.VS.BPREASND for (j in seq_len(nrow(tests))) { test <- tests[j, ] testcd <- test$VSTESTCD result_item <- paste0("IT.VS.", testcd) unit_item <- paste0("IT.VS.", testcd, "U") result <- if (result_item %in% names(visit)) visit[[result_item]] else NA_character_ unit <- if (unit_item %in% names(visit)) visit[[unit_item]] else NA_character_ has_result <- !is.na(result) && result != "" if (testcd %in% c("SYSBP", "DIABP") && not_done) { has_result <- FALSE } if (!has_result) { orres <- NA_character_ orresu <- NA_character_ stresn <- NA_real_ stresc <- NA_character_ stresu <- NA_character_ pos <- NA_character_ meth <- NA_character_ } else { orres <- result orresu <- unit stresn <- suppressWarnings(as.numeric(result)) stresc <- fmt_text(stresn) stresu <- unit pos <- if (test$HAS_POSITION == "1") position else NA_character_ meth <- if (test$HAS_METHOD == "1") method else NA_character_ } if (test$HAS_NOTDONE == "1" && not_done) { stat <- "NOT DONE" reasnd <- reason } else { stat <- NA_character_ reasnd <- NA_character_ } rows[[length(rows) + 1]] <- tibble( STUDYID = visit$StudyOID, USUBJID = visit$SubjectKey, VISIT = visit$StudyEventOID, VSTESTCD = testcd, VSTEST = test$VSTEST, TESTORD = test$TESTORD, VSORRES = orres, VSORRESU = orresu, VSSTRESN = stresn, VSSTRESC = stresc, VSSTRESU = stresu, VSPOS = pos, VSMETHOD = meth, VSSTAT = stat, VSREASND = reasnd, VSDTC = vsdtc ) }}vs <- bind_rows(rows) |> arrange(USUBJID, VSDTC, TESTORD) |> group_by(USUBJID) |> mutate(VSSEQ = row_number()) |> ungroup() |> arrange(USUBJID, VSSEQ) |> transmute( DOMAIN = "VS", STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSMETHOD, VSSTAT, VSREASND, VSDTC )dir.create("/app/output", showWarnings = FALSE)write_csv(vs, "/app/output/vs.csv", na = "")solutions/sdtm-vs-collected-form/result.py
Edit140 lines# Reference solution for the yamaa benchmark sdtm-vs-collected-form (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 fmt_text(x: float | None) -> str | None: if x is None: return None import math if isinstance(x, float) and (math.isnan(x) or math.isinf(x)): return None if float(x).is_integer(): return str(int(float(x))) return f"{float(x):.15g}"odm = pl.read_csv("/app/input/odm.csv", infer_schema=False)tests = pl.read_csv("/app/input/vs_tests.csv", infer_schema=False).with_columns( pl.col("TESTORD").cast(pl.Int64),)test_list = sorted(tests.to_dicts(), key=lambda r: r["TESTORD"])# One wide form per subject per visit: its date, position, method, not-done# flag, reason, and each test's result and unit.visits: dict[tuple[str, str], dict] = {}for row in odm.to_dicts(): key = (row["SubjectKey"], row["StudyEventOID"]) entry = visits.setdefault( key, { "study": row["StudyOID"], "subject": row["SubjectKey"], "visit": row["StudyEventOID"], "items": {}, }, ) entry["items"][row["ItemOID"]] = row["Value"]records = []for entry in visits.values(): items = entry["items"] vsdtc = items.get("IT.VS.VSDTC") position = items.get("IT.VS.POSITION") method = items.get("IT.VS.TEMPMETHOD") not_done = items.get("IT.VS.BPNOTDONE") == "Y" reason = items.get("IT.VS.BPREASND") for test in test_list: testcd = test["VSTESTCD"] result_item = f"IT.VS.{testcd}" unit_item = f"IT.VS.{testcd}U" # WEIGHT unit in the form is IT.VS.WEIGHTU, which fits the pattern; # the same holds for every test. result = items.get(result_item) unit = items.get(unit_item) has_result = result is not None and result != "" if testcd in ("SYSBP", "DIABP") and not_done: has_result = False result = None unit = None if not has_result: orres = None orresu = None stresn = None stresc = None stresu = None pos = None meth = None else: orres = result orresu = unit stresn = float(result) stresc = fmt_text(stresn) stresu = unit pos = position if test["HAS_POSITION"] == "1" else None meth = method if test["HAS_METHOD"] == "1" else None if test["HAS_NOTDONE"] == "1" and not_done: stat = "NOT DONE" reasnd = reason else: stat = None reasnd = None records.append( { "DOMAIN": "VS", "STUDYID": entry["study"], "USUBJID": entry["subject"], "VISIT": entry["visit"], "VSTESTCD": testcd, "VSTEST": test["VSTEST"], "_testord": test["TESTORD"], "VSORRES": orres, "VSORRESU": orresu, "VSSTRESN": stresn, "VSSTRESC": stresc, "VSSTRESU": stresu, "VSPOS": pos, "VSMETHOD": meth, "VSSTAT": stat, "VSREASND": reasnd, "VSDTC": vsdtc, "_vsdtc": vsdtc or "", } )vs = ( pl.DataFrame(records) .sort(["USUBJID", "_vsdtc", "_testord"]) .with_columns(VSSEQ=pl.int_range(1, pl.len() + 1).over("USUBJID")) .sort(["USUBJID", "VSSEQ"]) .select( "DOMAIN", "STUDYID", "USUBJID", "VSSEQ", "VISIT", "VSTESTCD", "VSTEST", "VSORRES", "VSORRESU", "VSSTRESN", "VSSTRESC", "VSSTRESU", "VSPOS", "VSMETHOD", "VSSTAT", "VSREASND", "VSDTC", ))Path("/app/output").mkdir(exist_ok=True)vs.write_csv("/app/output/vs.csv")solutions/sdtm-vs-collected-form/result.sas
Edit50 lines/* opensas reference solution for the yamaa benchmark sdtm-vs-collected-form. 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;data vs_tests; length VSTESTCD VSTEST TESTORD HAS_POSITION HAS_METHOD HAS_NOTDONE $1024; infile "/app/input/vs_tests.csv" dsd dlm="," firstobs=2 truncover lrecl=32767; input VSTESTCD : $char1024. VSTEST : $char1024. TESTORD : $char1024. HAS_POSITION : $char1024. HAS_METHOD : $char1024. HAS_NOTDONE : $char1024.;run;proc sort data=odm out=ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey FormOID FormRepeatKey; run;data visits; length VSDTC POSITION TEMPMETHOD BPNOTDONE BPREASND $1024; retain VSDTC POSITION TEMPMETHOD BPNOTDONE BPREASND; set ordered; by StudyOID SubjectKey StudyEventOID StudyEventRepeatKey FormOID FormRepeatKey; if first.FormRepeatKey then do; VSDTC=''; POSITION=''; TEMPMETHOD=''; BPNOTDONE=''; BPREASND=''; end; if ItemOID='IT.VS.VSDTC' then VSDTC=Value; if ItemOID='IT.VS.POSITION' then POSITION=Value; if ItemOID='IT.VS.TEMPMETHOD' then TEMPMETHOD=Value; if ItemOID='IT.VS.BPNOTDONE' then BPNOTDONE=Value; if ItemOID='IT.VS.BPREASND' then BPREASND=Value; if last.FormRepeatKey then output;run;data tests; set vs_tests; length RESULTOID UNITOID $1024; RESULTOID=cats('IT.VS.',VSTESTCD); UNITOID=cats(RESULTOID,'U'); ORD=input(TESTORD,best32.); run;proc sql; create table grid as select a.*,b.* from visits a cross join tests b;create table joined as select a.*,b.Value as RESULTTEXT,c.Value as UNITTEXT from grid a left join odm b on a.StudyOID=b.StudyOID and a.SubjectKey=b.SubjectKey and a.StudyEventOID=b.StudyEventOID and a.StudyEventRepeatKey=b.StudyEventRepeatKey and a.FormOID=b.FormOID and a.FormRepeatKey=b.FormRepeatKey and a.RESULTOID=b.ItemOID left join odm c on a.StudyOID=c.StudyOID and a.SubjectKey=c.SubjectKey and a.StudyEventOID=c.StudyEventOID and a.StudyEventRepeatKey=c.StudyEventRepeatKey and a.FormOID=c.FormOID and a.FormRepeatKey=c.FormRepeatKey and a.UNITOID=c.ItemOID; quit;data prepared; set joined; length DOMAIN $2 STUDYID USUBJID VISIT VSORRES VSORRESU VSSTRESC VSSTRESU VSPOS VSMETHOD VSSTAT VSREASND $1024;DOMAIN='VS'; STUDYID=StudyOID; USUBJID=SubjectKey; VISIT=StudyEventOID; VSORRES=''; VSORRESU=''; VSSTRESN=.; VSSTRESC=''; VSSTRESU=''; VSPOS=''; VSMETHOD=''; VSSTAT=''; VSREASND='';if HAS_NOTDONE='1' and BPNOTDONE='Y' then do; VSSTAT='NOT DONE'; VSREASND=BPREASND; end;else if not missing(RESULTTEXT) then do; VSORRES=RESULTTEXT; VSORRESU=UNITTEXT; VSSTRESN=input(RESULTTEXT,best32.); VSSTRESC=strip(put(VSSTRESN,best15.)); VSSTRESU=UNITTEXT; if HAS_POSITION='1' then VSPOS=POSITION; if HAS_METHOD='1' then VSMETHOD=TEMPMETHOD; end; run;proc sort data=prepared; by STUDYID USUBJID VSDTC ORD; run;data result; set prepared; by STUDYID USUBJID; retain VSSEQ; if first.USUBJID then VSSEQ=0; VSSEQ+1;run;proc sql; create table final as select DOMAIN, STUDYID, USUBJID, VSSEQ, VISIT, VSTESTCD, VSTEST, VSORRES, VSORRESU, VSSTRESN, VSSTRESC, VSSTRESU, VSPOS, VSMETHOD, VSSTAT, VSREASND, VSDTC from result;quit;proc export data=final outfile="/app/output/vs.csv" dbms=csv replace; run;
yamaa
Comments
Signed in with GitHub; saved in repository Discussions