) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence...

20
1 PharmaSUG 2021 - Paper AP-066 Dynamic macro using the convergence dataset to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure Jagadish Katam, Princeps Technologies Inc. ABSTRACT This paper talks about the displays, where we are supposed to present the OR, RR and RD per SOC and PT across the treatment using the genmod procedure. Sometimes, when using this genmod the OR, RR and RD will not be created per SOC and PT when there are missing/less subjects in any of the treatment group. AE displays will have many SOCs and PTs. If a particular SOC or PT does not have the sufficient subjects across the treatments, the genmod procedure will throw an error and do not generate the OR, RR or RD results/outputs. In these cases, the macro that I have created will be useful to dynamically handle the situation to produce the results. This macro is based on the convergence criteria. Using this macro, the convergence criteria will be checked using the genmod procedure for each SOC/PT for the OR, RR and RD values and store the convergence criteria in a separate dataset. We will then use this convergence dataset for the SOC/PT per OR, RR and RD to dynamically execute the genmod procedure depending on the convergence criteria there by avoid any such errors. This paper is going to discuss about the sequence of steps that this macro takes to avoid any such errors caused by genmod procedure. INTRODUCTION We notice that when we work with statistical procedures which have the model statement giving us error or warning and there by no result. It happens when the data passed in the procedure does not support the model. However, it is still expected that, we need to run the model and check if the data supports the model or not and if it does then we get the result and if it doesn’t, we won’t. The challenge is that when we run the model and the data does not support, we should not get the error or warning in the log, this is the general expectation as we want to see the log clean. To achieve this, we need to understand whether the data supports the model or not. This paper helps to know the approach if the model works or not with the given data and use that data to execute the model only if the given data supports it. This way we can avoid the error or warnings in the log and ensure the clean execution of the code. We are using the ADAE data and try to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure. MOCK SHELL Before we start working on a table/output we need to look at the mock shell. Based on the mock shell, the programming approach can be planned. Here is the example mock shell which we need to generate. As per the mock shell and footnote, we need to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure. It is also clear that we need to generate the small n counts for TRT1 and TRT2, but the main focus of this paper is to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure.

Transcript of ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence...

Page 1: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

1

PharmaSUG 2021 - Paper AP-066

Dynamic macro using the convergence dataset to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod

procedure Jagadish Katam, Princeps Technologies Inc.

ABSTRACT

This paper talks about the displays, where we are supposed to present the OR, RR and RD per SOC and PT across the treatment using the genmod procedure. Sometimes, when using this genmod the OR, RR and RD will not be created per SOC and PT when there are missing/less subjects in any of the treatment group. AE displays will have many SOCs and PTs. If a particular SOC or PT does not have the sufficient subjects across the treatments, the genmod procedure will throw an error and do not generate the OR, RR or RD results/outputs. In these cases, the macro that I have created will be useful to dynamically handle the situation to produce the results. This macro is based on the convergence criteria. Using this macro, the convergence criteria will be checked using the genmod procedure for each SOC/PT for the OR, RR and RD values and store the convergence criteria in a separate dataset. We will then use this convergence dataset for the SOC/PT per OR, RR and RD to dynamically execute the genmod procedure depending on the convergence criteria there by avoid any such errors. This paper is going to discuss about the sequence of steps that this macro takes to avoid any such errors caused by genmod procedure.

INTRODUCTION

We notice that when we work with statistical procedures which have the model statement giving us error or warning and there by no result. It happens when the data passed in the procedure does not support the model. However, it is still expected that, we need to run the model and check if the data supports the model or not and if it does then we get the result and if it doesn’t, we won’t. The challenge is that when we run the model and the data does not support, we should not get the error or warning in the log, this is the general expectation as we want to see the log clean. To achieve this, we need to understand whether the data supports the model or not. This paper helps to know the approach if the model works or not with the given data and use that data to execute the model only if the given data supports it. This way we can avoid the error or warnings in the log and ensure the clean execution of the code. We are using the ADAE data and try to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure.

MOCK SHELL

