Sei sulla pagina 1di 10

Paper CC06

A SAS Macro for Creating Adverse Event Analysis Dataset


Daniel Li, Cangene Corporation, Winnipeg, MB.
Suwen Li, Cangene Corporation, Winnipeg, MB.
Stephanie Sproule, Cangene Corporation, Winnipeg, MB.
ABSTRACT
Adverse events (AE) are commonly reported and analyzed through the use of various summary tables as requested
by FDA and other regulatory agencies. However, writing and validating these SAS programs are very labour-intensive
and time-consuming. In this paper, we introduce a SAS macro program that creates one adverse event analysis
dataset for generating different summary tables.

INTRODUCTION
Summaries of adverse events are important for physicians and companies to assess and analyze the testing drug
safety profile. Based on ICH E3 Structure and Content of Clinical Study Reports, these summary tables should
include number of adverse events, the number of patients in each treatment group in whom the event occurred, and
the incidence of occurrence and should be grouped by system organ class, preferred terms and/or other interested
variables (e.g., relatedness, intensity and seriousness). Some common summary of adverse events tables formats
are included in the Examples of AE Summary Table section for illustration purposes. For SAS programmers, it is
usually tedious and time-consuming to manipulate raw adverse event data to generate such summary tables. In this
paper, we present a SAS macro program (%aesum) by utilizing PROC SQL and DO LOOP to create one adverse
event analysis dataset which can be used to construct all kinds of adverse event summary tables. By using this AE
analysis dataset, various AE summary tables could be generated using one PROC REPORT procedure with or
without any data manipulation. This SAS marco program is simple to follow and easy to validate. We include the
%aesum program in Appendix I with comments and explanations. In addition, we also provide the PROC REPORT
procedures in appendix 2 used to create adverse events tables illustrated in this paper.

%AESUM MACRO:
The complete macro code is attached in Appendix 1 and the %aesum takes the following form:
%macro aesum(aedsn=, safdsn=, trt=, var1=, var2=, soc=, pref=, subjid=, outdsn=);
The %aesum macro has 9 parameters, as described below:
AEDSN

the raw adverse event dataset with at least subject ID, coded system organ class and preferred terms.

SAFDSN

the safety population (subjects received at least one dose of study drugs) dataset with at least distinct
subject ID and treatment arm variables.

TRT

treatment variable in the AEDSN.

VAR1

first interested stratified variable in the AEDSN, such as, relatedness, intensity, seriousness and etc.
When there is no stratification, it can be simple left as null.

VAR2

second interested stratified variable in the AEDSN, such as, relatedness, intensity, seriousness and etc.
When there is no stratification, it can be simple left as null.

SOC

system organ class variable in the AEDSN.

PREF

preferred AE term variable in the AEDSN.

SUBJID

unique subject ID variable in the AEDSN.

OUTDSN

user defined output dataset name.

ADVERSE EVENT ANALYSIS DATASET (OUTDSN):


The output AE analysis dataset contains the following variables, as described below:
AEBODSYS

system organ class variable

AEPREF

preferred AE term variable

EVENTxyz, SUBJxyz,
PERCENTxyz

number of events, number of distinct subjects and percentage of subjects for treatment
arm x, the yth category of VAR1 and the zth category of VAR2.

EXAMPLES OF ADVERSE EVENTS SUMMARY TABLES:


In this section, we provide some examples of adverse event summary tables. All the outputs are extracted from the
completed AE summary tables and are included here only for illustration purpose. The %aesum and PROC REPORT
used to generate these tables are provided in Appendix II.
Example 1: Overall Adverse Events by Body System and Preferred Terms
Overall N=70
------------------------System Organ Class
Subject
Preferred Term
Event
n(%)
--------------------------------------------------------Overall
20
14(20)
Cardiac disorders
Atrial flutter
Cardiac failure
Palpitations
Tachycardia

6
1
1
3
1

4
1
1
2
1

(6)
(1)
(1)
(3)
(1)

