注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

公卫人

博学而笃志 切问而近思

 
 
 

日志

 
 

真的很不错的SAS函数总结  

2015-11-07 00:02:07|  分类: 统计分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
最有用的一些SAS函数
A Survey of Some of the Most Useful SAS Functions

by Dr. Ron Cody   翻译 sxlion

摘要:

     SAS函数为你的数据步(DATA step)编程提供惊人的能力。有一部分SAS函数是精华,能够帮助你节省大量不必要的代码。这篇文章内容覆盖其中最有用的一些SAS函数。有些函数可能对你来说有些陌生,然后她们将会改变你的编程方式,并助你轻松完成日常的编程任务。

 介绍:

         本文写到的大部分函数都与字符数据有关,这些函数的功能包括搜索字符串,查找和替代字符串,或连接字符串,还有些函数能测量两个字符串之间的距离(这对于“模糊”配对非常有用)。一些最新和最叹为观止的函数还以Call例程的形式存在。你知道怎么用函数在同一个观察值进行排序吗?你知道你不仅可以定位一列变量中极大或极小值,而且可以定位到第二位、第三位最大或最小的值? 如果你觉得上面的介绍能够吸引你的兴趣,请继续往下读! http://saslist.net

SAS是怎样存储字符值的?

 在我们讨论字符函数之前,了解SAS怎样存储函数值非常重要。为了帮助讨论,你首先需要理解两个重要的字符函数:LENGTHN和 LENGTHC。

LENGTHN和 LENGTHC

        这两个函数返回字符值的长度信息,其中LENGTHN返回未计算后缀空格语句的长度。LENGTHC返回字符变量的存储长度。你也许对旧的SAS函数LENGTH比较熟悉,大部分情况下函数LENGTH和LENGTHC返回同样的值。有一个例外的是,当语句里是缺失值时,LENGTH函数返回的是1,而LENGTHN返回0。有一些新函数看起来是在旧函数后面加个字母“n”,这里的“n”代表“空字符串”(“null string”)。在SAS9中,长度为0的字符串概念被引进。在大多数情况下,如果你看到一个你似曾相识的新函数(比如说新函数TRIMN,你已经知道函数TRIM),你应该使用带“n”的新函数。     看一下下面的程序:

 程序1

1
2
3
4
5
6
7
8
9
10
 data chars1;
length String $ 7;
String = 'abc';
Storage_length = Lengthc(string);
Length = lengthn(String);
Display = ":" || String || ":";
put Storage_length= /
Length= /
Display=;
run;

图1:程序1的结果

Storage_length=7
Length=3
Display=:abc    :

 记住,SAS字符变量的存储长度在编译阶段被设置。因为LENGTH语句在字符设置语句之前,SAS为字符串设置了长度为7。因为字符串长度为3,LENGTHN函数没有计算字符后面的空格,于是返回3。最后,在字符串的两边各加一个冒号,这样就清楚的看到字符串后面包含了四个空格。http://saslist.net

如果你把LENGTH语句往后面移一步,像下面:

程序2

1
2
3
4
5
6
7
8
9
10
data chars2;
String = 'abc';
length String $ 7;
Storage_length = lengthc(String);
Length = lengthn(String);
Display = ":" || String || ":";
put Storage_length= /
Length= /
Display=;
run;

你将得到下面结果:

图2:程序2的结果

Storage_length=3

Length=3

Display=:abc:

        注意到上面结果中,LENGTH语句被忽略了。因为Sting = “abc”出现在LENGTH语句之前,字符串的长度已经被设置。根据经验,运行PROC CONTENTS来查看所有数据集,检查所有字符变量的存储长度。如果你看到有些字符串的长度达到200,你不用吃惊,因为很多SAS字符函数的默认长度就是200,也就是说如果你没有显示的用LENGTH语句或其他方式定义变量长度的话,SAS就会默认字符串长度为200。

缺失值函数和CALL MISS例程

 在过去,你可以像下面这样检查一个数据步中的缺失值:

 *老方法 ;

if Age = . then . . .

If Char = ‘ ‘ then . . .

*新方法 ;

if missing(Age) then . . .

if missing(Char) then . . .

     这个missing函数的新语句可以确认数值变量和字符变量是否为空 ,如果是空,则返回1,否则返回0。我个人非常推荐你在任何需要测试一个变量是否为空的程序中使用这个函数。这样你会发现你的程序更漂亮。

      如果你需要设置一个或多个字符或数字变量为空值,你可以使用下面的老方法,比如:

1
2
3
4
5
6
7
8
9
 array x[10] x1-x10;
array chars[5] a b c;
do i = 1 to 10;
x[i] = .;
end;
do I = 1 to 3;
chars[i] = ' ';
end;
drop i;

或者你也可以使用像下面这样的call missing历程,以便节约更多的时间。

 call missing(of x1-x10, a b c);

INPUT函数

      这是我学习SAS(多年前)是遇到理解困难的函数之一。一个简单的方法就是思考INPUT函数,问自己“INPUT语句是干什么的?”,它通常从按照预先提供的INFORMAT格式从一个文件中读取文本。同样,INPUT函数做类似的工作,它根据函数提供的第二个语句INFORMAT,读取函数提供的第一个语句中的文本。也许下面的程序可以跟直观的帮助理解。

程序3

1
2
3
4
5
6
7
data _null_;
c_date = "9/15/2004";
c_num = "123";
Sas_Date = input(c_date,mmddyy10.);
Number = input(c_num,10.);
put SAS_Date= Number=;
run;

                程序3中有两个字符变量c_date和c_num。使用INPUT函数,你可以根据第一个语句的值新建一个真实的SAS数据(数值型),根据第二个语句将字符转化为数值。注意,c_num的读入格式是10.。和从文件中读入文本不同的是,INFORMAT不能读完字符值。运行这段代码,SAS_Date和Number的值显示如下: http://saslist.net

图3:程序3的结果

 Sas_Date=16329 Number=123

PUT函数

          PUT函数是与INPUT函数对应的函数。同样,你可以考虑PUT语句的作用——读取一个SAS值(字符或数值型),应用输出格式FORMAT,并通常写出到一个文件的结果中。PUT函数获取第一个语句的SAS值,并且按照第二个语句的格式写出到结果中。PUT函数的用途之一就是将数值型转化为字符型,另外一个用途就是使用用户自定义格式建立一个新变量。下面是一个例子:

程序4

1
2
3
4
5
6
7
8
9
data _null_;
SAS_Date = 1;
Number = 1234;
SS_num = 123456789;
Char_Date = put(SAS_Date,mmddyy10.);
Money = put(Number,dollar8.2);
SS_char = put(SS_num,ssn.);
put Char_date= Money= SS_char=;
run;

 这个例子使用了三个数值(分别是SAS日期,一个数和一个社会安全号),并且新建了三个字符型变量。运行这段代码后,字符变量的值为:

图4,程序4的结果

Char_Date=01/02/1960

Money=$1234.00

SS_char=123-45-6789

     下一段程序告诉你使用一个格式将不同年龄组进行分类,这种方式在某种程度上比使用一系列的IF-THEN-ELSE语句更简单。下面是程序例子:

程序5

1
2
3
4
5
6
7
8
9
10
11
12
13
proc format;
value agegrp 0-20='0 to 20'
21-40='21 to 40'
41-high='41+';
run;
data PutEx;
input Age @@;
AgeGroup = put(Age,agegrp.);
datalines;
15 25 60
;
proc print;
run;

               新变量AgeGroup是一个使用了格式化的Age值的字符变量。新变量的存储长度是与格式化值最大长度是一致的。下面,你可以看看变量Age和AgeGroup的值。

 图5:程序5的结果

Age    AgeGroup

 15    0 to 20

 25    21 to 40

 60    41+

FIND和FINDC函数

          FIND函数使用第一个语句定义的字符串,查找有第二个语句定义的子字符串,如果子字符串找到,函数返回它的位置。如果没有找到,则返回0。FIND函数中有两个可选的语句——修饰符和起始位置。最常用的修饰符为“i”,表明忽略大小写。起始位置定义在开始搜索的位置,如果起始值为负数,则搜索开始于起始位置绝对值的位置,并且从右往左搜索。随便提一下,两个可选语句的位置可以颠倒。为什么这样也可以呢? 修饰符通常是字符值,而起始位置通常是数字值,哈哈,厉害吧!如果你仅仅输入了一个修饰符或起始位置值,放第三个语句位置即可。下面是一个例子。http://saslist.net

程序6

