Clinwright Public work of Helper Software LLC · free Start free

ADRS

Tumor Response Analysis Dataset

BASIC DATA STRUCTURE · one record per subject per parameter per assessment · keys STUDYID, USUBJID, PARAMCD, ASEQ

Variables

VariableLabelTypeLengthDerivation
STUDYIDStudy IdentifiertextPredecessor RS.STUDYID
USUBJIDUnique Subject IdentifiertextPredecessor RS.USUBJID
SUBJIDSubject Identifier for the StudytextPredecessor ADSL.SUBJID
SITEIDStudy Site IdentifiertextPredecessor ADSL.SITEID
AGEAgeinteger8Predecessor ADSL.AGE
SEXSextextPredecessor ADSL.SEX
RACERacetextPredecessor ADSL.RACE
TRT01PPlanned Treatment for Period 01textPredecessor ADSL.TRT01P
TRT01PNPlanned Treatment for Period 01 (N)integer8Predecessor ADSL.TRT01PN
TRT01AActual Treatment for Period 01textPredecessor ADSL.TRT01A
TRT01ANActual Treatment for Period 01 (N)integer8Predecessor ADSL.TRT01AN
SAFFLSafety Population FlagtextPredecessor ADSL.SAFFL
ITTFLIntent-To-Treat Population FlagtextPredecessor ADSL.ITTFL
RANDDTDate of Randomizationinteger8Predecessor ADSL.RANDDT
ASEQAnalysis Sequence Numberinteger8Sequence number of the records of the RECIST 1.1 overall response and best overall response data of a subject, by PARAMCD, ADT, VISITNUM and RSSEQ.
PARAMCDParameter CodetextOVR for the overall response at each tumor assessment (RS.RSTESTCD = 'OVRLRESP' of the investigator, RECIST 1.1), and one record per subject for each response parameter: BOR and CBOR (best overall response without and with confirmation), RSP and CRSP (response), CB and CCB (clinical benefit), BCP, CBCP, PD, DEATH, LSTA and MDIS.
PARAMParametertextThe response parameter's name, set with PARAMCD by the RECIST 1.1 response derivation, e.g. Best Overall Response by Investigator (confirmation not required).
PARCAT1Parameter Category 1textTumor Response; Reference Event for DEATH (RECIST 1.1 response parameters).
PARCAT2Parameter Category 2textInvestigator (the evaluator of the RECIST 1.1 overall response).
PARCAT3Parameter Category 3textRECIST 1.1 (the response criteria of the overall response).
AVALCAnalysis Value (C)textThe overall response (RS.RSSTRESC) at each assessment. For the parameters, by RECIST 1.1: the best overall response (CR, PR, SD at least 42 days after randomization, NON-CR/NON-PD, PD, NE; MISSING without one), confirmed when a CR or PR is followed by a CR (or CR/PR) at least 28 days later with at most one NE in between; Y or N for response, clinical benefit, progression, death and measurable disease.
AVALAnalysis Valueinteger8The overall response as a number (CR 1, PR 2, SD 3, NON-CR/NON-PD 4, PD 5, NE 6, MISSING 7); 1 or 0 for the Y/N response parameters (RECIST 1.1).
ADTAnalysis Dateinteger8Date of the assessment: RS.RSDTC converted, a missing day imputed to the last day of the month; for a response parameter the date of the assessment it comes from; DEATH: DTHDT (RECIST 1.1 response).
ADTFAnalysis Date Imputation FlagtextD when the day of the RECIST 1.1 overall response date was imputed.
AVISITAnalysis VisittextRS.VISIT of the overall response assessment (of the assessment a response parameter comes from).
ANL01FLAnalysis Flag 01textY for the overall response assessments that count: on or after randomization, the worst one of a date (RECIST 1.1); Y on every response parameter.
ANL02FLAnalysis Flag 02textY for the overall response assessments up to and including the first PD (RECIST 1.1 response).
RSSEQSequence Numberinteger8Predecessor RS.RSSEQ
RSTESTCDAssessment Short NametextPredecessor RS.RSTESTCD
RSEVALEvaluatortextPredecessor RS.RSEVAL
RSSTRESCAssessment Result in Std FormattextPredecessor RS.RSSTRESC
VISITVisit NametextPredecessor RS.VISIT
VISITNUMVisit Numberfloat8Predecessor RS.VISITNUM
RSDTCDate/Time of AssessmenttextPredecessor RS.RSDTC

Program adrs.sas

Version 1, written from Clinwright's patterns, 2026-10-17, checks passed.

/*==== 00-header ====*/
/*----------------------------------------------------------------------------
  Data set : ADRS (Tumor Response Analysis Dataset)
  Reporting effort : CSR
  SDTM     : RS
  ADaM     : ADSL
  The header and the finalize step follow from the specification.
----------------------------------------------------------------------------*/
libname sdtm "/work/sdtm" access=readonly;
libname adam "/work/adam";
options validvarname=upcase nofmterr msglevel=i;