Before we start working on a table/output we need to look at the mock shell. Based on the mock shell, the programming approach can be planned. Here is the example mock shell which we need to generate. As per the mock shell and footnote, we need to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure. It is also clear that we need to generate the small n counts for TRT1 and TRT2, but the main focus of this paper is to generate the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure.

Page 2: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

2

CONVERGENCE DATASET

In order to know if the model will work with the given data or not, we need to pass the given data into the proc genmod and check if the convergence criteria is met or not. The way we can determine if the convergence criteria is met or not is based on the convergence dataset. This convergence dataset is generated from the proc genmod procedure. For the Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) we will see the example pseudo codes.

GENMOD PROCEDURE PSEUDO CODE FOR RD

If we use the ods output statement convergencestatus, then we will get the convergence dataset. Here in the below pseudo code we can generate the convergencestatus in rd_convergence dataset.

proc genmod data = fin; class trt01an; model respond = trt01an / dist=bin link=identity type3; estimate "Risk Diff" trt01an 1 -1/exp; ods output Estimates = cpdiff convergencestatus=rd_convergence; run; GENMOD PROCEDURE PSEUDO CODE FOR OR

If we use the ods output statement convergencestatus, then we will get a convergence dataset. Here in the below pseudo code we can generate the convergencestatus in or_convergence dataset.

proc genmod data = fin; class trt01an; model respond = trt01an / dist=bin type3 ; estimate "Odds Ratio" trt01an 1 -1/exp; Ods output Estimates = ests convergencestatus=or_convergence; run;

Page 3: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

3

GENMOD PROCEDURE PSEUDO CODE FOR RR

If we use the ods output statement convergencestatus, then we will get a convergence dataset. Here in the below pseudo code we can generate the convergencestatus in rr_convergence dataset.

proc genmod data = fin; class trt01an; model respond = trt01an / dist=bin link=log type3 ; estimate "Risk Ratio" trt01an 1 -1/exp; ods output Estimates = rrests convergencestatus=rr_convergence; run; RESPOND VARIABLE In all the pseudo codes the common variable is the respond variable, which is derived as below. data fin; merge adsl(in=a) adae(in=b ); by usubjid; if a; if a and b then respond=1; if a and not b then respond=2; run; proc sort data= fin ; by trt01an respond; run; The MODEL statement specifies the conditional distribution of the data given the parameters (the likelihood function). You specify a single dependent variable. The respond variable is that dependent variable here. In the respond variable we have values as 1 and 2, value 1 indicates that the subject in ADAE dataset is also part of the safety population (from ADSL), whereas if the subject is available in safety population but does not experience any AE for those subjects the respond variable value is 2.

%CONVERGENCE MACRO

We talked about the mock shell, convergence dataset and the response variable. Now we will see the %convergence macro in order to generate the convergence datasets for each Odds Ratio (OR), Risk Ratio (RR) and Risk Difference (RD) per SOC/PT using Genmod procedure.

%convergence macro to create the convergence data set for every SOC and PT within the given data.

It produces a convergence data set which has SOC and PT variables along with the convergence STATUS variable and STAT variable which has OR, RR and RD values.

Example, of a typical macro call to create the convergence data set will look like this.

%convergence(whr=anl07fl="Y", row=row, soc=aesoc, pt=aedecod, subset=);

Page 4: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

4

%COVERGENCE MACRO KEYWORD PARAMETERS

The macro expects that the input ADAE dataset has the required variables to be passed in the macro keyword parameters. As we can see in the macro call, all the keyword parameters have variables that we can expect within the ADAE dataset except for the ROW variable. We followed the below derivation for ROW within the macro. proc sort data=adam.adae out=adaecall2(keep=aesoc aedecod) nodupkey; by aesoc aedecod; where saffl='Y' ; run; data adaecallall; set adaecall2; by aesoc; row=_n_; run; The row variable is derived to assign a distinct row number per subject per overall AE, per SOC and per PT. It also helps in sorting the final data in the output to follow the order of Overall AE or Any AE, followed by SOC and then PT. CALLING THE %CONVERGENCE MACRO The below code gives all the details about the convergence macro, about the input dataset and how we are using the call execute to pass the dataset values into macro parameters via input dataset.

