歡迎關(guān)注,SAS茶談!
在臨床試驗的Safety分析中,簡單的統(tǒng)計描述與頻數(shù)匯總的表格占據(jù)絕對的大頭。
頻數(shù)匯總表本質(zhì)是分子除以分母。在編程的第一步,需要搞明白表格中每個統(tǒng)計量的含義。在這個前提下,編程思路會很清晰。
分母一般是各人群試驗組的計數(shù),人群的篩選一般很直接,Title和Footnotes會有詳細說明。試驗分組的劃分中,有可能涉及匯總組(Total)的處理,常規(guī)有兩種方法,參考文章SAS編程:生成Table時,匯總組(Total)組如何處理?。
0. 分子的3種類別分類
分子是各類別的計數(shù),相對復(fù)雜一點。從類別數(shù)目角度看,我將其劃分為3類:固定類別,非固定類別,以及固定與非固定類別結(jié)合。
固定類別是指,類別數(shù)目在Shell中已經(jīng)明確定。例如,以下這張AE Summary的表,左側(cè)輸出類別是清楚明確的。

非固定類別是指,類別數(shù)目在Shell中是未知的。例如,AE PT的受試者發(fā)生率的表,PT數(shù)目暫且未知,根據(jù)實際數(shù)據(jù)集內(nèi)容確定。

固定與非固定類別結(jié)合是指,既含有固定類別,又含有非固定類別。例如,AE PT與毒性等級的受試者發(fā)生率的表,PT數(shù)目不確定,毒性等級數(shù)目確定。

不知道讀者對于這3類表格,是否有自己的編程思路。下面我來介紹一下,我對這三類表格的處理。
前2類簡單一點,讀者可以參考下文所提之前文章的處理;第3類復(fù)雜一點,會介紹的詳細一些。
1、固定類別的處理
常見的固定類別的table,除了上面舉例比較復(fù)雜的外,還有比較簡單的。例如,人口學(xué)的Race的頻數(shù)匯總。