1
2
3
4
5
6
7
8
9
10
11
data locate;
input String $10.;
First = find(String,'xyz','i');
First_c = findc(String,'xyz','i');
/* i means ignore case */
datalines;
abczyx1xyz
1234567890
abcz1y2x39
XYZabcxyz
;

       这个例子中的两个函数使用修饰符“i”,使用了此修饰符,你可以省去改变一个或多个字符串大小写形式的麻烦。

图6:程序6的结果

String      First    First_c

abczyx1xyz      8         4

1234567890      0         0

abcz1y2x39      0         4

XYZabcxyz       1         1

    第一个观察值,子字符串‘xyz’在String的第八个位置才找到。因为FINDC函数在寻找字母“x”或“y”或“z”,在第一个观察值中返回一个4,因为字母‘z’在第四个位置找到了。注意但在观察值2中没有找到任何配对成功时,函数返回0。

COMPRESS函数

     这个当然是我最喜欢的SAS函数之一。据我所知,这是唯一从版本8升级到版本9的函数。这怎么可能呢?该函数写法在SAS8和SAS9中是一模一样的么?答案是“yes“,除了在版本9中增加了一个可选的第三项外。

在COMPRESS函数中的三个语句是:

Compress(String, characters-to-remove, optional-modifiers)

其中,Sting是你想要压缩的字符串(除非你使用‘k’修饰符)

characters-to-remove是你一列你想要从String中去掉的字符串。如果你仅仅给出了COMPRESS函数一个语句,那么就将除掉String中所有的空格。

可选修饰符允许你指定字符串子语句,例如:

  • a    大写 或小写字母
  •  d     数值(dignits)
  •  i     忽略大小写
  •  k     保留列表字符串,而不是去掉
  • s     空间(blank,tabs,if,cr)
  •  p     标点符号

         我相信修饰符‘k’让这个函数变得真正强大。‘k’选项告诉函数所需要保留的字符列表。设定你需要保留的通常要优于你想去掉的。比如说,如果你使用两个修饰符‘k’和‘d’,函数将会保留字符串中所有数字,丢掉其他东西。这个在你的字符串中包含不可打印的字符串非常有用。下面是一个例子:

程序7

1
2
3
4
5
6
7
8
9
data phone;
input Phone $15.;
Phone1 = compress(Phone);
Phone2 = compress(Phone,'(-) ');
Phone3 = compress(Phone,,'kd');
datalines;
(908)235-4490
(201) 555-77 99
;

        COMPRESS函数将除掉Phone1中的所有空格,因为你仅仅使用了一个语句。对于Phone2,你指定了左右括号,破折号和空格。对于Phone3,你指定了两个修饰符‘k’和‘d’。注意两个冒号,这个非常有必要,可以告诉SAS‘kd’是修饰符(第三个语句),而不是需要除掉的字符(第二个语句)。注意下面结果中,Phone2和Phone3是同样的结果,然而在Phone中有多余的符号字符。

图7:程序7的结果

      Phone            Phone1          Phone2        Phone3

 (908)235-4490      (908)235-4490    9082354490    9082354490

 (201) 555-77 99    (201)555-7799    2015557799    2015557799

       这里有另外一个非常有用的例子,将演示COMPRESS函数能够用于从包括像包含单位的非数字字符串中提取数值。看下面:

程序8

1
2
3
4
5
6
7
8
9
10
data Units;
input @1 Wt $10.;
Wt_Lbs =
input(compress(Wt,,'kd'),8.);
if findc(Wt,'K','i') then
Wt_Lbs = 2.2*Wt_Lbs;
datalines;
155lbs
90Kgs.
;

       你看输入数据中包括像lbs.或kgs.的单位。这是非常的问题。使用COMPRESS函数可以得到一个非常简单和漂亮的方法。你开始可以仅保留原始值中的数值,然后使用INPUT函数把字符转化成数值。现在你需要判定,是否原始值中包括一个大写或小写的‘k’。如果是,你需要把公斤单位转化成英镑。带修饰符‘i’的FINDC函数可以轻而易举的完成这个判断。http://saslist.net

图8:程序8的结果

   Wt      Wt_Lbs

 155lbs      155

 90Kgs.      198

 

