SAS编程-Table:层级关系的频数汇总处理 ——层级拼接法

临床试验TFL输出中,有一类频数汇总表的各条目是包含层级关系的。例如,之前介绍的SAS编程:按SOC和PT类别汇总AE的受试者发生率,单个SOC下,可能会对应多个PT。

对于具有层级关系的频数汇总表,常规的处理方式是,先对各个层级进行单独统计,之后再汇总进行排序,前面提到的AE SOCPT表格就是这样处理的。

今天,介绍另一种方法,我给这种方法取名为层级拼接法层级拼接法的本质是,将多层级转化为单层级进行处理。这个方法的效率要比各个层级单独处理高很多,推荐大家尝试使用。

层级拼接的处理主要在文章3.3、3.5、3.6部分,结合代码和输出结果,希望读者能够掌握这样的处理方法

1. 层级处理介绍

看过我前面介绍受试者处置频数汇总的读者,应该有印象。频数汇总中,各条目对应不同的变量条件的情况,是很常见的。对应的处理方法,无外乎单条目条件处理后汇总,或者各条目条件合并为一个变量控制,统一处理

具有层级关系的频数表,跟之前介绍的汇总表有2点不同。第一,层级关系的条目数量由具体数据确认,没有事先指定;第二,层级关系的条目需按所要求的顺序排序。

基于此,将多层级转化为单层级时,需要保留各层级信息,方便后续排序。

临床试验TFL中,常见的具有层级关系的内容有,地区国家、AE SOCPT的频数汇总。

这篇介绍,国家地区汇总表的层级拼接处理,后续介绍AE SOCPT的层级处理。

2. 具体示例

示例

这是一个涉及多个地区、国家的多中心1期试验,这张Table需要汇总Region、Country、Site的信息。

这些信息保存在ADSL中,Site信息由两个变量拼接而成,这里看成一个变量进行处理。

这三个变量中“范围”最小的是Site,一个Site有对应的Country,一个Country有对应的Region。一行记录有对应3个变量的信息,每个变量信息都会被计数,也就是说一行记录会被计数3次。

这样的“重复计数”,可以通过两种方式实现:第一,计数3次,每次计数不同的变量;第二,将记录拆分成3条,一次计数3个变量的信息

层次拼接法,就是采取第二种方式,拆分时,通过变量拼接,保留对应的层级关系

3. SAS代码演示

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

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

3.1 汇总组的设置

这是一个Ongoing的1期剂量扩大试验,计划有6个试验组,目前数据中只有前4个试验组的信息。缺失组别的内容,我会使用Means过程步中的preloadfmt选项补充缺失组别的信息,具体参考SAS编程:频数汇总时如何处理分析分组种类不全的情况?

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

基于上面两点考虑,部分Format可以这样设置:

***1. Craete Formats for output;
proc format;
  value trt01pn(notsorted multilabel)
    1 = 1
    2 = 2
    3 = 3
    4 = 4
    5 = 5
    6 = 6
    1-6 = 99 /*For total*/
  ;
run;
3.2 计算BigN的人群获取
***2. Get data for analysis;

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

  *Flag for count;
  flag = 1;

 length site $50;
 site = catx("/", siteidss, invnam);
run;
3.3 计算小n的人群获取——层级拼接
**2.2 Get data for small n;
data adsl_n;
  set adsl;
  
  length cat $200;
  
  cat = strip(geogr1); output;
  cat = strip(geogr1)||"!"||strip(propcase(countryl)); output;
  cat = strip(geogr1)||"!"||strip(propcase(countryl))||"!"||strip(site); output;

  proc sort;
    by cat;
run;

第2部分提到,一条记录的3类信息会被计数 (Region、Country、Site),利用Output语句将1条记录输出为3条,每一条对应需要计数的那一类信息。拆分时,各变量以感叹号!分隔,这样可以通过感叹号的数目来判断所属具体层级。

排序变量是新建的cat变量,其实这就相当于按照by geogr1 countryl site进行排序。因为ADSL数据集本身是Subject Level,1个受试者1条记录,所以计算频率时,数据集是不需要去重的

3.4 计算BigN

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

***3. Calculate statistics;

**3.1 Derive BigN and save them to macro vars; 
proc means data =adsl nway completetypes;
  format trt01pn trt01pn.;
  class trt01pn / preloadfmt mlf order = data; 
  var flag;
  output n = bign nmiss = nmiss out = BigN; 
run;

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

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

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

**3.2 Calculate small n and percentage;

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

  class cat;

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

*Get percentage; 
data count2;
  merge count1 bign; 
  by trt01pn;
  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 trt01pn;
run;

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

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

