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 ADaM: 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: assign each systolic blood pressure (SYSBP) record to its analysis window, adding the analysis visit (AVISIT), its numeric order (AVISITN), and ANL01FL; add an expected record for each planned visit whose window holds no measurement.

Input: SDTM vital signs records (VS) carrying the test code (VSTESTCD), the collected visit (VISIT, VISITNUM), the collection date (VSDTC), the study day (VSDY), and the numeric result (VSSTRESN); and SDTM subject visits (SV) listing every planned visit of every subject, including planned visits that did not take place, alongside the unscheduled visits.

Variables:

  • ADT is the analysis date, carried from the SDTM collection date (VSDTC); ADY is the analysis relative day, carried from the SDTM study day (VSDY); AVAL is the analysis value, carried from the SDTM numeric result (VSSTRESN). PARAMCD mirrors the SDTM test code (VSTESTCD).
  • AVISIT is the analysis visit whose window holds the record's study day: SCREENING before day 0, BASELINE on day 1 (study days skip from day -1 to day 1), WEEK 2 on days 2 through 21, WEEK 4 on days 22 through 42, and POST-TREATMENT on day 43 onward with no upper bound. It is missing when the study day is missing, so such a record belongs to no window.
  • AVISITN is the numeric order of the analysis visit: -1 for SCREENING, 0 for BASELINE, 2 for WEEK 2, 4 for WEEK 4, and 99 for POST-TREATMENT; missing when there is no window.
  • ANL01FL is Y on the record that represents its study, subject, parameter, and analysis visit: the earliest by study day, with the lower sequence number breaking a tie on the same day. It is blank on every other record, including any record with no study day; an expected record never takes the flag, even when it is the only record in its window.

Note: windows follow the study day rather than the collected visit name, so a record the site left unscheduled still belongs to whichever window its day falls in, and a record on or past day 43 falls in the open-ended final window. Each window starts with its first day and ends before the next window's first day, so day 22 opens Week 4 and day 43 opens post-treatment. A planned screening, baseline, week 2, or week 4 visit whose window holds no measured study day still appears as an expected record carrying the planned visit and window and a sequence number continuing past the subject's measurements, but no date, day, or value, so summaries see a complete visit spine. Unscheduled visits and planned visits after week 4 never get one.

Inputs

Under review
2 source files

input/sv.csv

Edit
18 rows
input/sv.csv
STUDYIDUSUBJIDVISITNUMVISITSVPRESPSVOCCURSVSTDTC
YAMAA-011SCREENINGYY2025-01-02
YAMAA-012BASELINEYY2025-01-08
YAMAA-01—UNSCHEDULED—Y2025-01-09
YAMAA-01—UNSCHEDULED—Y2025-01-15
YAMAA-013WEEK 2YY2025-01-22
YAMAA-014WEEK 4YY2025-01-29
YAMAA-015WEEK 8YY2025-03-08
YAMAA-011SCREENINGYY2025-02-01
YAMAA-012BASELINEYY2025-02-08
YAMAA-013WEEK 2YY2025-02-21
YAMAA-01—UNSCHEDULED—Y2025-02-21
YAMAA-014WEEK 4YY2025-03-01
YAMAA-016FOLLOW-UPYY2025-03-22
YAMAA-011SCREENINGYY2025-03-01
YAMAA-012BASELINEYY2025-03-08
YAMAA-013WEEK 2YN—
YAMAA-014WEEK 4YN—
YAMAA-015WEEK 8YN—

input/vs.csv

Edit
15 rows
input/vs.csv
STUDYIDUSUBJIDVSTESTCDVSSEQVISITVISITNUMVSDTCVSDYVSSTRESN
YAMAA-01SYSBP1SCREENING12025-01-02-6128
YAMAA-01SYSBP2BASELINE22025-01-081132
YAMAA-01SYSBP3UNSCHEDULED—2025-01-158145
YAMAA-01SYSBP4WEEK 232025-01-2215138
YAMAA-01SYSBP5WEEK 442025-02-0529130
YAMAA-01SYSBP6WEEK 852025-03-0860126
YAMAA-01SYSBP7UNSCHEDULED—2025-01-092134
YAMAA-01SYSBP8WEEK 442025-01-2922131
YAMAA-01SYSBP1BASELINE22025-02-081118
YAMAA-01SYSBP2WEEK 232025-02-2114122
YAMAA-01SYSBP3WEEK 44——120
YAMAA-01SYSBP4UNSCHEDULED—2025-02-2114119
YAMAA-01SYSBP5WEEK 442025-03-0122124
YAMAA-01SYSBP6FOLLOW-UP62025-03-2243117
YAMAA-01SYSBP1BASELINE22025-03-081121