Page 5: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

5

%macro convergence(whr=,row=,soc=,pt=,subset=); options obs=max replace nosyntaxcheck; proc sort data=adam.adae out=adae; by usubjid ; run; data adae; set adae; output; aesoc='Overall'; output; run; proc sort data=adam.adsl out=pop(keep=usubjid trt01a trt01an); by usubjid trt01an trt01a; where saffl='Y'; run; data adae2; merge pop(in=a) adae(in=b drop=trt01a trt01an); by usubjid; if a and b; run; proc sort data=adae2 out=adae3(keep=usubjid aesoc aedecod) nodupkey; by usubjid; where &whr; run; data fin; merge pop(in=a) adae3(in=b ); by usubjid; if a; if a and b then respond=1; if a and not b then respond=2; run; proc sort data=fin ; by trt01an respond; run; ods exclude all; proc genmod data = fin ; ⊂ class trt01an; model respond = trt01an / dist=bin link=identity type3 ; estimate "Risk Diff" trt01an 1 -1/exp; ods output Estimates = cpdiff convergencestatus=rd_convergence&row.; run; data rd_convergence&row.; length aesoc $67 aedecod $53.; set rd_convergence&row.; aesoc=&soc; aedecod=&pt; run;

Page 6: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

6

proc genmod data = fin ; ⊂ class trt01an; model respond = trt01an / dist=bin type3 ; /*link function=logit*/ estimate "Odds Ratio" trt01an 1 -1/exp; ods output Estimates = ests convergencestatus=or_convergence&row.; run; data or_convergence&row.; length aesoc $67 aedecod $53.; set or_convergence&row.; aesoc=&soc; aedecod=&pt; run; proc genmod data = fin ; ⊂ class trt01an; model respond = trt01an / dist=bin link=log type3 ; /*link function=log*/ estimate "Risk Ratio" trt01an 1 -1/exp; ods output Estimates = rrests convergencestatus=rr_convergence&row.; run; data rr_convergence&row.; length aesoc $67 aedecod $53.; set rr_convergence&row.; aesoc=&soc; aedecod=&pt; run; ods exclude none; %mend; To use the convergence macro with the call execute we need to prepare the input dataset which has the macro parameter values. The below code helps in developing that input dataset. Here we are using the ADAE dataset to generate a dataset which has distinct rows per subject per overall AE, per SOC, per PT along with analysis flags per subject. The analysis flag ANL01FL is used to subset the AE data to select distinct subject with any AE. The analysis flag ANL02FL is used to subset the AE data to select distinct subject per distinct SOC. The analysis flag ANL03FL is used to subset the AE data to select distinct subject per distinct SOC per distinct PT. Since there are three analysis flags, we need to subset the ADAE, three times to get the three sections for OR, RR and RD values, so we repeat the call execute thrice.

Page 7: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

7

proc sort data=adam.adae out=adaecall(keep=aesoc ) nodupkey; by aesoc ; where saffl='Y'; run; data adaecall; set adaecall; output; aesoc='Overall'; output; run; proc sort data=adam.adae out=adaecall2(keep=aesoc aedecod) nodupkey; by aesoc aedecod; where saffl='Y'; run; proc sort data=adaecall; by aesoc; run; proc sort data=adaecall2; by aesoc; run; data adaecallall; set adaecall adaecall2; by aesoc; row=_n_; run; proc sort data=adaecallall nodupkey; by aesoc aedecod; run; options noquotelenmax mprint ; data _null_; set ADAECALLALL; if aesoc ne '' and aedecod eq '' then call execute('%convergence(whr=%nrstr(anl02fl="Y" and aesoc='||quote(strip(aesoc))||'),row='||row||',soc='||quote(aesoc)||',pt='||quote(aedecod)||',subset=%nrstr());'); if aesoc ne '' and aedecod ne '' then call execute('%convergence (whr=%nrstr(anl03fl="Y" and aesoc='||quote(strip(aesoc))||' and aedecod='||quote(strip(aedecod))||'),row='||row||',soc='||quote(aesoc)||',pt='||quote(aedecod)||',subset=%str());'); if aesoc eq 'Overall' and aedecod eq '' then call execute('%convergence (whr=%nrstr(anl01fl="Y" and aesoc='||quote(strip(aesoc))||'),row='||row||',soc='||quote(aesoc)||',pt='||quote(aedecod)||',subset=%str());'); run;

