Review ask

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.

  1. 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.
  2. Inputs: Do they look like data collected in a real trial, and do they include the situations you would expect to meet in one?
  3. 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

Lifecycle: draftEditUnder review
What this benchmark means
README.md

Goal: pick the reference range the way a multi-lab study does -- by lab, test, sex and age band, and by the period the range was in effect -- and flag each result: LBSTNRLO, LBSTNRHI, and LBNRIND.

Input: lab results as Operational Data Model (ODM) item data -- one row per collected item, with the test code, the collecting lab, the collection date and the numeric result as IT.LB.* items of a repeating IG.LB panel, and recorded sex and birth date as IT.DM.* items -- plus a reference dictionary keyed by lab, test code, sex and age band, each entry carrying the dates it was in effect, the unit and the lower and upper limits.

Variables:

  • LBTESTCD is the test code from the IT.LB.LBTESTCD item.
  • SEX is recorded sex from the IT.DM.SEX item.
  • LBNAM is the collecting lab from the IT.LB.LBNAM item; different labs may carry different ranges for the same test.
  • LBSTRESN is the numeric result in standard units from the IT.LB.LBSTRESN item; missing when the result was not collected.
  • LBORRESU is the unit of the reference entry for the result's lab, test code and sex whose age band holds the subject's age at collection and which was in effect on the collection date; blank, like both limits, when no entry matches.
  • LBSTNRLO is the lower reference limit in standard units from that entry.
  • LBSTNRHI is the upper reference limit in standard units from that entry.
  • LBNRIND is LOW when the result is below the lower limit, HIGH when it is above the upper limit, and NORMAL otherwise; blank when the result or either reference limit is missing.

Note: when a lab revises a range, the entry in effect on the collection date wins -- a result collected on the first day of the new range is judged against the new limits -- and a subject whose age falls on an age-band boundary takes the band that starts at that age. Each output row is built from one IT.LB.LBDTC item, with the other items of its panel read alongside; LBSEQ is the panel's repeat key.

Inputs

Under review
2 source files

input/lbrange.csv

Edit
10 rows
input/lbrange.csv
LBNAMLBTESTCDSEXAGELOAGEHIEFFSTDTEFFENDTUNITNRLONRHI
Central LabALTM18992024-01-012025-06-30U/L040
Central LabALTM18992025-07-01—U/L045
Central LabALTF18992024-01-012025-06-30U/L040
Central LabALTF18992025-07-01—U/L038
Central LabCREATM18992024-01-01—mg/dL0.71.3
Local Lab AHGBF0172024-01-01—g/dL11.515.5
Local Lab AHGBF18992024-01-01—g/dL1216
Local Lab AHGBM0172024-01-01—g/dL1216.5
Local Lab AHGBM18992024-01-01—g/dL13.517.5
Local Lab APOTASSIUMF18992024-01-01—mmol/L3.55.1

input/odm.csv