/*==== 10-inputs ====*/
/* The RS records, with the subject's TRTSDT, TRTEDT, SUBJID, SITEID, AGE, SEX, RACE, TRT01P, TRT01PN, TRT01A, TRT01AN, SAFFL, ITTFL, RANDDT from ADSL. */
proc sort data=sdtm.rs(keep=studyid usubjid rsseq rstestcd rsstresc rseval visitnum visit rsdtc) out=work._ds; by usubjid; run;
proc sort data=adam.adsl(keep=usubjid trtsdt trtedt subjid siteid age sex race trt01p trt01pn trt01a trt01an saffl ittfl randdt) out=work._adsl; by usubjid; run;
data work._ds;
  merge work._ds(in=_in) work._adsl;
  by usubjid;
  if _in;
run;

/*==== 20-parameters ====*/
/* ADRS by RECIST 1.1 (investigator): the overall response at each assessment (OVR) and the response parameters
   derived from it, with the rules of admiralonco (see SKILL.md).
   Inputs: SDTM.RS (RSTESTCD OVRLRESP), SDTM.TU (target lesions at screening), ADAM.ADSL (RANDDT, DTHDT).
   Output: WORK.ADRS, one record per subject per parameter (OVR: per assessment), with ADSL's variables. */

proc fcmp outlib=work.funcs.recist;
  /* AVAL of a response: CR 1, PR 2, SD 3, NON-CR/NON-PD 4, PD 5, NE 6, MISSING 7. */
  function resp_aval(resp $);
    select (resp);
      when ('CR') v = 1;
      when ('PR') v = 2;
      when ('SD') v = 3;
      when ('NON-CR/NON-PD') v = 4;
      when ('PD') v = 5;
      when ('NE') v = 6;
      when ('MISSING') v = 7;
      otherwise v = .;
    end;
    return(v);
  endsub;
  /* Of several assessments on one date the worst counts; it sorts last. */
  function resp_worst(resp $);
    select (resp);
      when ('NE') v = 1;
      when ('CR') v = 2;
      when ('PR') v = 3;
      when ('SD') v = 4;
      when ('NON-CR/NON-PD') v = 5;
      when ('PD') v = 6;
      otherwise v = 0;
    end;
    return(v);
  endsub;
  /* A date from ISO 8601 text, a missing day imputed to the month's last day (a missing month: no date). */
  function dt_last_day(dtc $);
    length part $ 10;
    part = substr(strip(dtc), 1, 10);
    result = .;
    if length(part) = 10 then result = input(part, ?? yymmdd10.);
    else if length(part) = 7 then result = intnx('month', input(cats(part, '-01'), ?? yymmdd10.), 0, 'end');
    return(result);
  endsub;
run;
options cmplib=work.funcs;

/* ---- Overall response at each assessment ------------------------------------------------------------------- */
proc sql;
  create table _ovr as
  select r.*, s.RANDDT as RANDDT
  from sdtm.rs as r left join adam.adsl as s on r.STUDYID = s.STUDYID and r.USUBJID = s.USUBJID
  where r.RSEVAL = 'INVESTIGATOR' and r.RSTESTCD = 'OVRLRESP';
quit;

data _ovr;
  set _ovr;
  length PARAMCD $ 8 PARAM $ 100 PARCAT1 PARCAT2 PARCAT3 $ 40 AVALC $ 20 AVISIT $ 40 ADTF $ 1;
  PARAMCD = 'OVR';
  PARAM = 'Overall Response by Investigator';
  PARCAT1 = 'Tumor Response';
  PARCAT2 = 'Investigator';
  PARCAT3 = 'RECIST 1.1';
  ADT = dt_last_day(RSDTC);
  if not missing(ADT) and length(strip(RSDTC)) < 10 then ADTF = 'D';
  AVISIT = VISIT;
  AVALC = RSSTRESC;
  AVAL = resp_aval(AVALC);
  /* What counts: a response on or after randomization; of a date, the worst (below). */
  _elig = not missing(AVAL) and not missing(ADT) and not missing(RANDDT) and ADT >= RANDDT;
  _worst = resp_worst(AVALC);
  format ADT RANDDT yymmdd10.;
run;

/* ANL01FL: the worst counting assessment of each date. */
proc sort data=_ovr;
  by STUDYID USUBJID ADT _elig _worst RSSEQ;
run;
data _ovr;
  set _ovr;
  by STUDYID USUBJID ADT _elig _worst RSSEQ;
  length ANL01FL ANL02FL $ 1;
  if last.ADT and _elig then ANL01FL = 'Y';
run;

/* ANL02FL: assessments up to and including the first PD (all of them when there is none). */
proc sort data=_ovr;
  by STUDYID USUBJID ADT RSSEQ;
