Clinwright Public work of Helper Software LLC · free Start free

ADLBH

Analysis Dataset Lab Hematology

BASIC DATA STRUCTURE · one record per subject per parameter per analysis visit · keys USUBJID, PARAMCD, AVISIT, LBSEQ

Variables

VariableLabelTypeLengthDerivation
STUDYIDStudy IdentifiertextPredecessor ADSL.STUDYID
SUBJIDSubject Identifier for the StudytextPredecessor ADSL.SUBJID
USUBJIDUnique Subject IdentifiertextPredecessor LB.USUBJID
TRTPPlanned TreatmenttextPredecessor ADSL.TRT01P
TRTPNPlanned Treatment (N)integer8Predecessor ADSL.TRT01PN
TRTAActual TreatmenttextPredecessor ADSL.TRT01A
TRTANActual Treatment (N)integer8Predecessor ADSL.TRT01AN
TRTSDTDate of First Exposure to Treatmentinteger8Predecessor ADSL.TRTSDT
TRTEDTDate of Last Exposure to Treatmentinteger8Predecessor ADSL.TRTEDT
AGEAgeinteger8Predecessor ADSL.AGE
AGEGR1Pooled Age Group 1textPredecessor ADSL.AGEGR1
AGEGR1NPooled Age Group 1 (N)integer8Predecessor ADSL.AGEGR1N
RACERacetextPredecessor ADSL.RACE
RACENRace (N)integer8Predecessor ADSL.RACEN
SEXSextextPredecessor ADSL.SEX
DSRAEFLDiscontinued due to AE?textPredecessor ADSL.DSRAEFL
SAFFLSafety Population FlagtextPredecessor ADSL.SAFFL
AVISITAnalysis VisittextAnalysis Visit
AVISITNAnalysis Visit (N)integer8Assigned Sponsor
ADYAnalysis Relative Dayinteger8Predecessor LB.LBDY
ADTAnalysis Dateinteger8LB.LBDTC converted to SAS date
VISITVisit NametextPredecessor LB.VISIT
VISITNUMVisit Numberfloat8Predecessor LB.VISITNUM
PARAMParametertextFor PARAMN<100: same as LBTEST concatenated with LBSTRESU. For PARAMN>100: LBTEST concatenated with LB.LBSTRESU concatenated with "change from previous visit, relative to normal range"
PARAMCDParameter CodetextFor PARAMN<100: same as LBTESTCD. For PARAMN>100: _ + LBTESTCD
PARAMNParameter (N)integer8Assigned Sponsor
PARCAT1Parameter Category 1textAssigned Sponsor
AVALAnalysis Valuefloat8
BASEBaseline Valuefloat8LBSTRESN when ADLBC.ABLFL=Y
CHGChange from Baselinefloat8AVAL - BASE, for post baseline records.
A1LOAnalysis Range 1 Lower Limitfloat8Predecessor LB.LBSTNRLO
A1HIAnalysis Range 1 Upper Limitfloat8Predecessor LB.LBSTNRHI
R2A1LORatio to Analysis Range 1 Lower Limitfloat8AVAL / A1LO
R2A1HIRatio to Analysis Range 1 Upper Limitfloat8AVAL / A1HI
BR2A1LOBase Ratio to Analysis Range 1 Lower Limfloat8AVAL / A1LO at baseline
BR2A1HIBase Ratio to Analysis Range 1 Upper Limfloat8AVAL / A1HI at baseline
ANL01FLAnalysis Flag 01textANL01FL="Y" if the value is to be included in a per visit analysis. ANL01FL is missing for values from unscheduled visits.
ALBTRVALAmount Threshold Rangefloat8Maximum of [LBSTRESN-(1.5*ULN)] and [(.5*LLN) - LBSTRESN]
ANRINDAnalysis Reference Range IndicatortextIf AVAL < (0.5*LBSTNRLO) then ANRIND='L'. If LAVAL > 1.5* LBSTNRHI then ANRIND='H'. ANRIND='N' otherwise. Used for shift tables.
BNRINDBaseline Reference Range IndicatortextADLBC.ANRIND when ADLBC.ABLFL=Y
ABLFLBaseline Record FlagtextY on the last record with a value on or before TRTSDT, per subject and PARAMCD.
LBSEQSequence Numberinteger8Predecessor LB.LBSEQ
LBNRINDReference Range IndicatortextPredecessor LB.LBNRIND
LBSTRESNNumeric Result/Finding in Standard Unitsfloat8Predecessor LB.LBSTRESN