Edit
48 rows
input/odm.csv
StudyOIDMetaDataVersionOIDSubjectKeyStudyEventOIDStudyEventRepeatKeyItemGroupOIDItemGroupRepeatKeyItemOIDValue
PILOT7MDV.1P7-501SCREENING1IG.DM1IT.DM.SEXM
PILOT7MDV.1P7-501SCREENING1IG.DM1IT.DM.BRTHDTC1980-05-01
PILOT7MDV.1P7-501LAB1IG.LB1IT.LB.LBTESTCDALT
PILOT7MDV.1P7-501LAB1IG.LB1IT.LB.LBNAMCentral Lab
PILOT7MDV.1P7-501LAB1IG.LB1IT.LB.LBDTC2025-03-15
PILOT7MDV.1P7-501LAB1IG.LB1IT.LB.LBSTRESN15
PILOT7MDV.1P7-502SCREENING1IG.DM1IT.DM.SEXF
PILOT7MDV.1P7-502SCREENING1IG.DM1IT.DM.BRTHDTC1990-02-10
PILOT7MDV.1P7-502LAB1IG.LB1IT.LB.LBTESTCDALT
PILOT7MDV.1P7-502LAB1IG.LB1IT.LB.LBNAMCentral Lab
PILOT7MDV.1P7-502LAB1IG.LB1IT.LB.LBDTC2025-07-01
PILOT7MDV.1P7-502LAB1IG.LB1IT.LB.LBSTRESN39
PILOT7MDV.1P7-503SCREENING1IG.DM1IT.DM.SEXF
PILOT7MDV.1P7-503SCREENING1IG.DM1IT.DM.BRTHDTC2017-09-20
PILOT7MDV.1P7-503LAB1IG.LB1IT.LB.LBTESTCDHGB
PILOT7MDV.1P7-503LAB1IG.LB1IT.LB.LBNAMLocal Lab A
PILOT7MDV.1P7-503LAB1IG.LB1IT.LB.LBDTC2025-02-01
PILOT7MDV.1P7-503LAB1IG.LB1IT.LB.LBSTRESN12.5
PILOT7MDV.1P7-504SCREENING1IG.DM1IT.DM.SEXM
PILOT7MDV.1P7-504SCREENING1IG.DM1IT.DM.BRTHDTC2007-06-01
PILOT7MDV.1P7-504LAB1IG.LB1IT.LB.LBTESTCDHGB
PILOT7MDV.1P7-504LAB1IG.LB1IT.LB.LBNAMLocal Lab A
PILOT7MDV.1P7-504LAB1IG.LB1IT.LB.LBDTC2025-06-01
PILOT7MDV.1P7-504LAB1IG.LB1IT.LB.LBSTRESN12.5
PILOT7MDV.1P7-505SCREENING1IG.DM1IT.DM.SEXM
PILOT7MDV.1P7-505SCREENING1IG.DM1IT.DM.BRTHDTC1975-11-11
PILOT7MDV.1P7-505LAB1IG.LB1IT.LB.LBTESTCDCREAT
PILOT7MDV.1P7-505LAB1IG.LB1IT.LB.LBNAMCentral Lab
PILOT7MDV.1P7-505LAB1IG.LB1IT.LB.LBDTC2025-04-01
PILOT7MDV.1P7-505LAB1IG.LB1IT.LB.LBSTRESN0.9
PILOT7MDV.1P7-506SCREENING1IG.DM1IT.DM.SEXM
PILOT7MDV.1P7-506SCREENING1IG.DM1IT.DM.BRTHDTC1988-03-03
PILOT7MDV.1P7-506LAB1IG.LB1IT.LB.LBTESTCDALT
PILOT7MDV.1P7-506LAB1IG.LB1IT.LB.LBNAMCentral Lab
PILOT7MDV.1P7-506LAB1IG.LB1IT.LB.LBDTC2025-08-01
PILOT7MDV.1P7-506LAB1IG.LB1IT.LB.LBSTRESN—
PILOT7MDV.1P7-507SCREENING1IG.DM1IT.DM.SEXF
PILOT7MDV.1P7-507SCREENING1IG.DM1IT.DM.BRTHDTC1995-12-12
PILOT7MDV.1P7-507LAB1IG.LB1IT.LB.LBTESTCDPOTASSIUM
PILOT7MDV.1P7-507LAB1IG.LB1IT.LB.LBNAMLocal Lab A
PILOT7MDV.1P7-507LAB1IG.LB1IT.LB.LBDTC2025-05-05
PILOT7MDV.1P7-507LAB1IG.LB1IT.LB.LBSTRESN5.5
PILOT7MDV.1P7-508SCREENING1IG.DM1IT.DM.SEXM
PILOT7MDV.1P7-508SCREENING1IG.DM1IT.DM.BRTHDTC1980-01-01
PILOT7MDV.1P7-508LAB1IG.LB1IT.LB.LBTESTCDGLUCOSE
PILOT7MDV.1P7-508LAB1IG.LB1IT.LB.LBNAMCentral Lab
PILOT7MDV.1P7-508LAB1IG.LB1IT.LB.LBDTC2025-05-05
PILOT7MDV.1P7-508LAB1IG.LB1IT.LB.LBSTRESN5

