Review ask

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

Lifecycle: draftEditUnder review
What this benchmark means
README.md

Public entries: Python and R. Passing coverage requires an installed inventory.

Goal: map a pre-specified medical history checklist into MH and carry any number of volunteered free-text conditions alongside it.

Input: the ODM extract has one item record for each checklist condition asked, with a blank value when it was not answered. An item-definition table gives each checklist item its reported term and form order. Each volunteered condition has its own repeated free-text record. Repeat numbers can be reused at another visit.

Variables:

  • MHTERM is the checklist condition from the item-definition table, or the volunteered text exactly as reported. A checklist item the table does not define stops the run.
  • MHCAT is DISEASE-SPECIFIC HISTORY for checklist records and GENERAL HISTORY for volunteered records.
  • MHPRESP is Y for checklist records and blank for volunteered records.
  • MHOCCUR is Y or N for an answered checklist condition and blank otherwise.
  • MHSTAT is NOT DONE for an unanswered checklist condition and blank otherwise.
  • MHSEQ orders checklist conditions as shown on the form, then volunteered conditions by visit and form repeat.

Note: an unanswered checklist item still has an ODM record. A question entirely absent from the extract is not assumed to have been asked. Each volunteered condition is its own MH record, even when its repeat number is reused at another visit.

Inputs

Under review
2 source files

input/mh_items.csv

Edit
3 rows
input/mh_items.csv
ItemOIDMHTERMSORTORD
IT.MH.DIABETESDIABETES MELLITUS1
IT.MH.HYPERTENSIONHYPERTENSION2
IT.MH.ASTHMAASTHMA3

input/odm.csv

Edit
10 rows
input/odm.csv
StudyOIDMetaDataVersionOIDSubjectKeyStudyEventOIDStudyEventRepeatKeyItemGroupOIDItemGroupRepeatKeyItemOIDValue
YAMAA-01MDV.1YAMAA-01-0001SCREENING1IG.MH.CHECKLIST1IT.MH.DIABETESY
YAMAA-01MDV.1YAMAA-01-0001SCREENING1IG.MH.CHECKLIST1IT.MH.HYPERTENSIONN
YAMAA-01MDV.1YAMAA-01-0001SCREENING1IG.MH.CHECKLIST1IT.MH.ASTHMA—
YAMAA-01MDV.1YAMAA-01-0001SCREENING1IG.MH.FREETEXT1IT.MH.MHTERMSeasonal allergies
YAMAA-01MDV.1YAMAA-01-0002SCREENING1IG.MH.CHECKLIST1IT.MH.DIABETESN
YAMAA-01MDV.1YAMAA-01-0002SCREENING1IG.MH.CHECKLIST1IT.MH.HYPERTENSIONY
YAMAA-01MDV.1YAMAA-01-0002SCREENING1IG.MH.CHECKLIST1IT.MH.ASTHMAN
YAMAA-01MDV.1YAMAA-01-0001SCREENING1IG.MH.FREETEXT2IT.MH.MHTERMMigraine
YAMAA-01MDV.1YAMAA-01-0001BASELINE1IG.MH.FREETEXT1IT.MH.MHTERMBack pain
YAMAA-01MDV.1YAMAA-01-0002SCREENING1IG.MH.FREETEXT1IT.MH.MHTERMAppendectomy

Expected output

Under review
10 expected rows

expected/mh.csv

Edit
10 rows
expected/mh.csv
DOMAINSTUDYIDUSUBJIDMHSEQMHTERMMHCATMHPRESPMHOCCURMHSTAT
MHYAMAA-011DIABETES MELLITUSDISEASE-SPECIFIC HISTORYYY—
MHYAMAA-012HYPERTENSIONDISEASE-SPECIFIC HISTORYYN—
MHYAMAA-013ASTHMADISEASE-SPECIFIC HISTORYY—NOT DONE
MHYAMAA-014Seasonal allergiesGENERAL HISTORY———
MHYAMAA-015MigraineGENERAL HISTORY———
MHYAMAA-016Back painGENERAL HISTORY———
MHYAMAA-011DIABETES MELLITUSDISEASE-SPECIFIC HISTORYYN—
MHYAMAA-012HYPERTENSIONDISEASE-SPECIFIC HISTORYYY—
MHYAMAA-013ASTHMADISEASE-SPECIFIC HISTORYYN—
MHYAMAA-014AppendectomyGENERAL HISTORY———
Shaded columns are derived— = missing value