Expected output

Under review
19 expected rows

expected/advs.csv

Edit
19 rows
expected/advs.csv
STUDYIDUSUBJIDPARAMCDVSSEQVISITVISITNUMADTADYAVALAVISITAVISITNANL01FL
YAMAA-01SYSBP1SCREENING12025-01-02-6128SCREENING-1Y
YAMAA-01SYSBP2BASELINE22025-01-081132BASELINE0Y
YAMAA-01SYSBP3UNSCHEDULED—2025-01-158145WEEK 22—
YAMAA-01SYSBP4WEEK 232025-01-2215138WEEK 22—
YAMAA-01SYSBP5WEEK 442025-02-0529130WEEK 44—
YAMAA-01SYSBP6WEEK 852025-03-0860126POST-TREATMENT99Y
YAMAA-01SYSBP7UNSCHEDULED—2025-01-092134WEEK 22Y
YAMAA-01SYSBP8WEEK 442025-01-2922131WEEK 44Y
YAMAA-01SYSBP1BASELINE22025-02-081118BASELINE0Y
YAMAA-01SYSBP2WEEK 232025-02-2114122WEEK 22Y
YAMAA-01SYSBP3WEEK 44——120———
YAMAA-01SYSBP4UNSCHEDULED—2025-02-2114119WEEK 22—
YAMAA-01SYSBP5WEEK 442025-03-0122124WEEK 44Y
YAMAA-01SYSBP6FOLLOW-UP62025-03-2243117POST-TREATMENT99Y
YAMAA-01SYSBP1BASELINE22025-03-081121BASELINE0Y
YAMAA-01SYSBP7SCREENING1———SCREENING-1—
YAMAA-01SYSBP2SCREENING1———SCREENING-1—
YAMAA-01SYSBP3WEEK 23———WEEK 22—
YAMAA-01SYSBP4WEEK 44———WEEK 44—
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

Dataset NameVariable NameVariable LabelTypeCodelist/Controlled TermsCoreComputational MethodOrigin
ADVSSTUDYIDStudy IdentifiercharacterCopy value from VS.STUDYID.Predecessor
ADVSUSUBJIDUnique Subject IdentifiercharacterCopy value from VS.USUBJID.Predecessor
ADVSPARAMCDParameter CodecharacterCopy value from VS.VSTESTCD.Predecessor
ADVSVSSEQSequence NumbernumericCopy value from VS.VSSEQ.Predecessor
ADVSVISITVisit NamecharacterCopy value from VS.VISIT.Predecessor
ADVSVISITNUMVisit NumbernumericCopy value from VS.VISITNUM.Predecessor
ADVSADTAnalysis DatenumericISO 8601Copy value from VS.VSDTC.Predecessor
ADVSADYAnalysis Relative DaynumericCopy value from VS.VSDY.Predecessor
ADVSAVALAnalysis ValuenumericCopy value from VS.VSSTRESN.Predecessor
ADVSAVISITAnalysis VisitcharacterSCREENING, BASELINE, WEEK 2, WEEK 4, POST-TREATMENTBand ADY at 0, 2, 22, 43 into "SCREENING", "BASELINE", "WEEK 2", "WEEK 4" and "POST-TREATMENT", intervals closed on the left; a missing source → blank.Derived
ADVSAVISITNAnalysis Visit (N)numericRecode AVISIT: "SCREENING" → "-1", "BASELINE" → "0", "WEEK 2" → "2", "WEEK 4" → "4", "POST-TREATMENT" → "99"; missing values stay missing; unlisted values are errors.Derived
ADVSANL01FLAnalysis Record Flag 01character"Y" when AWRANK = 1 AND ADY IS NOT NULL; blank otherwise.Derived
TemplateInclude whenEXPECTED_ORDERSTUDYIDUSUBJIDPARAMCDVISITVISITNUMADTADYAVALAVISITVSSEQ
collectedConstant "None"NoneNoneNoneNoneNoneNoneNoneNoneNoneNone
expectedAVISIT IS NOT NULL AND WINDOW_COVER.VSSEQ IS NULLRow number within each group of STUDYID, USUBJID, PARAMCD, ordered by SV.VISITNUMCopy SV.STUDYIDCopy SV.USUBJIDConstant "SYSBP"Copy SV.VISITCopy SV.VISITNUMConstant "None"Constant "None"Constant "None"Recode SV.VISIT: "SCREENING" → "SCREENING", "BASELINE" → "BASELINE", "WEEK 2" → "WEEK 2", "WEEK 4" → "WEEK 4"; missing or unlisted values stay missingCompute PRIOR_MAX.VSSEQ + EXPECTED_ORDER
VariablePermitted values
AVISITSCREENING, BASELINE, WEEK 2, WEEK 4, POST-TREATMENT
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/adam-advs-windows/full.md