data final1;
  set count3;

  length c1 - c8 $200;

  if count(cat, "!") = 0 then c1 = cat;
  else if count(cat, "!") = 1 then c1 = scan(cat, 2, "!");
  else if count(cat, "!") = 2 then c1 = scan(cat, 3, "!");

  c2 = trt_1;
  c3 = trt_2;
  c4 = trt_3;
  c5 = trt_4;
  c6 = trt_5;
  c7 = trt_6;
  c8 = trt_99;
  
  keep c:;

  proc sort;
    by cat;
run;

输出结果如下,结果排序为各层级字母排序,以下基本为完整的输出内容了。

Results
3.6 按汇总列频数降序排序

具有层次关系的频数汇总表,如果结果按字符排序,上面输出就是最后的结果。

但这类表通常需要按汇总组的频数降序排列,频数相同,按字符顺序排序。这时候还需要对数据集进一步处理,以达到排序的要求。

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

为3个层级新建对应的分组变量 (cat1, cat2, cat3),并按层级分组排序

*Create group vars;
data final1;
  set count3;

  length c1 - c8 $200;

  if count(cat, "!") = 0 then c1 = cat;
  else if count(cat, "!") = 1 then c1 = scan(cat, 2, "!");
  else if count(cat, "!") = 2 then c1 = scan(cat, 3, "!");

  c2 = trt_1;
  c3 = trt_2;
  c4 = trt_3;
  c5 = trt_4;
  c6 = trt_5;
  c7 = trt_6;
  c8 = trt_99;

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

  cat1 = scan(cat, 1, "!");
  cat2 = scan(cat, 2, "!");
  cat3 = scan(cat, 3, "!");
  
  keep num c:;

  proc sort;
    by cat1 cat2 cat3;
run;

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

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

  retain cat1n cat2n cat3n;

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

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

输出结果如下:

Results 2

这个就是最后输出结果的主体部分,结合cat1n, cat2n, cat3n的具体取值,希望读者能够掌握频数倒序排序的处理。

4. 主体程序代码汇总:

***1. Craete Formats for output;
proc format;
  value trt01pn(notsorted multilabel)
    1 = 1
    2 = 2
    3 = 3
    4 = 4
    5 = 5
    6 = 6
    1-6 = 99 /*For total*/
  ;
run;


***2. Get data for analysis;

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

  *Flag for count;
  flag = 1;

 length site $50;
 site = catx("/", siteidss, invnam);
run;

**2.2 Get data for small n;
data adsl_n;
  set adsl;
  
  length cat $200;
  
  cat = strip(geogr1); output;
  cat = strip(geogr1)||"!"||strip(propcase(countryl)); output;
  cat = strip(geogr1)||"!"||strip(propcase(countryl))||"!"||strip(site); output;

  proc sort;
    by cat;
run;


***3. Calculate statistics;

**3.1 Derive BigN and save them to macro vars; 
proc means data =adsl nway completetypes;
  format trt01pn trt01pn.;
  class trt01pn / preloadfmt mlf order = data; 
  var flag;
  output n = bign nmiss = nmiss out = BigN; 
run;

data _null_;
  set BigN;
  call symputx("N_"||strip(trt01pn), 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 = adsl_n nway completetypes;
  format trt01pn trt01pn.;
  class trt01pn / preloadfmt mlf order = data;

  class cat;

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

*Get percentage; 
data count2;
  merge count1 bign; 
  by trt01pn;
  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 trt01pn;
run;

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

*Create group vars;
data final1;
  set count3;

  length c1 - c8 $200;

  if count(cat, "!") = 0 then c1 = cat;
  else if count(cat, "!") = 1 then c1 = scan(cat, 2, "!");
  else if count(cat, "!") = 2 then c1 = scan(cat, 3, "!");

  c2 = trt_1;
  c3 = trt_2;
  c4 = trt_3;
  c5 = trt_4;
  c6 = trt_5;
  c7 = trt_6;
  c8 = trt_99;

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

  cat1 = scan(cat, 1, "!");
  cat2 = scan(cat, 2, "!");
  cat3 = scan(cat, 3, "!");
  
  keep num c:;

  proc sort;
    by cat1 cat2 cat3;
run;

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

  retain cat1n cat2n cat3n;

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

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


***4. Create dataset for QC;
略

***5. Compare;
略

总结

这篇文章介绍了处理层级关系频数表的方法——层级拼接法。这个方法的效率比常规各层级单独处理要高很多,推荐大家使用。

这类表格的排序,可能按字母顺序进行排序,也可能按汇总组频数倒序排列。文章也介绍了这两种排序的处理。

SAS编程-Table:层级拼接法输出AE SOC、PT的受试者发生率介绍了,关于AE SOC、PT层级关系嵌套的处理。SOC和PT是两层嵌套,处理上比Region、Country、Site 3层嵌套要简单点。

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

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

推荐阅读更多精彩内容