Human review and assessment

Your review

3 questions and your comments · View benchmark discussions

Mapping spec

Generated from the benchmark YAML; do not edit by hand

Variable NameVariable LabelTypeLengthControlled Terms or FormatOriginCoreConversion DefinitionVariable TypeVariable OrderComments for Define
DOMAINDomain AbbreviationCharAssignedSet constant value "MH".SDTM1
STUDYIDStudy IdentifierCharCollectedCopy value from ODM.StudyOID.SDTM2
USUBJIDUnique Subject IdentifierCharCollectedCopy value from ODM.SubjectKey.SDTM3
MHTERMReported Term for the Medical HistoryCharDerivedSet per constructed row; see the Row construction sheet.SDTM4
MHCATCategory for Medical HistoryCharDerivedSet per constructed row; see the Row construction sheet.SDTM5
MHPRESPPre-Specified Medical History IndicatorCharYDerivedSet per constructed row; see the Row construction sheet.SDTM6
MHOCCURMedical History OccurrenceCharY, NDerivedSet per constructed row; see the Row construction sheet.SDTM7
MHSTATCompletion StatusCharNOT DONEDerivedSet per constructed row; see the Row construction sheet.SDTM8
MHSEQSequence NumberNumDerivedRow number within each group of STUDYID, USUBJID, ordered by SORTORD then EVENTORD then EVENTREPEAT then REPEATKEY.SDTM9
TemplateInclude whenMHTERMMHCATMHPRESPMHOCCURMHSTATSORTORDEVENTORD
checklistODM.StudyEventOID = 'SCREENING' AND ODM.ItemGroupOID = 'IG.MH.CHECKLIST'If ODM.ItemGroupOID = 'IG.MH.CHECKLIST' then "MH_MAP.MHTERM"; otherwise "None"Constant "DISEASE-SPECIFIC HISTORY"Constant "Y"If ODM.Value = 'Y' then "Y"; If ODM.Value = 'N' then "N"; otherwise "None"If ODM.Value IS NULL then "NOT DONE"; otherwise "None"If ODM.ItemGroupOID = 'IG.MH.CHECKLIST' then "MH_MAP.SORTORD"; otherwise "None"If ODM.StudyEventOID IN ('SCREENING', 'BASELINE') then recode ODM.StudyEventOID: "SCREENING" → "1", "BASELINE" → "2"; missing values stay missing; unlisted values are errors; otherwise "99"
volunteeredODM.ItemGroupOID = 'IG.MH.FREETEXT' AND ODM.ItemOID = 'IT.MH.MHTERM' AND ODM.Value IS NOT NULLCopy ODM.ValueConstant "GENERAL HISTORY"Constant "None"Constant "None"Constant "None"Constant "4"If ODM.ItemGroupOID = 'IG.MH.FREETEXT' AND ODM.ItemOID = 'IT.MH.MHTERM' AND ODM.Value IS NOT NULL then recode ODM.StudyEventOID: "SCREENING" → "1", "BASELINE" → "2"; missing values stay missing; unlisted values are errors; otherwise "99"
VariablePermitted values
MHPRESPY
MHOCCURY, N
MHSTATNOT DONE
VersionDateAuthorDescriptionReviewerSign-off
1.72026-10-20yamaa docs buildRead 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.62026-10-17yamaa docs buildDescribe collected date parts written as ISO 8601 date text and times of day counted in seconds since midnight
1.52026-10-17yamaa docs buildName 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.42026-10-15yamaa docs buildState datetime precision codes through the hour and minute, and complete only the time parts a collected datetime lacks
1.32026-09-29yamaa docs buildShow study codelist names, external dictionaries, and ISO 8601 formats in variable rows; list the document's codelist values and metadata
1.22026-09-29yamaa docs buildClassify ADaM copies from a declared input as Predecessor and recodes as Derived, and SDTM recodes of locally derived columns as Derived
1.12026-09-29yamaa docs buildResolve lookups into the donor record they select, and give every derivation shape a plain-language rule instead of its raw YAML
1.0yamaa docs buildInitial generation from spec.yaml

