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

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

Goal: keep every pharmacokinetic (PK) parameter record of a multiple-dose study and add, per subject, the accumulation ratios of the area under the curve over the dosing interval (ARAUC) and of the maximum concentration (ARCMAX): the steady-state value divided by the day 1 value.

Input: the SDTM PK parameters (PP), one record per subject, parameter and profile visit, with the standard numeric result (PPSTRESN) and unit (PPSTRESU); and the subject-level dataset (ADSL) with the planned arm (ARMCD, ARM). Arm A is dosed once daily on days 1 to 7, arm B on days 1 to 5.

Variables:

  • PARAMCD, PARAM: the collected PPTESTCD and PPTEST; on the new records ARAUC "Accumulation Ratio AUCTAU" and ARCMAX "Accumulation Ratio Cmax", the controlled terms.
  • AVISIT: the collected visit (DAY 1, DAY 5, DAY 7); on a ratio record the visit of the steady-state profile.
  • AVAL: the collected standard result, empty when the parameter was not calculated; on a ratio record the steady-state AUCTAU or CMAX divided by the day 1 value of the same subject, not rounded and possibly below 1.
  • AVALU: the collected standard unit; RATIO on a ratio record.

Note: the steady-state profile is the one on the last planned dosing day of the subject's arm (day 7 for arm A, day 5 for arm B), and the first-dose profile is the day 1 profile. A profile on any other day, such as an interim profile during dosing or the last profile of a subject who stopped dosing early, enters neither value. A ratio record exists only when both values are present; a parameter that was not calculated keeps its own record with no value.

Inputs

Under review
2 source files

input/adsl.csv

Edit
5 rows
input/adsl.csv
STUDYIDUSUBJIDARMCDARM
YAMAA-01A100 MG ONCE DAILY DAYS 1-7
YAMAA-01A100 MG ONCE DAILY DAYS 1-7
YAMAA-01B100 MG ONCE DAILY DAYS 1-5
YAMAA-01B100 MG ONCE DAILY DAYS 1-5
YAMAA-01B100 MG ONCE DAILY DAYS 1-5

input/pp.csv

Edit
20 rows
input/pp.csv
STUDYIDDOMAINUSUBJIDPPSEQPPTESTCDPPTESTPPSTRESNPPSTRESUPPSTATPPREASNDVISITNUMVISIT
YAMAA-01PP1AUCTAUAUC Over Dosing Interval412.5h*ng/mL——1DAY 1
YAMAA-01PP2CMAXMax Conc48.6ng/mL——1DAY 1
YAMAA-01PP3AUCTAUAUC Over Dosing Interval618h*ng/mL——5DAY 5
YAMAA-01PP4CMAXMax Conc70.2ng/mL——5DAY 5
YAMAA-01PP5AUCTAUAUC Over Dosing Interval703.2h*ng/mL——7DAY 7
YAMAA-01PP6CMAXMax Conc77.9ng/mL——7DAY 7
YAMAA-01PP1AUCTAUAUC Over Dosing Interval389h*ng/mL——1DAY 1
YAMAA-01PP2CMAXMax Conc45.2ng/mL——1DAY 1
YAMAA-01PP3AUCTAUAUC Over Dosing Interval598.4h*ng/mL——5DAY 5
YAMAA-01PP4CMAXMax Conc69.1ng/mL——5DAY 5
YAMAA-01PP1AUCTAUAUC Over Dosing Interval455h*ng/mL——1DAY 1
YAMAA-01PP2CMAXMax Conc62.4ng/mL——1DAY 1
YAMAA-01PP3AUCTAUAUC Over Dosing Interval731.6h*ng/mL——5DAY 5
YAMAA-01PP4CMAXMax Conc58.7ng/mL——5DAY 5
YAMAA-01PP1AUCTAUAUC Over Dosing Interval——NOT DONEINSUFFICIENT DATA1DAY 1
YAMAA-01PP2CMAXMax Conc51.3ng/mL——1DAY 1
YAMAA-01PP3AUCTAUAUC Over Dosing Interval688h*ng/mL——5DAY 5
YAMAA-01PP4CMAXMax Conc80.2ng/mL——5DAY 5
YAMAA-01PP1AUCTAUAUC Over Dosing Interval702.4h*ng/mL——5DAY 5
YAMAA-01PP2CMAXMax Conc74.6ng/mL——5DAY 5

