SAS编程:通过Picture语句设置变量Format

输出一张频数汇总表,遇到一个问题。TFLShell中,频数百分比保留一位小数,我直接用5.1的格式将百分比put出来 (put(percent, 5.1))。这样做,本来没什么问题。但这张表的BigN大于1000,分类中有频数为0和1的情况,这样对于频数为0和1的类别,百分比输出都为“(0.0)”,直观上看这两个百分比相同。

跟统计师进行确认,对于频数不为0的记录,如果频率小于0.1,显示为“(<0.1)”。这样,频率百分比显示要求具体如下:

  1. 对于>0以及<0.1的值,显示“<0.1”;
  2. 对于0或≥0.1的值,显示5.1的格式。

对于每一个试验分组,我目前的代码如下:

c1 = strip(put(_0, 5)) || " (" || strip(put(_0/&n_0.*100, 5.1)) ||")";

基于新的逻辑,我需要判断频数是否为0,以及频率是否小于0.1之后,再进行赋值。同时,5个不同的试验分组需要单独处理。

直接进行条件判断,程序更新比较繁琐,所以我考虑通过设置百分比的Format来实现统计师的要求。设置好符合条件的Format之后,直接替换格式5.1,就可以完成程序的更新。

而这个格式的设置,就是通过Format过程步中的pircture语句来实现。

这里为什么不能使用value语句来设置Format呢?value语句设置的Format,是固定的值或范围对应具体的字符串,而条件2显示具体的格式,是无法用value语句实现的。

下面开始介绍Format过程步中的Picture语句,读者也可以直接跳到第4部分,先看一下Picture语句的具体应用。

0. Picture语句语法

Picture语句是用于创建输出数值的模板 (Creates a template for printing numbers.),其主要语法为:

proc format;
  picture fmtname
  value-or-range-1 = 'picture-1'
  value-or-range-2 = 'picture-2'
    ... ...
  ;
run;

1. value-or-range

等号左侧的value-or-range, 有两类形式,一是具体的值,二是具体的范围。具体的离散值不做详细的介绍,具体的范围主要有三种形式:

  1. 范围端点包含无限值(正无穷或负无穷),举例:0-high, low-0, low-high;
  2. 范围端点为有限值,且包含有限值,举例:0-1,1-100,-1-6
  3. 范围端点为有限值,但不包含有限值,举例:0<-1, 1-<100, -1<-<6。

同时,value-or-range的两类形式可以互相并列,中间用,间隔:

proc format;
  picture fmtname
  1,3, 4-10 = 'picture1'
  11-20, 21 = 'picture2'
  ;
run;

2. 'Picture'

等号右侧的'Picture'可以理解成一种具体的数值模板,主要有3类:

  1. 数值选择符 (digit selectors);
  2. 信息字符 (message characters)
  3. 指令 (directives)
2.1 数值选择符 (digit selectors);

数值选择符,用于定义数值位置的0-9的字符,1个选择符代表1位数字。如果是非0选择符在最左侧,不足位的数值将会用0补位;如果是0选择符在最左侧,不足位的数值将不会用0补位;通常用数字9来表示非0字符。代码示例如下:

proc format;
  picture fmt
    1-5 = '009.9'
    5<-10 = '999.9'
  ;
run;

data tmp;
    a = 1; b = put(a, fmt.); output; 
    a = 10; b = put(a, fmt.); output;
run;
tmp

格式fmt的含义为,对于1到5之间的数值,保留1位小数;对于5到10之间的数据值,保留1位小数,如果小数点左侧位数小于3位,则用0补位。

数值1的格式为0选择符在最左侧,整数位不足3位时,不需要用0补足位数;数值10的格式为非0选择符在最左侧,整数位不足3位时,需要用0补足位数。

2.2 信息字符 (message characters);

信息字符,是指非数字字符,直接输出字符串的内容,这类似于Value语句生成的格式。