Edit
47 lines

Following CDISC ADaM standards, use the provided SDTM VS and SV datasets to create an ADVS dataset with one record per measurement, plus one expected record for each planned visit whose window holds no measurement. Derive PARAMCD from VSTESTCD, ADT from VSDTC, ADY from VSDY, and AVAL from VSSTRESN; then window on ADY as specified below.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, VSSEQ, VISIT, VISITNUM, ADT, ADY, AVAL, AVISIT, AVISITN, ANL01FL

AVISIT is the analysis visit whose window holds the record's study day: SCREENING before day 0, BASELINE on day 1, WEEK 2 on days 2 through 21, WEEK 4 on days 22 through 42, and POST-TREATMENT on day 43 onward with no upper bound. It has no value when the study day is missing, so such a record belongs to no window. Study days skip from day -1 to day 1.

AVISITN is the numeric order of the analysis visit: -1 for SCREENING, 0 for BASELINE, 2 for WEEK 2, 4 for WEEK 4, and 99 for POST-TREATMENT; it has no value when there is no window.

ANL01FL is Y on the record that represents its study, subject, parameter, and analysis visit: the earliest by study day, with the lower sequence number breaking a tie on the same day. It has no value on every other record, including any record with no study day.

A planned visit whose window holds no measurement still appears as an expected record, so summaries see a complete visit spine. SV lists each subject's planned visits, including those that did not take place, and the unscheduled visits. Each planned SCREENING, BASELINE, WEEK 2, or WEEK 4 visit gets one SYSBP expected record when no collected record's study day falls in the analysis window of that name; no other visit gets one. An expected record carries the planned visit name and number and the window's AVISIT and AVISITN, has no date, study day, or value, and never takes ANL01FL, even when it is the only record in its window. Expected records continue the subject's sequence numbering after the highest collected VSSEQ, in VISITNUM order.

Windows follow the study day rather than the collected visit name, so a record left unscheduled still belongs to whichever window its day falls in. Each window starts with its first day and ends before the next window's first day, so day 22 opens Week 4 and day 43 opens post-treatment.

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

prompts/adam-advs-windows/conventions.md

Edit
20 lines

Following CDISC ADaM standards, use the provided SDTM VS and SV datasets to create an ADVS dataset with one record per measurement, plus one expected record for each planned visit whose window holds no measurement. Derive PARAMCD from VSTESTCD, ADT from VSDTC, ADY from VSDY, and AVAL from VSSTRESN; then window on ADY as specified below.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, VSSEQ, VISIT, VISITNUM, ADT, ADY, AVAL, AVISIT, AVISITN, ANL01FL

AVISIT is SCREENING, BASELINE, WEEK 2, WEEK 4, or POST-TREATMENT, or has no value. AVISITN is -1, 0, 2, 4, or 99 in that order. ANL01FL is Y or has no value.

An expected record carries the planned visit name and number, the window's AVISIT and AVISITN, a continued VSSEQ, and no date, day, or value; it never takes ANL01FL. POST-TREATMENT never gets one.

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

prompts/adam-advs-windows/brief.md

Edit
12 lines

Following CDISC ADaM standards, use the provided SDTM VS and SV datasets to create an ADVS dataset with one record per measurement, plus one expected record for each planned visit whose window holds no measurement. Derive PARAMCD from VSTESTCD, ADT from VSDTC, ADY from VSDY, and AVAL from VSSTRESN; then window on ADY as specified below.