Expected output

Under review
25 expected rows

expected/adpp.csv

Edit
25 rows
expected/adpp.csv
STUDYIDUSUBJIDPARAMCDPARAMAVISITAVALAVALU
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 1412.5h*ng/mL
YAMAA-01CMAXMax ConcDAY 148.6ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 5618h*ng/mL
YAMAA-01CMAXMax ConcDAY 570.2ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 7703.2h*ng/mL
YAMAA-01CMAXMax ConcDAY 777.9ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 1389h*ng/mL
YAMAA-01CMAXMax ConcDAY 145.2ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 5598.4h*ng/mL
YAMAA-01CMAXMax ConcDAY 569.1ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 1455h*ng/mL
YAMAA-01CMAXMax ConcDAY 162.4ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 5731.6h*ng/mL
YAMAA-01CMAXMax ConcDAY 558.7ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 1——
YAMAA-01CMAXMax ConcDAY 151.3ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 5688h*ng/mL
YAMAA-01CMAXMax ConcDAY 580.2ng/mL
YAMAA-01AUCTAUAUC Over Dosing IntervalDAY 5702.4h*ng/mL
YAMAA-01CMAXMax ConcDAY 574.6ng/mL
YAMAA-01ARAUCAccumulation Ratio AUCTAUDAY 71.7047272727272729RATIO
YAMAA-01ARAUCAccumulation Ratio AUCTAUDAY 51.607912087912088RATIO
YAMAA-01ARCMAXAccumulation Ratio CmaxDAY 71.602880658436214RATIO
YAMAA-01ARCMAXAccumulation Ratio CmaxDAY 50.9407051282051283RATIO
YAMAA-01ARCMAXAccumulation Ratio CmaxDAY 51.5633528265107215RATIO
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
ADPPSTUDYIDStudy IdentifiercharacterSet per constructed row; see the Row construction sheet.Derived
ADPPUSUBJIDUnique Subject IdentifiercharacterSet per constructed row; see the Row construction sheet.Derived
ADPPPARAMCDParameter CodecharacterSet per constructed row; see the Row construction sheet.Derived
ADPPPARAMParametercharacterSet per constructed row; see the Row construction sheet.Derived
ADPPAVISITAnalysis VisitcharacterSet per constructed row; see the Row construction sheet.Derived
ADPPAVALAnalysis ValuenumericSet per constructed row; see the Row construction sheet.Derived
ADPPAVALUAnalysis Value UnitcharacterSet per constructed row; see the Row construction sheet.Derived
TemplateInclude whenSTUDYIDUSUBJIDPARAMCDPARAMAVISITARMCDSSVALD1VALAVALAVALU
collectedCopy STUDYID from the PP record matching on the shared output keys, and no match yields blankCopy USUBJID from the PP record matching on the shared output keys, and no match yields blankCopy PPTESTCD from the PP record matching on the shared output keys, and no match yields blankCopy PPTEST from the PP record matching on the shared output keys, and no match yields blankCopy VISIT from the PP record matching on the shared output keys, and no match yields blankCopy ARMCD from the PP record matching on the shared output keys, where ARMCD is derived as ARMCD from the ADSL record matching on STUDYID and USUBJID, an error when unmatched, and no match yields blankConstant "None"Constant "None"Copy PPSTRESN from the PP record matching on the shared output keys, and no match yields blankCopy PPSTRESU from the PP record matching on the shared output keys, and no match yields blank
araucAVAL IS NOT NULLCopy STUDYID from the PP record matching on the shared output keys, and no match yields blankCopy USUBJID from the PP record matching on the shared output keys, and no match yields blankConstant "ARAUC"Constant "Accumulation Ratio AUCTAU"Copy SSVISIT from the PP record matching on the shared output keys, where SSVISIT is derived as if ARMCD = 'A' then "DAY 7"; If ARMCD = 'B' then "DAY 5"; otherwise blank, and no match yields blankCopy ARMCD from the PP record matching on the shared output keys, where ARMCD is derived as ARMCD from the ADSL record matching on STUDYID and USUBJID, an error when unmatched, and no match yields blankAggregate ONLY(PPSTRESN), with PPSTRESN taken from the PP record matching on the shared output keys, and no match yields blank over records matching on the shared output keys, where PPTESTCD = 'AUCTAU' AND VISIT = SSVISIT, with PPTESTCD taken from the PP record matching on the shared output keys, and no match yields blank, VISIT taken from the PP record matching on the shared output keys, and no match yields blank and SSVISIT taken from the PP record matching on the shared output keys, where SSVISIT is derived as if ARMCD = 'A' then "DAY 7"; If ARMCD = 'B' then "DAY 5"; otherwise blank, and no match yields blankAggregate ONLY(PPSTRESN), with PPSTRESN taken from the PP record matching on the shared output keys, and no match yields blank over records matching on the shared output keys, where PPTESTCD = 'AUCTAU' AND VISIT = 'DAY 1', with PPTESTCD taken from the PP record matching on the shared output keys, and no match yields blank and VISIT taken from the PP record matching on the shared output keys, and no match yields blankCompute SSVAL / NULLIF(D1VAL, 0)Constant "RATIO"
arcmaxAVAL IS NOT NULLCopy STUDYID from the PP record matching on the shared output keys, and no match yields blankCopy USUBJID from the PP record matching on the shared output keys, and no match yields blankConstant "ARCMAX"Constant "Accumulation Ratio Cmax"Copy SSVISIT from the PP record matching on the shared output keys, where SSVISIT is derived as if ARMCD = 'A' then "DAY 7"; If ARMCD = 'B' then "DAY 5"; otherwise blank, and no match yields blankCopy ARMCD from the PP record matching on the shared output keys, where ARMCD is derived as ARMCD from the ADSL record matching on STUDYID and USUBJID, an error when unmatched, and no match yields blankAggregate ONLY(PPSTRESN), with PPSTRESN taken from the PP record matching on the shared output keys, and no match yields blank over records matching on the shared output keys, where PPTESTCD = 'CMAX' AND VISIT = SSVISIT, with PPTESTCD taken from the PP record matching on the shared output keys, and no match yields blank, VISIT taken from the PP record matching on the shared output keys, and no match yields blank and SSVISIT taken from the PP record matching on the shared output keys, where SSVISIT is derived as if ARMCD = 'A' then "DAY 7"; If ARMCD = 'B' then "DAY 5"; otherwise blank, and no match yields blankAggregate ONLY(PPSTRESN), with PPSTRESN taken from the PP record matching on the shared output keys, and no match yields blank over records matching on the shared output keys, where PPTESTCD = 'CMAX' AND VISIT = 'DAY 1', with PPTESTCD taken from the PP record matching on the shared output keys, and no match yields blank and VISIT taken from the PP record matching on the shared output keys, and no match yields blankCompute SSVAL / NULLIF(D1VAL, 0)Constant "RATIO"
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-adpp-accumulation-ratio/full.md