Example 2: Adverse Events by Body System, Preferred Term, and Treatment Arm
Treatment N=45
Placebo N=25
------------------------- ------------------------System Organ Class
Subject
Subject
Preferred Term
Event
n(%)
Event
n(%)
-----------------------------------------------------------------------------------Overall
11
9(20)
9
5(20)
Cardiac disorders
Atrial flutter
Cardiac failure
Palpitations
Tachycardia

2
1
0
1
0

2
1
0
1
0

(4)
(2)
(0)
(2)
(0)

4
0
1
2
1

2
0
1
1
1

(8)
(0)
(4)
(4)
(4)

Example 3: Adverse Events by Body System, Preferred Term, and Severity


Treatment N=45
------------------------------------------------Mild
Moderate
Severe
--------------- --------------- --------------System Organ Class
Subject
Subject
Subject
Preferred Term
Event
n(%) Event
n(%) Event
n(%)
--------------------------------------------------------------------------------Overall
6
4 (9)
1
1 (2)
4
4 (9)
Cardiac disorders
Atrial flutter
Cardiac failure
Palpitations
Tachycardia

1
1
0
0
0

1
1
0
0
0

(2)
(2)
(0)
(0)
(0)

0
0
0
0
0

0
0
0
0
0

(0)
(0)
(0)
(0)
(0)

1
0
0
1
0

1
0
0
1
0

(2)
(0)
(0)
(2)
(0)

Example 4: Adverse Events by Body System, Preferred Term, Severity, and Relatedness in Treatment Arm
Treatment N=45
----------------------------------------------------------------------------Mild
Moderate
Severe
-------------------------- ------------------------ ----------------------Not Related
Related
Not Related
Related
Not Related
Related
------------ ------------ ------------ ------------ ------------ -----------System Organ Class Eve Subject Eve Subject Eve Subject Eve Subject Eve Subject Eve Subject
Preferred Term
n(%)
n(%)
n(%)
n(%)
n(%)
n(%)
-----------------------------------------------------------------------------------------------Overall
4
3 (7)
2
2 (4)
0
0 (0)
1
1 (2)
3
3 (7)
1
1 (2)
Cardiac disorders
Cardiac failure
Palpitations
Tachycardia

1
1
0
0

1
1
0
0

(2)
(2)
(0)
(0)

0
0
0
0

0
0
0
0

(0)
(0)
(0)
(0)

0
0
0
0

0
0
0
0

(0)
(0)
(0)
(0)

0
0
0
0

0
0
0
0

(0)
(0)
(0)
(0)

1
0
0
1

1
0
0
1

(2)
(0)
(0)
(2)

0
0
0
0

0
0
0
0

(0)
(0)
(0)
(0)

CONCOLUSION
This paper introduces an alternative approach to generate adverse event summary tables by creating an adverse
event analysis dataset using the %aesum macro. The output AE analysis dataset is one PROC REPORT step away
for final reporting and is flexible for various AE summary purposes.

REFERENCE
1.

Liu, Frank. 1997. A Standard Adverse Event Summary Report Generating. Proceedings of the SAS Users
Group International 22, California, US, Mar 16-19.

2.

International Conference on Harmonization (1995). ICH Expert Working Group: Structure and Content of Clinical
Study Reports, E3, Section 12.2.

CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at:
Daniel Li
Cangene Corporation
155 Innovation Drive
Winnipeg, Manitoba
Canada R3T 3K1
(204) 275-4164
dli@cangene.com

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.

APPENDIX 1: %AESUM MACRO


%macro aesum(aedsn=, safdsn=, trt=, var1=, var2=, soc=, pref=, subjid=, outdsn=);
/***********************************************************************************
* Manipulate Treatment Variable
*
***********************************************************************************/
/*create NEWTRT for treatment variable*/
/*TRT not specified*/
%if %length(&trt)=0 %then %do;
data _&aedsn;
set &aedsn;
newtrt='NA';
run;
data _&safdsn;
set &safdsn;
newtrt='NA';
run;
%end;
/*TRT Specified*/
%else %do;
/*obtain the type (character or numeric) of the treatment var1iable for summary
purpose*/
data _null_;
set &aedsn end=last;
if last then do;
call symput ('trvtype', VTYPE(&trt));
call symput('trvformat', vformat(&trt));
end;
run;
%if &trvtype=N %then %do; /*reformat numeric variable to character*/
data _&aedsn;
set &aedsn;
newtrt=left(put(&trt,&trvformat));
run;
data _&safdsn;
set &safdsn;
newtrt=left(put(&trt,&trvformat));
run;
%end;
%else %do;
data _&aedsn;
set &aedsn;
newtrt=&trt;
run;
data _&safdsn;
set &safdsn;
newtrt=&trt;
run;
%end;
%end;