Page 8: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

8

When we run the above macro call, we get separate convergence datasets per subject, per SOC, per PT for OR, RR and RD. All the convergence datasets for OR, RR and RD per subject, per SOC and per PT need to be appended. When we append all of them we get a final dataset here in this code we call it as convergenceds. proc sort data=adam.adae out=adaecall(keep=aesoc ) nodupkey; by aesoc ; where saffl='Y'; run; data adaecall; set adaecall; output; aesoc='Overall'; output; run; proc sort data=adam.adae out=adaecall2(keep=aesoc aedecod) nodupkey; by aesoc aedecod; where saffl='Y'; run; proc sort data=adaecall; by aesoc ; run; data adaecallall; set adaecall adaecall2; by aesoc; row=_n_; run; proc sort data=adaecallall nodupkey; by aesoc aedecod; run; data stats; stats='RD'; output; stats='OR'; output; stats='RR'; output; run; proc sql; create table allsubgroups as select distinct a.*, c.* from adaecallall as a , stats as c order by a.aesoc,a.aedecod; quit; data convergencestatus; set rd_convergence: (in=a) or_convergence: (in=b) rr_convergence: (in=c); if a then stats='RD'; if b then stats='OR'; if c then stats='RR'; run;

Page 9: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

9

proc sort data=convergencestatus nodupkey; by aesoc aedecod stats; run; data all ; merge convergencestatus(in=a) allsubgroups(in=b where=(aesoc^='')); by aesoc aedecod stats; if b; run; proc sort data=all; by row aesoc aedecod stats ; run; data transall_; set all; subset=catx(',',quote(strip(_1)),quote(strip(_2)),quote(strip(_3))); subset=catx(',',quote(strip(_1)),quote(strip(_2))); run; data all2 ; length subset $100.; merge all(in=a) transall_; by row aesoc aedecod stats ; if a; run; proc sort data=all2; by row aesoc aedecod stats status; run; data convergenceds; set all2; by row aesoc aedecod stats status; if last.stats; run; proc datasets lib=work memtype=data; save rd_convergence: or_convergence: rr_convergence:; run; In the convergenceds dataset, we can see the AESOC, AEDECOD, STATUS, REASON, STATS and ROW variables. The STATUS and REASON variables per SOC per PT come from the convergence dataset of proc genmod. The STATS variable indicates whether the convergence status belongs to OR, RR or RD. The ROW variable represents unique number per any AE event i.e., overall , per SOC and per PT. We use the row variable also to merge with the main ADAE dataset on AESOC, AEDECOD and ROW. Here I am using the AESOC and AEDECOD as well which are synonyms of SOC and PT.

Page 10: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

10

The execution of convergence macro results in the generation of the convergence dataset but at the same time, we can expect the log to have the errors and warnings. So, this is something you need to make a note of, this macro is supposed to be used only for the generation of the convergence dataset and we should not concern about the log issues. The reason being that when we are executing this macro, we are running the proc genmod procedure per every subject, per every SOC and per every PT and there might be few cases where the data does not support the model and we may get the error and warnings, but that information is captured in the convergence dataset. This is acceptable because when we are running the main macro, we will use the convergence dataset and thereby in the macro when the main macro is executed the prod genmod procedures are executed conditionally per subject, per SOC and per PT.

CONVERGENCE STATUS

As per the dataset we can see the numeric STATUS variable with values as 0,1,2 and 3 and a character column named reason.

Please check below the definition of the STATUS values.

0: converged; everything seemed fine.

1: converged, but there was some minor problem or a reason to question the results.