Edit
34 lines

Following CDISC ADaM standards, use the provided ADSL and PP datasets to create an ADPP dataset with one record per subject, parameter and visit.

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

  • Every record of PP appears in the output as a collected record.
  • On a collected record PARAMCD is the collected PPTESTCD, PARAM is the collected PPTEST, and AVISIT is the collected visit.
  • On a collected record AVAL is the collected standard result and AVALU is the collected standard unit. AVAL has no value when the parameter was not calculated, and that record stays in the output with no value.
  • A ratio record for the area under the curve over the dosing interval has PARAMCD "ARAUC" and PARAM "Accumulation Ratio AUCTAU"; a ratio record for the maximum concentration has PARAMCD "ARCMAX" and PARAM "Accumulation Ratio Cmax".
  • The steady-state profile is the profile on the last planned dosing day of the subject's planned arm in ADSL, "DAY 7" for arm A and "DAY 5" for arm B; the first-dose profile is the "DAY 1" profile.
  • On a ratio record AVISIT is the visit of the steady-state profile.
  • On a ratio record AVAL is the steady-state AUCTAU or CMAX value divided by the day 1 value of the same parameter for the same subject. AVAL on a ratio record is not rounded and may be below 1.
  • On a ratio record AVALU is "RATIO".
  • A profile on any other day, such as an interim profile during dosing or the last profile of a subject who stopped dosing early, enters neither value.
  • A ratio record exists only when both the steady-state value and the day 1 value are present.
  • When the day 1 value is zero, the ratio has no value and no ratio record exists for that parameter.

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