proc format;
  picture fmt
    1-5 = 'ha'
    5<-10 = 'hei'
  ;
run;

data tmp;
    a = 1; b = put(a, fmt.); output; 
    a = 10; b = put(a, fmt.); output;
run;
tmp

Picture模板中,也可以同时包括数值选择符和信息字符,不过数值字符必须在模板的开头,这样数值选择符的格式才能正常显示

proc format;
  picture fmt
    1-5 = '000.00 ha'
    5<-10 = '999.9 hei'
  ;
run;

data tmp;
    a = 1; b = put(a, fmt.); output; 
    a = 10; b = put(a, fmt.); output;
run;
tmp
2.3 指令 (directives);

指令,是一些特殊字符,可以用来格式化日期、时间或日期时间值。这一内容在日常工作中,比较少用到,这里不做过多介绍,感兴趣的读者可以自行查看SAS官方文档(SAS Help Center: Syntax: PROC FORMAT PICTURE Statement)。

3. 常用选项

Picture语句中的选项,分为Format选项和Picture选项。在Format过程步使用选项时,要将选项放置到括号()中。Format选项放在格式名称之后,Picture选项放在模板'Picture'之后。介绍3个常用选项,RoundNoedit以及Prefix=""

3.1 Format选项——Round

Round选项的作用是,对数值进行格式化时,会将数值四舍五入到最近的整数。直接看定义不方便理解,直接看代码演示:

**Without Round option;
proc format;
  picture fmt
    1-5 = '000.00'
    5<-10 = '999.99 '
  ;
run;

data tmp1;
  a = 1.444; b = put(a, fmt.); output; 
  a = 1.445; b = put(a, fmt.); output;

  a = 9.444; b = put(a, fmt.); output; 
  a = 9.445; b = put(a, fmt.); output;
run;
tmp1

未使用Round选项时,对数值进行保留两位小数的操作,会直接取小数位的后两位,不管小数点后第3位数值的大小

**With Round option;
proc format;
  picture fmt (round)
    1-5 = '000.00'
    5<-10 = '999.99 '
  ;
run;

data tmp2;
  a = 1.444; b = put(a, fmt.); output; 
  a = 1.445; b = put(a, fmt.); output;

  a = 9.444; b = put(a, fmt.); output; 
  a = 9.445; b = put(a, fmt.); output;
run;
tmp2

使用Round选项后,对数值进行保留两位小数的操作,会根据小数点后第3位数值的大小进行四舍五入。代码示例中,第3位为5时,会向前进一位。

3.2 Picture选项——Noedit

Noedit选项作用是,将Picture模板中的数值当做信息字符 (message characters),而不是数值选择符 (digit selectors)。前面介绍到,模板中的数字都有对应的含义,Noedit选项会抹去模板中数字的含义,直接将数字当作纯粹的字符,与Value语句生成Format的作用完全相同。

代码举例如下:

**Noedit option;
proc format;
  picture fmt
    1-5 = '000.00 ha' (noedit)
    5<-10 = '999.9 hei'
  ;
run;

data tmp;
    a = 1; b = put(a, fmt.); output; 
    a = 10; b = put(a, fmt.); output;
run;
tmp

数字1的Format,对应字符000.00 ha;数字10的Format,对应保留1位小数,并在数值后面添加字符"hei"。

3.3 Picture选项——Prefix=

Prefix=选项的作用是,指定一个字符作为格式化值的前缀。这个比较容易理解,直接看代码实例,输出结果中直接添加前缀中的内容。关于Prefix=选项的具体应用,我们在下一部分介绍。

**Prefix= option;
proc format;
  picture fmt
    1-5 = '000.00' (prefix = "Haha - ")
    5<-10 = '999.99' (prefix = "Heihei - ")
  ;
run;

data tmp;
    a = 1; b = put(a, fmt.); output; 
    a = 10; b = put(a, fmt.); output;
run;
tmp

4. Picture语句应用举例

