SAS9.2函数的变化

SAS内置函数个数随SAS各版本的变化

sas number

(ALL RIGHTS RESERVED!)
 
另外SAS函数史上发生最大的变革——SAS9.2可以自定义函数
 
SAS9.2终于在广大用户强烈需求下推出了自定义功能(BASE下的,以前在某些模块中可以自定义)。
SAS9.2以后,用户可以自定义函数。
两种途径来自定义函数:使用C,C++或者SAS语言。
提供了PROC PROTO语句来编译C,C++语言编写的函数
提供了PROC FCMP 语句来编译SAS编写的函数   
 
可以像其他的SAS内置函数一样被DATA步,PROC SQL查询语句和FCMP例程调用。这使得程序员使用独立和重复利用的子程序来读写和维护代码变得更容易。
sample:
 
proc fcmp outlib=sasuser.funcs.trial;
  function study_day(start, event);
    n = event – start;
    if n >= 0 then
      return(n + 1);
    else
      return(n);
  endsub;
 
options cmplib=sasuser.funcs;
data _null_;
  start = ’15Feb2006’d;
  today = ’27Mar2006’d;
  sd = study_day(start, today);
  put sd=;
run;

 

log:

sd=41

有着其他如MACRO等工具无可比拟的优点:  自定义函数为SQL查询语句和WHERE子句中提供了数据步逻辑,并且包括了变量范围的安全机制,同样,只要把函数保存在数据集中,同一单位中的SAS程序员使用不同的操作系统也易于分享这些函数。 

数据清洗之单个数据集操作-重复记录

现有一堆数据,里面有很多是重复的,要去掉?要重新保留成另外的数据集?
 
2 2 3
2 2 3
4 5 6
4 5 6
4 5 6
4 5 6
4 4 4
7 7 8
9 5 5
 
我想去掉里面重复的观察值,实现的四种方法:
1,proc语句
2,sql语句(之所以单独出来,是因为sql本来都强大,可以独顶一方)
3,data步
4,hash对象方法
 
 
第一种,PROC 两种方法:
1,SAS语句proc sort:
data ex;
input a b c;
cards;
2 2 3
2 2 3
4 5 6
4 5 6
4 5 6
4 5 6
4 4 4
4 4 5
7 7 8
9 5 5
;
run;
proc sort NODUPRECS out=ex1 DUPOUT=ex2;
by a b  ;
run;
 
不重复的保留在数据集ex1里面,重复的保留在数据集ex2里面。
这里重复有两种情况,如果指定关键词with by的重复的操作的话,那么sort的option:NODUPRECS要换成NODUPKEY,这样得出的结果是两种不同的情况。
 
2,SAS语句SUMMARY,SAS_Dream大侠做过深入的讨论,详情见:
http://www.mysas.net/forum/viewtopic.php?t=815
 
第二种,PROC  SQL,有多种途径:
还有上面SAS_Dream的。
 
 
 
第三种,DATA步内微操作,(这样操作有个前提,就是此数据集需要进行排序或者已经排好序了)。
data ex;
input a b c;
cards;
2 2 3
2 2 3
4 5 6
4 5 6
4 5 6
4 5 6
4 4 4
4 4 5
7 7 8
9 5 5
;
run;
proc sort;
by a b c ;
run;
data ex1 ex2;
   set ex;
   by a b c;
   retain n 0;
   if first.c then output ex1;
    else output ex2;
   run;
 
这只是一个DATA步而已,并没有表现出“微操作”的特性,如果我们需要的重复记录的数据集里面的记录不重复,怎么办?是否需要在对重复记录的重复记录再操作一次?
这个问题用DATA步的微操作可以一步实现:
data ex;
input a b c;
cards;
2 2 3
2 2 3
4 5 6
4 5 6
4 5 6
4 5 6
4 4 4
4 4 5
7 7 8
9 5 5
;
run;
proc sort;
by a b c ;
run;
data ex1 ex2;
   set ex;
   by a b c;
   retain n 0;
   if first.c then do;
                      n=0;
                      output ex1;
                   end;
         n+1;
               else output ex2;
   if last.c and n ge 2 then output ex2;
   run;
 
这样的结果,好像proc sort不能一步晚成。
其实这个DATA步是一个经典的DATA步,用于很多场合,非常稳定有效,让人满意。
 
 
第四种,HASH 对象方法:
 

数据清洗DATA CLEARING之infile选项之六 与INPUT控制符号联用

碰到这样的一个源数据需要读入:
000001 p1,p2,p3
000002 p2,p3
500003 p5,p6
080004 p2,p7
008005 p3,p4,p1,p2
000006 p4,p5,p6
009007 p2,p9
000408 p10
 
目标数据集为:
000001    p1
000001    p2
000001    p3
000002    p2
000002    p3
500003    p5
500003    p6
080004    p2
080004    p7
008005    p3
008005    p4
008005    p1
008005    p2
000006    p4
000006    p5
000006    p6
009007    p2
009007    p9
000408    p10
 
源数据看起来非常的有规律,需要后一个变量对应的字段扯开。这时候会有许多的小技巧方法,有如下几种思想:
1,把后面的全部当一个字段读入到一个变量,然后一个一个拆开(用函数),然后拼成。
2,用x1-xn,把后面全部读入,没有的设置成缺失(后面再T掉),然后用数组和循环的方法修正为要求的数据集格式。
3,用矩阵分组转制,然后merge。
 
