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: reviewedEditUnder review
What this benchmark means
README.md

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

Goal: carry each physical functioning item response into PARAMCD, PARAM, and AVAL, add a subscale score record coded PFSCORE holding the mean answered-item response multiplied by 25 (mean divided by four, times 100), and say in AREASND why a value is missing.

Input: collected questionnaire responses for the physical functioning scale, with study, subject, and visit (STUDYID, USUBJID, VISIT), category (QSCAT), test code and test name (QSTESTCD, QSTEST), and numeric result (QSSTRESN) on the zero to four answer scale. An unanswered item is marked not done (QSSTAT), with the reason (QSREASND) when one was recorded. Records of other questionnaire scales are left out, so only the physical functioning records feed the dataset.

Variables:

  • PARAMCD: the test code (PF01 through PF04) on an item record, or PFSCORE on the added score record.
  • PARAM: the test name on an item record, or the subscale name on the added score record.
  • AVAL: on an item record, the numeric result on the zero to four answer scale, empty when the item was not answered; on the score record, the mean of the answered items at that visit multiplied by 25, empty when fewer than three of the four items were answered.
  • AREASND: on an item record, the reason recorded for an unanswered item, empty otherwise. On a score record without a value, the reason the visit's items record when none was answered and they record one reason between them, and NOT CALCULABLE otherwise; empty on a score record with a value.

Note: every visit with a PF01 item record, answered or not, carries a score record, so a visit with too few answers to score keeps an empty score and stays apart from a visit with no records at all.

Inputs

Under review
1 source file

input/qs.csv

Edit
21 rows
input/qs.csv
STUDYIDUSUBJIDVISITQSSEQQSCATQSTESTCDQSTESTQSSTRESNQSSTATQSREASND
PILOT7BASELINE1PHYSICAL FUNCTIONING SCALEPF01Trouble with strenuous activity2——
PILOT7BASELINE2PHYSICAL FUNCTIONING SCALEPF02Trouble with a long walk0——
PILOT7BASELINE3PHYSICAL FUNCTIONING SCALEPF03Trouble with a short walk2——
PILOT7BASELINE4PHYSICAL FUNCTIONING SCALEPF04Needs to stay in bed or a chair1——
PILOT7WEEK 125PHYSICAL FUNCTIONING SCALEPF01Trouble with strenuous activity3——
PILOT7WEEK 126PHYSICAL FUNCTIONING SCALEPF02Trouble with a long walk3——
PILOT7WEEK 127PHYSICAL FUNCTIONING SCALEPF03Trouble with a short walk—NOT DONE—
PILOT7WEEK 128PHYSICAL FUNCTIONING SCALEPF04Needs to stay in bed or a chair3——
PILOT7BASELINE1PHYSICAL FUNCTIONING SCALEPF01Trouble with strenuous activity1——
PILOT7BASELINE2PHYSICAL FUNCTIONING SCALEPF02Trouble with a long walk—NOT DONE—
PILOT7BASELINE3PHYSICAL FUNCTIONING SCALEPF03Trouble with a short walk—NOT DONE—
PILOT7BASELINE4PHYSICAL FUNCTIONING SCALEPF04Needs to stay in bed or a chair2——
PILOT7WEEK 125PHYSICAL FUNCTIONING SCALEPF01Trouble with strenuous activity—NOT DONEPATIENT REFUSAL
PILOT7WEEK 126PHYSICAL FUNCTIONING SCALEPF02Trouble with a long walk—NOT DONEPATIENT REFUSAL
PILOT7WEEK 127PHYSICAL FUNCTIONING SCALEPF03Trouble with a short walk—NOT DONEPATIENT REFUSAL
PILOT7WEEK 128PHYSICAL FUNCTIONING SCALEPF04Needs to stay in bed or a chair—NOT DONEPATIENT REFUSAL
PILOT7BASELINE1PHYSICAL FUNCTIONING SCALEPF01Trouble with strenuous activity4——
PILOT7BASELINE2PHYSICAL FUNCTIONING SCALEPF02Trouble with a long walk4——
PILOT7BASELINE3PHYSICAL FUNCTIONING SCALEPF03Trouble with a short walk4——
PILOT7BASELINE4PHYSICAL FUNCTIONING SCALEPF04Needs to stay in bed or a chair4——
PILOT7BASELINE5EMOTIONAL FUNCTIONING SCALEEF01Feeling tense3——