prompts/adam-adpp-accumulation-ratio/conventions.md

Edit
13 lines

Following CDISC ADaM standards, use the provided ADSL and PP datasets to create an ADPP dataset with one record per subject, parameter and visit.

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

A ratio record for the area under the curve over the dosing interval has PARAMCD "ARAUC" and PARAM "Accumulation Ratio AUCTAU"; a ratio record for the maximum concentration has PARAMCD "ARCMAX" and PARAM "Accumulation Ratio Cmax". AVAL on a ratio record is not rounded.

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

prompts/adam-adpp-accumulation-ratio/brief.md

Edit
8 lines

Following CDISC ADaM standards, use the provided ADSL and PP datasets to create an ADPP dataset with one record per subject, parameter and visit.

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

Read the source datasets from /app/input and save the completed dataset as /app/output/adpp.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-adpp-accumulation-ratio/result.R

Edit
68 lines
# Reference solution for the yamaa benchmark adam-adpp-accumulation-ratio (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)pp <- read_csv(  "/app/input/pp.csv",  col_types = cols(PPSTRESN = col_double(), .default = col_character()))adsl <- read_csv("/app/input/adsl.csv", col_types = cols(.default = col_character()))# Every PP record, a parameter that was not calculated included.collected <- pp |>  transmute(    STUDYID, USUBJID,    PARAMCD = PPTESTCD,    PARAM = PPTEST,    AVISIT = VISIT,    AVAL = PPSTRESN,    AVALU = PPSTRESU  )# The steady-state profile is the last planned dosing day of the arm.steady_state <- tibble(ARMCD = c("A", "B"), SSVISIT = c("DAY 7", "DAY 5"))subjects <- pp |>  distinct(STUDYID, USUBJID) |>  inner_join(select(adsl, STUDYID, USUBJID, ARMCD), by = c("STUDYID", "USUBJID")) |>  inner_join(steady_state, by = "ARMCD")params <- tibble(  PARAMCD = c("ARAUC", "ARCMAX"),  PARAM = c("Accumulation Ratio AUCTAU", "Accumulation Ratio Cmax"),  PPTESTCD = c("AUCTAU", "CMAX"))steady <- pp |>  select(STUDYID, USUBJID, PPTESTCD, VISIT, SSVALUE = PPSTRESN)first <- pp |>  filter(VISIT == "DAY 1") |>  select(STUDYID, USUBJID, PPTESTCD, FIRSTVALUE = PPSTRESN)# A ratio needs both values; it sits at the steady-state visit.ratios <- params |>  cross_join(subjects) |>  inner_join(    steady,    by = c("STUDYID", "USUBJID", "PPTESTCD", "SSVISIT" = "VISIT")  ) |>  inner_join(first, by = c("STUDYID", "USUBJID", "PPTESTCD")) |>  filter(!is.na(SSVALUE), !is.na(FIRSTVALUE), FIRSTVALUE != 0) |>  transmute(    STUDYID, USUBJID, PARAMCD, PARAM,    AVISIT = SSVISIT,    AVAL = SSVALUE / FIRSTVALUE,    AVALU = "RATIO"  )adpp <- bind_rows(collected, ratios) |>  select(STUDYID, USUBJID, PARAMCD, PARAM, AVISIT, AVAL, AVALU)dir.create("/app/output", showWarnings = FALSE)write_csv(adpp, "/app/output/adpp.csv", na = "")