run;
data _ovr;
  set _ovr;
  by STUDYID USUBJID;
  retain _after_pd;
  if first.USUBJID then _after_pd = 0;
  if not _after_pd then ANL02FL = 'Y';
  if AVALC = 'PD' then _after_pd = 1;
  drop _after_pd _elig _worst;
run;

/* ---- Response parameters ----------------------------------------------------------------------------------- */
/* Each parameter is one assessment of the subject (or none): in the first kind of event that has one, its earliest
   assessment, as admiralonco's derive_extreme_event chooses. A kind is a condition on assessment i:
   d[i] its date, r[i] its response, conf[i] whether it is confirmed, rand the randomization date.
   Macro comparisons are quoted: unquoted, a value like ne or or reads as an operator. */
%macro kind(kind);
  %if "&kind" = "rsp" %then (r[i] in ('CR', 'PR'));
  %else %if "&kind" = "cb" %then (r[i] in ('CR', 'PR', 'SD', 'NON-CR/NON-PD') and d[i] >= rand + 42);
  %else %if "&kind" = "cr" %then (r[i] = 'CR');
  %else %if "&kind" = "pr" %then (r[i] = 'PR');
  %else %if "&kind" = "sd" %then (r[i] in ('CR', 'PR', 'SD') and d[i] >= rand + 42);
  %else %if "&kind" = "noncrpd" %then (r[i] = 'NON-CR/NON-PD' and d[i] >= rand + 42);
  %else %if "&kind" = "pd" %then (r[i] = 'PD');
  %else %if "&kind" = "ne" %then (r[i] in ('CR', 'PR', 'SD', 'NON-CR/NON-PD', 'NE'));
  %else %if "&kind" = "ccr" %then (r[i] = 'CR' and conf[i]);
  %else %if "&kind" = "cpr" %then (r[i] = 'PR' and conf[i]);
%mend;
%macro value(kind);
  %if "&kind" = "cr" or "&kind" = "ccr" %then 'CR';
  %else %if "&kind" = "pr" or "&kind" = "cpr" %then 'PR';
  %else %if "&kind" = "sd" %then 'SD';
  %else %if "&kind" = "noncrpd" %then 'NON-CR/NON-PD';
  %else %if "&kind" = "pd" %then 'PD';
  %else %if "&kind" = "ne" %then 'NE';
  %else 'Y';
%mend;
/* Kinds tried in turn (BOR, CBOR, RSP): the earliest assessment of the first kind that has one. */
%macro pick(param, kinds);
  _p = "&param";
  _seq = .;
  _val = '';
  %do q = 1 %to %sysfunc(countw(&kinds, %str( )));
    %let kind = %scan(&kinds, &q, %str( ));
    if missing(_seq) then do i = 1 to n while (missing(_seq));
      if %kind(&kind) then do;
        _seq = s[i];
        _val = %value(&kind);
      end;
    end;
  %end;
  output;
%mend;
/* Kinds that all mean "Y" (CB, CRSP, CCB): the earliest assessment of any of them. */
%macro pick_any(param, kinds);
  _p = "&param";
  _seq = .;
  _val = '';
  do i = 1 to n while (missing(_seq));
    if 0 %do q = 1 %to %sysfunc(countw(&kinds, %str( ))); or %kind(%scan(&kinds, &q, %str( ))) %end; then do;
      _seq = s[i];
      _val = 'Y';
    end;
  end;
  output;
%mend;

%let maxa = 500;
data _picks(keep=STUDYID USUBJID _p _seq _val);
  length _p $ 8 _val $ 20;
  array d[&maxa] _temporary_;
  array r[&maxa] $ 20 _temporary_;
  array s[&maxa] _temporary_;
  array conf[&maxa] _temporary_;
  /* One subject's counting assessments (ANL01FL and ANL02FL), in date order. */
  n = 0;
  do until (last.USUBJID);
    set _ovr(where=(ANL01FL = 'Y' and ANL02FL = 'Y') keep=STUDYID USUBJID ADT AVALC RSSEQ RANDDT ANL01FL ANL02FL);
    by STUDYID USUBJID;
    n + 1;
    d[n] = ADT;
    r[n] = AVALC;
    s[n] = RSSEQ;
    rand = RANDDT;
  end;

  /* Confirmed (crsp_y_cr, crsp_y_pr): a CR followed by a CR at least 28 days later, or a PR followed by a CR or PR at
     least 28 days later; the assessments after it up to that one may only be CR/NE (for a CR) or CR/PR/NE (for a PR),
     with at most one NE, and for a PR no PR after a CR. */
  do i = 1 to n;
    conf[i] = 0;
    if r[i] in ('CR', 'PR') then do;
      j = 0;
      do k = i + 1 to n while (j = 0);
        if d[k] >= d[i] + 28 and (r[k] = 'CR' or (r[i] = 'PR' and r[k] = 'PR')) then j = k;
      end;
      if j > 0 then do;
        ok = 1;
        ne = 0;
        first_cr = .;
        last_pr = .;
        do k = i + 1 to j;
          if r[i] = 'CR' and r[k] not in ('CR', 'NE') then ok = 0;
          if r[i] = 'PR' and r[k] not in ('CR', 'PR', 'NE') then ok = 0;
          ne + (r[k] = 'NE');
          if r[k] = 'CR' and missing(first_cr) then first_cr = d[k];
          if r[k] = 'PR' then last_pr = d[k];
        end;
        if ne > 1 then ok = 0;
        if r[i] = 'PR' and not missing(first_cr) and not missing(last_pr) and first_cr <= last_pr then ok = 0;
        conf[i] = ok;
      end;
    end;
  end;

  %pick(RSP, rsp)
  %pick_any(CB, rsp cb)
  %pick(BOR, cr pr sd noncrpd pd ne)
  %pick_any(CRSP, ccr cpr)
  %pick_any(CCB, ccr cpr cb)
  %pick(CBOR, ccr cpr sd noncrpd pd ne)