Program adlbh.sas

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

/*==== 00-header ====*/
/*----------------------------------------------------------------------------
  Data set : ADLBH (Analysis Dataset Lab Hematology)
  Reporting effort : CSR
  SDTM     : LB
  ADaM     : ADLBC, 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 LB records of the tests of the parameter table, with the subject's TRTSDT, TRTEDT, STUDYID, SUBJID, TRT01P, TRT01PN, TRT01A, TRT01AN, AGE, AGEGR1, AGEGR1N, RACE, RACEN, SEX, DSRAEFL, SAFFL from ADSL. */
proc sort data=sdtm.lb(keep=studyid usubjid lbseq lbtestcd lbstresn lbstnrlo lbstnrhi lbnrind visitnum visit lbdtc lbdy where=(lbtestcd in ('ANISO', 'BASO', 'EOS', 'HCT', 'HGB', 'LYM', 'MACROCY', 'MCH', 'MCHC', 'MCV', 'MICROCY', 'MONO', 'PLAT', 'POIKILO', 'POLYCHR', 'RBC', 'WBC'))) out=work._ds; by usubjid; run;
proc sort data=adam.adsl(keep=usubjid trtsdt trtedt studyid subjid trt01p trt01pn trt01a trt01an age agegr1 agegr1n race racen sex dsraefl saffl) out=work._adsl; by usubjid; run;
data work._ds;
  merge work._ds(in=_in) work._adsl;
  by usubjid;
  if _in;
  length trtp $200;
  trtp = trt01p;
  trtpn = trt01pn;
  length trta $200;
  trta = trt01a;
  trtan = trt01an;
  ady = lbdy;
  aval = lbstresn;
  a1lo = lbstnrlo;
  a1hi = lbstnrhi;
run;

/*==== 20-parameters ====*/
/* PARAM: from LBTESTCD by the parameter table (one value format per mapping). */
proc format;
  value $_param
    "ANISO" = "Anisocytes"
    "BASO" = "Basophils (GI/L)"
    "EOS" = "Eosinophils (GI/L)"
    "HCT" = "Hematocrit"
    "HGB" = "Hemoglobin (mmol/L)"
    "LYM" = "Lymphocytes (GI/L)"
    "MACROCY" = "Macrocytes"
    "MCH" = "Ery. Mean Corpuscular Hemoglobin (fmol(Fe))"
    "MCHC" = "Ery. Mean Corpuscular HGB Concentration (mmol/L)"
    "MCV" = "Ery. Mean Corpuscular Volume (fL)"
    "MICROCY" = "Microcytes"
    "MONO" = "Monocytes (GI/L)"
    "PLAT" = "Platelet (GI/L)"
    "POIKILO" = "Poikilocytes"
    "POLYCHR" = "Polychromasia"
    "RBC" = "Erythrocytes (TI/L)"
    "WBC" = "Leukocytes (GI/L)"
    other = " ";
run;
data work._ds;
  set work._ds;
  length param $200;
    param = put(lbtestcd, $_param.);
run;

/* PARAMN: the code of PARAM. */
proc format;
  invalue paramn_ (upcase just)

    "HEMOGLOBIN (MMOL/L)" = 1

    "HEMATOCRIT" = 2

    "ERY. MEAN CORPUSCULAR VOLUME (FL)" = 3

    "ERY. MEAN CORPUSCULAR HEMOGLOBIN (FMOL(FE))" = 4

    "ERY. MEAN CORPUSCULAR HGB CONCENTRATION (MMOL/L)" = 5

    "LEUKOCYTES (GI/L)" = 6

    "LYMPHOCYTES (GI/L)" = 7

    "MONOCYTES (GI/L)" = 8

    "EOSINOPHILS (GI/L)" = 9

    "BASOPHILS (GI/L)" = 10

    "PLATELET (GI/L)" = 11

    "ERYTHROCYTES (TI/L)" = 12

    "ANISOCYTES" = 13

    "MACROCYTES" = 14

    "MICROCYTES" = 15

    "POIKILOCYTES" = 16

    "POLYCHROMASIA" = 17

    other = .;