solutions/adam-adpp-accumulation-ratio/result.py

Edit
108 lines
# Reference solution for the yamaa benchmark adam-adpp-accumulation-ratio (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_DIR = "/app/input"OUTPUT_DIR = "/app/output"STEADY_STATE_VISIT = {"A": "DAY 7", "B": "DAY 5"}FIRST_DOSE_VISIT = "DAY 1"RATIO_PARAMS = [    ("AUCTAU", "ARAUC", "Accumulation Ratio AUCTAU"),    ("CMAX", "ARCMAX", "Accumulation Ratio Cmax"),]def read_csv(path):    with open(path, newline="") as f:        return list(csv.DictReader(f))def main():    adsl = read_csv(f"{INPUT_DIR}/adsl.csv")    pp = read_csv(f"{INPUT_DIR}/pp.csv")    arm_by_subject = {row["USUBJID"]: row["ARMCD"] for row in adsl}    # Index collected values: (subject, testcd, visit) -> (value, studyid)    values = {}    for row in pp:        raw = (row.get("PPSTRESN") or "").strip()        value = float(raw) if raw else None        values[(row["USUBJID"], row["PPTESTCD"], row["VISIT"])] = (            value,            row["STUDYID"],        )    out_rows = []    # Collected records, one per PP record, in input order, grouped by    # subject so each subject's ratio records can follow its own records.    subjects_in_order = []    for row in pp:        if row["USUBJID"] not in subjects_in_order:            subjects_in_order.append(row["USUBJID"])    pp_by_subject = OrderedDict()    for row in pp:        pp_by_subject.setdefault(row["USUBJID"], []).append(row)    for subject in subjects_in_order:        for row in pp_by_subject[subject]:            raw = (row.get("PPSTRESN") or "").strip()            out_rows.append(                {                    "STUDYID": row["STUDYID"],                    "USUBJID": subject,                    "PARAMCD": row["PPTESTCD"],                    "PARAM": row["PPTEST"],                    "AVISIT": row["VISIT"],                    "AVAL": raw if raw else "",                    "AVALU": row["PPSTRESU"] if raw else row["PPSTRESU"],                }            )        # Ratio records for this subject.        arm = arm_by_subject.get(subject)        ss_visit = STEADY_STATE_VISIT.get(arm)        if ss_visit is None:            continue        for testcd, ratio_cd, ratio_name in RATIO_PARAMS:            ss = values.get((subject, testcd, ss_visit))            d1 = values.get((subject, testcd, FIRST_DOSE_VISIT))            if ss is None or d1 is None:                continue            ss_value, studyid = ss            d1_value, _ = d1            if ss_value is None or d1_value is None:                continue            if d1_value == 0:                continue            ratio = ss_value / d1_value            out_rows.append(                {                    "STUDYID": studyid,                    "USUBJID": subject,                    "PARAMCD": ratio_cd,                    "PARAM": ratio_name,                    "AVISIT": ss_visit,                    "AVAL": repr(ratio),                    "AVALU": "RATIO",                }            )    fieldnames = ["STUDYID", "USUBJID", "PARAMCD", "PARAM", "AVISIT", "AVAL", "AVALU"]    with open(f"{OUTPUT_DIR}/adpp.csv", "w", newline="") as f:        writer = csv.DictWriter(f, fieldnames=fieldnames)        writer.writeheader()        writer.writerows(out_rows)if __name__ == "__main__":    main()