run;

/* PD: the first progression among the assessments of ANL01FL. LSTA: the last of those assessments. */
data _picks2(keep=STUDYID USUBJID _p _seq _val);
  length _p $ 8 _val $ 20;
  set _ovr(where=(ANL01FL = 'Y'));
  by STUDYID USUBJID;
  retain _pd_seq;
  if first.USUBJID then _pd_seq = .;
  if AVALC = 'PD' and missing(_pd_seq) then do;
    _pd_seq = RSSEQ;
    _p = 'PD';
    _seq = RSSEQ;
    _val = 'Y';
    output;
  end;
  if last.USUBJID then do;
    _p = 'LSTA';
    _seq = RSSEQ;
    _val = AVALC;
    output;
  end;
run;

/* Every subject of ADSL gets every parameter: what was picked, or the value for none. */
data _params;
  length _p $ 8 PARAM $ 100 PARCAT1 $ 40 _none $ 20 _aval $ 4;
  input _p $ _none $ _aval $ PARCAT1 & $40. PARAM & $100.;
  datalines;
BOR MISSING resp Tumor Response  Best Overall Response by Investigator (confirmation not required)
BCP N yn Tumor Response  Best Overall Response of CR/PR by Investigator (confirmation not required)
CB N yn Tumor Response  Clinical Benefit by Investigator (confirmation for response not required)
CBCP N yn Tumor Response  Best Confirmed Overall Response of CR/PR by Investigator
CBOR MISSING resp Tumor Response  Best Confirmed Overall Response by Investigator
CCB N yn Tumor Response  Confirmed Clinical Benefit by Investigator
CRSP N yn Tumor Response  Confirmed Response by Investigator
DEATH N yn Reference Event  Death
LSTA . none Tumor Response  Last Disease Assessment by Investigator
MDIS N yn .  Measurable Disease at Baseline by Investigator
PD N yn Tumor Response  Disease Progression by Investigator
RSP N yn Tumor Response  Response by Investigator (confirmation not required)
;
run;

/* MDIS: target lesions at screening (investigator). */
proc sql;
  create table _mdis as
  select distinct USUBJID
  from sdtm.tu
  where TUEVAL = 'INVESTIGATOR' and TUSTRESC = 'TARGET' and VISIT = 'SCREENING';

  create table _d1 as
  select a.STUDYID, a.USUBJID, a.DTHDT as DTHDT, p._p, p.PARAM, p.PARCAT1, p._aval,
         coalesce(k._seq, k2._seq) as _seq,
         case
           when p._p = 'DEATH' then case when a.DTHDT is not missing then 'Y' else 'N' end
           when p._p = 'MDIS' then case when m.USUBJID is not missing then 'Y' else 'N' end
           else coalescec(k._val, k2._val, p._none)
         end as _avalc length=20
  from adam.adsl as a
       cross join _params as p
       left join _picks as k on k.USUBJID = a.USUBJID and k._p = p._p
       left join _picks2 as k2 on k2.USUBJID = a.USUBJID and k2._p = p._p
       left join _mdis as m on m.USUBJID = a.USUBJID;
quit;

/* BCP and CBCP follow BOR and CBOR: Y, with that assessment, when it is CR or PR.
   (Data set options are applied in DATA steps: StatScript ignores them in SQL's FROM, OSI-0018.) */
data _bor(keep=USUBJID _bor _bor_seq) _cbor(keep=USUBJID _cbor _cbor_seq);
  set _d1;
  length _bor _cbor $ 20;
  if _p = 'BOR' then do; _bor = _avalc; _bor_seq = _seq; output _bor; end;
  if _p = 'CBOR' then do; _cbor = _avalc; _cbor_seq = _seq; output _cbor; end;