Expected output

Under review
8 expected rows

expected/lb.csv

Edit
8 rows
expected/lb.csv
DOMAINSTUDYIDUSUBJIDLBSEQLBTESTCDSEXLBNAMLBSTRESNLBORRESULBSTNRLOLBSTNRHILBNRIND
LBPILOT71ALTMCentral Lab15U/L040NORMAL
LBPILOT71ALTFCentral Lab39U/L038HIGH
LBPILOT71HGBFLocal Lab A12.5g/dL11.515.5NORMAL
LBPILOT71HGBMLocal Lab A12.5g/dL13.517.5LOW
LBPILOT71CREATMCentral Lab0.9mg/dL0.71.3NORMAL
LBPILOT71ALTMCentral Lab—U/L045—
LBPILOT71POTASSIUMFLocal Lab A5.5mmol/L3.55.1HIGH
LBPILOT71GLUCOSEMCentral Lab5————
Shaded columns are derived— = missing value

Comments and assessment materials

Comments

Signed in with GitHub; saved in repository 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 "LB".SDTM1
STUDYIDStudy IdentifierCharCollectedCopy value from ODM.StudyOID.SDTM2
USUBJIDUnique Subject IdentifierCharCollectedCopy value from ODM.SubjectKey.SDTM3
LBSEQSequence NumberNumCollectedCopy value from ODM.ItemGroupRepeatKey.SDTM4
LBTESTCDLab Test or Examination Short NameCharDerivedTake Value from the ODM record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID, ItemGroupOID equals ODM.ItemGroupOID and ItemGroupRepeatKey equals ODM.ItemGroupRepeatKey, where ODM.ItemOID = 'IT.LB.LBTESTCD', and a row with no match is an error.SDTM5
SEXSexCharDerivedTake Value from the ODM record whose StudyOID equals ODM.StudyOID and SubjectKey equals ODM.SubjectKey, where ODM.ItemOID = 'IT.DM.SEX', and a row with no match is an error.SDTM6
LBNAMLab NameCharDerivedTake Value from the ODM record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID, ItemGroupOID equals ODM.ItemGroupOID and ItemGroupRepeatKey equals ODM.ItemGroupRepeatKey, where ODM.ItemOID = 'IT.LB.LBNAM', and a row with no match is an error.SDTM7
LBSTRESNNumeric Result/Finding in Standard UnitsNumDerivedTake Value from the ODM record whose StudyOID equals ODM.StudyOID, SubjectKey equals ODM.SubjectKey, StudyEventOID equals ODM.StudyEventOID, ItemGroupOID equals ODM.ItemGroupOID and ItemGroupRepeatKey equals ODM.ItemGroupRepeatKey, where ODM.ItemOID = 'IT.LB.LBSTRESN', and a row with no match is an error.SDTM8
LBORRESUOriginal UnitsCharDerivedTake UNIT from the LBRANGE record matching on LBNAM, LBTESTCD and SEX, where AGE falls within AGELO to AGEHI, where LBRANGE.EFFSTDT <= ODM.Value AND (LBRANGE.EFFENDT IS NULL OR ODM.Value <= LBRANGE.EFFENDT), and no match yields blank.SDTM9
LBSTNRLOReference Range Lower Limit-Std UnitsNumDerivedTake NRLO from the LBRANGE record matching on LBNAM, LBTESTCD and SEX, where AGE falls within AGELO to AGEHI, where LBRANGE.EFFSTDT <= ODM.Value AND (LBRANGE.EFFENDT IS NULL OR ODM.Value <= LBRANGE.EFFENDT), and no match yields blank.SDTM10
LBSTNRHIReference Range Upper Limit-Std UnitsNumDerivedTake NRHI from the LBRANGE record matching on LBNAM, LBTESTCD and SEX, where AGE falls within AGELO to AGEHI, where LBRANGE.EFFSTDT <= ODM.Value AND (LBRANGE.EFFENDT IS NULL OR ODM.Value <= LBRANGE.EFFENDT), and no match yields blank.SDTM11
LBNRINDReference Range IndicatorCharDerivedIf LBSTRESN IS NULL OR LBSTNRLO IS NULL OR LBSTNRHI IS NULL then "None"; If LBSTRESN < LBSTNRLO then "LOW"; If LBSTRESN > LBSTNRHI then "HIGH"; otherwise "NORMAL".SDTM12
TemplateInclude when
resultODM.ItemOID = 'IT.LB.LBDTC'
VersionDateAuthorDescriptionReviewerSign-off
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-lb-lab-specific-ranges/full.md