/***********************************************************************************
* Manipulate Stratification Variable
*
***********************************************************************************/
/*Not interested in any stratification variable 1*/

%if %length(&var1)=0 %then %do;


data _&aedsn;
set _&aedsn;
newvar1='NA';
run;
%end;
/*Format stratification variable 1*/
%else %do;
data _null_;
set &aedsn end=last;
if last then do;
call symput('vavformat1', vformat(&var1));
call symput ('vavtype1', VTYPE(&var1));
end;
run;
%if &vavtype1=N %then %do;
data _&aedsn;
set _&aedsn;
newvar1=left(put(&var1,&vavformat1));
run;
%end;
%else %do;
data _&aedsn;
set _&aedsn;
newvar1=&var1;
run;
%end;
%end;
/*Not interested in any stratification variable 2*/
%if %length(&var2)=0 %then %do;
data _&aedsn;
set _&aedsn;
newvar2='NA';
run;
%end;
/*Format stratification variable 2*/
%else %do;
data _null_;
set &aedsn end=last;
if last then do;
call symput('vavformat2', vformat(&var2));
call symput ('vavtype2', vtype(&var2));
end;
run;
%if &vavtype2=N %then %do;
data _&aedsn;
set _&aedsn;
newvar2=left(put(&var2,&vavformat2));
run;
%end;
%else %do;
data _&aedsn;
set _&aedsn;
newvar2=&var2;
run;
%end;
%end;

/***********************************************************************************
* Create mock table for imputation of zero occurrence
*
***********************************************************************************/
proc sql;
create table
as
select *
from (select
(select
(select
(select
;
quit;

_mock

distinct
distinct
distinct
distinct

newtrt from _&aedsn),


newvar1 from _&aedsn),
newvar2 from _&aedsn),
&soc, &pref from _&aedsn)

/***********************************************************************************
* Calculate number of subjects in safety population by TRT
*
***********************************************************************************/
data _&aedsn;
set _&aedsn _mock;
run;
/*count the number of subjects in each arm*/
proc freq data=_&safdsn noprint;
table newtrt/out=_safreq;
run;
data _null_;
set _safreq;
call symput('ntrt', _N_);
run;
%do m=1 %to &ntrt;
%global pop&m;
%end;
data _null_;
set _safreq;
call symput('pop'||left(put(_n_,8.)), count);
call symput('trt'||left(put(_n_,8.)),newtrt);
run;
%do m=1 %to &ntrt;
%let pop&m=&&pop&m;
%Let trt&m=&&trt&m;
%end;
/***********************************************************************************
* Obtain the levels of stratification variables
*
***********************************************************************************/
proc sql noprint;
select count(distinct newvar1) into: nvar1
from _&aedsn;
%let nvar1=&nvar1;
select distinct newvar1 into :type1 -:type&nvar1
from _&aedsn;
quit;
%do n=1 %to &nvar1;