run;
proc sql;
  create table _d2 as
  select d.STUDYID, d.USUBJID, d.DTHDT, d._p, d.PARAM, d.PARCAT1, d._aval,
         case when d._p = 'BCP' and b._bor in ('CR', 'PR') then b._bor_seq
              when d._p = 'CBCP' and c._cbor in ('CR', 'PR') then c._cbor_seq
              else d._seq end as _seq,
         case when d._p = 'BCP' and b._bor in ('CR', 'PR') then 'Y'
              when d._p = 'CBCP' and c._cbor in ('CR', 'PR') then 'Y'
              else d._avalc end as _avalc length=20
  from _d1 as d
       left join _bor as b on b.USUBJID = d.USUBJID
       left join _cbor as c on c.USUBJID = d.USUBJID;
quit;

/* A derived record takes the variables of the assessment it comes from. */
data _src;
  set _ovr(drop=PARAMCD PARAM PARCAT1 AVALC AVAL ANL01FL rename=(STUDYID=_studyid USUBJID=_usubjid));
run;
proc sql;
  create table _derived as
  select d.STUDYID, d.USUBJID, d._p as PARAMCD length=8, d.PARAM, d.PARCAT1, d._aval, d._avalc, d.DTHDT, o.*
  from _d2 as d left join _src as o on o._usubjid = d.USUBJID and o.RSSEQ = d._seq;
quit;

data _derived;
  set _derived;
  length AVALC $ 20 ANL01FL $ 1;
  AVALC = _avalc;
  ANL01FL = 'Y';
  /* DEATH is a reference event, not an assessment. */
  if PARAMCD = 'DEATH' then do;
    ADT = DTHDT;
    call missing(PARCAT2, PARCAT3);
  end;
  else do;
    PARCAT2 = 'Investigator';
    PARCAT3 = 'RECIST 1.1';
  end;
  if _aval = 'yn' then AVAL = ifn(AVALC = 'Y', 1, ifn(AVALC = 'N', 0, .));
  else AVAL = resp_aval(AVALC);
  drop _aval _avalc DTHDT _studyid _usubjid;
run;

/* ---- All records, their sequence, and ADSL's variables -------------------------------------------------------- */
data _all;
  set _ovr _derived;
run;
proc sort data=_all;
  by STUDYID USUBJID PARAMCD ADT VISITNUM RSSEQ;
run;
data _all;
  set _all;
  by STUDYID USUBJID;
  if first.USUBJID then ASEQ = 0;
  ASEQ + 1;
run;
/* ADSL's variables on every record: in a one-to-many merge a variable both have would keep ADSL's value only on a
   subject's first record, so the records drop what ADSL brings (RANDDT). */
data work._ds;
  merge _all(in=_in drop=RANDDT) adam.adsl;
  by STUDYID USUBJID;
  if _in;
run;

/*==== 89-final ====*/
/* The data set as derived; the finalize step keeps the specification's variables. */
data work._final;
  set work._ds;
run;