2: the iteration limit was reached without convergence, but everything seemed fine.

3: an error prevented further iteration.

The model in genmod executes only when the convergence status is 0 and 2, when status is 1,3 or missing then it won’t execute. Considering this we need to make the values 1 and 3 also missing, reason is that the main macro what we are going to use is programmed to execute the genmod procedure only when the status is not missing. If the status has 1 and 3 values, then genmod will execute and will result in an error in the log which we want to avoid. Since the convergence status values are vertical per OR, RR and RD. we need to transpose them to get horizontal structure.

TRANSPOSE THE CONVERGENCE DATASET FOR USE IN MAIN MACRO

We need to prepare the final convergence dataset for use in the main macro. The structure of the final convergence dataset will be as below.

Page 11: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

11

%OR_RR_RD MACRO TO GENERATE THE OUTPUT

This macro creates the final output with OR, RR and RD as variables per every SOC and PT within the given data.

It uses the macro parameters values of OR, RR and RD to conditionally execute the genmod procedure. If the macro parameter values of RD, OR or RR are missing then genmod will not execute, if they are not missing then genmod will execute.

Example, a typical macro call to create the or_rr_rd data set will look like this

%or_rr_rd(whr=anl07fl="Y", row=row, soc=aesoc, pt=aedecod, rd=rd or=or rr=rr);

%OR_RR_RD MACRO KEYWORD PARAMETERS

CALLING THE %OR_RR_RD MACRO Trans dataset used here is the transposed convergence dataset for use in %or_rr_rd macro. This dataset variable values form the macro parameters values via call execute and the macro is executed using the call execute row by row. And then finally we get the desired output with clean log.

Page 12: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

12

%macro or_rr_rd(whr=,row=,soc=,pt=,rd=,or=,rr=); ods exclude all; options obs=max replace nosyntaxcheck; proc sort data=adam.adae out=adae; by usubjid ; run; data adae; set adae; output; aesoc='Overall'; output; run; proc sort data=adam.adsl out=pop(keep=usubjid trt01a trt01an); by usubjid trt01an trt01a; where saffl='Y'; run; proc freq data=pop noprint; table trt01an*trt01a/out=popcount(drop=percent rename=(count=pop)); run; data adae2; merge pop(in=a) adae(in=b drop=trt01a trt01an); by usubjid; if a and b; run; proc sort data=adae2 out=adae3(keep=usubjid aesoc aedecod) nodupkey; by usubjid; where &whr; run; proc sort data=adae2 out=adae4 nodupkey; by usubjid; where &whr; run; proc freq data=adae4 noprint; table trt01an*trt01a/out=adae4count(drop=percent rename=(count=event)); run; data count; merge popcount(in=a) adae4count(in=b); by trt01an trt01a; if a; if event gt 0 and pop gt 0 then tt_pct=(event/pop)*100; else tt_pct=.; if tt_pct gt 0 then tt_pct_c=right(reverse(')%'||substr(reverse(trim(left(put(tt_pct,pcnt_f.)))),2))); if 0 lt tt_pct lt 1/(10**0) then tt_pct_c="(<"||trim(left(put(1/(10**0),best.)))||'%)';

Page 13: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

13

if (100-1/(10**0)) lt tt_pct lt 100 then tt_pct_c="(>"||trim(left(put(100-1/(10**0),best.)))||'%)'; count_per=put(event,3.)||" "||tt_pct_c; run; proc sort data=count; by trt01an; run; proc transpose data=count out=trans_count&row. prefix=trt; id trt01an; idlabel trt01a; var count_per; run; data trans_count&row.; set trans_count&row.; row=&row.; aesoc=&soc.; aedecod=&pt.; drop _name_; run; data fin; merge pop(in=a) adae3(in=b ); by usubjid; if a; if a and b then respond=1; if a and not b then respond=2; run; proc sort data=fin ; by trt01an respond; run; **************************************************************************** * Risk Difference ****************************************************************************; %if &rd ne %then %do; ods exclude all; proc genmod data = fin ; class trt01an; model respond = trt01an / dist=bin link=identity type3 ; estimate "Risk Diff" trt01an 1 -1/exp; ods output Estimates = cpdiff ; run; ods exclude none; data cpdiff1; set cpdiff; where label = 'Risk Diff'; if meanEstimate ne . and meanEstimate <=999 then rdestimate=put(meanEstimate*100,8.4 -l); else rdestimate='-'; if