The output dataset should contain the following columns in this order: STUDYID, USUBJID, PARAMCD, VSSEQ, VISIT, VISITNUM, ADT, ADY, AVAL, AVISIT, AVISITN, ANL01FL

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

yamaa code

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

run.R

Edit
4 lines
library(yamaa)advs <- yamaa_domain("spec.yaml")$outputadvs

run.py

Edit
4 lines
import yamaaadvs = yamaa.yamaa_domain("spec.yaml").outputadvs

Solution without yamaa

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

solutions/adam-advs-windows/result.R

Edit
107 lines
# Reference solution for the yamaa benchmark adam-advs-windows (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)# SDTM VS staged as the ADaM input; rename to the analysis names the# rest of the solution works with.raw <- read_csv(  "/app/input/vs.csv",  col_types = cols(    VSSEQ = col_integer(), VISITNUM = col_double(), VSDTC = col_date(),    VSDY = col_integer(), VSSTRESN = col_double(), .default = col_character()  )) |>  rename(PARAMCD = VSTESTCD, ADT = VSDTC, ADY = VSDY, AVAL = VSSTRESN)# Windows follow the study day: screening before day 0, baseline on day# 1, weeks 2 and 4 on days 2-21 and 22-42, and post-treatment from day# 43 on with no upper bound. No study day means no window.framed <- raw |>  mutate(    AVISIT = case_when(      is.na(ADY) ~ NA_character_,      ADY < 0 ~ "SCREENING",      ADY < 2 ~ "BASELINE",      ADY < 22 ~ "WEEK 2",      ADY < 43 ~ "WEEK 4",      .default = "POST-TREATMENT"    ),    AVISITN = case_when(      AVISIT %in% "SCREENING" ~ -1L,      AVISIT %in% "BASELINE" ~ 0L,      AVISIT %in% "WEEK 2" ~ 2L,      AVISIT %in% "WEEK 4" ~ 4L,      AVISIT %in% "POST-TREATMENT" ~ 99L    )  )# SV lists every planned visit of every subject, including those that did# not take place, and the unscheduled visits.sv <- read_csv(  "/app/input/sv.csv",  col_types = cols(VISITNUM = col_double(), .default = col_character()))# Each planned SCREENING, BASELINE, WEEK 2, or WEEK 4 visit gets one SYSBP# expected record when no collected record's study day falls in the# analysis window of that name: the planned visit name and number and the# window, and no date, study day, or value. No other visit gets one.# Expected records continue the subject's sequence numbering after the# highest collected VSSEQ, in VISITNUM order.windows <- c("SCREENING" = -1L, "BASELINE" = 0L, "WEEK 2" = 2L, "WEEK 4" = 4L)top <- framed |>  group_by(STUDYID, USUBJID) |>  summarise(top_seq = max(VSSEQ), .groups = "drop")expected <- sv |>  filter(VISIT %in% names(windows)) |>  mutate(    PARAMCD = "SYSBP", AVISIT = VISIT, AVISITN = unname(windows[VISIT])  ) |>  anti_join(    filter(framed, !is.na(AVISIT)),    by = c("STUDYID", "USUBJID", "PARAMCD", "AVISIT")  ) |>  left_join(top, by = c("STUDYID", "USUBJID")) |>  group_by(STUDYID, USUBJID, PARAMCD) |>  arrange(VISITNUM, .by_group = TRUE) |>  mutate(    VSSEQ = coalesce(top_seq, 0L) + row_number(),    ADT = as.Date(NA),    ADY = NA_integer_,    AVAL = NA_real_  ) |>  ungroup() |>  select(    STUDYID, USUBJID, PARAMCD, VSSEQ, VISIT, VISITNUM, ADT, ADY, AVAL,    AVISIT, AVISITN  )framed <- bind_rows(framed, expected)# The earliest record by study day in each study, subject, parameter,# and visit; the lower sequence number breaks a same-day tie. Expected# records have no study day and never take the flag.flagged <- framed |>  filter(!is.na(AVISIT), !is.na(ADY)) |>  arrange(ADY, VSSEQ) |>  distinct(STUDYID, USUBJID, PARAMCD, AVISIT, .keep_all = TRUE) |>  select(STUDYID, USUBJID, PARAMCD, AVISIT, VSSEQ) |>  mutate(ANL01FL = "Y")advs <- framed |>  left_join(flagged, by = c("STUDYID", "USUBJID", "PARAMCD", "AVISIT", "VSSEQ")) |>  arrange(STUDYID, USUBJID, VSSEQ) |>  select(    STUDYID, USUBJID, PARAMCD, VSSEQ, VISIT, VISITNUM, ADT, ADY, AVAL,    AVISIT, AVISITN, ANL01FL  )dir.create("/app/output", showWarnings = FALSE)write_csv(advs, "/app/output/advs.csv", na = "")