Expected output

Under review
25 expected rows

expected/adqs.csv

Edit
25 rows
expected/adqs.csv
STUDYIDUSUBJIDVISITPARAMCDPARAMAVALAREASND
PILOT7BASELINEPF01Trouble with strenuous activity2—
PILOT7BASELINEPF02Trouble with a long walk0—
PILOT7BASELINEPF03Trouble with a short walk2—
PILOT7BASELINEPF04Needs to stay in bed or a chair1—
PILOT7WEEK 12PF01Trouble with strenuous activity3—
PILOT7WEEK 12PF02Trouble with a long walk3—
PILOT7WEEK 12PF03Trouble with a short walk——
PILOT7WEEK 12PF04Needs to stay in bed or a chair3—
PILOT7BASELINEPF01Trouble with strenuous activity1—
PILOT7BASELINEPF02Trouble with a long walk——
PILOT7BASELINEPF03Trouble with a short walk——
PILOT7BASELINEPF04Needs to stay in bed or a chair2—
PILOT7WEEK 12PF01Trouble with strenuous activity—PATIENT REFUSAL
PILOT7WEEK 12PF02Trouble with a long walk—PATIENT REFUSAL
PILOT7WEEK 12PF03Trouble with a short walk—PATIENT REFUSAL
PILOT7WEEK 12PF04Needs to stay in bed or a chair—PATIENT REFUSAL
PILOT7BASELINEPF01Trouble with strenuous activity4—
PILOT7BASELINEPF02Trouble with a long walk4—
PILOT7BASELINEPF03Trouble with a short walk4—
PILOT7BASELINEPF04Needs to stay in bed or a chair4—
PILOT7BASELINEPFSCOREPhysical Functioning Subscale Score31.25—
PILOT7WEEK 12PFSCOREPhysical Functioning Subscale Score75—
PILOT7BASELINEPFSCOREPhysical Functioning Subscale Score—NOT CALCULABLE
PILOT7WEEK 12PFSCOREPhysical Functioning Subscale Score—PATIENT REFUSAL
PILOT7BASELINEPFSCOREPhysical Functioning Subscale Score100—
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

Dataset NameVariable NameVariable LabelTypeCodelist/Controlled TermsCoreComputational MethodOrigin
ADQSSTUDYIDStudy IdentifiercharacterCopy value from QS.STUDYID.Predecessor
ADQSUSUBJIDUnique Subject IdentifiercharacterCopy value from QS.USUBJID.Predecessor
ADQSVISITVisit NamecharacterCopy value from QS.VISIT.Predecessor
ADQSPARAMCDParameter CodecharacterPF01, PF02, PF03, PF04, PFSCORESet per constructed row; see the Row construction sheet.Derived
ADQSPARAMParametercharacterSet per constructed row; see the Row construction sheet.Derived
ADQSAVALAnalysis ValuenumericIf PARAMCD = 'PFSCORE' AND ITEMANS >= 3 then compute 100 * (ITEMSUM / ITEMANS) / 4; If PARAMCD = 'PFSCORE' then "None"; otherwise "ITEMRESN".Derived
ADQSAREASNDAnalysis Reason Not DonecharacterIf PARAMCD = 'PFSCORE' AND ITEMANS >= 3 then "None"; If PARAMCD = 'PFSCORE' AND ITEMANS = 0 AND ITEMRSNN = 1 then "ITEMRSN"; If PARAMCD = 'PFSCORE' then "NOT CALCULABLE"; otherwise "ITEMRSND".Derived
TemplateInclude whenPARAMCDPARAMITEMRESNITEMRSND
itemQS.QSCAT = 'PHYSICAL FUNCTIONING SCALE'Copy QS.QSTESTCDCopy QS.QSTESTCopy QS.QSSTRESNCopy QS.QSREASND
subscaleQS.QSCAT = 'PHYSICAL FUNCTIONING SCALE' AND QS.QSTESTCD = 'PF01'Constant "PFSCORE"Constant "Physical Functioning Subscale Score"Constant "None"Constant "None"
VariablePermitted values
PARAMCDPF01, PF02, PF03, PF04, PFSCORE
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/adam-adqs-subscale-score/full.md

Edit
25 lines