Agent prompt

The request an agent receives with the input files; each tier states no more than the one above

prompts/sdtm-mh-prespecified-conditions/full.md

Edit
26 lines

Following CDISC SDTM standards, use the provided MH_ITEMS and ODM datasets to create an MH dataset with one record per condition asked about on the checklist or volunteered in free text.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, MHSEQ, MHTERM, MHCAT, MHPRESP, MHOCCUR, MHSTAT

DOMAIN is "MH". STUDYID is the ODM study OID and USUBJID is the ODM subject key, each as given. Each checklist condition asked of a subject gives one record, answered or not, and a checklist condition that does not appear for the subject gives no record. MHTERM is the condition the item-definition table gives for a checklist item. For a volunteered condition, MHTERM is the volunteered text exactly as reported. MHCAT is "DISEASE-SPECIFIC HISTORY" for a checklist record and "GENERAL HISTORY" for a volunteered record. MHPRESP is "Y" for a checklist record and has no value for a volunteered record. MHOCCUR is "Y" or "N" for an answered checklist condition, as answered, and has no value otherwise. MHSTAT is "NOT DONE" when a checklist condition was not answered and has no value otherwise. Each volunteered condition is its own record, even when its form repeat number is used again at another visit. MHSEQ numbers each subject's records from 1: the checklist conditions first, in the order they are shown on the form, then the volunteered conditions by visit, screening before baseline, and then by form repeat number.

Read the source datasets from /app/input and save the completed dataset as /app/output/mh.csv.

prompts/sdtm-mh-prespecified-conditions/conventions.md

Edit
13 lines

Following CDISC SDTM standards, use the provided MH_ITEMS and ODM datasets to create an MH dataset with one record per condition asked about on the checklist or volunteered in free text.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, MHSEQ, MHTERM, MHCAT, MHPRESP, MHOCCUR, MHSTAT

DOMAIN is "MH". MHCAT is "DISEASE-SPECIFIC HISTORY" or "GENERAL HISTORY". MHPRESP is "Y" or has no value. MHOCCUR is "Y" or "N", or has no value. MHSTAT is "NOT DONE" or has no value.

Read the source datasets from /app/input and save the completed dataset as /app/output/mh.csv.

prompts/sdtm-mh-prespecified-conditions/brief.md

Edit
9 lines

Following CDISC SDTM standards, use the provided MH_ITEMS and ODM datasets to create an MH dataset with one record per condition asked about on the checklist or volunteered in free text.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, MHSEQ, MHTERM, MHCAT, MHPRESP, MHOCCUR, MHSTAT

Read the source datasets from /app/input and save the completed dataset as /app/output/mh.csv.

yamaa code

Public calls generated from benchmark.yaml; results must match the expected output

R

Edit
3 lines
library(yamaa)yamaa_spec("spec.yaml")$run()$output

Python

Edit
3 lines
import yamaayamaa.spec("spec.yaml").run().output

Solution without yamaa

Written from the full prompt and the inputs alone; CI checks it reproduces the expected output

solutions/sdtm-mh-prespecified-conditions/result.R