Page 14: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

14

/*index(rdestimate,'E')*/ meanEstimate >999 then rdestimate='>999'; else if indexw(rdestimate,'0.0000') or indexw(rdestimate, '-0.0000') then rdestimate='<0.0001'; if meanLowerCL ne . and meanLowerCL <=999 then rdLowerCL=put(meanLowerCL*100,8.4 -l); else rdLowerCL='-';if /*index(rdLowerCL,'E')*/ meanLowerCL >999 then rdLowerCL='>999';else if indexw(rdLowerCL,'0.0000') or indexw(rdLowerCL, '-0.0000') then rdLowerCL='<0.0001'; if meanUpperCL ne . and meanUpperCL <=999 then rdUpperCL=put(meanUpperCL*100,8.4 -l); else rdUpperCL='-';if /*index(rdUpperCL,'E')*/ meanUpperCL >999 then rdUpperCL='>999';else if indexw(rdUpperCL,'0.0000') or indexw(rdUpperCL, '-0.0000') then rdUpperCL='<0.0001'; rdx=strip(rdestimate)||' ('||catx(', ',rdLowerCL,rdUpperCL)||')'; keep rdx; run; data cpdiff2; set cpdiff; where label = 'Risk Diff'; if ProbChiSq ne . then rdProbChiSq=put(ProbChiSq,pvalue8.3 -l); else rdProbChiSq='NC'; if substr(strip(rdProbChiSq),1,1)="1" then rdProbChiSq=">0.999"; keep rdProbChiSq ; run; data cpdiff3; merge cpdiff1 cpdiff2; rd=catx('~/',rdx,rdProbChiSq); drop rdx rdProbChiSq; aesoc=&soc; aedecod=&pt; row=&row.; run; %end; %if &rd eq %then %do; data cpdiff3; aesoc=&soc; aedecod=&pt; row=&row.; run; %end; ***************************************************************************** * Odds Ratio ****************************************************************************; %if &or ne %then %do; ods exclude all; proc genmod data = fin; class trt01an; model respond = trt01an / dist=bin type3 ; /*link function=logit*/ estimate "Odds Ratio" trt01an 1 -1/exp; ods output Estimates = ests ;

Page 15: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

15

run; ods exclude none; data ests1; set ests; where label = 'Exp(Odds Ratio)'; if LBetaEstimate ne . and LBetaEstimate<=999 then orestimate=put(LBetaEstimate,8.4 -l); else orestimate='-'; if LBetaEstimate>999 then orestimate='>999'; else if indexw(orestimate,'0.0000') or indexw(orestimate, '-0.0000') then orestimate='<0.0001'; if LBetaLowerCL ne . and LBetaLowerCL<=999 then orLowerCL=put(LBetaLowerCL,8.4 -l); else orLowerCL='-'; if LBetaLowerCL>999 then orLowerCL='>999';else if indexw(orLowerCL,'0.0000') or indexw(orLowerCL, '-0.0000') then orLowerCL='<0.0001'; if LBetaUpperCL ne . and LBetaUpperCL<=999 then orUpperCL=put(LBetaUpperCL,8.4 -l); else orUpperCL='-'; if LBetaUpperCL>999 then orUpperCL='>999';else if indexw(orUpperCL,'0.0000') or indexw(orUpperCL, '-0.0000') then orUpperCL='<0.0001'; orx=strip(orestimate)||' ('||catx(', ',orLowerCL,orUpperCL)||')'; keep orx ; run; data ests2; set ests; where label = 'Odds Ratio'; if ProbChiSq ne . then orProbChiSq=put(ProbChiSq,pvalue8.3); else orProbChiSq='NC'; if substr(strip(orProbChiSq),1,1)="1" then orProbChiSq=">0.999"; keep orProbChiSq ; run; data ests3; merge ests1 ests2; or=catx('~/',orx,orProbChiSq); drop orx orProbChiSq; aesoc=&soc; aedecod=&pt; row=&row.; run; %end; %if &or eq %then %do; data ests3; aesoc=&soc; aedecod=&pt; row=&row.; run; %end;