Following CDISC ADaM standards, use the provided QS dataset to create an ADQS dataset with one record per subject per visit per parameter.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, VISIT, PARAMCD, PARAM, AVAL, AREASND

The item records are the physical functioning records; a record of the emotional functioning scale is not in the output. On an item record, PARAMCD is the item's test code, PF01 through PF04, and PARAM is its test name. AVAL on an item record is the numeric result on the zero to four answer scale, and has no value when the item was not answered. Every visit with a PF01 item record, answered or not, also has a score record, with PARAMCD "PFSCORE" and PARAM "Physical Functioning Subscale Score". AVAL on the score record is the mean of the answered items at that visit multiplied by 25, and has no value when fewer than three of the four items were answered. AVAL is not rounded. AREASND on an item record is the reason recorded for the item not being done, and has no value when none was recorded. On a score record whose AVAL has no value, AREASND is the reason recorded on the visit's items when none of them was answered and they record one reason between them, and "NOT CALCULABLE" otherwise; it has no value on a score record with a value.

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

prompts/adam-adqs-subscale-score/conventions.md

Edit
13 lines

Following CDISC ADaM standards, use the provided QS dataset to create an ADQS dataset with one record per subject per visit per parameter.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, VISIT, PARAMCD, PARAM, AVAL, AREASND

AVAL on an item record is the numeric result on the zero to four answer scale. On the score record, PARAMCD is "PFSCORE" and PARAM is "Physical Functioning Subscale Score". AVAL is not rounded. AREASND holds a recorded reason, or is "NOT CALCULABLE", or has no value.

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

prompts/adam-adqs-subscale-score/brief.md

Edit
8 lines

Following CDISC ADaM standards, use the provided QS dataset to create an ADQS dataset with one record per subject per visit per parameter.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, VISIT, PARAMCD, PARAM, AVAL, AREASND

Read the source datasets from /app/input and save the completed dataset as /app/output/adqs.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/adam-adqs-subscale-score/result.R

Edit
72 lines
# Reference solution for the yamaa benchmark adam-adqs-subscale-score (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)qs <- read_csv(  "/app/input/qs.csv",  col_types = cols(    QSSEQ = col_integer(),    QSSTRESN = col_double(),    .default = col_character()  ))# Each physical functioning item response, on its zero to four scale.items <- qs |>  filter(QSTESTCD %in% c("PF01", "PF02", "PF03", "PF04")) |>  mutate(    PARAMCD = QSTESTCD, PARAM = QSTEST, AVAL = QSSTRESN, AREASND = QSREASND  ) |>  select(STUDYID, USUBJID, VISIT, PARAMCD, PARAM, AVAL, AREASND)# One score record per visit with a PF01 item record: the mean of the# answered items on a zero to one hundred scale, with no value when fewer# than three of the four items were answered. A score without a value takes# the items' reason when none was answered, and reads NOT CALCULABLE# otherwise.visits <- items |>  filter(PARAMCD %in% "PF01") |>  distinct(STUDYID, USUBJID, VISIT)stats <- items |>  group_by(STUDYID, USUBJID, VISIT) |>  summarise(    n_answered = sum(!is.na(AVAL)),    mean_answered = mean(AVAL, na.rm = TRUE),    n_reasons = n_distinct(AREASND, na.rm = TRUE),    reason = first(na.omit(AREASND)),    .groups = "drop"  ) |>  mutate(    mean_answered = if_else(      n_answered < 3 | is.nan(mean_answered),      NA_real_,      mean_answered * 25    ),    reason = case_when(      n_answered >= 3 ~ NA_character_,      n_answered == 0 & n_reasons == 1 ~ reason,      .default = "NOT CALCULABLE"    )  )scores <- visits |>  left_join(stats, by = c("STUDYID", "USUBJID", "VISIT")) |>  mutate(    PARAMCD = "PFSCORE",    PARAM = "Physical Functioning Subscale Score",    AVAL = mean_answered,    AREASND = reason  ) |>  select(STUDYID, USUBJID, VISIT, PARAMCD, PARAM, AVAL, AREASND)adqs <- bind_rows(items, scores) |>  arrange(STUDYID, USUBJID, VISIT, PARAMCD)dir.create("/app/output", showWarnings = FALSE)write_csv(adqs, "/app/output/adqs.csv", na = "")

solutions/adam-adqs-subscale-score/result.py