solutions/adam-adpp-accumulation-ratio/result.sas

Edit
69 lines
/* opensas reference solution for the yamaa benchmark adam-adpp-accumulation-ratio.   Reads the benchmark inputs and derives the requested output.   Execute with the existing opensas runtime: opensas /app/output/result.sas. */data pp;  length STUDYID DOMAIN USUBJID PPSEQ PPTESTCD PPTEST PPSTRESU PPSTAT PPREASND VISITNUM VISIT $1024;  infile "/app/input/pp.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. DOMAIN : $char1024. USUBJID : $char1024. PPSEQ : $char1024.        PPTESTCD : $char1024. PPTEST : $char1024. PPSTRESN PPSTRESU : $char1024.        PPSTAT : $char1024. PPREASND : $char1024. VISITNUM : $char1024. VISIT : $char1024.;  ORD = _n_;run;data adsl;  length STUDYID USUBJID ARMCD ARM SSVISIT $1024;  infile "/app/input/adsl.csv" dsd dlm="," firstobs=2 truncover lrecl=32767;  input STUDYID : $char1024. USUBJID : $char1024. ARMCD : $char1024. ARM : $char1024.;  /* The steady-state profile is the last planned dosing day of the arm. */  if ARMCD = 'A' then SSVISIT = 'DAY 7';  else if ARMCD = 'B' then SSVISIT = 'DAY 5';  else SSVISIT = '';run;/* Every PP record, a parameter that was not calculated included. */data collected;  length STUDYID USUBJID PARAMCD PARAM AVISIT AVALU $1024;  set pp;  PARAMCD = PPTESTCD; PARAM = PPTEST; AVISIT = VISIT; AVAL = PPSTRESN; AVALU = PPSTRESU;  keep STUDYID USUBJID PARAMCD PARAM AVISIT AVAL AVALU ORD;run;data firstdose;  set pp;  if VISIT = 'DAY 1';  FIRSTVAL = PPSTRESN;  keep STUDYID USUBJID PPTESTCD FIRSTVAL;run;proc sql;  create table steady as    select p.STUDYID, p.USUBJID, p.PPTESTCD, p.VISIT, p.PPSTRESN as SSVAL, p.ORD    from pp as p inner join adsl as a      on p.STUDYID = a.STUDYID and p.USUBJID = a.USUBJID and p.VISIT = a.SSVISIT;  create table pairs as    select s.STUDYID, s.USUBJID, s.PPTESTCD, s.VISIT, s.SSVAL, f.FIRSTVAL, s.ORD    from steady as s inner join firstdose as f      on s.STUDYID = f.STUDYID and s.USUBJID = f.USUBJID and s.PPTESTCD = f.PPTESTCD;quit;/* A ratio needs both values; it sits at the steady-state visit. */data ratios;  length STUDYID USUBJID PARAMCD PARAM AVISIT AVALU $1024;  set pairs;  if not missing(SSVAL) and not missing(FIRSTVAL) and FIRSTVAL ne 0;  if PPTESTCD = 'AUCTAU' then do; PARAMCD = 'ARAUC'; PARAM = 'Accumulation Ratio AUCTAU'; end;  else if PPTESTCD = 'CMAX' then do; PARAMCD = 'ARCMAX'; PARAM = 'Accumulation Ratio Cmax'; end;  else delete;  AVISIT = VISIT; AVAL = SSVAL / FIRSTVAL; AVALU = 'RATIO';  ORD = ORD + 100000;  keep STUDYID USUBJID PARAMCD PARAM AVISIT AVAL AVALU ORD;run;data result; set collected ratios; run;proc sort data=result; by ORD; run;proc sql;  create table final as select STUDYID, USUBJID, PARAMCD, PARAM, AVISIT, AVAL, AVALU from result;quit;proc export data=final outfile="/app/output/adpp.csv" dbms=csv replace; run;