這里的“復(fù)雜”和“簡單”如何區(qū)分呢?如果分類條件涉及單個變量,我將其稱為簡單;分類條件涉及多個變量,稱為復(fù)雜。
對于固定類別的Table,我建議大家嘗試,“簡單”地處理——將多變量條件轉(zhuǎn)化為單變量條件處理。
對于前面那張AE Sum的table,有19個類別。編程時,有人可能會嘗試直接調(diào)用19次單行計數(shù)的宏程序,然后將19個結(jié)果數(shù)據(jù)集進行拼接。這樣可以實現(xiàn),但編程效率有點低。
大家可以嘗試,新建一個變量(如,catn),其取值對應(yīng)每一個類別。這樣多變量條件就轉(zhuǎn)化成單變量條件,利用一個Means過程步就可以獲取到每個類別的計數(shù),簡潔高效很多。
這個方法可以參考之前介紹類似表格的處理,SAS編程-Table:受試者處置中止理由頻數(shù)匯總。
這里需要注意的是,對于固定類別的表格(試驗分組trt01an也屬于固定分類),計數(shù)頻數(shù)為0的情況也需要輸出。我習(xí)慣使用Means過程步中Class語句的Preloadfmt選項,具體參考鏈接文章中的程序介紹,計數(shù)的主要過程步如下:
proc means data = adae nway completetypes;
format trt01an trt01an.;
class trt01an / preloadfmt mlf order = data;
format catn catn.;
class catn / preloadfmt order = data;
var flag;
output n = count out = count1;
run;
額外提一點,受試者發(fā)生率是人數(shù)除以人數(shù),前期處理分析數(shù)據(jù)時,需要對受試者進行去重。
proc sort data = adae1 out = adae nodupkey;
by catn usubjid;
run;
2、非固定類別的處理
非固定類別與固定類別相比,很大的一點不同是,類別數(shù)目的不確定。這里不需要考慮所有可能的輸出結(jié)果,也就不需要“Preloadfmt”處理。
這也就自然引出了類別排序的問題,一般有兩種排序方式:1)按照某組別頻數(shù)降序排序;2)按照類別名稱進行排序。
排序一般在footnotes中有明確說明,若沒有,需要及時與統(tǒng)計師進行溝通確認。AE相關(guān)Table一般都是匯總組頻數(shù)降序排序。
處理這一類別的Table,F(xiàn)req和Means過程步都方便實現(xiàn)。我習(xí)慣使用Means過程步,方便試驗組別的生成。由于Means過程步分析變量只能處理數(shù)值變量,需要新建一個Flag變量以方便計數(shù)。
以上面舉例的PT表為例,前期數(shù)據(jù)處理需要注意兩點。第一,首行內(nèi)容的處理;第二,受試者的去重。
對于首行,我一般在Data步中通過新增記錄進行處理,這樣只需對一個數(shù)據(jù)集進行一次“Means”處理,就可以獲取想要的計數(shù)。參考代碼如下:
data adae;
set adae1;
length cat $200;
cat1n = 1; cat = "Number of subjects reporting treatment-emergent adverse events"; output;
cat1n = 2; cat = aedecod; output;
flag = 1;
proc sort nodupkey;
by cat1n cat usubjid;
run;
計數(shù)時,為確保首行內(nèi)容排在第一列,分組變量加上cat1n。這里需要注意,不同的cat1n變量對應(yīng)不同的cat,所以cat1n變量不能用class語句進行分組,否則completetypes選項會帶來多余的組合,需使用by語句。讀者可以自行調(diào)試驗證下。
proc means data = adae nway completetypes;
by cat1n;
format trt01an trt01an.;
class trt01an / preloadfmt mlf order = data;
class cat;
var flag;
output n = count out = count1;
run;
這里也可以不新建cat1n變量,直接在首行內(nèi)容前加上字符,使其字符排序時,排在前面。最后,在輸出結(jié)果中進行處理下。例如,添加“00-”:
cat = "00-Number of subjects reporting treatment-emergent adverse events";
后序的BigN計算、轉(zhuǎn)置、排序,這里就不詳細介紹,可以參考上一小節(jié)中的鏈接文章。
3、固定與非固定類別結(jié)合的處理
第3類是前兩者的結(jié)合,編程也是結(jié)合兩者的特點,過程復(fù)雜一些。上面舉例的Table中,涉及到某一類的AE以及最差等級的信息。這些內(nèi)容一般在ADAE中進行處理,TFL編程里直接引用變量條件,這里就不再介紹。
處理頻數(shù)表內(nèi)容,我首選是將復(fù)雜的變量條件轉(zhuǎn)化為簡單的變量條件,考慮排序的需要,會新建排序變量方便排序。
固定類別內(nèi)容需要輸出頻數(shù)為0的記錄,這一塊可以通過Means過程步中的Preloadfmt選項實現(xiàn)。
首先,需要新建對應(yīng)的Format??v向的試驗組別的信息也屬于固定類別,也是需要建立對應(yīng)的Format,匯總組的生成可以利用multilabel的選項。(試驗分組取值以1、2舉例)
***1. Create formats for output;
proc format;
value trt01an (notsorted multilabel)
1 = 1
2 = 2
1, 2 = 99
;
value catn
0 = 0
1 = 1
2 = 2
3 = 3
4 = 4
;
value cat
0 = "0"
1 = "Grade >= 2"
2 = "Grade >= 3"
3 = "Grade >= 4"
4 = "Fatal"
;
run;
第2步獲取分析數(shù)據(jù)集,有2個來源,用于計算BigN的ADSL,以及用于計算小n的ADAE。
ADSL 數(shù)據(jù)集選擇對應(yīng)的人群的記錄。
***2. Get data for analysis;
**2.1 Get data for BigN;
data adsl;
set adam.adsl;
where xxxx and trt01an in (1, 2);
*Flag for count;
flag = 1;
run;
ADAE數(shù)據(jù)集需要將多變量條件轉(zhuǎn)化為單變量條件,最后需要對每一類計數(shù)的受試者ID去重。以這張Table的Shell為例,我會新建catn(0,1,2,3,4,5)變量來代表每一個類別,不同組塊的類別使用變量cat1n(1,2,3)變量。不同組塊的首行內(nèi)容展示也有區(qū)別,新建變量cat1進行處理。
**2.2 Get data for small n;
data adae;
merge adam.adae(in = a) adsl(in = b keep = usubjid);
by usubjid;
if a and b and trtemfl = "Y" ;
flag = 1;
length cat1 $200;
cat1n = 1; cat1 = "Number of subjects reporting treatment-emergent adverse events"; catn = 0; otuptut;
if aecat = xxx then do;
cat1n = 2; cat1 = "Any xxx Preferred Term";
if 1 then do; catn = 0; otuput; end;
if aetoxgrn >= 2 then do; catn = 1; output; end;
if aetoxgrn >= 3then do; catn = 2; output; end;
if aetoxgrn >= 4then do; catn = 3; output; end;
if aetoxgrn = 5then do; catn = 4; output; end;
cat1n = 3; cat1 = strip(aedecod);
if 1 then do; catn = 0; otuput; end;
if aetoxgrn >= 2 then do; catn = 1; output; end;
if aetoxgrn >= 3then do; catn = 2; output; end;
if aetoxgrn >= 4then do; catn = 3; output; end;
if aetoxgrn = 5then do; catn = 4; output; end;
end;
proc sort nodupkey;
by cat1n cat1 catn usubjid;
run;
第3步統(tǒng)計量的計算,也就是大N和小n的計算。大N的值會用于小n百分比的計算以及Header中大N的展示。
對于固定類別(trt01an, catn),即便內(nèi)容在數(shù)據(jù)集中缺失,也是需要全部展現(xiàn)在表中,所以固定類別在Means過程步Class語句中使用preloadfmt選項;
對于非固定類別(cat1n),如果只與其他特定類別有關(guān)聯(lián)(cat1n=1, cat1="Number of XXX"; cat1n=2, cat1="Any XXX"; cat1n=3, cat1=aedecod.),分析分組使用by語句;
對于非固定類別(cat1),如果與其他所有類別(trt01an, catn)都有關(guān)聯(lián),分析分組使用class語句,completetypes選項會補齊所有可能結(jié)果。
如果采用Format過程步中的multilabel選項來構(gòu)建匯總組,對應(yīng)的class語句中需要添加mlf選項。
***3. Calculate statistics;
***3.1 Derive BigN and save them to macro vars;
proc means data = adsl nway completetypes;
format trt01an trt01an.;
class trt01an / preloadfmt mlf order = data;
var flag;
output n = bign out = BigN(keep = trt01an bign);
run;
data _null_;
set BigN;
call symputx("N_"||strip(trt01an), strip(put(bign, best.)) );
run;
小n的處理,在分析數(shù)據(jù)集處理好之后,生成過程不復(fù)雜。cat1n變量對應(yīng)不同的cat,所以cat1n變量不能用class語句進行分組,否則completetypes選項會帶來多余的組合,使用by語句進行分組。
**3.2 Calculate small n and percentage;
*Get small n;
proc means data = adae nway completetypes;
by cat1n;
format trt01an trt01an.;
class trt01an/ preloadfmt mlf order = data;
class cat1;
format catn catn.;
class catn / preloadfmt order = data;
var flag;
output n = count out = count1;
run;
小n計算完成后,與BigN數(shù)據(jù)集拼接,計算百分比。完成后,需要排序,方便轉(zhuǎn)置。轉(zhuǎn)置的作用,是將橫向放置的Treatment分組信息縱向放置。
*Get percentage;
data count2;
merge count1 bign;
by trt01an;
length freq $200;
if bign ne 0 then freq = strip(put(count,best.))||" ("||strip(put(count/bign*100, 8.1)) || ")";
else freq = "0 (-)";
proc sort;
by cat1n cat1 catn trt01an;
run;
進行轉(zhuǎn)置:
*Transpose results;
proc transpose data = count2 out = count3 prefix = trt_;
by cat1n cat1 catn;
var freq;
id trt01an;
run;
轉(zhuǎn)置完成后,需要需要微調(diào)下數(shù)據(jù)集,包含Col1文本信息的處理、Cat1n = 1的情況中多余的Catn的記錄需要刪除(catn >= 1, 有Preloadfmt選項生成),以及需要為每一個Cat組新建一個頻數(shù)變量以后降序排序(Retain語句)。
data final1;
set count3;
by cat1n cat1;
length c1-c3 $200;
c1 = put(catn, cat.);
c2 = trt_1;
c3 = trt_2;
c4 = trt_99;
retain cat2n;
if first.cat1 then cat2n = input(strip(scan(c3, 1,"(")), best.);
if c1 = "0" then c1 = cat1;
if cat1n = 1 and catn ne 0 then delete;
proc sort;
by cat1n descending cat2n cat1 cat;
run;
data final2;
set final1;
row_num = _n_;
keep row_num c1-c4;
run;
接下來生成Header數(shù)據(jù)集,QC的內(nèi)容要包含大N的信息。
data header;
row_num = 0;
length c1 - c4 $200;
c1 = "Preferred Term Worst Grade";
c2 = "TRT A (N = &N_1.) n (%)";
c3 = "TRT B (N = &N_2.) n (%)";
c4 = "Total (N = &N_99.) n (%)";
run;
最后,生成QC數(shù)據(jù)集。
**4.2 Create dataset for QC;
data qc;
set header final2;
run;
4. 其他
頻數(shù)匯總表涉及到的內(nèi)容,基本不出這3類情況。之前介紹的AE SOCPT的嵌套表格,是第二類非固定類別的處理;介紹的Shift表格,是第一類固定類別的處理;關(guān)于第三類固定與非固定結(jié)合,之前也有SOC、PT、Severity3層嵌套表格的介紹。
具體可以參閱對應(yīng)文章:
感謝閱讀, 歡迎關(guān)注:SAS茶談!
若有疑問,歡迎評論交流!