SAS中文论坛

 找回密码
 立即注册

扫一扫,访问微社区

查看: 1811|回复: 4
打印 上一主题 下一主题

[SASOR转贴] SAS Programming III: Advanced Tech笔记

[复制链接]

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
楼主
 楼主| 发表于 2004-3-26 08:16:01 | 只看该作者

[SASOR转贴] SAS Programming III: Advanced Tech笔记

SAS Prog III 延续SAS Prog I 和SAS Prog II,系统介绍SAS语言中的高级技巧,一共有8章。第一章为课程所用数据的情景介绍。笔记只记录重要的技巧和语法,希望给没上过该课程的SASORs一点启发。运行原理教程中已有描述,也可以参阅SAS帮助。

有疑问欢迎在论坛提。


SAS Programming III: Advanced Tech笔记(1)

Chapter 2: 访问观测

2.1 数据取样

使用SET语句的POINT=选项进行随机访问。

语法:
SET SAS-data-set POINT = point-variable;

point-variable


为一临时的数字变量,存放需要读出的观测号;
必须在SET语句执行之前附值;
必须是一个变量,如X;不能为一常数,如12。

POINT=选项使用直接访问来读取数据,不对文件结束进行检测,为了防止数据步进入死循环,需要与STOP语句连用。

语法:
STOP;

案例:建立等距取样。

data ia.subset;
  do PickIt = 100 to 500 by 100;
    set ia.sale2000 point = PickIt;
    output;
  end;
  stop;
run;

如果事前不知道观测数,可以使用SET语句的NOBS选项,来检测数据集中的观测数。

语法:
SET SAS-data-set NOBS = varible;
NOBS= 选项建立一个临时变量:

其值为输入数据集的观测数
在编译时赋值
变量值始终保留
在执行时不能修改

例如:
data ia.subset;
  do PickIt = 100 to TotObs by 100;
    set ia.sale2000 point = PickIt nobs = TotObs;
    output;
  end;
  stop;
run;

NOBS=返回的是所有的观测,包括标志删除的观测。由于TotObs在在编译时赋值,它可以在SET语句之前被引用。

建立随机取样

有几个随机函数能够返回随机数,常用的是RANUNI

语法:
RANUNI(seed)

seed为小于2^31
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
沙发
 楼主| 发表于 2004-3-31 13:32:41 | 只看该作者

2

2.2 建立和管理索引

对比正常情况下的顺序访问,索引可以为以下操作提供观测的直接访问:


访问较少于总体的子集(WHERE)
返回已排序的观测(BY)
进行表查询操作(使用KEY=的SET语句)
连接观测(PROC SQL)
修改观测(使用KEY=的MODIFY语句)

索引是一种辅助的数据结构,基于一个或则多个关键变量来指明观测的位置。
[img:55690]http://sasor.feoh.net/_usrdata/images/article/sas_column6a.gif[/img:55690]

SAS数据集索引的结构如上图,它保存着已排序的关键值(Key Value),和关键值出现的位置(Location)。其中 页 (page)是可以在一次I/O请求中完成传输的数据集合(一组观测)。每个数据文件都有预先指定的页大小,配置适当的页大小可以大大优化访问速度。

在没有使用索引时,任何的查询都要遍历所有的纪录。使用索引后,SAS只读出满足查询条件的页。如果满足查询条件的页大大小于整个数据集,查询速度当然得到极大的提高。如果这些页接近整个数据集,由于使用索引还要增加读取索引文件的开销,查询数据反而会降低。SAS中的语言优化器会自动判断是否需要使用索引。

SAS索引有两大类:

简单索引,只基于一个变量的值,索引名自动等于其关键变量名。
复合索引,基于多于一个变量组合成的值,必须有一个表级的唯一名。

索引常用的选项有: UNIQUE:关键变量的取值必须为一。该选项防止对于关键变量冗余的记录加入到数据集中。

建立索引:

可以在建立数据集同时建立索引。在数据步中,输出数据集选项使用INDEX=选项。
可以使用SQL过程,或者DATASETS过程,在现存的数据集上建立索引。

一般而言,建立索引有三步
1. 标出关键变量
2. 如果是复合索引,选择有效的SAS名称作为索引名
3. 确定索引选项

一个数据集可以拥有:

多个简单和复合索引
字符型和数字型的关键变量
在实际使用中,出于效率考虑,我们只会为经常在WHERE条件中使用的变量,和用来合并SAS数据集的变量建立索引。

在数据步中建立索引
语法:
DATA SAS-data-file-name(INDEX =
  (index-specification-1
  …>));

例如:
/* 建立简单索引 */
DATA class(INDEX=(age));
  SET sashelp.class;
RUN;