Edit
29 lines

Following CDISC SDTM standards, use the provided ODM and LBRANGE datasets to create an LB dataset with one record per collected laboratory result.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, LBSEQ, LBTESTCD, SEX, LBNAM, LBSTRESN, LBORRESU, LBSTNRLO, LBSTNRHI, LBNRIND

STUDYID is the ODM StudyOID and USUBJID is the SubjectKey, each as given.

LBTESTCD is the collected test code, SEX the recorded sex, LBNAM the collecting lab, and LBSTRESN the numeric result in standard units, which has no value when the result was not collected. LBSEQ is the panel repeat key.

Each result is judged against the reference entry for its lab, test code, and sex whose age band holds the subject's age at collection and which was in effect on the collection date: a result collected on the first day of a new range uses the new limits, and an age on a band boundary takes the band that starts at that age. LBORRESU, LBSTNRLO, and LBSTNRHI are the unit and the lower and upper limits from that entry; all three have no value when no entry matches.

LBNRIND is LOW when the result is below the lower limit, HIGH when it is above the upper limit, and NORMAL otherwise; it has no value when the result or either limit is missing. A record whose result is missing still carries its unit and limits.

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

prompts/sdtm-lb-lab-specific-ranges/conventions.md

Edit
11 lines

Following CDISC SDTM standards, use the provided ODM and LBRANGE datasets to create an LB dataset with one record per collected laboratory result.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, LBSEQ, LBTESTCD, SEX, LBNAM, LBSTRESN, LBORRESU, LBSTNRLO, LBSTNRHI, LBNRIND

LBNRIND is LOW, HIGH, or NORMAL, or has no value.

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

prompts/sdtm-lb-lab-specific-ranges/brief.md

Edit
9 lines

Following CDISC SDTM standards, use the provided ODM and LBRANGE datasets to create an LB dataset with one record per collected laboratory result.

The output dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, LBSEQ, LBTESTCD, SEX, LBNAM, LBSTRESN, LBORRESU, LBSTNRLO, LBSTNRHI, LBNRIND

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

yamaa code

Runs the yamaa specification; its result must match the expected output

run.R

Edit
4 lines
library(yamaa)lb <- yamaa_domain("spec.yaml")$outputlb

run.py

Edit
4 lines
import yamaalb = yamaa.yamaa_domain("spec.yaml").outputlb

Solution without yamaa

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

solutions/sdtm-lb-lab-specific-ranges/result.R