Page 16: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

16

**************************************************************************** * Risk Ratio ****************************************************************************; %if &rr ne %then %do; ods exclude all; proc genmod data = fin ; class trt01an; model respond = trt01an / dist=bin link=log type3 ; /*link function=log*/ estimate "Risk Ratio" trt01an 1 -1/exp; ods output Estimates = rrests ; run; ods exclude none; data rrests1; set rrests; where label = 'Risk Ratio'; if meanEstimate ne . and meanEstimate<=999 then rrestimate=put(meanEstimate,8.4 -l); else rrestimate='-'; if meanEstimate>999 then rrestimate='>999'; else if indexw(rrestimate,'0.0000') or indexw(rrestimate, '-0.0000') then rrestimate='<0.0001'; if meanLowerCL ne . and meanLowerCL<=999 then rrLowerCL=put(meanLowerCL,8.4 -l); else rrLowerCL='-'; if meanLowerCL>999 then rrLowerCL='>999';else if indexw(rrLowerCL,'0.0000') or indexw(rrLowerCL, '-0.0000') then rrLowerCL='<0.0001'; if meanUpperCL ne . and meanUpperCL<=999 then rrUpperCL=put(meanUpperCL,8.4 -l); else rrUpperCL='-'; if meanUpperCL>999 then rrUpperCL='>999';else if indexw(rrUpperCL,'0.0000') or indexw(rrUpperCL, '-0.0000') then rrUpperCL='<0.0001'; rrx=strip(rrestimate)||' ('||catx(', ',rrLowerCL,rrUpperCL)||')'; keep rrx ; run; data rrests2; set rrests; where label = 'Risk Ratio'; if ProbChiSq ne . then rrProbChiSq=put(ProbChiSq,pvalue8.3); else rrProbChiSq='NC'; if substr(strip(rrProbChiSq),1,1)="1" then rrProbChiSq=">0.999"; keep rrProbChiSq ; label probchisq = 'Pvalue'; run; data rrests3; merge rrests1 rrests2; rr=catx('~/',rrx,rrProbChiSq); drop rrx rrProbChiSq; aesoc=&soc; aedecod=&pt; row=&row.; run; %end; %if &rr eq %then %do; data rrests3; set rrests3; aesoc=&soc;

Page 17: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

17

aedecod=&pt; row=&row.; run; %end; proc sort data=cpdiff3; by row aesoc aedecod ; run; proc sort data=ests3; by row aesoc aedecod ; run; proc sort data=rrests3; by row aesoc aedecod ; run; proc sort data=trans_count&row.; by row aesoc aedecod; run; data finrdorrr&row.; merge cpdiff3(in=a) ests3(in=b) rrests3(in=c) trans_count&row.; by row aesoc aedecod; run; proc sort data=finrdorrr&row. out=dummy(keep=aesoc aedecod) nodupkey; by aesoc aedecod; run; proc sort data=finrdorrr&row. out=_rdorrr&row.; by aesoc aedecod ; run; data rdorrr&row.; set _rdorrr&row.; by aesoc aedecod; run; ods exclude none; %mend; data subgroupdata; set reference.&dsout; run; proc sort data=subgroupdata out=testall; by row aesoc aedecod; run; data testall; set testall(rename=(status=_status)); if _status in (3,1) then status=.; else status=_status; run; proc sort data=testall;

Page 18: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

18

