SAS编程-Table:层级拼接法输出AE SOC、PT的受试者发生率

之前的文章SAS编程:按SOC和PT类别汇总AE的受试者发生率 介绍了一种输出AE受试者发生率的方法,是通过各层级单独计算后,再统一汇总输出。

前面文章SAS编程-Table:层级关系的频数汇总处理 ——层级拼接法也介绍了具有层级关系的频数汇总表,可以使用层级拼接法进行输出。

这篇文章使用层级拼接法输出SOC和PT嵌套汇总表格,与单独计算各层级再汇总输出相比,层级拼接法效率要高很多,建议读者尝试使用。

层级拼接处理在文章2.3、2.5、2.6部分有详细描述,第3部分有主体程序代码的汇总。AE相关的去重在2.3部分,计算小n的人群获取时进行处理。

关于嵌套Table统计量的介绍、SOC和PT的具体含义,在第一篇文章中有过详细说明,这里不再具体介绍。

需要明确一点,受试者的发生率是“人数比人数”,所以我们所有的计数都是基于“数人数”的前提

直接看示例。

1. 具体示例

AE-SOCPT

以上就是一张普通的AE受试者发生率的SOC、PT嵌套的频数汇总表。人群是Safety Analysis Set,对应至少有一次给药记录的受试者,试验分组应采用实际用药分组

2. SAS代码演示

首先,附上我的QC“5段论”:

***1. Create formats for output;
***2. Get data for analysis;
***3. Calculate statistics;
***4. Create dataset for QC;
***5. Compare;

2.1 汇总组的设置

举例示例有两个试验分组,对应的trt01an的值为450、451。

这次演示加上汇总组(Total),我会采用Format过程步中multilabel选项进行实现,具体内容可以参考SAS编程:生成Table时,汇总组(Total)组如何处理?

用于Means过程步Preloadfmt选项的受试者分组Format设置如下:

***1. Craete Formats for output;
proc format;
  value trt01an(notsorted multilabel)
    450 = 450
    451 = 451
    450, 451 = 999 /*For total*/
  ;
run;
2.2 计算BigN的人群获取

BigN人群取saffl = "Y"的人群。

***2. Get data for analysis;

**2,1 Get data for BigN;
data adsl;
  set adam.adsl;
  where saffl= "Y";

  *Flag for count;
  flag = 1;
run;
2.3 计算小n的人群获取——层级拼接

计算小n的人群是在BigN人群范围之内,SAP中通常都是要求分析Treatment-emergent Adverse Events,需要添加对应的Flag条件aetrtem = "Y"

收集的AETERM可能没有对应的标准编码值,在AEBODSYS、AEDECOD为空的情况下,需要手动补充缺失值。

一条AE记录的3类信息会被计数 (Reporting TEAE、SOC、PT),利用Output语句将1条记录输出为3条,每一条对应需要计数的那一类信息

为了方便层级信息分类和排序,新生成的记录需要包含对应的层级关系。AE的层级关系,不像Region、Country、Site那样,可以通过感叹号数量进行判断,于是我添加序号来确认层级。

在数据中只有1个SOC的情况下,第1层级和第2层级频数是相同的,需要进一步用序号来区分排列顺序。对于第2层级和第3层级,"!"||strip(aedecod)可以自动区分字符顺序,因此序号赋值为相同值。

受试者发生率的计算是基于“人数比人数”,每一个条目只需要保留受试者的一条信息。如果某个AE,受试者发生多次,计数时也“只算1人”,所以需要对受试者去重。

**2.2 Get data for small n;
data adae;
  merge adam.adae(in = a) adsl(in = b);
  by usubjid;

  if a and b and aetrtem = "Y";

  *Set for missing values;
  if missing(aebodsys) then aebodsys = "Missing system organ class";
  if missing(aedecod) then aedecod = "Missing preferred term";
  
  *Combine all categories in one variable;
  length cat $200;
  
  cat = "01!"||"Number of subjects reporting treatment-emergent adverse events"; output;
  cat = "02!"||strip(aebodsys); output;
  cat = "02!"||strip(aebodsys)||"!"||strip(aedecod); output;

  proc sort nodupkey;
    by cat usubjid;
run;
2.4 计算BigN

处理好分析数据后,BigN的计算与之前的文章一样:

***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 nmiss = nmiss out = BigN; 
run;

data _null_;
  set BigN;
  call symputx("N_"||strip(trt01an), put(bign,best.)); 
run;

data check_bign;
  set sashelp.vmacro;
  where index(name, "N_") and length(name)<=6;
  keep name value; 
run;
2.5 计算小n

小n的计算也与之前文章大体类似,只是不需要为分析变量cat提前设置Format。

**3.2 Calculate small n and percentage;

*Get small n;
proc means data = adae nway completetypes;
  format trt01an trt01an.;
  class trt01an/ preloadfmt mlf order = data;

  class cat;

  var flag;
  output n = count nmiss = nmiss out = count1; 
run;

*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 cat trt01an;
run;

*Transpose results;
proc transpose data = count2 out = count3 prefix= trt_ ;
  by cat; 
  var freq; 
  id trt01an; 
run;

百分比处理完毕后,下面要对第一列的内容进行处理。计数时,是按拼接后的变量信息计数。计数完毕后,需要根据所在层级输出第一列的内容。而所在层级是通过所赋值序号以及所含感叹号数量进行判断。

data final1;
  set count3;

  length c1 - c4 $200;

  if index(cat, "01")  then c1 = scan(cat, 2, "!");
  else if index(cat, "02") and count(cat, "!") = 1 then c1 = scan(cat, 2, "!");
  else if index(cat, "02") and count(cat, "!") = 2 then c1 = scan(cat, 3, "!");

  c2 = trt_450;
  c3 = trt_451;
  c4 = trt_999;
  
  keep c:;

  proc sort;
    by cat;