4.1 频率格式自带括号——Prefix=

通常我们在输出频数汇总时,频数和频率的输出都是以n (xx.x)的形式输出。常规的做法是将数值Put出来后,与左右括号进行拼接。

c1 = strip(put(_0, 5)) || " (" || strip(put(_0/&n_0.*100, 5.1)) ||")";

通过Picture语句生成的频率格式也可以自带括号,这需要Prefix选项来实现

前面谈到,'Picture'模板,可以同时使用数值选择符和信息字符,但是数值选择符必须位于模板的开头。

特定范围的数值可以使用数值选择符设置特定的格式,后面添加信息字符右括号),而开头的左括号可以通过prefix="( "选项来实现。

具体演示代码如下,Format选项中min =选项指定格式的最小长度。如果不指定长度的话,默认长度是第一条记录Format值的长度,这可能造成后续值的截断。

代码中也展示了手动输出括号的结果,两者的显示略有区别。使用Picture格式的输出,左括号始终距离数字1个空格;而手动输出括号的方法,左括号的位置始终固定。

具体如何展示,需要看各个公司、项目、统计师的要求,不能一概而论。

proc format;
  picture fmt (round min = 10)
    0-<99.95 = '009.9 )'  (prefix = "( ")
    99.95-100 = '999.9 )'  (prefix = "( ")
  ;
run;

data tmp1;
    a = 0.15; b = put(a, fmt.); output; 
    a = 10.15; b = put(a, fmt.); output; 
    a = 99.92; b = put(a, fmt.); output;
    a = 99.96; b = put(a, fmt.); output;
run;

data tmp2;
    a = 0.15; b = "( " || put(a, 5.1) || " ) "; output; 
    a = 10.15; b = "( " || put(a, 5.1) || " ) "; output; 
    a = 99.92; b = "( " || put(a, 5.1) || " ) "; output;
    a = 99.96; b = "( " || put(a, 5.1) || " ) "; output;
run;
tmp1
tmp2
4.2 频率输出演示

最近手头项目的要求,频率百分比大于0且小于0.1,输出为<0.1;其他则输出保留1位小数。

具体实现的代码如下:

proc format;
  picture fmt (round min = 10)
    0<-<0.1 = '( <0.1 )'  (noedit)
    0, 0.1-high = '009.9 )'  (prefix = "( ")
  ;
run;

data tmp;
    a = 0.05; b = put(a, fmt.); output; 
    a = 0; b = put(a, fmt.); output; 
    a = 90.15; b = put(a, fmt.); output;
    a = 99.96; b = put(a, fmt.); output;
run;
tmp

取值为0与≥0.1的Format值相同,设置格式时可以使用,进行并列。Format选项round使保留小数位时,进行四舍五入。

4.3 p值输出演示

医学期刊一般对p值的输出有要求,例如:

  1. p > 0.1, p值保留2位小数;
  2. 0.1 > p ≥ 0.001,p值保留3位小数;
  3. p < 0.001,p值显示为"p < 0.001"。

实现代码如下:

proc format;
  picture fmt (round min = 10)
    0-<0.001 = 'p < 0.001'  (noedit)
    0.001-<0.01 = '9.999'  
    0.01-1 = '9.99'
  ;
run;

data tmp;
    a = 0.0005; b = put(a, fmt.); output; 
    a = 0.0015; b = put(a, fmt.); output; 
    a = 0.624; b = put(a, fmt.); output;
    a = 0.625; b = put(a, fmt.); output;
run;
tmp

总结

这篇文章介绍了Format过程步中Picture语句基本语法,Pircture语句生成的Format与Value语句生成的不同,模板设置的多样性可以应对更加复杂的格式要求。文章还介绍了3个常用的选项,以及3个Picture语句的应用举例。

希望给读者在日常工作中一些思路启发,提供不一样的解决格式问题的视角。

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

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

推荐阅读更多精彩内容