run;
data work._ds;
  set work._ds;
  if param ne '' then paramn = input(param, paramn_.);
run;

/* PARAMCD: from LBTESTCD by the parameter table (one value format per mapping). */
proc format;
  value $_paramcd
    "ANISO" = "ANISO"
    "BASO" = "BASO"
    "EOS" = "EOS"
    "HCT" = "HCT"
    "HGB" = "HGB"
    "LYM" = "LYM"
    "MACROCY" = "MACROCY"
    "MCH" = "MCH"
    "MCHC" = "MCHC"
    "MCV" = "MCV"
    "MICROCY" = "MICROCY"
    "MONO" = "MONO"
    "PLAT" = "PLAT"
    "POIKILO" = "POIKILO"
    "POLYCHR" = "POLYCHR"
    "RBC" = "RBC"
    "WBC" = "WBC"
    other = " ";
run;
data work._ds;
  set work._ds;
  length paramcd $200;
    paramcd = put(lbtestcd, $_paramcd.);
  /* The records of tests that are not parameters of the data set are not analyzed. */
  if missing(paramcd) then delete;
run;

/*==== 30-timing ====*/
/* ADT: LB.LBDTC as a SAS date. */
data work._ds;
  set work._ds;
  length _dtc $10;
  _dtc = lbdtc; /* (its first 10 characters: SUBSTR would complain of a shorter variable) */
  if length(_dtc) = 10 then adt = input(_dtc, ?? yymmdd10.);

  format adt date9.;
  drop _dtc;
run;

/*==== 40-visits ====*/
/* AVISIT: from the scheduled visit, by its name; other visits get none. */
data work._ds;
  set work._ds;
  length avisit $40;

  select (upcase(visit));

    when ("CYCLE 1 DAY 1") do; avisitn = 0; avisit = "Baseline"; end;

    when ("WEEK 3") do; avisitn = 3; avisit = "Week 3"; end;

    when ("WEEK 6") do; avisitn = 6; avisit = "Week 6"; end;

    when ("WEEK 9") do; avisitn = 9; avisit = "Week 9"; end;

    when ("WEEK 12") do; avisitn = 12; avisit = "Week 12"; end;

    when ("WEEK 15") do; avisitn = 15; avisit = "Week 15"; end;

    when ("WEEK 18") do; avisitn = 18; avisit = "Week 18"; end;

    when ("WEEK 21") do; avisitn = 21; avisit = "Week 21"; end;

    when ("WEEK 24") do; avisitn = 24; avisit = "Week 24"; end;

    when ("WEEK 27") do; avisitn = 27; avisit = "Week 27"; end;

    when ("WEEK 30") do; avisitn = 30; avisit = "Week 30"; end;

    when ("WEEK 33") do; avisitn = 33; avisit = "Week 33"; end;

    when ("WEEK 36") do; avisitn = 36; avisit = "Week 36"; end;

    when ("WEEK 39") do; avisitn = 39; avisit = "Week 39"; end;

    when ("WEEK 42") do; avisitn = 42; avisit = "Week 42"; end;

    when ("WEEK 45") do; avisitn = 45; avisit = "Week 45"; end;

    when ("WEEK 48") do; avisitn = 48; avisit = "Week 48"; end;

    when ("END OF TREATMENT") do; avisitn = 99; avisit = "End of Treatment"; end;

    otherwise call missing(avisit, avisitn);
  end;


run;

/* End of Treatment: each subject's last scheduled record after baseline, as a record of its own. */
proc sort data=work._ds(where=(avisitn > 0)) out=work._eot;
  by usubjid paramcd adt visitnum;