/*==== 90-finalize ====*/
/* 90-finalize: from the specification; the variables, their order and attributes are not changed here. */
/* The text variables the specification gives no length: as long as their longest value (at least 1). */
%let _len1 = 1; %let _len2 = 1; %let _len3 = 1; %let _len4 = 1; %let _len5 = 1; %let _len6 = 1; %let _len7 = 1; %let _len8 = 1; %let _len9 = 1; %let _len10 = 1; %let _len11 = 1; %let _len12 = 1; %let _len13 = 1; %let _len14 = 1; %let _len15 = 1; %let _len16 = 1; %let _len17 = 1; %let _len18 = 1; %let _len19 = 1; %let _len20 = 1; %let _len21 = 1; %let _len22 = 1; %let _len23 = 1; %let _len24 = 1; %let _len25 = 1;
data _null_;
  length _ren _asg _siz $32767;
  _dsid = open('work._final');
  if varnum(_dsid, 'STUDYID') > 0 then do; _ren = catx(' ', _ren, 'STUDYID=_rz1'); _asg = catx(' ', _asg, 'STUDYID = _rz1;');
    _siz = catx(' ', _siz, '_len1 = max(_len1, lengthn(STUDYID));'); end;
  if varnum(_dsid, 'USUBJID') > 0 then do; _ren = catx(' ', _ren, 'USUBJID=_rz2'); _asg = catx(' ', _asg, 'USUBJID = _rz2;');
    _siz = catx(' ', _siz, '_len2 = max(_len2, lengthn(USUBJID));'); end;
  if varnum(_dsid, 'SUBJID') > 0 then do; _ren = catx(' ', _ren, 'SUBJID=_rz3'); _asg = catx(' ', _asg, 'SUBJID = _rz3;');
    _siz = catx(' ', _siz, '_len3 = max(_len3, lengthn(SUBJID));'); end;
  if varnum(_dsid, 'SITEID') > 0 then do; _ren = catx(' ', _ren, 'SITEID=_rz4'); _asg = catx(' ', _asg, 'SITEID = _rz4;');
    _siz = catx(' ', _siz, '_len4 = max(_len4, lengthn(SITEID));'); end;
  if varnum(_dsid, 'SEX') > 0 then do; _ren = catx(' ', _ren, 'SEX=_rz5'); _asg = catx(' ', _asg, 'SEX = _rz5;');
    _siz = catx(' ', _siz, '_len5 = max(_len5, lengthn(SEX));'); end;
  if varnum(_dsid, 'RACE') > 0 then do; _ren = catx(' ', _ren, 'RACE=_rz6'); _asg = catx(' ', _asg, 'RACE = _rz6;');
    _siz = catx(' ', _siz, '_len6 = max(_len6, lengthn(RACE));'); end;
  if varnum(_dsid, 'TRT01P') > 0 then do; _ren = catx(' ', _ren, 'TRT01P=_rz7'); _asg = catx(' ', _asg, 'TRT01P = _rz7;');
    _siz = catx(' ', _siz, '_len7 = max(_len7, lengthn(TRT01P));'); end;
  if varnum(_dsid, 'TRT01A') > 0 then do; _ren = catx(' ', _ren, 'TRT01A=_rz8'); _asg = catx(' ', _asg, 'TRT01A = _rz8;');
    _siz = catx(' ', _siz, '_len8 = max(_len8, lengthn(TRT01A));'); end;
  if varnum(_dsid, 'SAFFL') > 0 then do; _ren = catx(' ', _ren, 'SAFFL=_rz9'); _asg = catx(' ', _asg, 'SAFFL = _rz9;');
    _siz = catx(' ', _siz, '_len9 = max(_len9, lengthn(SAFFL));'); end;
  if varnum(_dsid, 'ITTFL') > 0 then do; _ren = catx(' ', _ren, 'ITTFL=_rz10'); _asg = catx(' ', _asg, 'ITTFL = _rz10;');
    _siz = catx(' ', _siz, '_len10 = max(_len10, lengthn(ITTFL));'); end;
  if varnum(_dsid, 'PARAMCD') > 0 then do; _ren = catx(' ', _ren, 'PARAMCD=_rz11'); _asg = catx(' ', _asg, 'PARAMCD = _rz11;');
    _siz = catx(' ', _siz, '_len11 = max(_len11, lengthn(PARAMCD));'); end;
  if varnum(_dsid, 'PARAM') > 0 then do; _ren = catx(' ', _ren, 'PARAM=_rz12'); _asg = catx(' ', _asg, 'PARAM = _rz12;');
    _siz = catx(' ', _siz, '_len12 = max(_len12, lengthn(PARAM));'); end;
  if varnum(_dsid, 'PARCAT1') > 0 then do; _ren = catx(' ', _ren, 'PARCAT1=_rz13'); _asg = catx(' ', _asg, 'PARCAT1 = _rz13;');
    _siz = catx(' ', _siz, '_len13 = max(_len13, lengthn(PARCAT1));'); end;
  if varnum(_dsid, 'PARCAT2') > 0 then do; _ren = catx(' ', _ren, 'PARCAT2=_rz14'); _asg = catx(' ', _asg, 'PARCAT2 = _rz14;');
    _siz = catx(' ', _siz, '_len14 = max(_len14, lengthn(PARCAT2));'); end;
  if varnum(_dsid, 'PARCAT3') > 0 then do; _ren = catx(' ', _ren, 'PARCAT3=_rz15'); _asg = catx(' ', _asg, 'PARCAT3 = _rz15;');
    _siz = catx(' ', _siz, '_len15 = max(_len15, lengthn(PARCAT3));'); end;
  if varnum(_dsid, 'AVALC') > 0 then do; _ren = catx(' ', _ren, 'AVALC=_rz16'); _asg = catx(' ', _asg, 'AVALC = _rz16;');
    _siz = catx(' ', _siz, '_len16 = max(_len16, lengthn(AVALC));'); end;
  if varnum(_dsid, 'ADTF') > 0 then do; _ren = catx(' ', _ren, 'ADTF=_rz17'); _asg = catx(' ', _asg, 'ADTF = _rz17;');
    _siz = catx(' ', _siz, '_len17 = max(_len17, lengthn(ADTF));'); end;
  if varnum(_dsid, 'AVISIT') > 0 then do; _ren = catx(' ', _ren, 'AVISIT=_rz18'); _asg = catx(' ', _asg, 'AVISIT = _rz18;');
    _siz = catx(' ', _siz, '_len18 = max(_len18, lengthn(AVISIT));'); end;
  if varnum(_dsid, 'ANL01FL') > 0 then do; _ren = catx(' ', _ren, 'ANL01FL=_rz19'); _asg = catx(' ', _asg, 'ANL01FL = _rz19;');
    _siz = catx(' ', _siz, '_len19 = max(_len19, lengthn(ANL01FL));'); end;
  if varnum(_dsid, 'ANL02FL') > 0 then do; _ren = catx(' ', _ren, 'ANL02FL=_rz20'); _asg = catx(' ', _asg, 'ANL02FL = _rz20;');
    _siz = catx(' ', _siz, '_len20 = max(_len20, lengthn(ANL02FL));'); end;
  if varnum(_dsid, 'RSTESTCD') > 0 then do; _ren = catx(' ', _ren, 'RSTESTCD=_rz21'); _asg = catx(' ', _asg, 'RSTESTCD = _rz21;');
    _siz = catx(' ', _siz, '_len21 = max(_len21, lengthn(RSTESTCD));'); end;
  if varnum(_dsid, 'RSEVAL') > 0 then do; _ren = catx(' ', _ren, 'RSEVAL=_rz22'); _asg = catx(' ', _asg, 'RSEVAL = _rz22;');
    _siz = catx(' ', _siz, '_len22 = max(_len22, lengthn(RSEVAL));'); end;
  if varnum(_dsid, 'RSSTRESC') > 0 then do; _ren = catx(' ', _ren, 'RSSTRESC=_rz23'); _asg = catx(' ', _asg, 'RSSTRESC = _rz23;');
    _siz = catx(' ', _siz, '_len23 = max(_len23, lengthn(RSSTRESC));'); end;
  if varnum(_dsid, 'VISIT') > 0 then do; _ren = catx(' ', _ren, 'VISIT=_rz24'); _asg = catx(' ', _asg, 'VISIT = _rz24;');
    _siz = catx(' ', _siz, '_len24 = max(_len24, lengthn(VISIT));'); end;
  if varnum(_dsid, 'RSDTC') > 0 then do; _ren = catx(' ', _ren, 'RSDTC=_rz25'); _asg = catx(' ', _asg, 'RSDTC = _rz25;');
    _siz = catx(' ', _siz, '_len25 = max(_len25, lengthn(RSDTC));'); end;
  _rc = close(_dsid);
  if _ren ne '' then _ren = cats('(rename=(', _ren, '))');
  call symputx('_ren', _ren);
  call symputx('_asg', _asg);
  call symputx('_siz', _siz);