%Let type&n=&&type&n;
%end;
proc sql noprint;
select count(distinct newvar2) into: nvar2
from _&aedsn;
%let nvar2=&nvar2;
select distinct newvar2 into :stype1 -:stype&nvar2
from _&aedsn;
quit;
%do l=1 %to &nvar2;
%Let stype&l=&&stype&l;
%end;
%let labeltrt=;
%let labelvar1=;
%let labelvar2=;
/***********************************************************************************
* Summary of adverse events
*
***********************************************************************************/
/*count over all body system and all preferred terms;*/
%let k=0;
%do i=1 %to &ntrt;
%do j=1 %to &nvar1;
%do r=1 %to &nvar2;
%let k=%eval(&k+1);
%if %str(&&trt&i)^=NA %then %do;
%let labeltrt=&trt: &&trt&i;
%end;
%if %str(&&type&j)^=NA %then %do;
%let labelvar1=&var1: &&type&j;
%end;
%if %str(&&stype&r)^=NA %then %do;
%let labelvar2=&var2: &&stype&r;
%end;
%let label=&labeltrt &labelvar1 &labelvar2;
/*count overall AEs;*/
proc sql;
create table ae_tlt as
select count(distinct &subjid) as subj&i&j&r "Number of Subject &label",
count(&subjid) as event&i&j&r "Number of Events &label",
100*calculated subj&i&j&r/&&pop&i as percent&i&j&r "Percentage &label" ,
'ALL BODY SYSTEM' as &soc,
'OVERALL' as &pref,
/*create two sorting var1iables future use*/
1 as bodyorder, 2 as preforder
from _&aedsn
where newtrt="&&trt&i" and newvar1="&&type&j" and newvar2="&&stype&r";
/*count in each body system, over all AEs;*/
create table ae_soc as
select count(distinct &subjid) as subj&i&j&r "Number of Subject &label",
count(&subjid) as event&i&j&r "Number of Events &label",
100*calculated subj&i&j&r/&&pop&i as percent&i&j&r "Percentage &label" ,
&soc, &soc as &pref,
2 as bodyorder, 2 as preforder

from _&aedsn
where newtrt="&&trt&i" and newvar1="&&type&j" and newvar2="&&stype&r"
/*group by system organ class*/
group by &soc;
/*count in each preferred term;*/
create table ae_pref as
select count(distinct &subjid) as subj&i&j&r "Number of Subject &label",
count(&subjid) as event&i&j&r "Number of Events &label",
100*calculated subj&i&j&r /&&pop&i as percent&i&j&r "Percentage &label" ,
&soc, '
'||&pref as &pref,
2 as bodyorder, 3 as preforder
from _&aedsn
where newtrt="&&trt&i" and newvar1="&&type&j" and newvar2="&&stype&r"
/*group by system organ class and prefer terms*/
group by &soc,&pref;
/***********************************************************************************
* Merge all AE summary statistics into one AE dataset
*
***********************************************************************************/
create table _ae&k
as
select * from ae_tlt
outer union corresponding
select * from ae_soc
outer union corresponding
select * from ae_pref
order by bodyorder, &soc , preforder, &pref;
quit;
*merge all the interested AE dataset together;
%if &k=1 %then %do;
data &outdsn;
set _ae1;
run;
%end;
%else %do;
data &outdsn;
merge &outdsn _ae&k;
by bodyorder &soc preforder &pref;
run;
%end;
%end;
%end;
%end;
%mend;

APPENDIX 2: APPLICATION OF %AESUM WITH PROC REPORT


The %aesum and the PROC REPORT procedure used to create example AE summary tables are provided here.
options nodate nonumber orientation=portrait leftmargin=1
proc format ;
picture p(round) 0-100='0009)'(prefix=' (');
run;

rightmargin=1 ;

EXAMPLE 1:

%aesum(aedsn=ae,safdsn=treat,trt=, var1=, var2=, outdsn=_aesum1,


soc=aebodsys, pref=aedecod, subjid=subjid);
proc report data=_aesum1 nowd split='|' headline missing;
column aebodsys aedecod ("Overall N=&pop1|--" event111 subj111 percent111);
define aebodsys /noprint group width=2;
define aedecod/'System Organ Class|
Preferred Term' width=30 flow ;
define event111/'Event' width=5;
define subj111/"Sub| " width=8 right;
define percent111/'ject|n(%)' left width=10 format=p. spacing=0;
break after aebodsys/skip;
run;
EXAMPLE 2:

%aesum(aedsn=ae,safdsn=treat,trt=trtcd, var1=, var2=, outdsn=_aesum2,


soc=aebodsys, pref=aedecod , subjid=subjId );
proc report data=_aesum2 nowd split='|' headline missing;
column aebodsys aedecod ("Treatment N=&pop2|--" event211 subj211 percent211)
("Placebo N=&pop1|--" event111 subj111 percent111) ;
define aebodsys /noprint group width=2;
define aedecod/'System Organ Class|
Preferred Term' width=30 flow ;
define event211/'Event' width=5;
define subj211/"Sub| " width=8 right;
define percent211/'ject|n(%)' left width=10 format=p. spacing=0;
define event111/'Event' width=5;
define subj111/"Sub| " width=8 right;
define percent111/'ject|n(%)' left width=10 format=p. spacing=0;
break after aebodsys/skip;
run;