SUBSTR函数

         如果你需要从一个字符串中提取一个子字符串,你需要SUBSTR函数。需要提醒一下的是,有个函数SUBSTRN非常像SUBSTR,就是多了一些额外的特征。我不知道这些特征是否经常被需要,因此本文选择描述稍微简单的函数SUBSTR。

         此函数的第一个语句是输入字符串,第二个语句是你想要提取字符串的开始位置,第三个是,可选语句,设定子字符串的长度。如果你忽略第三个语句,函数将提取输入字符串中最后一个非空字母,也就是说,它忽略输入字符串的后面的空格(这个特征非常有用)。

         在我们继续例子之前,理解默认长度这个概念非常重要。比如说,在下一个程序中,如果你没有写LENGTH语句,SAS将仍然需要设置一个长度给State。对于这个函数,默认长度等于函数第一个语句的长度。你不能使得从字符串提取的子字符串长于字符串本身。很多其他的SAS字符函数默认长度为200。需要保证的是,具有这个功能的函数需要在DATA步中包含LENGTH语句。最好是包含LENGTH语句,即使是不需要,这样是没有风险的。http://saslist.net/

         下面是一个使用SUBSTR函数的简单例子。

程序9

1
2
3
4
5
6
7
8
9
data pieces_parts;
input Id $9.;
length State $ 2;
State = substr(Id,3,2);
Num = input(substr(Id,5),4.);
datalines;
XYNY123
XYNJ1234
;

         这里是你想要提取州号码(从位置3开始,一共两位),ID的数字部分从位置5开始。注意你忽略数值提取中的第三个语句。这个非常有用,因为有的数字是3个字符长,有的是4个字符长。这个例子中,你使用INPUT函数将字符转化为数值。

         图9,程序9的输出结果

    Id       State     Num

 XYNY123      NY       123

 XYNJ1234     NJ      1234

使用等号在左边的SUBSTR函数

         在早期学SAS的时候,等号在左边的SUBSTR函数被称为是SUBSTR假函数。据我所知,这是SAS唯一等号在左边的函数。下面是它的用途:

         允许你用新的字符来替代已有字符串中的字符。这个听起来挺复杂的,但是你通过下面的程序看到,这实际上是很直接的方式。下面的程序使用SUBSTR函数(等号在左边的)来遮住帐号的前五个字符。代码如下:

程序10

1
2
3
4
5
6
7
data bank;
input Id Account : $9. @@;
Account2 = Account;
substr(Account2,1,5) = '*****';
datalines;
001 123456789 002 049384756 003 119384757
;

         首先,你把Account的值赋给另一个变量(Account2),这样你不会改变原来变量的值。然后,你替换Account2中的字符,从位置1开始的用5个长度的5个星号。下面是结果:

图10:程序10的结果

Id     Account     Account2

 1    123456789    *****6789

 2    049384756    *****4756

 3    119384757    *****4757

SCAN函数

你可以使用SCAN函数来解析一个字符串。SCAN函数的第一个语句是你需要解析的字符串。第二个语句指定你需要提取的单词“word”。第三个语句(可选)是分隔符列表。我把“word”用引号引起来原因是SAS定义一个单词都会用一个分隔符分开。默认的分隔符号是特别长,并且与ASCII和EBCDIC编码稍稍有些不同。因此,这样便于更好的提供一个第三个语句和显示的指定你的分隔符。

这个函数一个非常有用的特征是,你能够使用负数值。这将导致扫描从右向左进行,这个对于类似First, Middle,Last或First, Last形式的姓名解析非常有用。       如果你使用语句值为-1时,你将会得到last name那么。下面是举例:http://saslist.net/

程序11

1
2
3
4
5
6
7
8
9
10
11
data first_last;
length Last_Name $ 15;
input @1 Name $20.;
Last_Name = scan(Name,-1,' ');
datalines;
Jeff W. Snoker
Raymond Albert
Alfred E. Newman
Steven J. Foster
Jose Romerez
;

         有些名字包含一个中间名,有的则没有。在这个函数的第二个语句中使用-1,你就可以总是得到last name。

图11:程序11的结果

  Last_

  Name            Name

 Snoker     Jeff W. Snoker

 Albert     Raymond Albert

 Newman     Alfred E. Newman

 Foster     Steven J. Foster

Romerez    Jose Romerez