Edit
117 lines
# Reference solution for the yamaa benchmark sdtm-lb-lab-specific-ranges (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)library(purrr)library(lubridate)odm <- read_csv(  "/app/input/odm.csv",  col_types = cols(.default = col_character())) |>  mutate(Value = na_if(Value, ""))lbrange <- read_csv(  "/app/input/lbrange.csv",  col_types = cols(    AGELO = col_integer(), AGEHI = col_integer(),    NRLO = col_double(), NRHI = col_double(),    .default = col_character()  ))age_years <- function(birth, collected) {  b <- ymd(birth)  c <- ymd(collected)  years <- year(c) - year(b)  before_birthday <- (month(c) < month(b)) |    (month(c) == month(b) & day(c) < day(b))  years - if_else(before_birthday, 1L, 0L)}dm <- odm |>  filter(ItemGroupOID == "IG.DM") |>  select(SubjectKey, ItemOID, Value) |>  pivot_wider(names_from = ItemOID, values_from = Value)labs <- odm |>  filter(ItemGroupOID == "IG.LB") |>  distinct(StudyOID, SubjectKey, ItemGroupRepeatKey)one_lab <- function(study, subj, igrep) {  sub <- odm |>    filter(      SubjectKey == subj, ItemGroupOID == "IG.LB",      ItemGroupRepeatKey == igrep    )  get <- function(oid) {    hit <- sub |> filter(ItemOID == oid)    if (nrow(hit) == 0) return(NA_character_)    hit$Value[[1]]  }  dsub <- dm |> filter(SubjectKey == subj)  sex <- if (nrow(dsub) == 0) NA_character_ else dsub$`IT.DM.SEX`[[1]]  birth <- if (nrow(dsub) == 0) NA_character_ else dsub$`IT.DM.BRTHDTC`[[1]]  code <- get("IT.LB.LBTESTCD")  lbnam <- get("IT.LB.LBNAM")  lbdtc <- get("IT.LB.LBDTC")  raw <- get("IT.LB.LBSTRESN")  stresn <- suppressWarnings(as.numeric(raw))  unit <- NA_character_  lo <- NA_real_  hi <- NA_real_  if (!is.na(birth) && !is.na(lbdtc) && !is.na(code) && !is.na(lbnam) && !is.na(sex)) {    age <- age_years(birth, lbdtc)    hit <- lbrange |>      filter(        LBNAM == lbnam, LBTESTCD == code, SEX == sex,        AGELO <= age, age < AGEHI,        EFFSTDT <= lbdtc,        is.na(EFFENDT) | lbdtc <= EFFENDT      ) |>      arrange(desc(EFFSTDT), desc(AGELO)) |>      slice_head(n = 1)    if (nrow(hit) == 1) {      unit <- hit$UNIT[[1]]      lo <- hit$NRLO[[1]]      hi <- hit$NRHI[[1]]    }  }  nrind <- if (is.na(stresn) || is.na(lo) || is.na(hi)) {    NA_character_  } else if (stresn < lo) {    "LOW"  } else if (stresn > hi) {    "HIGH"  } else {    "NORMAL"  }  tibble(    DOMAIN = "LB",    STUDYID = study,    USUBJID = subj,    LBSEQ = as.integer(igrep),    LBTESTCD = code,    SEX = sex,    LBNAM = lbnam,    LBSTRESN = stresn,    LBORRESU = unit,    LBSTNRLO = lo,    LBSTNRHI = hi,    LBNRIND = nrind  )}lb <- pmap_dfr(  list(labs$StudyOID, labs$SubjectKey, labs$ItemGroupRepeatKey),  one_lab) |>  arrange(STUDYID, USUBJID, LBSEQ)dir.create("/app/output", showWarnings = FALSE)write_csv(lb, "/app/output/lb.csv", na = "")

solutions/sdtm-lb-lab-specific-ranges/result.py