Edit
76 lines
# Reference solution for the yamaa benchmark sdtm-mh-prespecified-conditions (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)items <- read_csv(  "/app/input/mh_items.csv",  col_types = cols(SORTORD = col_integer(), .default = col_character()))odm <- read_csv(  "/app/input/odm.csv",  col_types = cols(.default = col_character()))checklist_oids <- intersect(items$ItemOID, unique(odm$ItemOID))checklist <- odm |>  filter(ItemOID %in% checklist_oids) |>  group_by(StudyOID, SubjectKey, ItemOID) |>  slice_tail(n = 1) |>  ungroup() |>  left_join(items |> select(ItemOID, MHTERM, SORTORD), by = "ItemOID") |>  mutate(    answered = Value %in% c("Y", "N"),    MHTERM = MHTERM,    MHCAT = "DISEASE-SPECIFIC HISTORY",    MHPRESP = "Y",    MHOCCUR = if_else(answered, Value, NA_character_),    MHSTAT = if_else(answered, NA_character_, "NOT DONE"),    visit_rank = 0L,    repeat_no = 0L  ) |>  select(    STUDYID = StudyOID, USUBJID = SubjectKey, ItemOID, MHTERM, MHCAT,    MHPRESP, MHOCCUR, MHSTAT, SORTORD, visit_rank, repeat_no  )volunteered <- odm |>  filter(!ItemOID %in% checklist_oids, !is.na(Value) & Value != "") |>  mutate(    MHTERM = Value,    MHCAT = "GENERAL HISTORY",    MHPRESP = NA_character_,    MHOCCUR = NA_character_,    MHSTAT = NA_character_,    SORTORD = NA_integer_,    visit_rank = case_when(      StudyEventOID == "SCREENING" ~ 0L,      StudyEventOID == "BASELINE" ~ 1L,      TRUE ~ 99L    ),    repeat_no = suppressWarnings(as.integer(ItemGroupRepeatKey))  ) |>  select(    STUDYID = StudyOID, USUBJID = SubjectKey, ItemOID, MHTERM, MHCAT,    MHPRESP, MHOCCUR, MHSTAT, SORTORD, visit_rank, repeat_no  )mh <- bind_rows(checklist, volunteered) |>  arrange(USUBJID, is.na(SORTORD), SORTORD, visit_rank, repeat_no) |>  group_by(USUBJID) |>  mutate(MHSEQ = row_number()) |>  ungroup() |>  mutate(DOMAIN = "MH") |>  arrange(USUBJID, MHSEQ) |>  select(    DOMAIN, STUDYID, USUBJID, MHSEQ, MHTERM, MHCAT, MHPRESP, MHOCCUR, MHSTAT  )dir.create("/app/output", showWarnings = FALSE)write_csv(mh, "/app/output/mh.csv", na = "")

solutions/sdtm-mh-prespecified-conditions/result.py

Edit
114 lines
# Reference solution for the yamaa benchmark sdtm-mh-prespecified-conditions (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) != ""items = pl.read_csv("/app/input/mh_items.csv", infer_schema=False)odm = pl.read_csv("/app/input/odm.csv", infer_schema=False)meta = {row["ItemOID"]: (row["MHTERM"], int(row["SORTORD"])) for row in items.to_dicts()}checklist_oids = {oid for oid in meta if (odm["ItemOID"] == oid).any()}visit_order = {"SCREENING": 0, "BASELINE": 1}records_by_subject: dict[tuple[str, str], dict] = {}for row in odm.to_dicts():    studyid, usubjid = str(row["StudyOID"]), str(row["SubjectKey"])    key = (studyid, usubjid)    records_by_subject.setdefault(key, {"checklist": {}, "volunteered": []})for row in odm.to_dicts():    key = (str(row["StudyOID"]), str(row["SubjectKey"]))    oid, value = str(row["ItemOID"]), row["Value"]    if oid in checklist_oids:        # Last collected answer wins when an item repeats, including blanks.        records_by_subject[key]["checklist"][oid] = value if _present(value) else None    elif not _present(value):        continue    else:        # Each volunteered condition is its own record, even when its        # repeat number is reused at another visit.        event = str(row["StudyEventOID"])        records_by_subject[key]["volunteered"].append(            {                "term": str(value),                "visit_rank": visit_order.get(event, 99),                "repeat": int(row["ItemGroupRepeatKey"] or 1),            }        )rows = []for (studyid, usubjid), parts in records_by_subject.items():    ordered = sorted(parts["checklist"].items(), key=lambda kv: meta[kv[0]][1])    seq = 0    for oid, answer in ordered:        seq += 1        term, _ = meta[oid]        answered = answer in ("Y", "N")        rows.append(            {                "DOMAIN": "MH",                "STUDYID": studyid,                "USUBJID": usubjid,                "MHSEQ": str(seq),                "MHTERM": term,                "MHCAT": "DISEASE-SPECIFIC HISTORY",                "MHPRESP": "Y",                "MHOCCUR": answer if answered else None,                "MHSTAT": None if answered else "NOT DONE",            }        )    volunteered = sorted(        parts["volunteered"], key=lambda v: (v["visit_rank"], v["repeat"])    )    for entry in volunteered:        seq += 1        rows.append(            {                "DOMAIN": "MH",                "STUDYID": studyid,                "USUBJID": usubjid,                "MHSEQ": str(seq),                "MHTERM": entry["term"],                "MHCAT": "GENERAL HISTORY",                "MHPRESP": None,                "MHOCCUR": None,                "MHSTAT": None,            }        )mh = (    pl.DataFrame(        rows,        schema={            "DOMAIN": pl.String,            "STUDYID": pl.String,            "USUBJID": pl.String,            "MHSEQ": pl.String,            "MHTERM": pl.String,            "MHCAT": pl.String,            "MHPRESP": pl.String,            "MHOCCUR": pl.String,            "MHSTAT": pl.String,        },    )    .with_columns(pl.col("MHSEQ").cast(pl.Int64).alias("_seq"))    .sort(["USUBJID", "_seq"])    .select(        "DOMAIN", "STUDYID", "USUBJID", "MHSEQ", "MHTERM", "MHCAT",        "MHPRESP", "MHOCCUR", "MHSTAT",    ))Path("/app/output").mkdir(exist_ok=True)mh.write_csv("/app/output/mh.csv")