run;
data work._eot;
  set work._eot;
  by usubjid paramcd;
  if last.paramcd;
  avisit = "End of Treatment";
  avisitn = 99;
run;
data work._ds;
  set work._ds work._eot;
run;
proc sort data=work._ds; by usubjid; run;

/*==== 50-baseline ====*/
/* R2A1LO: AVAL / A1LO. */
data work._ds;
  set work._ds;
  if not missing(aval) and a1lo not in (., 0) then r2a1lo = aval / a1lo;
run;

/* R2A1LO as the data set keeps it, for what is derived from it. */
data work._ds; set work._ds; if not missing(r2a1lo) then r2a1lo = round(r2a1lo, 1e-1); run;

/* R2A1HI: AVAL / A1HI. */
data work._ds;
  set work._ds;
  if not missing(aval) and a1hi not in (., 0) then r2a1hi = aval / a1hi;
run;

/* R2A1HI as the data set keeps it, for what is derived from it. */
data work._ds; set work._ds; if not missing(r2a1hi) then r2a1hi = round(r2a1hi, 1e-1); run;

/* ABLFL: the last record with a value on or before TRTSDT, per subject and PARAMCD. */

proc sort data=work._ds; by usubjid paramcd adt; run;
data work._ds;
  set work._ds;
  _row = _n_;
run;
/* The last record with a value on or before the first dose, per group. */
data work._bl(keep=_row);
  set work._ds(where=(. < adt <= trtsdt and not missing(aval)));
  by usubjid paramcd;
  if last.paramcd;
run;
data work._ds(drop=_row);
  merge work._ds work._bl(in=_isbl);
  by _row;
  length ablfl $1;
  if _isbl then ablfl = 'Y';
run;

/* BR2A1LO: R2A1LO of the baseline record (ABLFL = 'Y'), per subject and PARAMCD. */
proc sort data=work._ds; by usubjid paramcd; run;
data work._br2a1lo(keep=usubjid paramcd br2a1lo);
  set work._ds(where=(ablfl = 'Y'));
  br2a1lo = r2a1lo;
run;
data work._ds;
  merge work._ds work._br2a1lo;
  by usubjid paramcd;
run;

/* BR2A1HI: R2A1HI of the baseline record (ABLFL = 'Y'), per subject and PARAMCD. */
proc sort data=work._ds; by usubjid paramcd; run;
data work._br2a1hi(keep=usubjid paramcd br2a1hi);
  set work._ds(where=(ablfl = 'Y'));
  br2a1hi = r2a1hi;
run;
data work._ds;
  merge work._ds work._br2a1hi;
  by usubjid paramcd;
run;

/* BASE: AVAL of the baseline record (ABLFL = 'Y'), per subject and PARAMCD. */
proc sort data=work._ds; by usubjid paramcd; run;
data work._base(keep=usubjid paramcd base);
  set work._ds(where=(ablfl = 'Y'));
  base = aval;
run;
data work._ds;
  merge work._ds work._base;
  by usubjid paramcd;
run;

/* The baseline record without an analysis visit (a screening value, the test not done on the day of the first dose): the Baseline analysis visit. */
data work._ds;
  set work._ds;
  if ablfl = 'Y' and missing(avisit) then do; avisit = 'Baseline'; avisitn = 0; _blvis = 1; end;
run;
/* (Then a record of the same subject and PARAMCD at the Baseline visit that is not the baseline, the day of the first dose without a value: no analysis visit.) */
proc sort data=work._ds; by usubjid paramcd descending _blvis; run;
data work._ds;
  set work._ds;
  by usubjid paramcd;
  retain _blmov;
  if first.paramcd then _blmov = (_blvis = 1);
  if _blmov and ablfl ne 'Y' and avisit = 'Baseline' then call missing(avisit, avisitn);
  drop _blvis _blmov;
run;

/* BASE as the data set keeps it, for what is derived from it. */
data work._ds; set work._ds; if not missing(base) then base = round(base, 1e-5); run;