Edit
123 lines
# Reference solution for the yamaa benchmark sdtm-lb-lab-specific-ranges (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 datetime import datefrom pathlib import Pathimport polars as plodm = pl.read_csv("/app/input/odm.csv", infer_schema=False)lbrange = pl.read_csv("/app/input/lbrange.csv", infer_schema=False).with_columns(    pl.col("AGELO").cast(pl.Int64),    pl.col("AGEHI").cast(pl.Int64),    pl.col("NRLO").cast(pl.Float64, strict=False),    pl.col("NRHI").cast(pl.Float64, strict=False),)subjects = {}labs = {}study_of = {}for r in odm.to_dicts():    study_of[r["SubjectKey"]] = r["StudyOID"]    if r["ItemGroupOID"] == "IG.DM":        subjects.setdefault(r["SubjectKey"], {})[r["ItemOID"]] = (            r["Value"] if r["Value"] not in (None, "") else None        )    elif r["ItemGroupOID"] == "IG.LB":        key = (r["SubjectKey"], r["ItemGroupRepeatKey"])        labs.setdefault(key, {})[r["ItemOID"]] = (            r["Value"] if r["Value"] not in (None, "") else None        )def age_years(birth, collected):    b = date.fromisoformat(birth)    c = date.fromisoformat(collected)    years = c.year - b.year    if (c.month, c.day) < (b.month, b.day):        years -= 1    return yearsranges = sorted(lbrange.to_dicts(), key=lambda r: (r["EFFSTDT"], r["AGELO"]), reverse=True)rows = []for (subj, repkey), lab in labs.items():    dm = subjects.get(subj, {})    sex = dm.get("IT.DM.SEX")    birth = dm.get("IT.DM.BRTHDTC")    code = lab.get("IT.LB.LBTESTCD")    lbnam = lab.get("IT.LB.LBNAM")    lbdtc = lab.get("IT.LB.LBDTC")    raw = lab.get("IT.LB.LBSTRESN")    try:        stresn = float(raw) if raw not in (None, "") else None    except ValueError:        stresn = None    unit = lo = hi = None    if birth and lbdtc and code and lbnam and sex:        age = age_years(birth, lbdtc)        best = None        for rg in ranges:            if rg["LBNAM"] != lbnam or rg["LBTESTCD"] != code or rg["SEX"] != sex:                continue            if not (rg["AGELO"] <= age < rg["AGEHI"]):                continue            eff_start = rg["EFFSTDT"]            eff_end = rg["EFFENDT"] if rg["EFFENDT"] not in (None, "") else None            if lbdtc < eff_start:                continue            if eff_end is not None and lbdtc > eff_end:                continue            best = rg            break        if best is not None:            unit, lo, hi = best["UNIT"], best["NRLO"], best["NRHI"]    if stresn is None or lo is None or hi is None:        nrind = None    elif stresn < lo:        nrind = "LOW"    elif stresn > hi:        nrind = "HIGH"    else:        nrind = "NORMAL"    rows.append(        {            "DOMAIN": "LB",            "STUDYID": study_of[subj],            "USUBJID": subj,            "LBSEQ": int(repkey),            "LBTESTCD": code,            "SEX": sex,            "LBNAM": lbnam,            "LBSTRESN": stresn,            "LBORRESU": unit,            "LBSTNRLO": lo,            "LBSTNRHI": hi,            "LBNRIND": nrind,        }    )lb = pl.DataFrame(    rows,    schema={        "DOMAIN": pl.String,        "STUDYID": pl.String,        "USUBJID": pl.String,        "LBSEQ": pl.Int64,        "LBTESTCD": pl.String,        "SEX": pl.String,        "LBNAM": pl.String,        "LBSTRESN": pl.Float64,        "LBORRESU": pl.String,        "LBSTNRLO": pl.Float64,        "LBSTNRHI": pl.Float64,        "LBNRIND": pl.String,    },).sort(["STUDYID", "USUBJID", "LBSEQ"])Path("/app/output").mkdir(parents=True, exist_ok=True)lb.write_csv("/app/output/lb.csv")

solutions/sdtm-lb-lab-specific-ranges/result.sas