/* 建立一个简单索引和一个复合索引i_ah */
DATA class(INDEX=(age i_ah=(age height)));
  SET sashelp.class;
RUN;

使用全局选项 MSGLEVEL=,你可以在日志中看到索引的建立,使用情况。
语法:
OPTIONS MSGLEVEL = N | I
N:仅打印notes, warnings, 和error 信息。系统的默认选项。
I:打印N选项的信息,同时打印附属信息,包括索引使用,合并处理,排序等附加信息。

使用DATASETS过程管理索引
语法:
PROC DATASETS LIBRARY = libref ;
  MODIFY SAS-data-set-name;
  INDEX DELETE index-name;
  INDEX CREATE index-specification
  < / options>;
QUIT;

如果同名索引已经存在,INDEX CREATE语句将不能建立新的索引。
如果INDEX DELETE和INDEX CREATE在同一个过程步出现,INDEX DELETE会先执行,以便为建立新索引留下可用空间。

例如:
options msglevel = i;
proc datasets library = ia nolist;
  modify sale2000;
  index delete Origin;
  index delete FlightDate;
  index create Origin;
  index create FlightDate =
  (FlightID Date) / unique;
quit;

使用SQL过程管理索引
语法:
PROC SQL;
  CREATE INDEX index-name
  ON table-name(column-name-1,...
  column-name-n);
  DROP INDEX index-name
  FROM table-name;
QUIT;

同样,如果同名索引已经存在,必须删除才能使用语句重新建立。

例如:
options msglevel = i;
proc sql;
  drop index Origin
   from ia.Sale2000;
  drop index FlightDate
   from ia.Sale2000;
  create index Origin
   on ia.Sale2000(Origin);
  create unique index FlightDate
   on ia.Sale2000(FlightID,Date);
quit;

索引的存储
索引与被索引的数据集存放在同一个SAS逻辑库中。同时,索引是一个与数据集分离的SAS文件。一个索引文件可能包含单一数据集的多个索引。在z/OS操作系统文件列表上,索引文件不会像其他单独文件一样显示出来。

索引文件的命名:与被索引的数据集文件同名,后缀名为sas7bndx(SAS8以上)。例如:对于数据集sale2000,其数据文件名为sale2000.sas7bdat,索引文件名为sale2000.sas7bndx。

索引的内容可以通过以下方法查询:

PROC CONTENTS
PROC DATASETS
SAS浏览器

维护索引
以下列出不同数据管理任务对索引的影响

数据任务 -> 索引操作
使用COPY过程/DATASETS过程/SAS浏览器拖放操作复制数据 -> 为新数据文件建立索引文件
使用PROC COPY的MOVE选项移动数据 -> IN=逻辑库中的索引被删除,OUT=逻辑库中的索引被重建。
数据集重命名 -> 索引文件重命名
关键变量重命名 -> 索引文件中的相应变量也重命名
增加/删除/更新观测 -> 添加/删除/更新索引中的 值/数据位置 记录。
删除数据集/使用数据步重建数据集/使用FORCE选项对数据集自身排序 -> 删除索引文件。

另外,某些过程用选项控制是否复制索引。如UPLOAD和DOWNLOAD过程,默认为复制索引,通过INDEX=NO选项来禁止复制索引。

当以下情况满足时,SAS不会使用索引:

在数据步中使用IF语句取子集。
WHERE表达式只有部分包含关键变量。
SAS检测出顺序读取数据更有效率。
索引使用案例:(使用options msglevel = i;观察索引使用情况)

案例1:不使用索引。
options msglevel = i;
data rdu2000;
  set ia.sale2000;
  if Origin = 'RDU';
run;

案例2:不使用索引。
proc print data = ia.sale2000;
  where Origin = 'RDU' or Date = '01dec2000'd;
run;

案例3:不使用索引。
proc print data = ia.sale2000;
  where Origin ne 'RDU';
run;

案例4:使用索引。
proc print data = ia.sale2000;
  where Origin = 'ATH';
run;

案例5:使用索引。
proc print data = ia.sale2000;
  where FlightID = 'IA07400';
run;

对于玩数据库的人来说,这些都是常识了吧。
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
板凳
 楼主| 发表于 2004-4-2 13:33:25 | 只看该作者

3

Chapter 3: 纵合并
3.1 追加原始文件

原始数据可以使用以下的方法进行纵合并。


INFILE语句

FILENAME语句

FILEVAR=选项

操作系统自身的技术

首先,你可能要察看原始数据。可以用FSLIST过程。
语法:
PROC FSLIST FILE = file-specification;
RUN;

实际使用中,专门的编辑器,如UltraEdit之类的小软件,用来察看原始文件有着更多的便利。