UPCASE,LOWCASE和PROPCASE 函数

         这三个函数主要是用于改变语句的大小写状态。UPCASE和LOWCASE的功能是显而易见的。PROPCASE(意思是合适的大小写状态)将每一个“word”第一个字母大写,而将其余的字母小写。同样,你看到这里的“word”也放在引号里。这个默认的分隔符是空格,因此PROPCASE函数将会把每个单词首字母改成大写。你可以指定一系列分隔符作为该函数第二个可选语句。我推荐同时指定空格和单引号作为分隔符。然后这个函数就会正确的大写首字母,比如说D’Angelo。下面是例子:http://saslist.net/

程序12

1
2
3
4
5
6
7
8
9
data case;
input Name $15.;
Upper = upcase(Name);
Lower = lowcase(Name);
Proper = propcase(Name," '");
datalines;
gEOrge SMITH
D'Angelo
;

注意你需要将你指定的分隔符列表放在双引号中。

图12:程序12的结果

Name            Upper           Lower           Proper

 gEOrge SMITH    GEORGE SMITH    george smith    George Smith

D’Angelo        D’ANGELO        d’angelo        D’Angelo

TRANWRD函数

这个函数对给定字符串执行查找和替代的操作。这个函数有三个语句分别是输入字符串,需要查找的字符串和替代字符串。如果替代字符串比需要查找的字符串还要长,你可能需要使用LENGTH语句来新建一个变量,以防你的值被默认截断。TRANWRD使用的通常情况是地址标准化,如下面的程序所示。

程序13

1
2
3
4
5
6
7
8
9
10
11
12
data convert;
input @1 address $20. ;
*** Convert Street, Avenue and
Boulevard to their abbreviations;
Address = tranwrd(Address,'Street','St.');
Address = tranwrd(Address,'Avenue','Ave.');
Address = tranwrd(Address,'Road','Rd.');
datalines;
89 Lazy Brook Road
123 River Rd.
12 Main Street
;

         使用三个TRANWRD函数,你可以用Street,Avenue和Road缩写来替代它们。

图13:程序13的结果

Obs    address

 1     89 Lazy Brook Rd.

 2     123 River Rd.

3     12 Main St.

SPEDIS函数

         函数SPEDIS是模糊配对最有用的函数之一。这个函数计算两个字符串之间的拼写距离“spelling distance”。你曾经在微软办公软件Word中拼写错过单词吗?Ok,别人告诉我如果你拼写错了,Word将会在拼错单词下面划红线。你可以通过右击鼠标,然后下拉菜单中将出现可能正确的单词。函数SPEDIS使用了类似的算法,如果两个字符串(分别是语句1和语句2)配对非常准确,那么函数返回0。对于每一组拼写错误,函数给出一个罚分。例如,如果你的第一个字母错误,你就会得到一个大的罚分。如果你将两字母位置搞错(比如位置对换),你将会得到一个相对小的罚分。当函数合适每类错误,它除以根据第一个字符串的长度的总罚分。这样就有意义了。假设你在一个三个字母的单词和10字母单词中拼写一个错误相比,那么前面则是错误更大,拼写距离也就越大。

         对于拼写距离来说,什么样的值认为大呢?例如,如果你在对两个文件中进行姓名配对时允许非常大的拼写距离值,你可能将不属于一块的观察值联系起来。如果你允许姓名之间非常小的拼写距离,你可能不会将同样的姓名放一起。为了感受不同拼写错误导致的结果值,看看下面的程序: http://saslist.net/

         程序14

1
2
3
4
5
6
7
8
9
10
11
data compare;
length String1 String2 $ 15;
input String1 String2;
Points = spedis(String1,String2);
datalines;
same same
same sam
first xirst
last lasx
receipt reciept
;

图14:程序14的结果

String1    String2    Points

 same       same          0

 same       sam           8

 first      xirst        40

 last       lasx         25

 receipt    reciept       7

你可能考虑使用SOUNDEX来配对两个文件中的姓名,然而,我发现SOUNDEX更倾向于差异特别大的姓名配对。

TRIMN和STRIP函数

         函数TRIMN用来去掉后缀空格,函数STRIP用于去掉前面和后缀的空格。TRIMN和老的函数TRIM类似,除了在处理缺失值问题上。当你有一个缺失值时,TRIM函数返回一个单独的空格,而TRIMN则返回一个长度为0的字符串。当你使用连接符号连接字符串时,这两个函数都是有用的。下面程序会演示:

程序15

