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
README.mdPublic entries: Python and R. Passing coverage requires an installed inventory.
Goal: build one Demographics (DM) record per subject carrying race
(RACE) and ethnicity (ETHNIC), and one supplemental Demographics
(SUPPDM) record per reported race for each subject who marked several.
Input: electronic data capture (EDC) output in long form, one row per collected item; each race option a subject marks contributes one race row, and ethnicity contributes its own row. A race marked twice counts once.
Variables:
- RACE (DM): reported race in controlled terms:
WHITE,ASIAN,BLACK OR AFRICAN AMERICAN,AMERICAN INDIAN OR ALASKA NATIVE,NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER,MULTIPLEwhen several races were marked,OTHERfor a listed free-text answer that fits none of the named races,UNKNOWNfor a refused answer, andNOT REPORTEDfor a recorded non-answer; blank when no race item was marked or a single answer is not in the sponsor's list. - ETHNIC (DM): reported ethnicity in controlled terms:
HISPANIC OR LATINO,NOT HISPANIC OR LATINO,NOT REPORTED, orUNKNOWN; blank when no ethnicity item was collected or its answer is not in the sponsor's list. - IDVARVAL (SUPPDM): the subject identifier, linking the record to its
DM row through
IDVAR=USUBJID. - QNAM / QLABEL (SUPPDM):
RACE1/Race 1,RACE2/Race 2, and so on, one per distinct race the subject marked, numbered in alphabetical order of the collected answer because the input carries no report order. - QVAL (SUPPDM): that race in controlled terms, as in RACE.
- QORIG (SUPPDM): the origin, always
CRF; QEVAL is blank.
Note: SUPPDM carries race records exactly for the subjects whose DM
record says MULTIPLE, however many races they marked.
Inputs
Under review1 source fileinput/odm.csv
Edit30 rows| StudyOID | MetaDataVersionOID | SubjectKey | StudyEventOID | StudyEventRepeatKey | ItemGroupOID | ItemGroupRepeatKey | ItemOID | Value |
|---|---|---|---|---|---|---|---|---|
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-101 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Asian |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Black or African American |
| YAMAA-01 | MDV.1 | YAMAA-01-102 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-103 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-104 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | American Indian or Alaska Native |
| YAMAA-01 | MDV.1 | YAMAA-01-104 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-104 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not reported |
| YAMAA-01 | MDV.1 | YAMAA-01-105 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Other, specify: Fijian |
| YAMAA-01 | MDV.1 | YAMAA-01-105 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Unknown |
| YAMAA-01 | MDV.1 | YAMAA-01-106 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Subject refused |
| YAMAA-01 | MDV.1 | YAMAA-01-106 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-107 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Asian |
| YAMAA-01 | MDV.1 | YAMAA-01-107 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Black or African American |
| YAMAA-01 | MDV.1 | YAMAA-01-107 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-107 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-108 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-108 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-108 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-109 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-109 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Asian |
| YAMAA-01 | MDV.1 | YAMAA-01-109 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-109 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not Hispanic or Latino |
| YAMAA-01 | MDV.1 | YAMAA-01-110 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Not reported |
| YAMAA-01 | MDV.1 | YAMAA-01-111 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Pacific Islander |
| YAMAA-01 | MDV.1 | YAMAA-01-111 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Prefer not to answer |
| YAMAA-01 | MDV.1 | YAMAA-01-112 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | Not reported |
| YAMAA-01 | MDV.1 | YAMAA-01-112 | SCREENING | 1 | IG.DM | 1 | IT.DM.RACE | White |
| YAMAA-01 | MDV.1 | YAMAA-01-112 | SCREENING | 1 | IG.DM | 1 | IT.DM.ETHNIC | Not Hispanic or Latino |
Expected output
Under review23 expected rowsexpected/dm.csv
Edit12 rows| DOMAIN | STUDYID | USUBJID | SUBJID | RACE | ETHNIC |
|---|---|---|---|---|---|
| DM | YAMAA-01 | YAMAA-01-101 | WHITE | NOT HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-102 | MULTIPLE | HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-103 | — | NOT HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-104 | MULTIPLE | NOT REPORTED | |
| DM | YAMAA-01 | YAMAA-01-105 | OTHER | UNKNOWN | |
| DM | YAMAA-01 | YAMAA-01-106 | UNKNOWN | HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-107 | MULTIPLE | NOT HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-108 | WHITE | NOT HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-109 | MULTIPLE | NOT HISPANIC OR LATINO | |
| DM | YAMAA-01 | YAMAA-01-110 | NOT REPORTED | — | |
| DM | YAMAA-01 | YAMAA-01-111 | — | — | |
| DM | YAMAA-01 | YAMAA-01-112 | MULTIPLE | NOT HISPANIC OR LATINO |
expected/suppdm.csv
Edit11 rows| STUDYID | RDOMAIN | USUBJID | IDVAR | IDVARVAL | QNAM | QLABEL | QVAL | QORIG | QEVAL |
|---|---|---|---|---|---|---|---|---|---|
| YAMAA-01 | DM | USUBJID | YAMAA-01-102 | RACE1 | Race 1 | ASIAN | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-102 | RACE2 | Race 2 | BLACK OR AFRICAN AMERICAN | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-104 | RACE1 | Race 1 | AMERICAN INDIAN OR ALASKA NATIVE | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-104 | RACE2 | Race 2 | WHITE | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-107 | RACE1 | Race 1 | ASIAN | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-107 | RACE2 | Race 2 | BLACK OR AFRICAN AMERICAN | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-107 | RACE3 | Race 3 | WHITE | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-109 | RACE1 | Race 1 | ASIAN | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-109 | RACE2 | Race 2 | WHITE | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-112 | RACE1 | Race 1 | NOT REPORTED | CRF | — | |
| YAMAA-01 | DM | USUBJID | YAMAA-01-112 | RACE2 | Race 2 | WHITE | CRF | — |
Human review and assessment
| Variable Name | Variable Label | Type | Length | Controlled Terms or Format | Origin | Core | Conversion Definition | Variable Type | Variable Order | Comments for Define |
|---|---|---|---|---|---|---|---|---|---|---|
| STUDYID | Study Identifier | Char | Derived | Set per constructed row; see the Row construction sheet. | SUPP | 1 | ||||
| RDOMAIN | Related Domain Abbreviation | Char | Assigned | Set constant value "DM". | SUPP | 2 | ||||
| USUBJID | Unique Subject Identifier | Char | Derived | Set per constructed row; see the Row construction sheet. | SUPP | 3 | ||||
| IDVAR | Identifying Variable | Char | Assigned | Set constant value "USUBJID". | SUPP | 4 | ||||
| IDVARVAL | Identifying Variable Value | Char | Derived | Copy value from USUBJID. | SUPP | 5 | ||||
| QNAM | Qualifier Variable Name | Char | Derived | Fill the template "RACE{RACENUMC}". | SUPP | 6 | ||||
| QLABEL | Qualifier Label | Char | Derived | Fill the template "Race {RACENUMC}". | SUPP | 7 | ||||
| QVAL | Data Value | Char | AMERICAN INDIAN OR ALASKA NATIVE, ASIAN, BLACK OR AFRICAN AMERICAN, NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER, WHITE, OTHER, NOT REPORTED, UNKNOWN | Derived | Recode QVALRAW: "American Indian or Alaska Native" → "AMERICAN INDIAN OR ALASKA NATIVE", "Asian" → "ASIAN", "Black or African American" → "BLACK OR AFRICAN AMERICAN", "Native Hawaiian or Other Pacific Islander" → "NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER", "White" → "WHITE", "Other, specify: Fijian" → "OTHER", "Not reported" → "NOT REPORTED", "Subject refused" → "UNKNOWN"; missing values stay missing; unlisted values are errors. | SUPP | 8 | |||
| QORIG | Origin | Char | Assigned | Set constant value "CRF". | SUPP | 9 | ||||
| QEVAL | Evaluator | Char | Assigned | Set constant value "None". | SUPP | 10 |
| Template | Include when | STUDYID | USUBJID | ITEMOID | QVALRAW | DMRACE |
|---|---|---|---|---|---|---|
| reported_race | ITEMOID = 'IT.DM.RACE' AND DMRACE = 'MULTIPLE' | Copy ODM.StudyOID | Copy ODM.SubjectKey | Copy ODM.ItemOID | Copy ODM.Value | Copy RACE from the DM record whose STUDYID equals ODM.StudyOID and USUBJID equals ODM.SubjectKey, and a row with no match is an error |
| Variable | Permitted values |
|---|---|
| QVAL | AMERICAN INDIAN OR ALASKA NATIVE, ASIAN, BLACK OR AFRICAN AMERICAN, NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER, WHITE, OTHER, NOT REPORTED, UNKNOWN |
| Version | Date | Author | Description | Reviewer | Sign-off |
|---|---|---|---|---|---|
| 1.7 | 2026-10-20 | yamaa docs build | Read 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.6 | 2026-10-17 | yamaa docs build | Describe collected date parts written as ISO 8601 date text and times of day counted in seconds since midnight | ||
| 1.5 | 2026-10-17 | yamaa docs build | Name 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.4 | 2026-10-15 | yamaa docs build | State datetime precision codes through the hour and minute, and complete only the time parts a collected datetime lacks | ||
| 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-dm-race-ethnicity/full.md
Edit33 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create a DM dataset with one record per subject and a SUPPDM dataset with one record per reported race.
The DM dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, SUBJID, RACE, ETHNIC The SUPPDM dataset should contain the following columns in this order: STUDYID, RDOMAIN, USUBJID, IDVAR, IDVARVAL, QNAM, QLABEL, QVAL, QORIG, QEVAL
DOMAIN is "DM" and RDOMAIN is "DM". STUDYID is the ODM study OID and USUBJID is the ODM subject key, each as given. SUBJID is the same value as USUBJID. A race marked twice counts once. RACE is "MULTIPLE" when a subject marked several distinct races. When a subject marked one race, RACE is "WHITE" for "White", "ASIAN" for "Asian", "BLACK OR AFRICAN AMERICAN" for "Black or African American", and "AMERICAN INDIAN OR ALASKA NATIVE" for "American Indian or Alaska Native". RACE is "OTHER" for "Other, specify: Fijian", "NOT REPORTED" for "Not reported", and "UNKNOWN" for "Subject refused". RACE has no value when no race was marked or the one marked answer is not one of the answers above. ETHNIC is "HISPANIC OR LATINO" for "Hispanic or Latino", "NOT HISPANIC OR LATINO" for "Not Hispanic or Latino", "NOT REPORTED" for "Not reported", and "UNKNOWN" for "Unknown". ETHNIC has no value when no ethnicity was collected or the collected answer is not one of the answers above. SUPPDM holds one record for each distinct race a subject marked, for exactly the subjects whose RACE is "MULTIPLE". IDVAR is "USUBJID" and IDVARVAL is the subject's USUBJID. A subject's records are numbered in alphabetical order of the collected answer: QNAM is "RACE1", "RACE2", and so on, and QLABEL is "Race 1", "Race 2", and so on. QVAL is that race in controlled terms, as in RACE. QORIG is "CRF" and QEVAL has no value.
Read the source datasets from /app/input and save the completed DM dataset as /app/output/dm.csv and the completed SUPPDM dataset as /app/output/suppdm.csv.
prompts/sdtm-dm-race-ethnicity/conventions.md
Edit19 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create a DM dataset with one record per subject and a SUPPDM dataset with one record per reported race.
The DM dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, SUBJID, RACE, ETHNIC The SUPPDM dataset should contain the following columns in this order: STUDYID, RDOMAIN, USUBJID, IDVAR, IDVARVAL, QNAM, QLABEL, QVAL, QORIG, QEVAL
DOMAIN is "DM" and RDOMAIN is "DM". Each collected race answer gives the same name in upper case, except that "Other, specify: Fijian" gives "OTHER" and "Subject refused" gives "UNKNOWN"; RACE also has no value. Each collected ethnicity answer gives the same name in upper case; ETHNIC also has no value. IDVAR is "USUBJID". QNAM is "RACE1", "RACE2", and so on, and QLABEL is "Race 1", "Race 2", and so on. QORIG is "CRF" and QEVAL has no value.
Read the source datasets from /app/input and save the completed DM dataset as /app/output/dm.csv and the completed SUPPDM dataset as /app/output/suppdm.csv.
prompts/sdtm-dm-race-ethnicity/brief.md
Edit11 linesFollowing CDISC SDTM standards, use the provided ODM dataset to create a DM dataset with one record per subject and a SUPPDM dataset with one record per reported race.
The DM dataset should contain the following columns in this order: DOMAIN, STUDYID, USUBJID, SUBJID, RACE, ETHNIC The SUPPDM dataset should contain the following columns in this order: STUDYID, RDOMAIN, USUBJID, IDVAR, IDVARVAL, QNAM, QLABEL, QVAL, QORIG, QEVAL
Read the source datasets from /app/input and save the completed DM dataset as /app/output/dm.csv and the completed SUPPDM dataset as /app/output/suppdm.csv.
R
Edit4 lineslibrary(yamaa)yamaa_spec("spec_dm.yaml")$run()$save()yamaa_spec("spec_suppdm.yaml")$run()$outputPython
Edit4 linesimport yamaayamaa.spec("spec_dm.yaml").run().save()yamaa.spec("spec_suppdm.yaml").run().outputsolutions/sdtm-dm-race-ethnicity/result.R
Edit118 lines# Reference solution for the yamaa benchmark sdtm-dm-race-ethnicity (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(stringr)library(purrr)odm <- read_csv( "/app/input/odm.csv", col_types = cols(.default = col_character()))race_map <- c( "White" = "WHITE", "Asian" = "ASIAN", "Black or African American" = "BLACK OR AFRICAN AMERICAN", "American Indian or Alaska Native" = "AMERICAN INDIAN OR ALASKA NATIVE", "Native Hawaiian or Other Pacific Islander" = "NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER", "Other, specify: Fijian" = "OTHER", "Subject refused" = "UNKNOWN", "Not reported" = "NOT REPORTED")ethnic_map <- c( "Hispanic or Latino" = "HISPANIC OR LATINO", "Not Hispanic or Latino" = "NOT HISPANIC OR LATINO", "Not reported" = "NOT REPORTED", "Unknown" = "UNKNOWN")map_term <- function(x, lut) { out <- unname(lut[x]) out[is.na(out)] <- NA_character_ out}subjects <- odm |> distinct(StudyOID, SubjectKey) |> mutate( STUDYID = StudyOID, USUBJID = SubjectKey )race_distinct <- odm |> filter(ItemOID == "IT.DM.RACE", Value != "", !is.na(Value)) |> distinct(StudyOID, SubjectKey, Value)race_per_subject <- race_distinct |> group_by(StudyOID, SubjectKey) |> summarise(answers = list(sort(unique(Value))), .groups = "drop")ethnic_per_subject <- odm |> filter(ItemOID == "IT.DM.ETHNIC", Value != "", !is.na(Value)) |> group_by(StudyOID, SubjectKey) |> summarise(ethnic_answer = first(Value), .groups = "drop")dm <- subjects |> left_join(race_per_subject, by = c("StudyOID", "SubjectKey")) |> left_join(ethnic_per_subject, by = c("StudyOID", "SubjectKey")) |> rowwise() |> mutate( DOMAIN = "DM", SUBJID = USUBJID, RACE = if (is.null(answers) || length(answers) == 0 || all(is.na(answers))) { NA_character_ } else if (length(answers) >= 2) { "MULTIPLE" } else { map_term(answers[[1]], race_map) }, ETHNIC = map_term(ethnic_answer, ethnic_map) ) |> ungroup() |> select(STUDYID, USUBJID, DOMAIN, SUBJID, RACE, ETHNIC, StudyOID, SubjectKey) |> arrange(USUBJID)multi <- dm |> filter(RACE == "MULTIPLE")suppdm <- race_distinct |> inner_join( multi |> select(StudyOID, SubjectKey), by = c("StudyOID", "SubjectKey") ) |> left_join( subjects |> select(StudyOID, SubjectKey, STUDYID, USUBJID), by = c("StudyOID", "SubjectKey") ) |> arrange(USUBJID, Value) |> group_by(STUDYID, USUBJID) |> mutate( n = row_number(), QNAM = paste0("RACE", n), QLABEL = paste("Race", n) ) |> ungroup() |> mutate( RDOMAIN = "DM", IDVAR = "USUBJID", IDVARVAL = USUBJID, QVAL = map_term(Value, race_map), QORIG = "CRF", QEVAL = NA_character_ ) |> select(STUDYID, RDOMAIN, USUBJID, IDVAR, IDVARVAL, QNAM, QLABEL, QVAL, QORIG, QEVAL) |> arrange(USUBJID, QNAM)dir.create("/app/output", showWarnings = FALSE)write_csv( dm |> select(DOMAIN, STUDYID, USUBJID, SUBJID, RACE, ETHNIC), "/app/output/dm.csv", na = "")write_csv(suppdm, "/app/output/suppdm.csv", na = "")solutions/sdtm-dm-race-ethnicity/result.py
Edit121 lines# Reference solution for the yamaa benchmark sdtm-dm-race-ethnicity (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."""Build DM and SUPPDM from the ODM extract.Reads /app/input/odm.csv (long format: one row per collected item value)and writes /app/output/dm.csv (one record per subject) and/app/output/suppdm.csv (one record per distinct race marked, forsubjects whose RACE is "MULTIPLE")."""import csvfrom collections import OrderedDictINPUT_DIR = "/app/input"OUTPUT_DIR = "/app/output"RACE_MAP = { "White": "WHITE", "Asian": "ASIAN", "Black or African American": "BLACK OR AFRICAN AMERICAN", "American Indian or Alaska Native": "AMERICAN INDIAN OR ALASKA NATIVE", "Other, specify: Fijian": "OTHER", "Not reported": "NOT REPORTED", "Subject refused": "UNKNOWN",}ETHNIC_MAP = { "Hispanic or Latino": "HISPANIC OR LATINO", "Not Hispanic or Latino": "NOT HISPANIC OR LATINO", "Not reported": "NOT REPORTED", "Unknown": "UNKNOWN",}DM_COLUMNS = ["DOMAIN", "STUDYID", "USUBJID", "SUBJID", "RACE", "ETHNIC"]SUPPDM_COLUMNS = [ "STUDYID", "RDOMAIN", "USUBJID", "IDVAR", "IDVARVAL", "QNAM", "QLABEL", "QVAL", "QORIG", "QEVAL",]def main(): with open(f"{INPUT_DIR}/odm.csv", newline="", encoding="utf-8") as f: rows = list(csv.DictReader(f)) # Collect, per subject, every marked race answer and the ethnicity # answer, keeping subjects in order of first appearance. subjects = OrderedDict() for row in rows: key = (row["StudyOID"], row["SubjectKey"]) entry = subjects.setdefault(key, {"races": [], "ethnic": ""}) if row["ItemOID"] == "IT.DM.RACE": if row["Value"]: entry["races"].append(row["Value"]) elif row["ItemOID"] == "IT.DM.ETHNIC": entry["ethnic"] = row["Value"] dm_rows = [] suppdm_rows = [] for (studyid, usubjid), entry in subjects.items(): distinct_races = sorted(set(entry["races"])) if len(distinct_races) > 1: race = "MULTIPLE" elif len(distinct_races) == 1: race = RACE_MAP.get(distinct_races[0], "") else: race = "" ethnic = ETHNIC_MAP.get(entry["ethnic"], "") if entry["ethnic"] else "" dm_rows.append( { "DOMAIN": "DM", "STUDYID": studyid, "USUBJID": usubjid, "SUBJID": usubjid, "RACE": race, "ETHNIC": ethnic, } ) if race == "MULTIPLE": for i, answer in enumerate(distinct_races, start=1): suppdm_rows.append( { "STUDYID": studyid, "RDOMAIN": "DM", "USUBJID": usubjid, "IDVAR": "USUBJID", "IDVARVAL": usubjid, "QNAM": f"RACE{i}", "QLABEL": f"Race {i}", "QVAL": RACE_MAP.get(answer, ""), "QORIG": "CRF", "QEVAL": "", } ) with open(f"{OUTPUT_DIR}/dm.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=DM_COLUMNS) writer.writeheader() writer.writerows(dm_rows) with open(f"{OUTPUT_DIR}/suppdm.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=SUPPDM_COLUMNS) writer.writeheader() writer.writerows(suppdm_rows)if __name__ == "__main__": main()solutions/sdtm-dm-race-ethnicity/result.sas
Edit47 lines/* opensas reference solution for the yamaa benchmark sdtm-dm-race-ethnicity. 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;proc sql;create table subjects as select distinct StudyOID as STUDYID,SubjectKey as USUBJID from odm;create table races as select distinct StudyOID as STUDYID,SubjectKey as USUBJID,Value from odm where ItemOID='IT.DM.RACE' and not missing(Value);quit;data mapped; set races; length QVAL $1024; if Value in ('White','Asian','Black or African American','American Indian or Alaska Native','Native Hawaiian or Other Pacific Islander') then QVAL=upcase(Value);else if Value='Other, specify: Fijian' then QVAL='OTHER'; else if Value='Subject refused' then QVAL='UNKNOWN'; else if Value='Not reported' then QVAL='NOT REPORTED'; run;proc sql; create table racecounts as select STUDYID,USUBJID,count(*) as NRACE from mapped group by STUDYID,USUBJID; quit;proc sort data=odm out=ordered; by StudyOID SubjectKey; run;data ethnicity; length ETHNIC $1024; retain ETHNIC; set ordered; by StudyOID SubjectKey; if first.SubjectKey then do; ETHNIC=''; end; if ItemOID='IT.DM.ETHNIC' then ETHNIC=Value; if last.SubjectKey then output;run;data ethnic; set ethnicity; length STUDYID USUBJID $1024; STUDYID=StudyOID; USUBJID=SubjectKey;if ETHNIC in ('Hispanic or Latino','Not Hispanic or Latino','Not reported','Unknown') then ETHNIC=upcase(ETHNIC); else ETHNIC=''; run;proc sql;create table dmjoined as select a.*,b.NRACE,c.QVAL,d.ETHNIC from subjects a left join racecounts b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID left join mapped c on a.STUDYID=c.STUDYID and a.USUBJID=c.USUBJID and b.NRACE=1 left join ethnic d on a.STUDYID=d.STUDYID and a.USUBJID=d.USUBJID;create table multirace as select a.* from mapped a inner join racecounts b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID where b.NRACE>1;quit;data dmout; set dmjoined; length DOMAIN $2 SUBJID RACE $1024; DOMAIN='DM'; SUBJID=USUBJID; RACE=QVAL; if NRACE>1 then RACE='MULTIPLE'; keep DOMAIN STUDYID USUBJID SUBJID RACE ETHNIC; run;proc sort data=multirace; by STUDYID USUBJID Value; run;data suppout; set multirace; by STUDYID USUBJID; length RDOMAIN $2 IDVAR IDVARVAL QNAM QLABEL QORIG QEVAL $1024; retain N;if first.USUBJID then N=0; N+1; RDOMAIN='DM'; IDVAR='USUBJID'; IDVARVAL=USUBJID; QNAM=cats('RACE',put(N,best32.)); QLABEL=catx(' ','Race',strip(put(N,best32.))); QORIG='CRF'; QEVAL=''; run;/* KEEP drops variables but does not set their export order. */proc sql;create table dmexport as select DOMAIN,STUDYID,USUBJID,SUBJID,RACE,ETHNIC from dmout;create table suppdmout as select STUDYID,RDOMAIN,USUBJID,IDVAR,IDVARVAL,QNAM,QLABEL,QVAL,QORIG,QEVAL from suppout;quit;proc export data=dmexport outfile='/app/output/dm.csv' dbms=csv replace; run;proc export data=suppdmout outfile='/app/output/suppdm.csv' dbms=csv replace; run;
yamaa