INFILE
为了读取多个原始文件,可以使用多个INFILE语句。这样数据步会从第一个文件中读出一条记录,再到第二个文件读一条记录,以此类推,读完所有文件再返回第一个文件。如下图所示

[img:28dbf]http://sasor.feoh.net/_usrdata/images/article/sasor_column6b.gif[/img:28dbf]

FILENAME
你也可以使用FILENAME语句,对原始文件进行串联。如下图所示:

[img:28dbf]http://sasor.feoh.net/_usrdata/images/article/sasor_column6c.gif[/img:28dbf]

FILENAME语法如下:
FILENAME fileref ('external-file1'
'external-file2' … 'external-filen');

其中fileref是任何8位或以下的有效SAS名称。
'external-file' 是数据文件的物理名。

例如:
filename Q1 ('Month1.dat' 'Month2.dat' 'Month3.dat');
data firstq;
   infile Q1;
   input Flight $ Origin $ Dest $
      Date : date9.
      RevCargo : comma15.2;
run;

FILEVAR=
使INFILE语句的FILEVAR=选项,它可以帮助你有条件的连接多个原始文件,如下图所示:
[img:28dbf]http://sasor.feoh.net/_usrdata/images/article/sasor_column6d.gif[/img:28dbf]

语法:
infile xxx filevar = NextFile;

xxx 是任意的8位或以下的占位名,不是一个实际的文件名或者定义好的文件引用。SAS使用这个占位名在日志中报告处理信息。
NextFile 存放原始数据文件名的变量。

例如:
data movingq;
   do I = 11,10,9;
   NextFile = "Month"!!put(I,2.)!!".dat";
   NextFile = compress(NextFile,' ');
   infile xxx filevar = NextFile;
   input Flight $ Origin $ Dest $ Date : date9.
      RevCargo : comma15.2;
   output;
   end;
   stop;
run;

这个例程把Month11.dat, Month10.dat, Month9.dat三个原始文件装入到movingq数据集中,从中我们可以学到几点:
1. 需要使用STOP语句跳出数据步循环。
2. COMPRESS函数删除了字符串中的空格,使"Month 9.dat"变成"Month9.dat"。
3. 以上程序只在每个原始文件中读出头一条记录。

使用END=选项,我们可以控制数据步读完整个原始文件。
语法:
INFILE file-specification END = variable;

END=选项把是否到达输入文件结尾标志放进variable变量中,如果当前的输入没有到达文件尾,变量值为0;反之变量值为1
该变量不会被写入到输出SAS数据集中。

由此,完整的程序为:
data movingq;
   do I = 11,10,9;
      NextFile = "Month"!!put(I,2.)!!".dat";
      NextFile = compress(NextFile,' ');
      do until (LastObs);
      infile xxx filevar = NextFile end = LastObs;
      input Flight $ Origin $ Dest $ Date : date9.
         RevCargo : comma15.2;
      output;
   end;
end;
stop;
run;

项目中经常用到如下技巧:
data movingq;
   MonNum = month(today());
   MidMon = month(intnx(‘month’,today(),-1));
   LastMon = month(intnx(‘month’,today(),-2));
   do I = MonNum, MidMon, LastMon;
      NextFile = "Month"!!put(i,2.)!!".dat";
      NextFile = compress(NextFile,' ');
      do until (LastObs);
         infile xxx filevar = NextFile end = LastObs;
         input Flight $ Origin $ Dest $ Date : date9.
            RevCargo : comma15.2;
         output;
      end;
   end;
   stop;
run;

以上程序读出当月和之前两个月的原始数据。这里的重点是INTNX函数,如果我们直接用当前月-1,-2来得到前两个月,在1,2月时程序就会发生错误。INTNX作用是返回间隔某个时间端的时间。函数格式为:

INTNX('interval',start-from,increment)

'interval' 一个字符常量,或者是存放时间间隔单位的变量。表示间隔单位。
start-from 表示日期,日期时间或者时间的SAS表达式,作为起始点。
increment 一个正或者负整数,表示时间间隔的数量。
函数细节可以查询SAS在线帮助。

题外话,与考试无关。项目中用的更多的是读出一个路径下的同一类接口文件,简便方法是使用如下FILENAME。
FILENAME cf_list PIPE 'dir c:|rawdata|cf????.dat /b';
DATA cf_data;
   INFILE cf_list;
   LENGTH filename $20;
   INPUT filename $;
   INFILE in FILEVAR = filename END = LastFile;
   DO WHILE (LastFile = 0);
      INPUT ....
         ....
      ;
      OUTPUT;
   END;
RUN;
这段程序把c:|rawdata下面的所有cf0101.dat,cf0102.dat,...,cf????.dat读入cf_data数据集中。如果看不明白,说明以上的内容你还没有全部理解 <!-- s:-) --><img src="{SMILIES_PATH}/icon_smile.gif" alt=":-)" title="Smile" /><!-- s:-) -->
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
地板
 楼主| 发表于 2004-4-9 13:15:00 | 只看该作者