1
2
3
4
5
6
7
8
9
10
11
data _null_;
length Concat $ 8;
One = ' ABC ';
Two = 'XYZ';
One_two = ':' || One || Two || ':';
Trim = ':' || trimn(One) || Two || ':';
Strip = ':' || strip(One) || strip(Two) || ':';
Concat = cats(':',One,Two,':');
put one_two= / Trim= / Strip= /
Concat=;
run;

图15:程序15输出结果

One_two=:   ABC   XYZ:

Trim=:   ABCXYZ:

Strip=:ABCXYZ:

Concat=:ABCXYZ:

当你不使用任何一个函数连接两个字符串时,你能看到结果都保留空格。注意,变量Trim值中的’ABC’和’XYZ’之间没有空格,而Strip变量则没有任何空格。最后你会发现使用CATS函数可以更轻松的去掉前面和后缀的空格,然后连接字符串。

NOTALPHA,NOTDIGIT和 NOTALNUM函数

         我仅仅列出了三个“not”函数,如果你查SAS在线文档或文后列的参考文章,你会发现这样的函数还有更多。当遇到字符串中的不是alpha(字母),digit(数字),或alphameric(字母或数字)时,每个函数返回第一个遇到位置。由于所有的“not”函数会搜索字符串的每一个位置,包括后面的空格,因此你首先需要使用strip或trim去掉它们。

         这类函数的第二个语句是可选的,用于设置你要开始搜索的起始位置。如果你输入一个负值,那么搜索将从这个值的绝对位置处,从右往左执行。如果函数没有找到对应的字符,就返回0。

         这类函数对于字符型数据清理来说是强大无比的。你可以对一个字符值定义一个规则, 让其仅仅包含数字或者字符等等。下面是一个例子:

程序16

1
2
3
4
5
6
7
8
9
10
11
data data_cleaning;
input String $20.;
Not_alpha = notalpha(strip(String));
Not_digit = notdigit(strip(String));
Not_alnum = notalnum(strip(String));
datalines;
abcdefg
1234567
abc123
1234abcd
;

图16:程序16的输出结果

              Not_     Not_     Not_

  String     alpha    digit    alnum

 abcdefg       0        1        0

 1234567       1        0        0

 abc123        4        1        0

 1234abcd      1        5        0

 

CATS和CATX函数

         这两个函数用于连接字符串。函数CATS首先去掉每个要连接字符串的首尾空格。CATX也会去掉首尾的空格,并且还会在每个字符串之间插入分隔符(CATX函数的第一个语句)。

         这些函数需要非常注意的点是结果的存储长度,如果没有预先定义,默认是长度200。而你使用连接符号(||或!!)只是这些连接字符串长度之和。

         如果你有一系列的变量如Base1-Basen,你可以在列表前使用关键词‘OF’。最后,列表中的值可以是字母,也可以说数字。如果一些语句是数字,SAS将会把数字当作字母对待,并且不会在SAS log里面出现转换信息。

         下面的例子示意这些函数字母操作去掉空格,函数CATX字母插入分隔符。

程序17

1
2
3
4
5
6
7
8
data join_up;
length Cats $ 6 Catx $ 13;
String1 = 'ABC ';
String2 = ' XYZ ';
String3 = '12345';
Cats = cats(String1,string2);
Catx = catx('-',of String1-String3);
run;

图17:程序17的输出结果

Cats         Catx

ABCXYZ    ABC-XYZ-12345

COUNT和COUNTC函数

         SAS有两个计数函数,COUNT和COUNTC。它们之间的区别就像FIND和FINDC。COUNT计算一个子字符串在一个字符串中出现的次数,COUNTC计算单独字母在字符串中出现的次数。这些函数的语句和FIND和FINDC一样。第一个语句是你想要搜索的字符串,第二个语句是子字符串(COUNT)或一列字母(COUNTC)。最后你可以在第三个语句中使用可选的修饰符,其中修饰符‘i’(忽略大小写)最有用了。下面的程序演示这两个函数。

程序18

1
2
3
4
5
6
7
8
9
data Dracula; /* Get it Count Dracula */
input String $20.;
Count_abc = count(String,'abc');
Countc_abc = countc(String,'abc');
count_abc_i = count(String,'abc','i');
datalines;
xxabcxABCxxbbbb
cbacba
;

图18:程序18输出结果

                     Count_    Countc_    count_

  String               abc       abc       abc_i

  xxabcxABCxxbbbb       1         7          2

  cbacba                0         6          0