Edit
57 lines
/* opensas reference solution for the yamaa benchmark sdtm-lb-lab-specific-ranges.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data lbrange;  length LBNAM LBTESTCD SEX EFFSTDT EFFENDT UNIT $1024;  infile "/app/input/lbrange.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input LBNAM : $char1024. LBTESTCD : $char1024. SEX : $char1024. AGELO AGEHI EFFSTDT : $char1024. EFFENDT : $char1024. UNIT : $char1024. NRLO NRHI;  RANGE_ORDER=_n_;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;proc sort data=odm out=ordered; by StudyOID SubjectKey; run;data subjects;  length SEX BRTHDTC $1024;  retain SEX BRTHDTC;  set ordered;  where ItemGroupOID='IG.DM';  by StudyOID SubjectKey;  if first.SubjectKey then do;    SEX=''; BRTHDTC='';  end;    if ItemOID='IT.DM.SEX' then SEX=Value;  if ItemOID='IT.DM.BRTHDTC' then BRTHDTC=Value;  if last.SubjectKey then output;run;proc sort data=odm out=ordered; by StudyOID SubjectKey ItemGroupRepeatKey; run;data panels;  length LBDTC LBNAM LBTESTCD RESULTTEXT $1024;  retain LBDTC LBNAM LBTESTCD RESULTTEXT;  set ordered;  where ItemGroupOID='IG.LB';  by StudyOID SubjectKey ItemGroupRepeatKey;  if first.ItemGroupRepeatKey then do;    LBDTC=''; LBNAM=''; LBTESTCD=''; RESULTTEXT='';  end;    if ItemOID='IT.LB.LBDTC' then LBDTC=Value;  if ItemOID='IT.LB.LBNAM' then LBNAM=Value;  if ItemOID='IT.LB.LBTESTCD' then LBTESTCD=Value;  if ItemOID='IT.LB.LBSTRESN' then RESULTTEXT=Value;  if last.ItemGroupRepeatKey then output;run;proc sql; create table withbirth as select a.*,b.SEX,b.BRTHDTC from panels a left join subjects b on a.StudyOID=b.StudyOID and a.SubjectKey=b.SubjectKey; quit;data collected; set withbirth; AGE=.; if lengthn(BRTHDTC)=10 and lengthn(LBDTC)=10 then AGE=intck('year',input(BRTHDTC,yymmdd10.),input(LBDTC,yymmdd10.),'continuous'); LBSTRESN=input(RESULTTEXT,?? best32.); LBSEQ=input(ItemGroupRepeatKey,best32.); run;proc sql; create table joined as select a.*,b.UNIT as LBORRESU,b.NRLO as LBSTNRLO,b.NRHI as LBSTNRHI,b.EFFSTDT as RANGE_START,b.AGELO as RANGE_AGE,b.RANGE_ORDER from collected a left join lbrange b on a.LBNAM=b.LBNAM and a.LBTESTCD=b.LBTESTCD and a.SEX=b.SEX and a.AGE>=b.AGELO and a.AGE<b.AGEHI and a.LBDTC>=b.EFFSTDT and (missing(b.EFFENDT) or a.LBDTC<=b.EFFENDT); quit;proc sort data=joined; by StudyOID SubjectKey LBSEQ descending RANGE_START descending RANGE_AGE RANGE_ORDER; run;data selected; set joined; by StudyOID SubjectKey LBSEQ; if first.LBSEQ; run;data result; set selected; length DOMAIN $2 STUDYID USUBJID $1024 LBNRIND $8; DOMAIN='LB'; STUDYID=StudyOID; USUBJID=SubjectKey; LBNRIND=''; if not missing(LBSTRESN) and not missing(LBSTNRLO) and not missing(LBSTNRHI) then do; if LBSTRESN<LBSTNRLO then LBNRIND='LOW'; else if LBSTRESN>LBSTNRHI then LBNRIND='HIGH'; else LBNRIND='NORMAL'; end; run;proc sql;  create table final as select DOMAIN, STUDYID, USUBJID, LBSEQ, LBTESTCD, SEX, LBNAM, LBSTRESN, LBORRESU, LBSTNRLO, LBSTNRHI, LBNRIND from result;quit;proc export data=final outfile="/app/output/lb.csv" dbms=csv replace; run;