4

3.2 追加SAS数据集

以下几种方法都可以实现SAS数据的纵合并

PROC APPEND

PROC SQL INSERT INTO 语句

PROC SQL OUTER UNION CORRESPONDING 操作

数据步SET语句

本节只讨论PROC APPEND。

语法:
PROC APPEND BASE = SAS-data-set
   DATA = SAS-data-set
   ;

PROC APPEND作用是把DATA表中的数据追加到BASE表末尾。它只对DATA表进行读操作。
如果使用FORCE选项,当两个数据集的变量不一致时也会进行合并。否则PROC APPEND会显示错误信息然后退出。“不一致”指的是DATA表:

变量在BASE表中不存在

变量在BASE表中类型不一致

变量在BASE表中长度不一致

以下是当BASE表和DATA表不同时,使用FORCE可能发生的情况汇总。
[img:8f622]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sasor_column6e&#46;gif[/img:8f622]

例程:
请注意观察日志中的输出.

1) 变量长度,Label不同
proc contents data = airports;
run;
proc contents data = acities;
run;
proc append data = airports base = acities force;
run;
proc contents data = acities;
run;

2) 变量类型不同
proc contents data = allemps;
run;
proc contents data = pilots;
run;
proc append base = allemps data = pilots force;
run;
proc print data = allemps;
var phone;
run;
proc contents data = allemps;
run;
回复 支持 反对

使用道具 举报

49

主题

76

帖子

1462

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
1462
5#
 楼主| 发表于 2004-4-29 18:11:38 | 只看该作者

5

[b:10a12]Charpter 4: 横合并数据[/b:10a12]

[i:10a12]4.1在没有公共变量的情况下合并数据[/i:10a12]

这一节我们学习如何在没有公共变量的情况下合并数据。我们有3个表:
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f&#46;gif[/img:10a12]

需要把他们连接成一张新表, 我们可以使用Merge语句,或者是SQL来完成。

[u:10a12]Merge语句[/u:10a12]
[u:10a12]SQL过程[/u:10a12]

这里牵涉到不同实现方法的特点。数据步Merge的优点在于:
可以返回多值。
对表的大小没有限制。
可以通过多个by变量,对多个变量进行关联。
可以方便的使用数据步处理过程,例如数组,DO循环等实现复杂的业务逻辑。

缺点是:
数据集必须基于BY变量排序,或者建立索引
关键变量必须精确匹配。不象SQL Join可以用 int(a.var1) = b.var1这样的关系来连接。
BY变量必须在被合并的所有数据集中出现。

SQL Join的优点在于:
可以一次连接多个数据集,不需要一个在所有数据集中出现的公共变量。
数据不需要排序或者索引(会损失效率)。
可以建立表,视图或者查询报告。
PROC SQL遵守ANSI标准SQL定义,你可以利用过去的SQL知识。

缺点是:
一次连接的表不能超过32个。
一般而言,PROC SQL比数据步Merge需要更多的资源。
复杂的业务逻辑实现起来比较困难。

[b:10a12]要特别注意的是,两种方法得到的结果不一定相同。[/b:10a12]
只有一对一连接,或者是一对多连接时时,两种方法得到的结果才一样。
当多对多时,两者的连接情况如下:
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_1&#46;gif[/img:10a12]

当存在无法匹配的纪录时,两者的连接情况如下:
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_2&#46;gif[/img:10a12]

这些表现与两者的处理机制密切相关。当数据步遇到Merge时,会先建立目标表的PDV,然后根据BY变量的排序,在被合并的两表中寻找匹配的纪录。把匹配记录读入PDV后,两表的游标都会下移一行。如果无法匹配,会位相应变量产生一个缺失值,写入PDV中。如下图所示。
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_3&#46;gif[/img:10a12]

[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_4&#46;gif[/img:10a12]

[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_5&#46;gif[/img:10a12]

[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_6&#46;gif[/img:10a12]

最后得到合并结果如下:
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_7&#46;gif[/img:10a12]

PROC SQL进行SQL JOIN时,先是进行所有可能组合的连接,
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_8&#46;gif[/img:10a12]

然后再根据where条件,去除不符合条件的连接,
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_9&#46;gif[/img:10a12]

最后得到如下结果。
[img:10a12]http&#58;//sasor&#46;feoh&#46;net/_usrdata/images/article/sas_column6f_a&#46;gif[/img:10a12]
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|手机版|Archiver|SAS中文论坛  

GMT+8, 2025-5-3 12:00 , Processed in 0.089345 second(s), 31 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表