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: one record per reported result from serum, skin-biopsy,
saliva, and tape-strip forms, carrying LBTESTCD, LBTEST,
LBCAT, LBSPEC, LBLOC, LBORRES, LBORRESU, LBSTRESC,
LBSTRESN, LBSTRESU, LBSTAT, VISITNUM, and LBDTC.
Input: Operational Data Model (ODM) extract in long form, one record per collected item, with the collected entry in the value field; each form also carries its collection date as a sibling item record.
Variables:
VISITNUMis the visit number:1screening,2baseline,3day 21; each unscheduled visit is numbered after the baseline visit per occurrence (2.01,2.02), so repeated unscheduled visits stay distinct.VISITis the matching visit name.LBTESTCDis the short test code for the collected item:VITD25OH,IL13, orCAMPPRO.LBTESTis the full test name for that code:25-Hydroxyvitamin D,Interleukin 13 mRNA, orCathelicidin Protein.LBCATis the test category for that code:CHEMISTRY,GENE EXPRESSION, orANTIMICROBIAL PEPTIDE.LBSPECis the specimen type of the form the result came from:SERUM,SKIN BIOPSY,SALIVA, orTAPE STRIP.LBLOCis the sampled site where the form distinguishes one:LESIONALorNON-LESIONALfor biopsy and tape-strip results; blank for serum and saliva results.LBORRESis the collected result, exactly as reported; blank when the test was not done.LBORRESUis the unit fixed for each form:CYCLEfor biopsy results andng/mLotherwise; blank when the test was not done.LBSTRESCrepeats the reported result in standard form; blank when the test was not done.LBSTRESNis the numeric form of the reported result; missing when the test was not done.LBSTRESUis the standard unit, the same asLBORRESU; blank when the test was not done.LBSTATisNOT DONEwhen the test was not done; blank otherwise.LBDTCis the collection date from the date item on the same form.
Note: an item with no reported value produces no record, while
a collected zero is kept as a real result; an entry of NOT DONE
still produces a record, with the result and unit fields left blank
and the status recorded. Each record keeps the date of the form it
came from, so a form collected twice at one visit keeps each
occurrence's own date.
Inputs
Under review1 source fileinput/odm.csv
Edit38 rows| StudyOID | MetaDataVersionOID | SubjectKey | StudyEventOID | StudyEventRepeatKey | ItemGroupOID | ItemGroupRepeatKey | ItemOID | Value |
|---|---|---|---|---|---|---|---|---|
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.SCRN | 1 | IG.NCT00789880.DM | 1 | IT.NCT00789880.DM.DIAGGRP | AD |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.SCRN | 1 | IG.NCT00789880.LB | 1 | IT.NCT00789880.LB.LBDTC | 2025-01-01 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.SCRN | 1 | IG.NCT00789880.LB | 1 | IT.NCT00789880.LB.VITD | 23.2 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.BXDTC | 2025-01-08 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.IL13_LES | 5.45 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.IL13_NONLES | 3.71 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SALDTC | 2025-01-08 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SAL_CAMP | 0 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TSDTC | 2025-01-08 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TS_CAMP_LES | 0.34 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.BL | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TS_CAMP_NONLES | 2.1 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.BXDTC | 2025-01-29 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.IL13_LES | 12.95 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.IL13_NONLES | 0 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SALDTC | 2025-01-29 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SAL_CAMP | NOT DONE |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TSDTC | 2025-01-29 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TS_CAMP_LES | 0 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TS_CAMP_NONLES | — |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.UNSCH | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SALDTC | 2025-01-15 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.UNSCH | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SAL_CAMP | 2.1 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.UNSCH | 1 | IG.NCT00789880.SAL | 2 | IT.NCT00789880.SAL.SALDTC | 2025-01-16 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0001 | SE.NCT00789880.UNSCH | 1 | IG.NCT00789880.SAL | 2 | IT.NCT00789880.SAL.SAL_CAMP | 2.3 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.SCRN | 1 | IG.NCT00789880.DM | 1 | IT.NCT00789880.DM.DIAGGRP | NONAD |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.SCRN | 1 | IG.NCT00789880.LB | 1 | IT.NCT00789880.LB.LBDTC | 2025-02-01 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.SCRN | 1 | IG.NCT00789880.LB | 1 | IT.NCT00789880.LB.VITD | 18.4 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.BL | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.BXDTC | 2025-02-08 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.BL | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.IL13_NONLES | 1.22 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.BL | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SALDTC | 2025-02-08 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.BL | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SAL_CAMP | — |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.BL | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TSDTC | 2025-02-08 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.BL | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TS_CAMP_NONLES | 4.49 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.BXDTC | 2025-03-01 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.BX | 1 | IT.NCT00789880.BX.IL13_NONLES | 1.87 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SALDTC | 2025-03-01 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.SAL | 1 | IT.NCT00789880.SAL.SAL_CAMP | 5.24 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TSDTC | 2025-03-01 |
| ST.NCT00789880 | MDV.NCT00789880.1 | CATH-UCSD-0002 | SE.NCT00789880.D21 | 1 | IG.NCT00789880.TS | 1 | IT.NCT00789880.TS.TS_CAMP_NONLES | 4.65 |
Expected output
Under review18 expected rowsexpected/lb.csv
Edit18 rows| DOMAIN | STUDYID | USUBJID | LBSEQ | VISIT | VISITNUM | LBTESTCD | LBTEST | LBCAT | LBSPEC | LBLOC | LBORRES | LBORRESU | LBSTRESC | LBSTRESN | LBSTRESU | LBSTAT | LBDTC |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| LB | ST.NCT00789880 | 1 | SCREENING | 1 | VITD25OH | 25-Hydroxyvitamin D | CHEMISTRY | SERUM | — | 23.2 | ng/mL | 23.2 | 23.2 | ng/mL | — | 2025-01-01 | |
| LB | ST.NCT00789880 | 1 | SCREENING | 1 | VITD25OH | 25-Hydroxyvitamin D | CHEMISTRY | SERUM | — | 18.4 | ng/mL | 18.4 | 18.4 | ng/mL | — | 2025-02-01 | |
| LB | ST.NCT00789880 | 5 | BASELINE | 2 | IL13 | Interleukin 13 mRNA | GENE EXPRESSION | SKIN BIOPSY | LESIONAL | 5.45 | CYCLE | 5.45 | 5.45 | CYCLE | — | 2025-01-08 | |
| LB | ST.NCT00789880 | 11 | DAY 21 | 3 | IL13 | Interleukin 13 mRNA | GENE EXPRESSION | SKIN BIOPSY | LESIONAL | 12.95 | CYCLE | 12.95 | 12.95 | CYCLE | — | 2025-01-29 | |
| LB | ST.NCT00789880 | 6 | BASELINE | 2 | IL13 | Interleukin 13 mRNA | GENE EXPRESSION | SKIN BIOPSY | NON-LESIONAL | 3.71 | CYCLE | 3.71 | 3.71 | CYCLE | — | 2025-01-08 | |
| LB | ST.NCT00789880 | 12 | DAY 21 | 3 | IL13 | Interleukin 13 mRNA | GENE EXPRESSION | SKIN BIOPSY | NON-LESIONAL | 0 | CYCLE | 0 | 0 | CYCLE | — | 2025-01-29 | |
| LB | ST.NCT00789880 | 3 | BASELINE | 2 | IL13 | Interleukin 13 mRNA | GENE EXPRESSION | SKIN BIOPSY | NON-LESIONAL | 1.22 | CYCLE | 1.22 | 1.22 | CYCLE | — | 2025-02-08 | |
| LB | ST.NCT00789880 | 6 | DAY 21 | 3 | IL13 | Interleukin 13 mRNA | GENE EXPRESSION | SKIN BIOPSY | NON-LESIONAL | 1.87 | CYCLE | 1.87 | 1.87 | CYCLE | — | 2025-03-01 | |
| LB | ST.NCT00789880 | 2 | BASELINE | 2 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | SALIVA | — | 0 | ng/mL | 0 | 0 | ng/mL | — | 2025-01-08 | |
| LB | ST.NCT00789880 | 9 | DAY 21 | 3 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | SALIVA | — | — | — | — | — | — | NOT DONE | 2025-01-29 | |
| LB | ST.NCT00789880 | 7 | UNSCHEDULED | 2.01 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | SALIVA | — | 2.1 | ng/mL | 2.1 | 2.1 | ng/mL | — | 2025-01-15 | |
| LB | ST.NCT00789880 | 8 | UNSCHEDULED | 2.02 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | SALIVA | — | 2.3 | ng/mL | 2.3 | 2.3 | ng/mL | — | 2025-01-16 | |
| LB | ST.NCT00789880 | 4 | DAY 21 | 3 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | SALIVA | — | 5.24 | ng/mL | 5.24 | 5.24 | ng/mL | — | 2025-03-01 | |
| LB | ST.NCT00789880 | 3 | BASELINE | 2 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | TAPE STRIP | LESIONAL | 0.34 | ng/mL | 0.34 | 0.34 | ng/mL | — | 2025-01-08 | |
| LB | ST.NCT00789880 | 10 | DAY 21 | 3 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | TAPE STRIP | LESIONAL | 0 | ng/mL | 0 | 0 | ng/mL | — | 2025-01-29 | |
| LB | ST.NCT00789880 | 4 | BASELINE | 2 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | TAPE STRIP | NON-LESIONAL | 2.1 | ng/mL | 2.1 | 2.1 | ng/mL | — | 2025-01-08 | |
| LB | ST.NCT00789880 | 2 | BASELINE | 2 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | TAPE STRIP | NON-LESIONAL | 4.49 | ng/mL | 4.49 | 4.49 | ng/mL | — | 2025-02-08 | |
| LB | ST.NCT00789880 | 5 | DAY 21 | 3 | CAMPPRO | Cathelicidin Protein | ANTIMICROBIAL PEPTIDE | TAPE STRIP | NON-LESIONAL | 4.65 | ng/mL | 4.65 | 4.65 | ng/mL | — | 2025-03-01 |
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 | LB | Assigned | Set constant value "LB". | 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 | SCREENING, BASELINE, DAY 21, UNSCHEDULED | Collected | Recode ODM.StudyEventOID: "SE.NCT00789880.SCRN" → "SCREENING", "SE.NCT00789880.BL" → "BASELINE", "SE.NCT00789880.D21" → "DAY 21", "SE.NCT00789880.UNSCH" → "UNSCHEDULED"; missing or unlisted values are errors. | SDTM | 4 | |||
| VISITNUM | Visit Number | Num | 1, 2, 3, 2.01, 2.02 | Derived | If ODM.StudyEventOID = 'SE.NCT00789880.SCRN' then "1"; If ODM.StudyEventOID = 'SE.NCT00789880.BL' then "2"; If ODM.StudyEventOID = 'SE.NCT00789880.D21' then "3"; If ODM.StudyEventOID = 'SE.NCT00789880.UNSCH' then compute 2 + FORMREPEAT / 100; otherwise blank. | SDTM | 5 | |||
| LBTESTCD | Lab Test or Examination Short Name | Char | VITD25OH, IL13, CAMPPRO | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 6 | |||
| LBTEST | Lab Test or Examination Name | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 7 | ||||
| LBCAT | Category for Lab Test | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 8 | ||||
| LBSPEC | Specimen Material Type | Char | SERUM, SKIN BIOPSY, SALIVA, TAPE STRIP | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 9 | |||
| LBLOC | Location Used for Measurement | Char | LESIONAL, NON-LESIONAL | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 10 | |||
| LBORRES | Result or Finding in Original Units | Char | Derived | If RAWORRES = 'NOT DONE' then "None"; otherwise "RAWORRES". | SDTM | 11 | ||||
| LBORRESU | Original Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 12 | ||||
| LBSTRESC | Character Result/Finding in Std Format | Char | Derived | Copy value from LBORRES. | SDTM | 13 | ||||
| LBSTRESN | Numeric Result/Finding in Standard Units | Num | Derived | Copy value from LBORRES; a value that cannot be converted → blank. | SDTM | 14 | ||||
| LBSTRESU | Standard Units | Char | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 15 | ||||
| LBSTAT | Completion Status | Char | NOT DONE | Derived | If RAWORRES = 'NOT DONE' then "NOT DONE"; otherwise blank. | SDTM | 16 | |||
| LBDTC | Date/Time of Specimen Collection | Char | ISO 8601 | Derived | Set per constructed row; see the Row construction sheet. | SDTM | 17 | |||
| LBSEQ | Sequence Number | Num | Derived | Row number within each group of STUDYID, USUBJID, ordered by LBDTC then LBTESTCD then LBSPEC. | SDTM | 18 |
| Template | Include when | LBTESTCD | LBTEST | LBCAT | LBSPEC | LBLOC | RAWORRES | LBORRESU | LBSTRESU | LBDTC |
|---|---|---|---|---|---|---|---|---|---|---|
| serum_vitamin_d | RAWORRES IS NOT NULL | Constant "VITD25OH" | Constant "25-Hydroxyvitamin D" | Constant "CHEMISTRY" | Constant "SERUM" | Constant "None" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.LB.VITD' | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.LB.LBDTC' |
| biopsy_il13_lesional | RAWORRES IS NOT NULL | Constant "IL13" | Constant "Interleukin 13 mRNA" | Constant "GENE EXPRESSION" | Constant "SKIN BIOPSY" | Constant "LESIONAL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.BX.IL13_LES' | If RAWORRES = 'NOT DONE' then "None"; otherwise "CYCLE" | If RAWORRES = 'NOT DONE' then "None"; otherwise "CYCLE" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.BX.BXDTC' |
| biopsy_il13_nonlesional | RAWORRES IS NOT NULL | Constant "IL13" | Constant "Interleukin 13 mRNA" | Constant "GENE EXPRESSION" | Constant "SKIN BIOPSY" | Constant "NON-LESIONAL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.BX.IL13_NONLES' | If RAWORRES = 'NOT DONE' then "None"; otherwise "CYCLE" | If RAWORRES = 'NOT DONE' then "None"; otherwise "CYCLE" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.BX.BXDTC' |
| saliva_cathelicidin | RAWORRES IS NOT NULL | Constant "CAMPPRO" | Constant "Cathelicidin Protein" | Constant "ANTIMICROBIAL PEPTIDE" | Constant "SALIVA" | Constant "None" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.SAL.SAL_CAMP' | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.SAL.SALDTC' |
| tape_cathelicidin_lesional | RAWORRES IS NOT NULL | Constant "CAMPPRO" | Constant "Cathelicidin Protein" | Constant "ANTIMICROBIAL PEPTIDE" | Constant "TAPE STRIP" | Constant "LESIONAL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.TS.TS_CAMP_LES' | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.TS.TSDTC' |
| tape_cathelicidin_nonlesional | RAWORRES IS NOT NULL | Constant "CAMPPRO" | Constant "Cathelicidin Protein" | Constant "ANTIMICROBIAL PEPTIDE" | Constant "TAPE STRIP" | Constant "NON-LESIONAL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.TS.TS_CAMP_NONLES' | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | If RAWORRES = 'NOT DONE' then "None"; otherwise "ng/mL" | Aggregate ONLY(ODM.Value) over records matching on the shared output keys, where ODM.ItemOID = 'IT.NCT00789880.TS.TSDTC' |
| Variable | Permitted values |
|---|---|
| DOMAIN | LB |
| VISIT | SCREENING, BASELINE, DAY 21, UNSCHEDULED |
| VISITNUM | 1, 2, 3, 2.01, 2.02 |
| LBTESTCD | VITD25OH, IL13, CAMPPRO |
| LBSPEC | SERUM, SKIN BIOPSY, SALIVA, TAPE STRIP |
| LBLOC | LESIONAL, NON-LESIONAL |
| LBSTAT | NOT DONE |
| 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-lb-multiform/full.md
Edit41 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an LB dataset with one record per reported result from the serum, skin-biopsy, saliva, and tape-strip forms.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, LBSEQ, VISIT, VISITNUM, LBTESTCD, LBTEST, LBCAT, LBSPEC, LBLOC, LBORRES, LBORRESU, LBSTRESC, LBSTRESN, LBSTRESU, LBSTAT, LBDTC
STUDYID is the ODM StudyOID and USUBJID is the SubjectKey, each as given.
LBSEQ numbers each subject's records from 1 by collection date, then test code and specimen; lesional results come before non-lesional ones from the same form. VISIT is SCREENING, BASELINE, DAY 21, or UNSCHEDULED for the SCRN, BL, D21, and UNSCH study events, and VISITNUM is 1, 2, and 3 for the planned visits. An unscheduled visit is numbered after the baseline visit by its form occurrence (the item group repeat key): 2.01 for the first occurrence and 2.02 for the second.
LBTESTCD is VITD25OH, IL13, or CAMPPRO. LBTEST is 25-Hydroxyvitamin D, Interleukin 13 mRNA, or Cathelicidin Protein. LBCAT is CHEMISTRY, GENE EXPRESSION, or ANTIMICROBIAL PEPTIDE. LBSPEC is SERUM, SKIN BIOPSY, SALIVA, or TAPE STRIP. LBLOC is LESIONAL or NON-LESIONAL for biopsy and tape-strip results, and has no value for serum and saliva results.
LBORRES is the collected result, exactly as reported, and has no value when the test was not done. LBORRESU is CYCLE for biopsy results and ng/mL otherwise, and has no value when the test was not done. LBSTRESC repeats the reported result exactly as LBORRES holds it; LBSTRESN is its numeric form; and LBSTRESU repeats the unit. All three have no value when the test was not done. LBSTAT is NOT DONE when the test was not done, and has no value otherwise. LBDTC is the collection date from the date item on the same form.
An item with no reported value produces no record, while a collected zero is kept as a real result. An entry of NOT DONE still produces a record, with no value in the result and unit fields. A form collected twice at one visit keeps each occurrence's own date.
Read the source datasets from /app/input and save the completed dataset as /app/output/lb.csv.
prompts/sdtm-lb-multiform/conventions.md
Edit19 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an LB dataset with one record per reported result from the serum, skin-biopsy, saliva, and tape-strip forms.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, LBSEQ, VISIT, VISITNUM, LBTESTCD, LBTEST, LBCAT, LBSPEC, LBLOC, LBORRES, LBORRESU, LBSTRESC, LBSTRESN, LBSTRESU, LBSTAT, LBDTC
VISIT is SCREENING, BASELINE, DAY 21, or UNSCHEDULED for the SCRN, BL, D21, and UNSCH study events. VISITNUM is 1, 2, and 3 for the planned visits, and 2.01 and 2.02 for the first and second unscheduled visit after baseline. LBTESTCD is VITD25OH, IL13, or CAMPPRO; LBTEST is 25-Hydroxyvitamin D, Interleukin 13 mRNA, or Cathelicidin Protein; LBCAT is CHEMISTRY, GENE EXPRESSION, or ANTIMICROBIAL PEPTIDE. LBSPEC is SERUM, SKIN BIOPSY, SALIVA, or TAPE STRIP. LBLOC is LESIONAL or NON-LESIONAL, or has no value.
Read the source datasets from /app/input and save the completed dataset as /app/output/lb.csv.
prompts/sdtm-lb-multiform/brief.md
Edit11 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create an LB dataset with one record per reported result from the serum, skin-biopsy, saliva, and tape-strip forms.
The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, LBSEQ, VISIT, VISITNUM, LBTESTCD, LBTEST, LBCAT, LBSPEC, LBLOC, LBORRES, LBORRESU, LBSTRESC, LBSTRESN, LBSTRESU, LBSTAT, LBDTC
Read the source datasets from /app/input and save the completed dataset as /app/output/lb.csv.
run.R
Edit4 lineslibrary(yamaa)lb <- yamaa_domain("spec.yaml")$outputlbrun.py
Edit4 linesimport yamaalb = yamaa.yamaa_domain("spec.yaml").outputlbsolutions/sdtm-lb-multiform/result.R
Edit89 lines# Reference solution for the yamaa benchmark sdtm-lb-multiform (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()))test_meta <- tribble( ~short, ~LBTESTCD, ~LBTEST, ~LBCAT, ~LBSPEC, ~LBLOC, ~unit, "VITD", "VITD25OH", "25-Hydroxyvitamin D", "CHEMISTRY", "SERUM", NA_character_, "ng/mL", "IL13_LES", "IL13", "Interleukin 13 mRNA", "GENE EXPRESSION", "SKIN BIOPSY", "LESIONAL", "CYCLE", "IL13_NONLES", "IL13", "Interleukin 13 mRNA", "GENE EXPRESSION", "SKIN BIOPSY", "NON-LESIONAL", "CYCLE", "SAL_CAMP", "CAMPPRO", "Cathelicidin Protein", "ANTIMICROBIAL PEPTIDE", "SALIVA", NA_character_, "ng/mL", "TS_CAMP_LES", "CAMPPRO", "Cathelicidin Protein", "ANTIMICROBIAL PEPTIDE", "TAPE STRIP", "LESIONAL", "ng/mL", "TS_CAMP_NONLES", "CAMPPRO", "Cathelicidin Protein", "ANTIMICROBIAL PEPTIDE", "TAPE STRIP", "NON-LESIONAL", "ng/mL")wide <- odm |> mutate( short = sub(".*\\.", "", ItemOID), is_date = grepl("DTC$", short) )dates <- wide |> filter(is_date, !is.na(Value) & Value != "") |> select( StudyOID, SubjectKey, StudyEventOID, ItemGroupOID, ItemGroupRepeatKey, LBDTC = Value )results <- wide |> filter(!is_date, short %in% test_meta$short, !is.na(Value) & Value != "") |> left_join(dates, by = c( "StudyOID", "SubjectKey", "StudyEventOID", "ItemGroupOID", "ItemGroupRepeatKey" ) ) |> left_join(test_meta, by = "short") |> mutate( event_short = sub(".*\\.", "", StudyEventOID), VISIT = case_when( event_short == "SCRN" ~ "SCREENING", event_short == "BL" ~ "BASELINE", event_short == "D21" ~ "DAY 21", event_short == "UNSCH" ~ "UNSCHEDULED" ), VISITNUM = case_when( event_short == "SCRN" ~ 1, event_short == "BL" ~ 2, event_short == "D21" ~ 3, event_short == "UNSCH" ~ 2 + as.numeric(ItemGroupRepeatKey) / 100, TRUE ~ NA_real_ ), not_done = Value == "NOT DONE", LBORRES = if_else(not_done, NA_character_, Value), LBORRESU = if_else(not_done, NA_character_, unit), LBSTRESC = if_else(not_done, NA_character_, Value), LBSTRESN = suppressWarnings(as.numeric(if_else(not_done, NA_character_, Value))), LBSTRESU = if_else(not_done, NA_character_, unit), LBSTAT = if_else(not_done, "NOT DONE", NA_character_), loc_rank = case_when( LBLOC == "LESIONAL" ~ 0L, LBLOC == "NON-LESIONAL" ~ 1L, TRUE ~ 2L ) ) |> arrange(SubjectKey, LBDTC, LBTESTCD, LBSPEC, loc_rank) |> group_by(SubjectKey) |> mutate(LBSEQ = row_number()) |> ungroup() |> mutate(DOMAIN = "LB") |> arrange(SubjectKey, LBSEQ) |> select( DOMAIN, STUDYID = StudyOID, USUBJID = SubjectKey, LBSEQ, VISIT, VISITNUM, LBTESTCD, LBTEST, LBCAT, LBSPEC, LBLOC, LBORRES, LBORRESU, LBSTRESC, LBSTRESN, LBSTRESU, LBSTAT, LBDTC )dir.create("/app/output", showWarnings = FALSE)write_csv(results, "/app/output/lb.csv", na = "")solutions/sdtm-lb-multiform/result.py
Edit173 lines# Reference solution for the yamaa benchmark sdtm-lb-multiform (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 _present(value) -> bool: return value is not None and str(value) != ""odm = pl.read_csv("/app/input/odm.csv", infer_schema=False)# Each form occurrence keeps its own date; lesional comes before# non-lesional from the same form.test_meta = { "VITD": ("VITD25OH", "25-Hydroxyvitamin D", "CHEMISTRY", "SERUM", None, "ng/mL"), "IL13_LES": ("IL13", "Interleukin 13 mRNA", "GENE EXPRESSION", "SKIN BIOPSY", "LESIONAL", "CYCLE"), "IL13_NONLES": ("IL13", "Interleukin 13 mRNA", "GENE EXPRESSION", "SKIN BIOPSY", "NON-LESIONAL", "CYCLE"), "SAL_CAMP": ("CAMPPRO", "Cathelicidin Protein", "ANTIMICROBIAL PEPTIDE", "SALIVA", None, "ng/mL"), "TS_CAMP_LES": ("CAMPPRO", "Cathelicidin Protein", "ANTIMICROBIAL PEPTIDE", "TAPE STRIP", "LESIONAL", "ng/mL"), "TS_CAMP_NONLES": ("CAMPPRO", "Cathelicidin Protein", "ANTIMICROBIAL PEPTIDE", "TAPE STRIP", "NON-LESIONAL", "ng/mL"),}visit_meta = { "SCRN": ("SCREENING", 1.0), "BL": ("BASELINE", 2.0), "D21": ("DAY 21", 3.0),}def _result_key(itemoid: str) -> str | None: short = itemoid.split(".")[-1] return short if short in test_meta else Nonegroups: dict[tuple, dict] = {}for row in odm.to_dicts(): key = ( str(row["StudyOID"]), str(row["SubjectKey"]), str(row["StudyEventOID"]), str(row["ItemGroupOID"]), str(row["ItemGroupRepeatKey"]), ) groups.setdefault(key, {})[str(row["ItemOID"])] = row["Value"]rows = []for (studyid, subj, event_oid, _group_oid, group_rep), values in groups.items(): event_short = event_oid.split(".")[-1] # The collection date from the date item on the same form. lbdtc = None for oid, val in values.items(): if oid.split(".")[-1].endswith("DTC") and _present(val): lbdtc = str(val).strip() if event_short == "UNSCH": visit, visitnum = "UNSCHEDULED", 2 + float(group_rep) / 100 else: visit, visitnum = visit_meta.get(event_short, (None, None)) for oid, val in values.items(): rkey = _result_key(oid) if rkey is None: continue text = str(val) if _present(val) else "" # An item with no reported value produces no record. if text == "": continue testcd, test, cat, spec, loc, unit = test_meta[rkey] if text == "NOT DONE": rows.append( { "DOMAIN": "LB", "STUDYID": studyid, "USUBJID": subj, "VISIT": visit, "VISITNUM": visitnum, "LBTESTCD": testcd, "LBTEST": test, "LBCAT": cat, "LBSPEC": spec, "LBLOC": loc, "LBORRES": None, "LBORRESU": None, "LBSTRESC": None, "LBSTRESN": None, "LBSTRESU": None, "LBSTAT": "NOT DONE", "LBDTC": lbdtc, } ) else: try: number = float(text) except ValueError: number = None rows.append( { "DOMAIN": "LB", "STUDYID": studyid, "USUBJID": subj, "VISIT": visit, "VISITNUM": visitnum, "LBTESTCD": testcd, "LBTEST": test, "LBCAT": cat, "LBSPEC": spec, "LBLOC": loc, "LBORRES": text, "LBORRESU": unit, "LBSTRESC": text, "LBSTRESN": number, "LBSTRESU": unit, "LBSTAT": None, "LBDTC": lbdtc, } )loc_rank = {"LESIONAL": 0, "NON-LESIONAL": 1, None: 2}rows.sort( key=lambda r: ( r["USUBJID"], r["LBDTC"] or "", r["LBTESTCD"], r["LBSPEC"], loc_rank[r["LBLOC"]], ))seq_by_subject: dict[str, int] = {}for row in rows: seq_by_subject[row["USUBJID"]] = seq_by_subject.get(row["USUBJID"], 0) + 1 row["LBSEQ"] = str(seq_by_subject[row["USUBJID"]])lb = ( pl.DataFrame( rows, schema={ "DOMAIN": pl.String, "STUDYID": pl.String, "USUBJID": pl.String, "LBSEQ": pl.String, "VISIT": pl.String, "VISITNUM": pl.Float64, "LBTESTCD": pl.String, "LBTEST": pl.String, "LBCAT": pl.String, "LBSPEC": pl.String, "LBLOC": pl.String, "LBORRES": pl.String, "LBORRESU": pl.String, "LBSTRESC": pl.String, "LBSTRESN": pl.Float64, "LBSTRESU": pl.String, "LBSTAT": pl.String, "LBDTC": pl.String, }, ) .with_columns( pl.col("LBSEQ").cast(pl.Int64).alias("_seq"), pl.col("VISITNUM").cast(pl.Float64).alias("_visitnum"), ) .sort(["USUBJID", "_seq"]) .select( "DOMAIN", "STUDYID", "USUBJID", "LBSEQ", "VISIT", "VISITNUM", "LBTESTCD", "LBTEST", "LBCAT", "LBSPEC", "LBLOC", "LBORRES", "LBORRESU", "LBSTRESC", "LBSTRESN", "LBSTRESU", "LBSTAT", "LBDTC", ))Path("/app/output").mkdir(exist_ok=True)lb.write_csv("/app/output/lb.csv")solutions/sdtm-lb-multiform/result.sas
Edit34 lines/* opensas reference solution for the yamaa benchmark sdtm-lb-multiform. 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 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. ItemGroupOID : $char1024. ItemGroupRepeatKey : $char1024. ItemOID : $char1024. Value : $char1024.;run;data dateitems; set odm; if scan(ItemOID,-1,'.') in ('LBDTC','BXDTC','SALDTC','TSDTC'); run;proc sql; create table dates as select StudyOID,SubjectKey,StudyEventOID,StudyEventRepeatKey,ItemGroupOID,ItemGroupRepeatKey,max(Value) as LBDTC from dateitems group by StudyOID,SubjectKey,StudyEventOID,StudyEventRepeatKey,ItemGroupOID,ItemGroupRepeatKey;create table joined as select a.*,b.LBDTC from odm a inner join dates b on a.StudyOID=b.StudyOID and a.SubjectKey=b.SubjectKey and a.StudyEventOID=b.StudyEventOID and a.StudyEventRepeatKey=b.StudyEventRepeatKey and a.ItemGroupOID=b.ItemGroupOID and a.ItemGroupRepeatKey=b.ItemGroupRepeatKey; quit;data prepared; set joined; length DOMAIN $2 STUDYID USUBJID VISIT LBTESTCD LBTEST LBCAT LBSPEC LBLOC LBORRES LBORRESU LBSTRESC LBSTRESU LBSTAT KEY EVENT $1024;KEY=scan(ItemOID,-1,'.'); EVENT=scan(StudyEventOID,-1,'.'); if not missing(Value) and KEY in ('VITD','IL13_LES','IL13_NONLES','SAL_CAMP','TS_CAMP_LES','TS_CAMP_NONLES');DOMAIN='LB'; STUDYID=StudyOID; USUBJID=SubjectKey; LBORRES=Value; LBSTRESC=Value; LBSTRESN=.; LBORRESU='ng/mL'; LBLOC=''; LBSTAT=''; LOCORD=2;if Value ne 'NOT DONE' then LBSTRESN=input(Value,?? best32.);if KEY='VITD' then do; LBTESTCD='VITD25OH'; LBTEST='25-Hydroxyvitamin D'; LBCAT='CHEMISTRY'; LBSPEC='SERUM'; end;else if KEY in ('IL13_LES','IL13_NONLES') then do; LBTESTCD='IL13'; LBTEST='Interleukin 13 mRNA'; LBCAT='GENE EXPRESSION'; LBSPEC='SKIN BIOPSY'; LBORRESU='CYCLE'; end;else do; LBTESTCD='CAMPPRO'; LBTEST='Cathelicidin Protein'; LBCAT='ANTIMICROBIAL PEPTIDE'; if KEY='SAL_CAMP' then LBSPEC='SALIVA'; else LBSPEC='TAPE STRIP'; end;if KEY in ('IL13_LES','TS_CAMP_LES') then do; LBLOC='LESIONAL'; LOCORD=0; end;if KEY in ('IL13_NONLES','TS_CAMP_NONLES') then do; LBLOC='NON-LESIONAL'; LOCORD=1; end;VISITNUM=.; if EVENT='SCRN' then do; VISIT='SCREENING'; VISITNUM=1; end; if EVENT='BL' then do; VISIT='BASELINE'; VISITNUM=2; end; if EVENT='D21' then do; VISIT='DAY 21'; VISITNUM=3; end; if EVENT='UNSCH' then do; VISIT='UNSCHEDULED'; VISITNUM=2+input(ItemGroupRepeatKey,best32.)/100; end;LBSTRESU=LBORRESU; if Value='NOT DONE' then do; LBORRES=''; LBORRESU=''; LBSTRESC=''; LBSTRESN=.; LBSTRESU=''; LBSTAT='NOT DONE'; end; run;proc sort data=prepared; by STUDYID USUBJID LBDTC LBTESTCD LBSPEC LOCORD; run;data result; set prepared; by STUDYID USUBJID; retain LBSEQ; if first.USUBJID then LBSEQ=0; LBSEQ+1;run;proc sql; create table final as select DOMAIN, STUDYID, USUBJID, LBSEQ, VISIT, VISITNUM, LBTESTCD, LBTEST, LBCAT, LBSPEC, LBLOC, LBORRES, LBORRESU, LBSTRESC, LBSTRESN, LBSTRESU, LBSTAT, LBDTC from result;quit;proc export data=final outfile="/app/output/lb.csv" dbms=csv replace; run;
yamaa
Comments
Signed in with GitHub; saved in repository Discussions