EXAMPLE 3:

%aesum(aedsn=ae,safdsn=treat,trt=trtcd, var1=aesev, var2=,


outdsn=_aesum3, soc=aebodsys, pref=aedecod , subjid=subjId );
proc report data=_aesum3 nowd split='|' headline missing ;
column aebodsys aedecod
("Treatment N=&pop2|--" (' Mild|--'event211 subj211 percent211)
(' Moderate|--'event221 subj221 percent221)
(' Severe|--'event231 subj231 percent231))
("Placebo N=&pop1|--"
(' Mild|--'event111 subj111 percent111)
(' Moderate|--'event121 subj121 percent121)
(' Severe|--'event131 subj131 percent131)) ;
define aebodsys /noprint group width=2 id ;
define aedecod/'System Organ Class|
Preferred Term' width=30 flow id ;
define event211/'Event' width=5;
define subj211/"Sub| " width=4 right;
define percent211/'ject|n(%)' left width=4 format=p. spacing=0;

define
define
define
define
define
define
define
define
define
define
define
define
define
define
define

event221/'Event' width=5;
subj221/"Sub| " width=4 right;
percent221/'ject|n(%)' left width=4
event231/'Event' width=5;
subj231/"Sub| " width=4 right;
percent231/'ject|n(%)' left width=4
event111/'Event' width=5 page ;
subj111/"Sub| " width=4 right;
percent111/'ject|n(%)' left width=4
event121/'Event' width=5;
subj121/"Sub| " width=4 right;
percent121/'ject|n(%)' left width=4
event131/'Event' width=5;
subj131/"Sub| " width=4 right;
percent131/'ject|n(%)' left width=4

format=p. spacing=0;

format=p. spacing=0;

format=p. spacing=0;

format=p. spacing=0;

format=p. spacing=0;

break after aebodsys/skip;


run;

EXAMPLE 4:

%aesum(aedsn=ae,safdsn=treat,trt=trtcd, var1=aesev, var2=aerel,


outdsn=_aesum4, soc=aebodsys, pref=aedecod , subjid=subjId );
proc report data=_aesum4 nowd split='|' headline missing spacing=1;
column aebodsys aedecod
("Treatment N=&pop2|--" (' Mild|--'('Not Related|--' event211 subj211
percent211)
('Related|--' event212 subj212 percent212))
(' Moderate|--'('Not Related|--' event221 subj221 percent221)
('Related|--' event222 subj222 percent222))
(' Severe|--'('Not Related|--'event231 subj231 percent231)
('Related|--' event232 subj232 percent232)))
;
define aebodsys /noprint group width=2 id ;
define aedecod/'System Organ Class|
Preferred Term' width=30 flow id ;
define event211/'Eve' width=3 left;
define subj211/"Sub| " width=4 right;
define percent211/'ject|n(%)' left width=4 format=p. spacing=0;
define event221/'Eve' width=3 left;
define subj221/"Sub| " width=4 right;
define percent221/'ject|n(%)' left width=4 format=p. spacing=0;
define event231/'Eve' width=3 left;
define subj231/"Sub| " width=4 right;
define percent231/'ject|n(%)' left width=4 format=p. spacing=0;
define event212/'Evet' width=3 left;
define subj212/"Sub| " width=4 right;
define percent212/'ject|n(%)' left width=4 format=p. spacing=0;
define event222/'Eve' width=3 left;
define subj222/"Sub| " width=4 right;
define percent222/'ject|n(%)' left width=4 format=p. spacing=0;
define event232/'Eve' width=3 left;
define subj232/"Sub| " width=4 right;
define percent232/'ject|n(%)' left width=4 format=p. spacing=0;
break after aebodsys/skip;
run;

10

Potrebbero piacerti anche