by row aesoc aedecod; run; proc transpose data=testall out=trans_status; by row aesoc aedecod; id stats; var status; run; data trans; merge trans_status; by row aesoc aedecod; run; data trans(where=(coalescec(rd,or,rr)^=' ' )); set trans(rename=(rd=_rd or=_or rr=_rr)); if _rd eq . then rd=' '; else rd=strip(put(_rd,best.)); if _or eq . then or=' '; else or=strip(put(_or,best.)); if _rr eq . then rr=' '; else rr=strip(put(_rr,best.)); run; options noquotelenmax nomprint nosource; data _null_; set trans; if aesoc ne 'Overall' and aedecod eq '' then call execute('%or_rr_rd (whr=%nrstr(anl02fl=”Y” and aesoc='||quote(strip(aesoc))||'),row='||row||',soc='||quote(strip(aesoc))||',pt='||quote(strip(aedecod))||',rd='||rd||',or='||or||',rr='||rr||');'); if aesoc ne ' ' and aedecod ne '' then call execute('%or_rr_rd (whr=%nrstr(anl03fl=”Y” and aesoc='||quote(strip(aesoc))||' and aedecod='||quote(strip(aedecod))||'),row='||row||',soc='||quote(strip(aesoc))||',pt='||quote(strip(aedecod))||',rd='||rd||',or='||or||', rr='||rr||');'); if aesoc eq 'Overall' and aedecod eq '' then call execute('%or_rr_rd (whr=%nrstr(anl01fl=”Y” and aesoc='||quote(strip(aesoc))||'),row='||row||',soc='||quote(strip(aesoc))||',pt='||quote(strip(aedecod))||',rd='||rd||',or='||or||',rr='||rr||');'); run;

Page 19: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

19

FINAL OUTPUT

As we can see from the output the OR, RD and RR are displayed only for those SOC and PT where the convergence criteria is met, if convergence criteria is not met then the proc genmod is not executed due to which we displayed NC. As shown in the transposed convergence dataset (Page 10) the convergence criteria for RD are missing for some PTs and so the RD values missing in the display.

CONCLUSION

In this paper I tried to present two macros %convergence and %or_rr_rd. The %convergence macro generates convergence dataset for use in the %or_rr_rd macro. The %convergence macro while generating the convergence dataset we can see the log with errors and warnings, but while executing the %or_rr_rd macro we get a clean log without the errors or warnings. So, the %convergence macro is helping the main %or_rr_rd macro to execute conditionally using the convergence dataset to generate the desired output with clean log. So here the question, is it acceptable to use the convergence dataset which is generated from the macro where the log is not clean. Yes, it is acceptable as long as the issue is discussed internally and agreed upon with proper justification. Since mainly we check the main log of the output which if clean should be fine. However, it is important that this approach is detailed in the ADRG in the output section so that the regulatory authorities are informed about the approach taken to run the main macro with clean log. This is one of the approaches, I followed to run the models for OR, RR and RD conditionally using the call execute. There might be other approaches as well which programmers can explore.

REFERENCES

Leonid Batkhan. “CALL EXECUTE made easy for SAS data-driven programming.” SAS Users. August 2, 2017.

Available at https://blogs.sas.com/content/sgf/2017/08/02/call-execute-for-sas-data-driven-programming/.

Kechen Zhao. 2013. “Proper Estimation of Relative Risk Using PROC GENMOD in Population Studies.” WUSS paper. Available at https://www.lexjansen.com/wuss/2013/81_Paper.pdf

SAS Documentation on Convergence Status. “Convergence Status.”

https://documentation.sas.com/?cdcId=pgmsascdc&cdcVersion=9.4_3.3&docsetId=statug&docsetTarget=statug_introcom_sect072.htm&locale=en.

Page 20: ) per SOC/PT using Genmod procedure · 2021. 5. 16. · Title: Dynamic macro using the convergence dataset to generate the Odds Ratio \(OR\), Risk Ratio \(RR\) and Risk Difference

20

ACKNOWLEDGMENTS

The author would like to thank Bharat Buchupalli and Vijay Krishna, for their insightful comments and review.

CONTACT INFORMATION

Your comments and questions are valued and encouraged. Contact the author at:

Jagadish Katam Princeps Technologies Inc. [email protected]

SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration. Other brand and product names are trademarks of their respective companies.