run;
data _null_;
  set work._final end=_end;
  retain _len1 1 _len2 1 _len3 1 _len4 1 _len5 1 _len6 1 _len7 1 _len8 1 _len9 1 _len10 1 _len11 1 _len12 1 _len13 1 _len14 1 _len15 1 _len16 1 _len17 1 _len18 1 _len19 1 _len20 1 _len21 1 _len22 1 _len23 1 _len24 1 _len25 1;
  &_siz
  if _end then do;
    call symputx('_len1', _len1);
    call symputx('_len2', _len2);
    call symputx('_len3', _len3);
    call symputx('_len4', _len4);
    call symputx('_len5', _len5);
    call symputx('_len6', _len6);
    call symputx('_len7', _len7);
    call symputx('_len8', _len8);
    call symputx('_len9', _len9);
    call symputx('_len10', _len10);
    call symputx('_len11', _len11);
    call symputx('_len12', _len12);
    call symputx('_len13', _len13);
    call symputx('_len14', _len14);
    call symputx('_len15', _len15);
    call symputx('_len16', _len16);
    call symputx('_len17', _len17);
    call symputx('_len18', _len18);
    call symputx('_len19', _len19);
    call symputx('_len20', _len20);
    call symputx('_len21', _len21);
    call symputx('_len22', _len22);
    call symputx('_len23', _len23);
    call symputx('_len24', _len24);
    call symputx('_len25', _len25);
  end;