solutions/sdtm-mh-prespecified-conditions/result.sas

Edit
33 lines
/* opensas reference solution for the yamaa benchmark sdtm-mh-prespecified-conditions.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data mh_items;  length ItemOID MHTERM SORTORD $1024;  infile "/app/input/mh_items.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input ItemOID : $char1024. MHTERM : $char1024. SORTORD : $char1024.;run;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 authored; set odm; ordinal=_n_; run;proc sql; create table checklist as select a.*,b.MHTERM,b.SORTORD from authored a inner join mh_items b on a.ItemOID=b.ItemOID; quit;proc sort data=checklist; by StudyOID SubjectKey ItemOID ordinal; run;data checklistlast; set checklist; by StudyOID SubjectKey ItemOID; if last.ItemOID; run;data checklistout; set checklistlast; length DOMAIN $2 STUDYID USUBJID MHCAT MHPRESP MHOCCUR MHSTAT $1024; DOMAIN='MH'; STUDYID=StudyOID; USUBJID=SubjectKey; MHCAT='DISEASE-SPECIFIC HISTORY'; MHPRESP='Y'; MHOCCUR=''; MHSTAT='NOT DONE'; if Value in ('Y','N') then do; MHOCCUR=Value; MHSTAT=''; end; SECTION=0; ORD=input(SORTORD,best32.); VISITORD=0; REP=0; run;data volunteered; set odm; where ItemOID='IT.MH.MHTERM' and not missing(Value); length DOMAIN $2 STUDYID USUBJID MHTERM MHCAT MHPRESP MHOCCUR MHSTAT $1024; DOMAIN='MH'; STUDYID=StudyOID; USUBJID=SubjectKey; MHTERM=Value; MHCAT='GENERAL HISTORY'; MHPRESP=''; MHOCCUR=''; MHSTAT=''; SECTION=1; ORD=0; VISITORD=0; if StudyEventOID='BASELINE' then VISITORD=1; REP=input(ItemGroupRepeatKey,best32.); run;data prepared; set checklistout volunteered; run;proc sort data=prepared; by STUDYID USUBJID SECTION ORD VISITORD REP; run;data result; set prepared; by STUDYID USUBJID; retain MHSEQ;  if first.USUBJID then MHSEQ=0;  MHSEQ+1;run;proc sql;  create table final as select DOMAIN, STUDYID, USUBJID, MHSEQ, MHTERM, MHCAT, MHPRESP, MHOCCUR, MHSTAT from result;quit;proc export data=final outfile="/app/output/mh.csv" dbms=csv replace; run;