run;

输出结果如下:

Results

目前,排序是按SOC、PT的字母顺序进行排序的,AE嵌套表格都是需要按某试验组频数降序排序

需要进一步处理。

2.6 按汇总列频数降序排序

这一步,主要操作是为每一个层级附上对应的汇总列频数,通过BY语句、Retain语句为组内赋值实现

严格说来,AE的这张嵌套表格只有SOC和PT两个层级,可以新建变量CAT1、CAT2加以区分。

在只有一个SOC的情况下,即“Number of subjects reporting treatment-emergent adverse events”和SOC频数相同的情况下,为确保“Number of subjects”排在首行,将其对应的cat1、cat2置空。

这里的处理跟Region、Country、Site层级汇总表有点不同,后者字符排序就可以区分前两个层级,不需要额外处理,读者可以结合两者CAT变量的衍生理解下。

为SOC和PT新建一个分组变量 (cat1, cat2),并按层级分组排序

*Create group vars;
data final1;
  set count3;

  length c1 - c4 $200;

  if index(cat, "01")  then c1 = scan(cat, 2, "!");
  else if index(cat, "02") and count(cat, "!") = 1 then c1 = scan(cat, 2, "!");
  else if index(cat, "02") and count(cat, "!") = 2 then c1 = scan(cat, 3, "!");

  c2 = trt_450;
  c3 = trt_451;
  c4 = trt_999;

  *For freqency sort;
  num = input(scan(c4,1,"("), best.);

  cat1 = scan(cat, 2, "!");
  cat2 = scan(cat, 3, "!");

  if index(cat, "01") then call missing(of cat1-cat2);
  
  keep num c:;

  proc sort;
    by cat1 cat2;
run;

为每一分组赋上组内汇总频数 (cat1n, cat2n),并以频数降序、相同频数以字母顺序升序排列

*Get counts for the group vars;
data final2;
  set final1;
  by cat1 cat2;

  retain cat1n cat2n;

  if first.cat1 then cat1n = num;
  if first.cat2 then cat2n = num;

  proc sort;
    by descending cat1n cat1 descending cat2n cat2;
run;

输出结果如下:

Results 2

这个就是输出结果的主体部分。

3. 主体程序代码汇总:

***1. Craete Formats for output;
proc format;
  value trt01an(notsorted multilabel)
    450 = 450
    451 = 451
    450, 451 = 999 /*For total*/
  ;
run;


***2. Get data for analysis;

**2,1 Get data for BigN;
data adsl;
  set adam.adsl;
  where saffl= "Y";

  *Flag for count;
  flag = 1;
run;

**2.2 Get data for small n;
data adae;
  merge adam.adae(in = a) adsl(in = b);
  by usubjid;

  if a and b and aetrtem = "Y";

  *Set for missing values;
  if missing(aebodsys) then aebodsys = "Missing system organ class";
  if missing(aedecod) then aedecod = "Missing preferred term";
  
  *Combine all categories in one variable;
  length cat $200;
  
  cat = "01!"||"Number of subjects reporting treatment-emergent adverse events"; output;
  cat = "02!"||strip(aebodsys); output;
  cat = "02!"||strip(aebodsys)||"!"||strip(aedecod); output;

  proc sort nodupkey;
    by cat usubjid;
run;


***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 nmiss = nmiss out = BigN; 
run;

data _null_;
  set BigN;
  call symputx("N_"||strip(trt01an), put(bign,best.)); 
run;

data check_bign;
  set sashelp.vmacro;
  where index(name, "N_") and length(name)<=6;
  keep name value; 
run;

**3.2 Calculate small n and percentage;

*Get small n;
proc means data = adae nway completetypes;
  format trt01an trt01an.;
  class trt01an/ preloadfmt mlf order = data;

  class cat;

  var flag;
  output n = count nmiss = nmiss out = count1; 
run;

*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 cat trt01an;
run;

*Transpose results;
proc transpose data = count2 out = count3 prefix= trt_ ;
  by cat; 
  var freq; 
  id trt01an; 
run;

*Create group vars;
data final1;
  set count3;

  length c1 - c4 $200;

  if index(cat, "01")  then c1 = scan(cat, 2, "!");
  else if index(cat, "02") and count(cat, "!") = 1 then c1 = scan(cat, 2, "!");
  else if index(cat, "02") and count(cat, "!") = 2 then c1 = scan(cat, 3, "!");

  c2 = trt_450;
  c3 = trt_451;
  c4 = trt_999;

  *For freqency sort;
  num = input(scan(c4,1,"("), best.);

  cat1 = scan(cat, 2, "!");
  cat2 = scan(cat, 3, "!");

  if index(cat, "01") then call missing(of cat1-cat2);
  
  keep num c:;

  proc sort;
    by cat1 cat2;
run;

*Get counts for the group vars;
data final2;
  set final1;
  by cat1 cat2;

  retain cat1n cat2n;

  if first.cat1 then cat1n = num;
  if first.cat2 then cat2n = num;

  proc sort;
    by descending cat1n cat1 descending cat2n cat2;
run;


***4. Create dataset for QC;
略

***5. Compare;
略

总结

文章介绍了使用层级拼接法输出AE SOC和PT受试者发生率的汇总表格,这个方法的效率比单独计算表格各部分再统一汇总输出高很多。

关于层级拼接的处理、频数降序排序的处理,大家可以结合代码和输出结果,进行理解。

可以结合文章SAS编程-Table:层级关系的频数汇总处理 ——层级拼接法进一步理解这个方法,推荐大家尝试使用。

感谢阅读, 欢迎关注:SAS茶谈!
若有疑问,欢迎评论交流!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351