run;
data adam.adrs(label="Tumor Response Analysis Dataset");
  attrib
    STUDYID  length=$&_len1 label="Study Identifier"
    USUBJID  length=$&_len2 label="Unique Subject Identifier"
    SUBJID   length=$&_len3 label="Subject Identifier for the Study"
    SITEID   length=$&_len4 label="Study Site Identifier"
    AGE      length=8 label="Age"
    SEX      length=$&_len5 label="Sex"
    RACE     length=$&_len6 label="Race"
    TRT01P   length=$&_len7 label="Planned Treatment for Period 01"
    TRT01PN  length=8 label="Planned Treatment for Period 01 (N)"
    TRT01A   length=$&_len8 label="Actual Treatment for Period 01"
    TRT01AN  length=8 label="Actual Treatment for Period 01 (N)"
    SAFFL    length=$&_len9 label="Safety Population Flag"
    ITTFL    length=$&_len10 label="Intent-To-Treat Population Flag"
    RANDDT   length=8 label="Date of Randomization" format=DATE9.
    ASEQ     length=8 label="Analysis Sequence Number"
    PARAMCD  length=$&_len11 label="Parameter Code"
    PARAM    length=$&_len12 label="Parameter"
    PARCAT1  length=$&_len13 label="Parameter Category 1"
    PARCAT2  length=$&_len14 label="Parameter Category 2"
    PARCAT3  length=$&_len15 label="Parameter Category 3"
    AVALC    length=$&_len16 label="Analysis Value (C)"
    AVAL     length=8 label="Analysis Value"
    ADT      length=8 label="Analysis Date" format=DATE9.
    ADTF     length=$&_len17 label="Analysis Date Imputation Flag"
    AVISIT   length=$&_len18 label="Analysis Visit"
    ANL01FL  length=$&_len19 label="Analysis Flag 01"
    ANL02FL  length=$&_len20 label="Analysis Flag 02"
    RSSEQ    length=8 label="Sequence Number"
    RSTESTCD length=$&_len21 label="Assessment Short Name"
    RSEVAL   length=$&_len22 label="Evaluator"
    RSSTRESC length=$&_len23 label="Assessment Result in Std Format"
    VISIT    length=$&_len24 label="Visit Name"
    VISITNUM length=8 label="Visit Number"
    RSDTC    length=$&_len25 label="Date/Time of Assessment"
  ;
  set work._final&_ren;
  &_asg
  keep STUDYID USUBJID SUBJID SITEID AGE SEX RACE TRT01P TRT01PN TRT01A TRT01AN SAFFL ITTFL RANDDT ASEQ PARAMCD PARAM PARCAT1 PARCAT2 PARCAT3 AVALC AVAL ADT ADTF AVISIT ANL01FL ANL02FL RSSEQ RSTESTCD RSEVAL RSSTRESC VISIT VISITNUM RSDTC;
run;
proc sort data=adam.adrs; by STUDYID USUBJID PARAMCD ASEQ; run;

Data (first 20 of 4,950 records, simulated)

STUDYIDUSUBJIDSUBJIDSITEIDAGESEXRACETRT01PTRT01PNTRT01ATRT01ANSAFFLITTFLRANDDTASEQPARAMCDPARAMPARCAT1PARCAT2PARCAT3AVALCAVALADTADTFAVISITANL01FLANL02FLRSSEQRSTESTCDRSEVALRSSTRESCVISITVISITNUMRSDTC
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-111BCPBest Overall Response of CR/PR by Investigator (confirmation not required)Tumor ResponseInvestigatorRECIST 1.1Y12022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-112BORBest Overall Response by Investigator (confirmation not required)Tumor ResponseInvestigatorRECIST 1.1PR22022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-113CBClinical Benefit by Investigator (confirmation for response not required)Tumor ResponseInvestigatorRECIST 1.1Y12022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-114CBCPBest Confirmed Overall Response of CR/PR by InvestigatorTumor ResponseInvestigatorRECIST 1.1Y12022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-115CBORBest Confirmed Overall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-116CCBConfirmed Clinical Benefit by InvestigatorTumor ResponseInvestigatorRECIST 1.1Y12022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-117CRSPConfirmed Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1Y12022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-118DEATHDeathReference EventN0Y
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-119LSTALast Disease Assessment by InvestigatorTumor ResponseInvestigatorRECIST 1.1PD52022-11-29WEEK 42YY14OVRLRESPINVESTIGATORPDWEEK 42162022-11-29
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1110MDISMeasurable Disease at Baseline by InvestigatorInvestigatorRECIST 1.1Y1Y
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1111OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1112OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-05-11WEEK 12YY9OVRLRESPINVESTIGATORPRWEEK 1262022-05-11
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1113OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-06-23WEEK 18YY10OVRLRESPINVESTIGATORPRWEEK 1882022-06-23
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1114OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-08-02WEEK 24YY11OVRLRESPINVESTIGATORPRWEEK 24102022-08-02
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1115OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-09-13WEEK 30YY12OVRLRESPINVESTIGATORPRWEEK 30122022-09-13
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1116OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PR22022-10-24WEEK 36YY13OVRLRESPINVESTIGATORPRWEEK 36142022-10-24
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1117OVROverall Response by InvestigatorTumor ResponseInvestigatorRECIST 1.1PD52022-11-29WEEK 42YY14OVRLRESPINVESTIGATORPDWEEK 42162022-11-29
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1118PDDisease Progression by InvestigatorTumor ResponseInvestigatorRECIST 1.1Y12022-11-29WEEK 42YY14OVRLRESPINVESTIGATORPDWEEK 42162022-11-29
ONCO-EXONCO-EX-70100270100270157FWHITEStandard of Care1Standard of Care1YY2022-02-1119RSPResponse by Investigator (confirmation not required)Tumor ResponseInvestigatorRECIST 1.1Y12022-03-23WEEK 6YY8OVRLRESPINVESTIGATORPRWEEK 642022-03-23
ONCO-EXONCO-EX-70100370100370158MWHITEStandard of Care1Standard of Care1YY2022-03-161BCPBest Overall Response of CR/PR by Investigator (confirmation not required)Tumor ResponseInvestigatorRECIST 1.1N0Y