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: 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 | — |
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 |
|---|---|---|---|---|---|---|---|---|---|---|
| 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.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
Edit42 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 for each subject who marked several.
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
STUDYID is the ODM StudyOID and USUBJID is the SubjectKey, each as given.
Each collected race answer maps to a controlled term:
- "White", "Asian", "Black or African American", "American Indian or Alaska Native", and "Native Hawaiian or Other Pacific Islander" give the same name in upper case;
- "Other, specify: Fijian" gives OTHER;
- "Subject refused" gives UNKNOWN;
- "Not reported" gives NOT REPORTED;
- any other answer is not in the list and gives no value.
Every distinct answer a subject gave counts as one race, a non-answer such as "Not reported" included, and a race marked twice counts once. RACE is MULTIPLE when a subject has two or more races, and otherwise the mapped term of the single answer; it has no value when no race was marked or the single answer is not in the list. ETHNIC maps the collected answer: "Hispanic or Latino" gives HISPANIC OR LATINO, "Not Hispanic or Latino" gives NOT HISPANIC OR LATINO, "Not reported" gives NOT REPORTED, and "Unknown" gives UNKNOWN. ETHNIC has no value when no ethnicity was collected or its answer is not in the list. SUBJID repeats USUBJID.
SUPPDM carries records exactly for the subjects whose DM record says MULTIPLE, one per race. RDOMAIN is DM, IDVAR is USUBJID, and IDVARVAL is the subject identifier. QNAM is RACE1, RACE2, and so on, with QLABEL Race 1, Race 2, and so on, numbered in alphabetical order of the collected answer. QVAL maps the race as RACE does. QORIG is always 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
Edit20 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 for each subject who marked several.
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
Each collected race answer gives the same name in upper case, except that "Other, specify: Fijian" gives OTHER and "Subject refused" gives UNKNOWN; ETHNIC maps the collected answer to the same name in upper case. In SUPPDM, IDVAR is USUBJID; QNAM and QLABEL are RACE1 and Race 1, RACE2 and Race 2, and so on, in alphabetical order of the collected answer; QVAL maps the race as RACE does; and QORIG is CRF.
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
Edit13 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 for each subject who marked several.
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.
run.R
Edit5 lineslibrary(yamaa)dm <- yamaa_domain("spec_dm.yaml")$outputsuppdm <- yamaa_domain("spec_suppdm.yaml")$outputlist(dm = dm, suppdm = suppdm)run.py
Edit5 linesimport yamaadm = yamaa.yamaa_domain("spec_dm.yaml").outputsuppdm = yamaa.yamaa_domain("spec_suppdm.yaml").outputdm, suppdmsolutions/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
Edit157 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.from pathlib import Pathimport polars as plodm = pl.read_csv("/app/input/odm.csv", infer_schema=False)RACE_MAP = { "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 = { "Hispanic or Latino": "HISPANIC OR LATINO", "Not Hispanic or Latino": "NOT HISPANIC OR LATINO", "Not reported": "NOT REPORTED", "Unknown": "UNKNOWN",}# Distinct collected answers per subject.race_rows = odm.filter(pl.col("ItemOID") == "IT.DM.RACE", pl.col("Value") != "")ethnic_rows = odm.filter(pl.col("ItemOID") == "IT.DM.ETHNIC", pl.col("Value") != "")subjects = ( odm.select("StudyOID", "SubjectKey") .unique() .with_columns( STUDYID=pl.col("StudyOID"), USUBJID=pl.col("SubjectKey"), ))race_distinct = ( race_rows.select("StudyOID", "SubjectKey", "Value") .unique() .group_by("StudyOID", "SubjectKey") .agg(pl.col("Value").alias("answers")))ethnic_one = ( ethnic_rows.select("StudyOID", "SubjectKey", "Value") .group_by("StudyOID", "SubjectKey") .agg(pl.col("Value").first().alias("ethnic_answer")))dm = ( subjects.join(race_distinct, on=["StudyOID", "SubjectKey"], how="left") .join(ethnic_one, on=["StudyOID", "SubjectKey"], how="left") .with_columns( DOMAIN=pl.lit("DM"), SUBJID=pl.col("USUBJID"), RACE=pl.col("answers").map_elements( lambda ans: ( None if ans is None or len(ans) == 0 else ( "MULTIPLE" if len(ans) >= 2 else RACE_MAP.get(ans[0]) ) ), return_dtype=pl.String, ), ETHNIC=pl.col("ethnic_answer").map_elements( lambda v: ETHNIC_MAP.get(v) if v is not None else None, return_dtype=pl.String, ), ) .select("DOMAIN", "STUDYID", "USUBJID", "SUBJID", "RACE", "ETHNIC") .sort("USUBJID"))# SUPPDM: one record per race for MULTIPLE subjects, alphabetical by answer.multi = ( subjects.join(race_distinct, on=["StudyOID", "SubjectKey"], how="left") .with_columns( RACE_TMP=pl.col("answers").map_elements( lambda ans: ( None if ans is None or len(ans) == 0 else ("MULTIPLE" if len(ans) >= 2 else RACE_MAP.get(ans[0])) ), return_dtype=pl.String, ) ) .filter(pl.col("RACE_TMP") == "MULTIPLE"))supp_rows: list[dict] = []for row in multi.to_dicts(): studyid = row["StudyOID"] usubjid_val = row["USUBJID"] answers = sorted(row["answers"]) for i, ans in enumerate(answers, start=1): supp_rows.append( { "STUDYID": studyid, "RDOMAIN": "DM", "USUBJID": usubjid_val, "IDVAR": "USUBJID", "IDVARVAL": usubjid_val, "QNAM": f"RACE{i}", "QLABEL": f"Race {i}", "QVAL": RACE_MAP.get(ans), "QORIG": "CRF", "QEVAL": None, } )if supp_rows: suppdm = pl.DataFrame( supp_rows, schema={ "STUDYID": pl.String, "RDOMAIN": pl.String, "USUBJID": pl.String, "IDVAR": pl.String, "IDVARVAL": pl.String, "QNAM": pl.String, "QLABEL": pl.String, "QVAL": pl.String, "QORIG": pl.String, "QEVAL": pl.String, }, ).sort(["USUBJID", "QNAM"])else: suppdm = pl.DataFrame( [], schema={ "STUDYID": pl.String, "RDOMAIN": pl.String, "USUBJID": pl.String, "IDVAR": pl.String, "IDVARVAL": pl.String, "QNAM": pl.String, "QLABEL": pl.String, "QVAL": pl.String, "QORIG": pl.String, "QEVAL": pl.String, }, )Path("/app/output").mkdir(exist_ok=True)dm.write_csv("/app/output/dm.csv")suppdm.write_csv("/app/output/suppdm.csv")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
Comments
Signed in with GitHub; saved in repository Discussions