solutions/adam-advs-windows/result.py

Edit
122 lines
# Reference solution for the yamaa benchmark adam-advs-windows (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 pl# SDTM VS staged as the ADaM input; rename to the analysis names the# rest of the solution works with.raw = (    pl.read_csv("/app/input/vs.csv", infer_schema=False)    .rename(        {            "VSTESTCD": "PARAMCD",            "VSDTC": "ADT",            "VSDY": "ADY",            "VSSTRESN": "AVAL",        }    )    .with_columns(        pl.col("VSSEQ").cast(pl.Int64, strict=False),        pl.col("VISITNUM").cast(pl.Float64, strict=False),        pl.col("ADT").str.to_date(strict=False),        pl.col("ADY").cast(pl.Int64, strict=False),        pl.col("AVAL").cast(pl.Float64, strict=False),    ))def visit(day: int | None) -> tuple[str | None, int | None]:    if day is None:        return None, None    if day < 0:        return "SCREENING", -1    if day < 2:        return "BASELINE", 0    if day < 22:        return "WEEK 2", 2    if day < 43:        return "WEEK 4", 4    return "POST-TREATMENT", 99framed = raw.with_columns(    pl.col("ADY")    .map_elements(lambda d: visit(d)[0], return_dtype=pl.String)    .alias("AVISIT"),    pl.col("ADY")    .map_elements(lambda d: visit(d)[1], return_dtype=pl.Int64)    .alias("AVISITN"),)# SV lists every planned visit of every subject, including those that did# not take place, and the unscheduled visits.sv = pl.read_csv("/app/input/sv.csv", infer_schema=False).with_columns(    pl.col("VISITNUM").cast(pl.Float64, strict=False))# Each planned SCREENING, BASELINE, WEEK 2, or WEEK 4 visit gets one SYSBP# expected record when no collected record's study day falls in the# analysis window of that name: the planned visit name and number and the# window, and no date, study day, or value. No other visit gets one.# Expected records continue the subject's sequence numbering after the# highest collected VSSEQ, in VISITNUM order.windows = {"SCREENING": -1, "BASELINE": 0, "WEEK 2": 2, "WEEK 4": 4}keys = ["STUDYID", "USUBJID", "PARAMCD"]covered = framed.select(*keys, "AVISIT").drop_nulls().unique()top = framed.group_by("STUDYID", "USUBJID").agg(pl.col("VSSEQ").max().alias("TOP"))expected = (    sv.filter(pl.col("VISIT").is_in(list(windows)))    .with_columns(        PARAMCD=pl.lit("SYSBP"),        AVISIT=pl.col("VISIT"),        AVISITN=pl.col("VISIT").replace_strict(windows, return_dtype=pl.Int64),    )    .join(covered, on=[*keys, "AVISIT"], how="anti")    .join(top, on=["STUDYID", "USUBJID"], how="left")    .sort([*keys, "VISITNUM"])    .with_columns(        VSSEQ=pl.col("TOP").fill_null(0)        + pl.int_range(1, pl.len() + 1, dtype=pl.Int64).over(keys),        ADT=pl.lit(None, dtype=pl.Date),        ADY=pl.lit(None, dtype=pl.Int64),        AVAL=pl.lit(None, dtype=pl.Float64),    )    .select(        *keys, "VSSEQ", "VISIT", "VISITNUM", "ADT", "ADY", "AVAL",        "AVISIT", "AVISITN",    ))framed = pl.concat([framed, expected], how="vertical")# The earliest record by study day in each study, subject, parameter,# and visit; the lower sequence number breaks a same-day tie. Expected# records have no study day and never take the flag.flagged = (    framed.filter(pl.col("AVISIT").is_not_null() & pl.col("ADY").is_not_null())    .sort(["ADY", "VSSEQ"])    .unique(["STUDYID", "USUBJID", "PARAMCD", "AVISIT"], keep="first", maintain_order=True)    .select("STUDYID", "USUBJID", "PARAMCD", "AVISIT", "VSSEQ")    .with_columns(ANL01FL=pl.lit("Y")))advs = (    framed.join(        flagged,        on=["STUDYID", "USUBJID", "PARAMCD", "AVISIT", "VSSEQ"],        how="left",    )    .sort(["STUDYID", "USUBJID", "VSSEQ"])    .select(        "STUDYID", "USUBJID", "PARAMCD", "VSSEQ", "VISIT", "VISITNUM",        "ADT", "ADY", "AVAL", "AVISIT", "AVISITN", "ANL01FL",    ))Path("/app/output").mkdir(parents=True, exist_ok=True)advs.write_csv("/app/output/advs.csv")