/* CHG: AVAL - BASE, after baseline only. */
data work._ds;
  set work._ds;
  if nmiss(aval, base) = 0 and ablfl ne 'Y' and adt > trtsdt then chg = aval - base;
run;

/*==== 60-flags ====*/
/* ANRIND: AVAL against 0.5 x A1LO and 1.5 x A1HI. */
data work._ds;
  set work._ds;
  length anrind $8;
  if missing(aval) then anrind = '';
  else if not missing(a1lo) and aval < 0.5 * a1lo then anrind = 'L';
  else if not missing(a1hi) and aval > 1.5 * a1hi then anrind = 'H';
  else if nmiss(a1lo, a1hi) < 2 then anrind = 'N';
run;

/* ANL01FL: Y on the records with an analysis visit. */
data work._ds;
  set work._ds;
  length anl01fl $1;
  if not missing(avisit) then anl01fl = 'Y';
  else anl01fl = '';
run;

/* BNRIND: ANRIND of the baseline record (ABLFL = 'Y'), per subject and PARAMCD. */
proc sort data=work._ds; by usubjid paramcd; run;
data work._bnrind(keep=usubjid paramcd bnrind);
  set work._ds(where=(ablfl = 'Y'));
  bnrind = anrind;
run;
data work._ds;
  merge work._ds work._bnrind;
  by usubjid paramcd;
run;

/*==== 70-other ====*/
/* PARCAT1 = "HEM". */
data work._ds;
  set work._ds;
  length parcat1 $40;
  parcat1 = "HEM";
run;

/* ALBTRVAL (Amount Threshold Range): Maximum of [LBSTRESN-(1.5*ULN)] and [(.5*LLN) - LBSTRESN] -- to write. */