Edit
88 lines
# Reference solution for the yamaa benchmark adam-adqs-subscale-score (Python track).## Written from the benchmark's full prompt and its inputs alone, as an agent# would write it. Harbor's oracle agent runs it to check that the prompt can# be solved and that the grader scores a correct answer 1.import csvfrom collections import OrderedDictINPUT_QS = "/app/input/qs.csv"OUTPUT_ADQS = "/app/output/adqs.csv"ITEM_CODES = ("PF01", "PF02", "PF03", "PF04")SCORE_PARAMCD = "PFSCORE"SCORE_PARAM = "Physical Functioning Subscale Score"def fmt(value):    if value is None:        return ""    if value == int(value):        return str(int(value))    return repr(value)def main():    with open(INPUT_QS, newline="") as f:        rows = list(csv.DictReader(f))    # Item records: the physical functioning records (PF01..PF04).    items = [r for r in rows if r["QSTESTCD"] in ITEM_CODES]    # Group item records by subject and visit, preserving input order.    groups = OrderedDict()    for r in items:        key = (r["STUDYID"], r["USUBJID"], r["VISIT"])        groups.setdefault(key, []).append(r)    out = []    for (studyid, usubjid, visit), recs in groups.items():        answered = []        reasons = set()        for r in recs:            raw = (r["QSSTRESN"] or "").strip()            aval = float(raw) if raw != "" else None            if aval is not None:                answered.append(aval)            reason = (r["QSREASND"] or "").strip()            if reason:                reasons.add(reason)            out.append(                [studyid, usubjid, visit, r["QSTESTCD"], r["QSTEST"], fmt(aval), reason]            )        # Every visit with a PF01 item record also has a score record. A score        # without a value takes the items' reason when none was answered, and        # reads NOT CALCULABLE otherwise.        if any(r["QSTESTCD"] == "PF01" for r in recs):            if len(answered) >= 3:                score = sum(answered) / len(answered) * 25                areasnd = ""            elif not answered and len(reasons) == 1:                score = None                areasnd = next(iter(reasons))            else:                score = None                areasnd = "NOT CALCULABLE"            out.append(                [                    studyid,                    usubjid,                    visit,                    SCORE_PARAMCD,                    SCORE_PARAM,                    fmt(score),                    areasnd,                ]            )    with open(OUTPUT_ADQS, "w", newline="") as f:        writer = csv.writer(f)        writer.writerow(            ["STUDYID", "USUBJID", "VISIT", "PARAMCD", "PARAM", "AVAL", "AREASND"]        )        writer.writerows(out)if __name__ == "__main__":    main()

solutions/adam-adqs-subscale-score/result.sas

Edit
21 lines
/* opensas reference solution for the yamaa benchmark adam-adqs-subscale-score.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data qs;  length STUDYID USUBJID VISIT QSCAT QSTESTCD QSTEST QSSTAT QSREASND $1024;  infile "/app/input/qs.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. VISIT : $char1024. QSSEQ QSCAT : $char1024. QSTESTCD : $char1024. QSTEST : $char1024. QSSTRESN QSSTAT : $char1024. QSREASND : $char1024.;run;proc sql;  create table collected as select STUDYID,USUBJID,VISIT,QSTESTCD as PARAMCD,QSTEST as PARAM,QSSTRESN as AVAL,QSREASND as AREASND from qs where QSTESTCD in ('PF01','PF02','PF03','PF04');  create table anchors as select distinct STUDYID,USUBJID,VISIT from collected where PARAMCD='PF01';  create table scores as select a.STUDYID,a.USUBJID,a.VISIT,'PFSCORE' as PARAMCD,'Physical Functioning Subscale Score' as PARAM,    case when count(b.AVAL)>=3 then 25*mean(b.AVAL) else . end as AVAL,    case when count(b.AVAL)>=3 then ' ' when count(b.AVAL)=0 and count(distinct b.AREASND)=1 then max(b.AREASND) else 'NOT CALCULABLE' end as AREASND length=1024    from anchors as a left join collected as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID and a.VISIT=b.VISIT group by a.STUDYID,a.USUBJID,a.VISIT;quit;data result; set collected scores; run;proc export data=result(keep=STUDYID USUBJID VISIT PARAMCD PARAM AVAL AREASND) outfile="/app/output/adqs.csv" dbms=csv replace; run;