上面的两种思想都可以用代码来实现目的,但是代码是非常的不简洁,并且性能和通用性也不好。
其实SAS提供了一种可以说是专门解决这类问题的方法(在SAS公司培训资料上看过的,BASE的内容)。
data Ex;
infile cards missover dlm=’,’;
input Code $6. Per $ @;
do while (Per ne ”);
output;
input Per $ @;
end;
cards;
000001 p1,p2,p3
000002 p2,p3
500003 p5,p6
080004 p2,p7
008005 p3,p4,p1,p2
000006 p4,p5,p6
009007 p2,p9
000408 p10
;
run;
 
在之前,遇到一个类似例子,也是用同样的方法解决:
data ex  ;
  infile datalines missover;
   input status  family member@;
     do while (member ne .) ;
  output;
  input member @;
  end;
datalines;
9 1 3809 3810 3811 3812
9 2 3814 3815 3816
1 3 3817 3818 3819 3820
;
 
还有一个更一般的例子,这个可能是实际中遇到真实的情况。
 
001 A 头疼 轻度 感冒 重度 ALT增高 轻度
002 B 咳嗽 轻度 过敏 轻度
003 B 感冒 轻度
004 A 感染 中度
 
实现代码是:
data ex  ;
  infile datalines missover;
   input drug group  $ res $ chdu $ @;
     do while (res ne ”) ;
  output;
  input res $ chdu $ @;
  end;
datalines;
001 A 头疼 轻度 感冒 重度 ALT增高 轻度
002 B 咳嗽 轻度 过敏 轻度
003 B 感冒 轻度
004 A 感染 中度
;
run;
 
 
结合infile的选择和行读入控制符号@(input pointer),就可以很好的解决这个问题,并且是一种通用的方法。(前面有一个列控制符号#与infile选项联用的例子:数据清洗DATA CLEARING之infile选项之四 N OBS FIRSTOBS)。
 
小结:
1,SAS提供了非常灵活的数据接口,input就是其中技术之一。infile的选项和input的各种控制符号结合,使得SAS与外界的数据接口变得很灵活很强大。其实input本身读入数据过程是一个步骤非常多的一个过程,并且SAS提供了好多的方法对其各阶段进行控制。有机会偷窥一下SAS的input机制。
2,SAS9以后提供的HASH对象方法,也可以解决这个问题,以后补充。
 

数据清洗DATA CLEARING之infile选项之四 N FIRSTOBS OBS

现有源数据,我想从第四行开始读入数据,读到300行即可:
Title = CPMG Echo Peak File
Version = 1.5
Time(ms)   —   Amplitude
0.467           5369.750
1.467           3151.469
2.467           1593.875
3.467           1504.656
4.467           2836.906
5.467           1997.172
6.467           6213.953
7.467           3965.172
.                .     
485.967        81.633 
487.100        75.186 
488.600        82.873 
490.000        84.433 
496.000        69.029 
497.167        79.925 
实现代码:
data ex1;
infile datalines firstobs=4 obs=300;
input x y;
datalines;
Title = CPMG Echo Peak File
Version = 1.5
Time(ms)   —   Amplitude
0.467           5369.750
1.467           3151.469
2.467           1593.875
3.467           1504.656
4.467           2836.906
5.467           1997.172
6.467           6213.953
7.467           3965.172
.                .     
485.967        81.633 
487.100        75.186 
488.600        82.873 
490.000        84.433 
496.000        69.029 
497.167        79.925 
;
run;
一共读入了300-4+1=297行,结果里面有297条观察值。如果要想读入300条观测值的话,那么需要设定obs值为:300-1+4=303。
 
下面我们看看下面这种情况,即一条记录值需要从多行读入才能完成,这时候需要用到选项N,还有列input pointer指示符号“#”,这样才能完整读入。
data ex;
   infile datalines n=2  ;
   input name $ 1-15 #2 @3 id;
   datalines;
J. Brooks
  40974
T. R. Ansen
  4032
A. Brooks
  40974
B. R. Ansen
  4032
C. Brooks
  40974
D. R. Ansen
  4032
E. Brooks
  40974
F. R. Ansen
  4032
J. Brooks
  40974
H. R. Ansen
  4032
I. Brooks
  40974
J. R. Ansen
  4032
K. Brooks
  40974
L. R. Ansen
  4032
;
run;
 
如果碰到需要从中间读入的情况,需要结合N,FIRSTOBS,OBS来使用。
data ex;
   infile datalines n=2  firstobs=4  obs=19;
   input name $ 1-15 #2 @3 id;
   datalines;
This is a *****
and *****
haha ,over
J. Brooks
  40974
T. R. Ansen
  4032
A. Brooks
  40974
B. R. Ansen
  4032
C. Brooks
  40974
D. R. Ansen
  4032
E. Brooks
  40974
F. R. Ansen
  4032
J. Brooks
  40974
H. R. Ansen
  4032
;
run;
 
需要注意的是obs-firstobs+1必须是N的倍数才能读入完整的(obs-firstobs+1)/N条数据,否则余数部分都不会读入的。
 
INFILE的选项FIRSTOBS,OBS分别用来指定从第firstobs行读起,然后读到obs行,是读取源数据其中的某一部分数据。
N是每一次input需要读入多少行。
 
这几个options是infile非常简单的选项,由于经常性的读入一行作为一条观察值,所以有时会误以为firstobs,obs是指定观察值的条数,这样容易出现一些小小的差错。