有意思的组合:COUNTC和CATS函数

        COUNTC和CATS函数组合非常有意思和强大。假如你有一个调查,你对每一个结果记录’Y’或’N’。假如你想要对‘Y’进行计数(不论大小写)。你可能立即想到把每个调查变量放到一个数组中,然后循环每个问题,逐个对‘Y’进行加一计数。试试独创的COUNTC和CATS函数组合方法来实现这个目标。第一次见到组合函数是我的朋友Mike Zdeb发来的邮件。我相信是他首创的这个方法。下面是代码。

程序19

1
2
3
4
5
6
7
data Survey;
input (Q1-Q5)($1.);
Num = countc(cats(of Q1-Q5),'y','i');
datalines;
yynnY
nnnnn
;

         CATS函数连接所有的调查结果到一个字符串中,然后使用COUNTC函数对这个字符串中的‘Y’进行计数。我太喜欢这个程序了。

图19:程序19的结果

 Q1    Q2    Q3    Q4    Q5    Num

 y     y     n     n     Y      3

 n     n     n     n     n      0

一些日期函数 MDY, MONTH, WEEKDAY, DAY, YEAR和YRDIF

         这一节涉及到最常用(有用)的日期函数。函数MDY根据给定的月份、天和年份返回一个SAS日期。函数WEEKDAY,DAY,MONTH和YEAR的语句中使用SAS日期,分别返回一周中的一天(比如1等于星期日,2等于星期一,依此类推),一个月的天数(从1到31的数字),月份(从1到12的数字)和年份。

         函数YRDIF计算两个日期相隔的年数。前面两个语句为第一个日期和第二个日期。第三个可选的语句允许你指定一个月中的天数,一年中的天数。例如,对于特定的金融计算(比如债券利息),你可能指定‘30/360’达到每月30天,每年360天。函数YRDIF 在计算闰年的时候有点不同,这个问题在SAS9.3中已经解决。如果你使用的是9.3以前的版本,你需要指定‘ACT/ACT’作为YRDIF函数的第三语句。注意,当涉及闰年是,计算结果有一天的偏移。尽管如此,笔者认为这个仍然比把两个日期之差除以365.25。下面的程序演示所有的日期函数:

程序20

1
2
3
4
5
6
7
8
9
10
11
data DateExamples;
input (Date1 Date2)(:mmddyy10.) M D Y;
SAS_Date = MDY(M,D,Y);
WeekDay = weekday(Date1);
MonthDay = day(Date1);
Year = year(Date1);
Age = yrdif(Date1,Date2);
format Date: mmddyy10.;
datalines;
10/21/1955 10/21/2012 6 15 2011
;

图20:程序20的结果

            Week    Month

SAS_Date     Day     Day     Year    Age

18793       6       21     1955     57

ARRAY数组函数

你定义一个数组,发现对数组中的元素进行计数是件不方便的事。在一个数据集中,你定义了一个全部是数值或字符变量的数组。如果数据包含大量的变量,你可能不会数它们。函数DIM将数组名作为语句,返回该数组的元素个数。特别是当你定义数组,你使用星号来代替数组元素个数。

在下面的程序中,使用关键词_numeric_和 _character_定义了两个数组。在数据步定义了所有的数值或字符变量。在这个程序中,你想把所有的从999的数值转化成SAS缺失值。并且把所有的字母值转换成合适的大小写状态。

程序21

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data convert;
input (A B C)($) x1-x3 y z;
array nums[*] _numeric_;
array chars[*] _character_;
do i = 1 to dim(nums);
if nums[i]=999 then nums[i]=.;
end;
do i = 1 to dim(chars);
chars[i] = propcase(chars[i]," '");
end;
drop i;
datalines;
RON jOhN mary 1 2 999 3 999
;

像这样定数组将结果你大量的时间和编程精力。

图21:程序21的输出结果

     A      B       C      x1    x2    x3    y    z

 Ron    John    Mary     1     2     .    3    .

N,NMISS, SUM和MEAN函数

这类函数被称为描述统计函数。函数SUM和MEAN分别计算总和和平均值。记住,这些函数忽略所有的缺失值(不同于把缺失值当作0)。

函数N返回列表值中所有非零值的数目;函数NMISS返回列表值中缺失值的个数。

下面的程序演示了一个非常有用的函数N、NMISS和MEAN组合。当列表数据中存在特定数量的非缺失(或缺失)值,计算出平均值。下面的程序就是这样:

程序22

1
2
3
4
5
6
7
8
9
10
11
data descriptive;
input x1-x5;
Sum = sum(of x1-x5);
if n(of x1-x5) ge 4 then
Mean1 = mean(of x1-x5);
if nmiss(of x1-x5) le 3 then
Mean2 = mean(of x1-x5);
datalines;
1 2 . 3 4
. . . 8 9
;

         在这个程序中,仅当非缺失值个数大于或等于4时,计算Mean1;当缺失值等于或少于3个时,计算Mean2。

图22:程序22的输出结果

Sum    Mean1    Mean2

  10     2.5      2.5

  17      .       8.5

SMALLEST和LARGEST函数

         非常容易找到一列数值中最大(函数MAX)和最小的值(MIN),然而,想找到第二大或第二小的之类的值则非常困难。在SAS中,你可以使用SMALLEST和LARGEST函数找到第nth最大或最小值。这类函数的第一个语句指定排位数。例如,SMALLEST(1,of x1-x10)和MIN(of x1-x10);SMALLEST(2,of x1-x10)返回第二小的值。注意,这两个函数都忽略缺失值。下面的程序演示两个函数。

程序23

1
2
3
4
5
6
7
8
9
10
data descriptive;
input x1-x5;
S1 = smallest(1,of x1-x5);
S2 = smallest(2,of x1-x5);
L1 = largest(1,of x1-x5);
L2 = largest(2,of x1-x5);
datalines;
7 2 . 6 4
10 . 2 8 9
;

图23:程序23的输出结果

x1    x2    x3    x4    x5    S1    S2    L1    L2

7     2     .     6     4     2     4     7     6

10     .     2     8     9     2     8    10     9

LAG函数

         由于SAS每次处理一条观察值,你需要一种获取上一条观察值的方法。例如,你可能要计算这一条和下一条观察值之间的差异。函数LAG提供可能性,函数LAG返回上次这个函数执行时语句的值。理解这点非常重要! 如果你选择性的执行这个函数(比如在IF语句后面),当条件为否时,下次你执行这个函数是,你将得不到上条观察值——你将得到上一次IF语句为真时的值。

         这里用LAG函数的家族函数:LAG2返回向前第二次的值,LAG3返回前第三次的值,依此类推。如果你在数据步循环中,执行LAG函数,它将返回前一次,前两次,前三次值等等。

         一个LAG函数常用的作用是计算观察值之间的差异,另一个常用的是,计算移动平均,如下面程序所示。

程序24

1
2
3
4
5
6
data Moving;
input X @@;
Moving = mean(X,lag(x),lag2(x));
datalines;
50 40 55 20 70 50
;

在这个程序中,你得到当前值和前两次值的平均值。

图24:程序24的输出结果

  X     Moving

 50    50.0000

 40    45.0000

 55    48.3333

 20    38.3333

 70    48.3333

 50    46.6667

CALL SORTN例程

         你可以使用CALL SORTN例程在观察值内进行排序。你在例程中语句中放入变量,执行后,所有的值发生改变,并以升序的形式排列。看看下面程序演示是怎么工作的。注意,CALL SORTC例程是针对字母值的。当你你用这些例程时,所有的字母值必须是长度相同,结果是以字母顺序排列的。

         程序25

1
2
3
4
5
6
7
data Scores;
input Score1-Score5;
call sortn(of Score1-Score5);
Top3 = mean(of Score3-Score5);
datalines;
80 70 90 10 80
;

         输出结果如下,你看到原始scores值为80,70,90,10和80,结果中值位置发生改变,Score1最小,Score2次之,依此类推。

图25:程序25的输出结果

Score1    Score2    Score3    Score4    Score5      Top3

10        70        80        80        90      83.3333

结论

         这篇文章包括一些最有用的SAS函数。我认为有点多了,但是它们都是我最喜欢的。我想看完后该了解SAS函数对于数据步编程来说是多么的不可或缺。

参考

Cody, Ron, 2010, SAS Functions by Example, Second edition, SAS Press, Cary, NC., SAS OnLine Doc., SAS Institute, Cary, NC.

原文作者Cody, Ron  A Survey of Some of the Most Useful SAS ? Functions http://support.sas.com/resources/papers/proceedings12/241-2012.pdf

  评论这张
 
阅读(1018)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017