solutions/adam-advs-windows/result.sas

Edit
50 lines
/* opensas reference solution for the yamaa benchmark adam-advs-windows.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data sv;  length STUDYID USUBJID VISIT SVPRESP SVOCCUR SVSTDTC $1024;  infile "/app/input/sv.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. VISITNUM VISIT : $char1024. SVPRESP : $char1024. SVOCCUR : $char1024. SVSTDTC : $char1024.;run;data vs;  length STUDYID USUBJID VSTESTCD VISIT VSDTC VSDY $1024;  infile "/app/input/vs.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. VSTESTCD : $char1024. VSSEQ VISIT : $char1024. VISITNUM VSDTC : $char1024. VSDY : $char1024. VSSTRESN;run;data collected;  length PARAMCD $16 ADT $10 AVISIT $20 ANL01FL $1;  set vs;  PARAMCD=VSTESTCD; ADT=VSDTC; ADY=input(VSDY,best32.); AVAL=VSSTRESN;  if not missing(ADY) then do;    if ADY<0 then do; AVISIT='SCREENING'; AVISITN=-1; end;    else if ADY=1 then do; AVISIT='BASELINE'; AVISITN=0; end;    else if ADY>=2 and ADY<=21 then do; AVISIT='WEEK 2'; AVISITN=2; end;    else if ADY>=22 and ADY<=42 then do; AVISIT='WEEK 4'; AVISITN=4; end;    else if ADY>=43 then do; AVISIT='POST-TREATMENT'; AVISITN=99; end;  end;run;proc sort data=collected; by STUDYID USUBJID PARAMCD AVISITN ADY VSSEQ; run;data flagged; set collected; by STUDYID USUBJID PARAMCD AVISITN; if first.AVISITN and not missing(AVISITN) then ANL01FL='Y'; run;proc sql;  create table maxima as select STUDYID,USUBJID,max(VSSEQ) as maximum from collected group by STUDYID,USUBJID;  create table absent as select a.*,b.maximum from sv as a left join maxima as b on a.STUDYID=b.STUDYID and a.USUBJID=b.USUBJID  where a.VISIT in ('SCREENING','BASELINE','WEEK 2','WEEK 4') and not exists(select * from collected as c where a.STUDYID=c.STUDYID and a.USUBJID=c.USUBJID and c.PARAMCD='SYSBP' and a.VISIT=c.AVISIT);quit;proc sort data=absent; by STUDYID USUBJID VISITNUM; run;data expected;  length PARAMCD $16 ADT $10 AVISIT $20 ANL01FL $1;  set absent; by STUDYID USUBJID;  if first.USUBJID then counter=coalesce(maximum,0); counter+1; VSSEQ=counter;  PARAMCD='SYSBP'; AVISIT=VISIT;  select(VISIT); when('SCREENING') AVISITN=-1; when('BASELINE') AVISITN=0; when('WEEK 2') AVISITN=2; when('WEEK 4') AVISITN=4; otherwise; end;  ADY=.; AVAL=.;run;data result; set flagged expected; run;proc sql;  create table final as select STUDYID, USUBJID, PARAMCD, VSSEQ, VISIT, VISITNUM, ADT, ADY, AVAL, AVISIT, AVISITN, ANL01FL from result;quit;proc export data=final outfile="/app/output/advs.csv" dbms=csv replace; run;