/*==== 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;
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, 'SUBJID') > 0 then do; _ren = catx(' ', _ren, 'SUBJID=_rz2'); _asg = catx(' ', _asg, 'SUBJID = _rz2;');
    _siz = catx(' ', _siz, '_len2 = max(_len2, lengthn(SUBJID));'); end;
  if varnum(_dsid, 'USUBJID') > 0 then do; _ren = catx(' ', _ren, 'USUBJID=_rz3'); _asg = catx(' ', _asg, 'USUBJID = _rz3;');
    _siz = catx(' ', _siz, '_len3 = max(_len3, lengthn(USUBJID));'); end;
  if varnum(_dsid, 'TRTP') > 0 then do; _ren = catx(' ', _ren, 'TRTP=_rz4'); _asg = catx(' ', _asg, 'TRTP = _rz4;');
    _siz = catx(' ', _siz, '_len4 = max(_len4, lengthn(TRTP));'); end;
  if varnum(_dsid, 'TRTA') > 0 then do; _ren = catx(' ', _ren, 'TRTA=_rz5'); _asg = catx(' ', _asg, 'TRTA = _rz5;');
    _siz = catx(' ', _siz, '_len5 = max(_len5, lengthn(TRTA));'); end;
  if varnum(_dsid, 'AGEGR1') > 0 then do; _ren = catx(' ', _ren, 'AGEGR1=_rz6'); _asg = catx(' ', _asg, 'AGEGR1 = _rz6;');
    _siz = catx(' ', _siz, '_len6 = max(_len6, lengthn(AGEGR1));'); end;
  if varnum(_dsid, 'RACE') > 0 then do; _ren = catx(' ', _ren, 'RACE=_rz7'); _asg = catx(' ', _asg, 'RACE = _rz7;');
    _siz = catx(' ', _siz, '_len7 = max(_len7, lengthn(RACE));'); end;
  if varnum(_dsid, 'SEX') > 0 then do; _ren = catx(' ', _ren, 'SEX=_rz8'); _asg = catx(' ', _asg, 'SEX = _rz8;');
    _siz = catx(' ', _siz, '_len8 = max(_len8, lengthn(SEX));'); end;
  if varnum(_dsid, 'DSRAEFL') > 0 then do; _ren = catx(' ', _ren, 'DSRAEFL=_rz9'); _asg = catx(' ', _asg, 'DSRAEFL = _rz9;');
    _siz = catx(' ', _siz, '_len9 = max(_len9, lengthn(DSRAEFL));'); end;
  if varnum(_dsid, 'SAFFL') > 0 then do; _ren = catx(' ', _ren, 'SAFFL=_rz10'); _asg = catx(' ', _asg, 'SAFFL = _rz10;');
    _siz = catx(' ', _siz, '_len10 = max(_len10, lengthn(SAFFL));'); end;
  if varnum(_dsid, 'AVISIT') > 0 then do; _ren = catx(' ', _ren, 'AVISIT=_rz11'); _asg = catx(' ', _asg, 'AVISIT = _rz11;');
    _siz = catx(' ', _siz, '_len11 = max(_len11, lengthn(AVISIT));'); end;
  if varnum(_dsid, 'VISIT') > 0 then do; _ren = catx(' ', _ren, 'VISIT=_rz12'); _asg = catx(' ', _asg, 'VISIT = _rz12;');
    _siz = catx(' ', _siz, '_len12 = max(_len12, lengthn(VISIT));'); end;
  if varnum(_dsid, 'PARAM') > 0 then do; _ren = catx(' ', _ren, 'PARAM=_rz13'); _asg = catx(' ', _asg, 'PARAM = _rz13;');
    _siz = catx(' ', _siz, '_len13 = max(_len13, lengthn(PARAM));'); end;
  if varnum(_dsid, 'PARAMCD') > 0 then do; _ren = catx(' ', _ren, 'PARAMCD=_rz14'); _asg = catx(' ', _asg, 'PARAMCD = _rz14;');
    _siz = catx(' ', _siz, '_len14 = max(_len14, lengthn(PARAMCD));'); end;
  if varnum(_dsid, 'PARCAT1') > 0 then do; _ren = catx(' ', _ren, 'PARCAT1=_rz15'); _asg = catx(' ', _asg, 'PARCAT1 = _rz15;');
    _siz = catx(' ', _siz, '_len15 = max(_len15, lengthn(PARCAT1));'); end;
  if varnum(_dsid, 'ANL01FL') > 0 then do; _ren = catx(' ', _ren, 'ANL01FL=_rz16'); _asg = catx(' ', _asg, 'ANL01FL = _rz16;');
    _siz = catx(' ', _siz, '_len16 = max(_len16, lengthn(ANL01FL));'); end;
  if varnum(_dsid, 'ANRIND') > 0 then do; _ren = catx(' ', _ren, 'ANRIND=_rz17'); _asg = catx(' ', _asg, 'ANRIND = _rz17;');
    _siz = catx(' ', _siz, '_len17 = max(_len17, lengthn(ANRIND));'); end;
  if varnum(_dsid, 'BNRIND') > 0 then do; _ren = catx(' ', _ren, 'BNRIND=_rz18'); _asg = catx(' ', _asg, 'BNRIND = _rz18;');
    _siz = catx(' ', _siz, '_len18 = max(_len18, lengthn(BNRIND));'); end;
  if varnum(_dsid, 'ABLFL') > 0 then do; _ren = catx(' ', _ren, 'ABLFL=_rz19'); _asg = catx(' ', _asg, 'ABLFL = _rz19;');
    _siz = catx(' ', _siz, '_len19 = max(_len19, lengthn(ABLFL));'); end;
  if varnum(_dsid, 'LBNRIND') > 0 then do; _ren = catx(' ', _ren, 'LBNRIND=_rz20'); _asg = catx(' ', _asg, 'LBNRIND = _rz20;');
    _siz = catx(' ', _siz, '_len20 = max(_len20, lengthn(LBNRIND));'); 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;
  &_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);
  end;
run;
data adam.adlbh(label="Analysis Dataset Lab Hematology");
  attrib
    STUDYID  length=$&_len1 label="Study Identifier"
    SUBJID   length=$&_len2 label="Subject Identifier for the Study"
    USUBJID  length=$&_len3 label="Unique Subject Identifier"
    TRTP     length=$&_len4 label="Planned Treatment"
    TRTPN    length=8 label="Planned Treatment (N)"
    TRTA     length=$&_len5 label="Actual Treatment"
    TRTAN    length=8 label="Actual Treatment (N)"
    TRTSDT   length=8 label="Date of First Exposure to Treatment" format=DATE9.
    TRTEDT   length=8 label="Date of Last Exposure to Treatment" format=DATE9.
    AGE      length=8 label="Age"
    AGEGR1   length=$&_len6 label="Pooled Age Group 1"
    AGEGR1N  length=8 label="Pooled Age Group 1 (N)"
    RACE     length=$&_len7 label="Race"
    RACEN    length=8 label="Race (N)"
    SEX      length=$&_len8 label="Sex"
    DSRAEFL  length=$&_len9 label="Discontinued due to AE?"
    SAFFL    length=$&_len10 label="Safety Population Flag"
    AVISIT   length=$&_len11 label="Analysis Visit"
    AVISITN  length=8 label="Analysis Visit (N)"
    ADY      length=8 label="Analysis Relative Day"
    ADT      length=8 label="Analysis Date" format=DATE9.
    VISIT    length=$&_len12 label="Visit Name"
    VISITNUM length=8 label="Visit Number"
    PARAM    length=$&_len13 label="Parameter"
    PARAMCD  length=$&_len14 label="Parameter Code"
    PARAMN   length=8 label="Parameter (N)"
    PARCAT1  length=$&_len15 label="Parameter Category 1"
    AVAL     length=8 label="Analysis Value"
    BASE     length=8 label="Baseline Value"
    CHG      length=8 label="Change from Baseline"
    A1LO     length=8 label="Analysis Range 1 Lower Limit"
    A1HI     length=8 label="Analysis Range 1 Upper Limit"
    R2A1LO   length=8 label="Ratio to Analysis Range 1 Lower Limit"
    R2A1HI   length=8 label="Ratio to Analysis Range 1 Upper Limit"
    BR2A1LO  length=8 label="Base Ratio to Analysis Range 1 Lower Lim"
    BR2A1HI  length=8 label="Base Ratio to Analysis Range 1 Upper Lim"
    ANL01FL  length=$&_len16 label="Analysis Flag 01"
    ALBTRVAL length=8 label="Amount Threshold Range"
    ANRIND   length=$&_len17 label="Analysis Reference Range Indicator"
    BNRIND   length=$&_len18 label="Baseline Reference Range Indicator"
    ABLFL    length=$&_len19 label="Baseline Record Flag"
    LBSEQ    length=8 label="Sequence Number"
    LBNRIND  length=$&_len20 label="Reference Range Indicator"
    LBSTRESN length=8 label="Numeric Result/Finding in Standard Units"
  ;
  set work._final&_ren;
  &_asg
  if not missing(VISITNUM) then VISITNUM = round(VISITNUM, 1e-1);
  if not missing(AVAL) then AVAL = round(AVAL, 1e-5);
  if not missing(BASE) then BASE = round(BASE, 1e-5);
  if not missing(CHG) then CHG = round(CHG, 1e-5);
  if not missing(A1LO) then A1LO = round(A1LO, 1e-5);
  if not missing(A1HI) then A1HI = round(A1HI, 1e-5);
  if not missing(R2A1LO) then R2A1LO = round(R2A1LO, 1e-1);
  if not missing(R2A1HI) then R2A1HI = round(R2A1HI, 1e-1);
  if not missing(BR2A1LO) then BR2A1LO = round(BR2A1LO, 1e-1);
  if not missing(BR2A1HI) then BR2A1HI = round(BR2A1HI, 1e-1);
  if not missing(ALBTRVAL) then ALBTRVAL = round(ALBTRVAL, 1e-3);
  if not missing(LBSTRESN) then LBSTRESN = round(LBSTRESN, 1e-5);
  keep STUDYID SUBJID USUBJID TRTP TRTPN TRTA TRTAN TRTSDT TRTEDT AGE AGEGR1 AGEGR1N RACE RACEN SEX DSRAEFL SAFFL AVISIT AVISITN ADY ADT VISIT VISITNUM PARAM PARAMCD PARAMN PARCAT1 AVAL BASE CHG A1LO A1HI R2A1LO R2A1HI BR2A1LO BR2A1HI ANL01FL ALBTRVAL ANRIND BNRIND ABLFL LBSEQ LBNRIND LBSTRESN;
run;
proc sort data=adam.adlbh; by USUBJID PARAMCD AVISIT LBSEQ; run;

Data (first 20 of 58,125 records, simulated)

STUDYIDSUBJIDUSUBJIDTRTPTRTPNTRTATRTANTRTSDTTRTEDTAGEAGEGR1AGEGR1NRACERACENSEXDSRAEFLSAFFLAVISITAVISITNADYADTVISITVISITNUMPARAMPARAMCDPARAMNPARCAT1AVALBASECHGA1LOA1HIR2A1LOR2A1HIBR2A1LOBR2A1HIANL01FLALBTRVALANRINDBNRINDABLFLLBSEQLBNRINDLBSTRESN
ONCO-EXONCO-EX-7010012022-01-25SCREENING1AnisocytesANISO13HEM1191
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Basophils (GI/L)BASO10HEM0.050.20.3N20NORMAL0.05
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Eosinophils (GI/L)EOS9HEM21
ONCO-EXONCO-EX-7010012022-01-25SCREENING1HematocritHCT2HEM0.460.340.481.41N22NORMAL0.46
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Hemoglobin (mmol/L)HGB1HEM6.3857.149.810.90.7N23LOW6.385
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Lymphocytes (GI/L)LYM7HEM0.880.831.10.3N24NORMAL0.88
ONCO-EXONCO-EX-7010012022-01-25SCREENING1MacrocytesMACROCY14HEM1251
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Ery. Mean Corpuscular Hemoglobin (fmol(Fe))MCH4HEM2.1141.62.11.31N26HIGH2.114
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Ery. Mean Corpuscular HGB Concentration (mmol/L)MCHC5HEM18.126192410.8N27LOW18.126
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Ery. Mean Corpuscular Volume (fL)MCV3HEM90801001.10.9N28NORMAL90
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Monocytes (GI/L)MONO8HEM0.40.120.923.30.4N29NORMAL0.4
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Platelet (GI/L)PLAT11HEM2371303941.80.6N30NORMAL237
ONCO-EXONCO-EX-7010012022-01-25SCREENING1PolychromasiaPOLYCHR17HEM1311
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Erythrocytes (TI/L)RBC12HEM4.73.95.51.20.9N32NORMAL4.7
ONCO-EXONCO-EX-7010012022-01-25SCREENING1Leukocytes (GI/L)WBC6HEM5.123.810.71.30.5N33NORMAL5.12
ONCO-EX701002ONCO-EX-701002Standard of Care1Standard of Care12022-02-122022-12-1057<651WHITE1FNullY-32022-02-09SCREENING1AnisocytesANISO13HEM113071
ONCO-EX701002ONCO-EX-701002Standard of Care1Standard of Care12022-02-122022-12-1057<651WHITE1FNullY3362023-01-13SAFETY FOLLOW-UP91AnisocytesANISO13HEM1103231
ONCO-EX701002ONCO-EX-701002Standard of Care1Standard of Care12022-02-122022-12-1057<651WHITE1FNullYBaseline012022-02-12CYCLE 1 DAY 12AnisocytesANISO13HEM11YY3081
ONCO-EX701002ONCO-EX-701002Standard of Care1Standard of Care12022-02-122022-12-1057<651WHITE1FNullYEnd of Treatment992912022-11-29WEEK 4216AnisocytesANISO13HEM110Y3221
ONCO-EX701002ONCO-EX-701002Standard of Care1Standard of Care12022-02-122022-12-1057<651WHITE1FNullYWeek 1212892022-05-11WEEK 126AnisocytesANISO13HEM110Y3121