分享

SAS学习笔记 2

 ztykl1ophhaqvf 2020-06-15

3.5 SAS常用函数

SAS函数是编程语言的一个组件,可接受参数、执行计算或进行其他操作并返回值。返回值是字符型或数值型的结果,可用于赋值语句或表达式中。CALL例程转变变量值或执行其他系统函数。CALL例程和函数相似,不同的是CALL例程不能用于赋值语句或表达式。所有的CALL例程都使用CALL语句调用。关于CALL例程需要参考帮助文档,后面再补吧。。

3.5.1 函数语法

SAS函数形式如下:

函数名 (参数1 <, ...参数n>) 函数名 (OF 变量列表) 函数名 (参数 | OF变量列表 | OF数组名{*} <...,参数 | OF变量列表 | OF数组名{*} >)

其中: ● 函数名用于给出函数名称。 ● 参数可以是变量名、常量或任何SAS表达式。多个参数之间使用逗号(,)分隔。 ● 变量列表可以是任何形式的变量列表。多个列表之间使用空格分隔。例如sum(of x y z)、sum(of x1-x10)、sum(x, of x1-x5 y1-y5、sum(x, of x1-x5, of y1-y5)。最后两种表示方式具有同等效果。 ● 数组名{*}指在当前DATA步中已经定义的数组。

3.5.2 数值函数

数值函数使用说明

3.5.3 字符操作函数

字符操作函数表
字符操作函数表续

示例,将数据集saslib.contact2中的变量Name中的姓和名分开为Last_Name和First_Name。

原数据集saslib.contact2

data work.contact2; set saslib.contact2; split=index(Name,' '); /* 这里将Name变量中空格第一次出现的位置找到,并将位置值储存在split变量中 */ First_Name=substr(Name,1,split-1);/* First_Name的获取方式为对于Name变量中进行字符串的截取,截取起始位置为1,结束位置为split-1,即空格出现的前一位,以此可以正确获得First_Name */ Last_Name=substr(Name,split+1);/* Last_Name的获取方式为对于Name变量中进行字符串的截取,截取起始位置为split+1,即空格出现的后一位,结束位置为默认,则会一直截取至字符串最末尾,以此可以正确获得Last_Name */ drop split;run;proc print data=work.contact2 noobs;run;

输出数据集中姓和名分别储存在两个变量中

示例,将3个变量组合成1个变量。原始数据如下。

shop.dat为原始数据文件

可使用级联操作符||组合完整地址,应在Street、City、State之间使用标点符号,因为多个空格字符在HTML中会显示为一个空格。将PRINT过程的打印结果输出到PDF文件中,输出PDF文件使用了SAS的输出交付系统ODS(Output Delivery System)。

filename extfiles 'c:/sas/data/';data saslib.shop; infile extfiles(shop) dsd truncover; length Shop $40 Street $30 City $20; input Shop $ Telephone Street $ City $ State $ Zip;run;data work.shop_fulladdr; set saslib.shop(drop=telephone zip); Full_Address=Street || ',' || City || ',' || State; drop Street City State;run;ods pdf file='c:\sas\data\output\full_address.pdf';proc print data=work.shop_fulladdr noobs;run;ods pdf close;

输出结果为店铺与对应的完整地址
生成的PDF文件内容

PDF文件中,Full_Address列的地址中包含很多空格。这是因为原数据集中的变量Street和City数据值的长度小于变量的长度,这时SAS会在数据值后补充空格以达到给定变量长度。试验中发现,生成PDF文件的地址需要提前创建好,若不存在目标文件夹将不能成功输出PDF文件。

部分log:

88 ods pdf file='c:\sas\data\output\full_address.pdf';NOTE: Writing ODS PDF output to DISK destination 'c:\sas\data\output\full_address.pdf', printer 'PDF'.

91 ods pdf close;ERROR: Physical file does not exist, c:\sas\data\output\full_address.pdf.

示例,将各字符变量尾缀空格删除,再一次将其进行级联产生完整的地址信息。

此处使用TRIM函数去掉各变量尾缀空格。

data work.shop_fulladdr; set saslib.shop(drop=telephone zip); Full_Address=trim(Street) || ',' || trim(City) || ',' || trim(State); drop Street City State;run;ods pdf file='c:\sas\data\output\full_address.pdf';proc print data=work.shop_fulladdr noobs;run;ods pdf close;

去掉空格后的完整地址结果输出

示例,直接使用CATX函数删除各字符变量值的尾缀空格,并将其进行级联产生完整的地址信息。

data work.shop_fulladdr; set saslib.shop(drop=telephone zip); Full_Address2=CATX(',',Street,City,State); drop Street City State;run;ods pdf file='c:\sas\data\output\full_address.pdf';proc print data=work.shop_fulladdr noobs;run; ods pdf close;

SAS中结果输出
PDF文件的输出结果与前例中相同

应用CATX函数与应用“||”与TRIM函数的结合效果相同,生成同样的结果。CATX函数也可以删除字符串的前导空格,本例中字符串不含前导空格。

3.5.4 数值与字符转换函数

当代码中给出的值与所需要的类型不匹配时,SAS会试图自动将该值转换成所期望的类型,但是,自动转换有时候会出错或产生意外的结果。SAS提供了PUT和INPUT函数进行显式类型转换。这两个函数很有用,即使有些情况自动转换能够处理,使用显式类型转换也会更有效率。

PUT函数

PUT函数使用指定的格式返回值,可用于将数字值转换成字符值。基本形式如下:

PUT (源,格式)

其中,源为要进行重新格式化的常量、变量或表达式,可以使字符型或数值型。格式为要应用在源上的SAS格式。PUT函数可用于将数字根据格式转换为字符或将字符值转换为其他字符。默认情况下,如果源是数值型,则结果字符串会向右对齐;如果源是字符型,则结果字符串会向左对齐。也可以在格式中添加对齐标识-L、-C、-R分别表示左对齐、居中或右对齐,改变默认对齐方式。格式必须与源的类型一致。也就是说,如果源是字符,格式名必须以$符号开始;如果源是数字,格式则不能以$开始。PUT函数不影响数据集中的变量格式或属性。

示例,原员工信息中的ID为数值型,先需要将该数据集中的员工ID转换为字符型。

data saslib.employee2; set saslib.employee2; New_Emp_ID=put(Emp_ID, best10.);/* 使用PUT函数对Emp_ID的数值进行转换,并创建新的字符型变量New_Emp_ID */ drop Emp_ID;/* 将原始变量Emp_ID删除 */ rename New_Emp_ID=Emp_ID;/* 将New_Emp_ID该名为Emp_ID,从而实现了Emp_ID从数值型转换为字符型 */run;

INPUT函数

INPUT函数返回当SAS使用指定输入格式转换SAS值之后的结果,基本形式如下:

INPUT(源,输入格式)

其中,源为要应用输入格式的字符常量、字符变量或字符表达式。格式为要应用在源上的SAS输入格式。

INPUT函数会将源的值使用指定的输入格式进行转换。INPUT函数可用于将字符值转换为数字值或其他字符值。输入格式指定了结果是数值型还是字符型。INPUT函数也不影响数据集中的变量输入格式或属性。

示例,将saslib.sales数据集中日期以字符型日期格式输出。

data work.sales; set saslib.sales; Num_Date=input(Date,best12.); format Num_Date date9.; drop Date; rename Num_Date=Date;run;proc print data=work.sales noobs;run;proc contents data=work.sales;run;

本例中为了试验input的效果,新建一个变量Num_Date,因为Date本身为数值型,所以使用format语句将其格式转为字符日期输出。

日期以字符形式输出
Date本身还是以数值型来储存的

3.5.5 与日期时间相关的函数

SAS提供日期(date)、时间(time)和日期时间(datetime)函数从日期、时间和日期时间值中得到年份、月份、日、小时、分钟、秒等信息,它们也可以将这些信息组成SAS的日期、时间和日期时间值。

日期时间函数表

3.6 将数据集写出到外部文件

除了可以在SAS环境中对SAS数据集进行加工处理外,SAS数据集的数据还可以导出到外部文件中,以便在未安装SAS软件的环境中使用,SAS提供了EXPORT过程来实现此功能。EXPORT过程可以将SAS数据集按照指定格式写入外部文本文件。

EXPORT过程基本形式如下。

PROC EXPORT DATA=数据集 OUTFILE=文件名 | 文件引用 | OUTTABLE=表名 DBMS=数据源标识符 ;RUN;

其中:● DATA=指定要导出的SAS数据集。其后可添加数据集选项。● OUTFILE=指定要将SAS数据集中的数据写入的文件,可以是物理路径或文件引用;OUTTABLE=指定将SAS数据集的内容导入Microsoft Access数据库时的表名。● DBMS指定所导出的数据类型。SAS支持多种数据类型。

在EXPORT过程中还可以指定参数REPLACE,表示当该OUTFILE=指定的文件存在时覆盖该文件,如果不指定REPLACE,则EXPORT过程不会覆盖已经存在的文件。

导出到带分隔符的文件

SAS使用EXPORT过程将数据集中的数据导出到带分隔符的文件(包括DBMS为DLM、CSV和TAB)中。

EXPORT过程常用参数

示例:

proc export data=saslib.emplyee outfile='c:\sas\data\employee.dat' dbms=dlm replace; delimiter=','; putnames=no;run;

导出到CSV文件

proc export data=saslib.employee outfile='c:\sas\data\employee.dat' dbms=csv;run;

DBMS为CSV会时会默认使用逗号分隔所生成文件中的各列。

还可以将上面代码中的DBMS=指定为“TAB”,这时会生成各个列之间由制表符分隔的文件。

使用数据集选项导出部分数据

在PROC EXPORT中还可以使用数据集选项导出部分变量或部分观测。

示例,使用KEEP=选项指定要导出的变量。

proc export data=saslib.emplyee (keep=Name Dept Entry_Date) outfile='c:\sas\data\employee.csv' dbms=csv replace;run;

示例,使用WHERE选项选定符合条件的列导出到CSV文件中。

proc export data=saslib.employee (where=(Dept='DSG')) outfile='c:\sas\data\dsg.csv' dbms=csv replace;run;

导出到Microsoft Excel文件

proc export data=saslib.employee outfile='c:\sas\data\employee.xlsx' dbms=xlsx;run;

第四章 对多个数据集的处理

4.1 数据集的纵向串接

数据集的纵向串接指的是,将两个或多个数据集首尾相连,形成一个新的数据集。

对数据集的纵向串接可以通过以下两种方式实现:

● 使用SAS DATA步的SET语句。● 使用SAS PROC步的APPEND语句。

4.1.1 使用SET语句实现纵向串接

基本形式

DATA 新数据集; SET 数据集1 数据集2 <数据集3 数据集4 ...>;RUN;

其中:

● SET语句中的数据集1、数据集2都为输入数据集。串接后的数据存储在DATA语句的新数据集中。● SET语句可以同时读入多个数据集,新数据集将包含各输入数据集中的所有变量。

对数据集work.new_employee和work.old_employee进行串接。

data work.new_employee; input Emp_ID $ Emp_Name $ @@; datalines;ET001 Jimmy ED003 Emy EC002 Alfred EQ004 Kim;run;data work.old_employee; input Emp_ID $ Emp_Name $ @@; datalines;EQ122 Molly ET121 Dillion ET123 Helen ED124 John;run;data work.employee; set work.old_employee work.new_employee;run;proc print data=work.old_employee; title 'Old Employee';run;proc print data=work.new_employee; title 'New Employee';run;proc print data=work.employee; title 'All Employee';run;

老员工信息输出
新员工信息输出
串接后全部员工的信息输出

两个数据集含有相同的变量Emp_ID和Emp_Name。从输出内容中可以观察到新数据集中包含了2个变量和8条观测,work.old_employee的观测直接加在了work.new_employee的后面,名称相同的字段放在同一变量下。

示例,若两个数据集中含有变量不全一致,将两个数据集进行串接时的情况。

data work.new_employee_dept; set work.new_employee; if substr(Emp_ID,2,1)='T' then Dept='TSG'; else if substr(Emp_ID,2,1)='D' then Dept='DSG'; else if substr(Emp_ID,2,1)='Q' then Dept='QSG'; else if substr(Emp_ID,2,1)='C' then Dept='CSG';run;data work.old_employee_gen; set work.old_employee; if mod(substr(Emp_ID,length((trim(Emp_ID))),1),2)=0 then Gender='F'; else Gender='M';run;data work.employee_update; set work.old_employee_gen work.new_employee_dept;run;proc print data=work.employee_update; title 'All Employee Update';run;

在代码中为work.new_employee和work.old_employee中分别用IF语句创建了新变量Dept和Gender并建立新数据集,然后再对于新数据集work.new_employee_dept和work.old_employee_gen进行串接。

含有不同变量的数据集合并后的结果

合并后的新数据集work.employee_update和work.employee同样有8条观测,但work.employee_update中有4个变量Emp_ID、Emp_Name、Gender和Dept。由于work.old_employee_gen不含Dept而work.new_employee_dept不含Gender,串接后前四条观测Dept为缺失值而后四条观测Gender为缺失值。

注意:开始读入每一个数据集第一条观测前,SAS将对PDV进行初始化,将其中所有变量重置为缺失值。另外在读入每一个数据集的循环过程中,由SAS数据集中读入的变量值将被保留在PDV中,直到下一条观测被复制进PDV,它才会被新的变量值覆盖。而对于DATA步中新创建的变量,在DATA步的每一个循环前,系统默认将PDV中的值置为缺失。如果要使这些变量保留在PDV中,得使用RETAIN语句。

使用RETAIN语句的基本形式如下:

RETAIN 变量1 < 变量2 变量3 ··· <初始值>> <变量4 变量5 ···>;

RETAIN语句的初始值在DATA步的编译阶段进行赋值,在DATA步执行阶段的循环过程中,RETAIN语句中变量的值可以被保留在PDV中,并进入下一个循环。如果不指定初始值,系统默认初始值为缺失值。

示例:

retain month1-month5 1 year 0 a b c 'XYZ';

使用BY语句进行穿插串接

穿插串接就是使得新数据集的观测按照一定顺序排列的串接方法。基本形式如下:

DATA 新数据集; SET 数据集1 数据集2 <数据集3 数据集4 ···>; BY 变量1 <变量2 变量3 变量4 ···>;RUN;

BY语句在处理多个数据集时经常使用,需要注意BY变量、BY变量组、BY变量值和BY组合的概念。

● BY语句中的变量称为BY变量,一个BY语句中可以有多个BY变量。当BY语句中有多个变量时,称这些变量为BY变量组。● BY变量值指的是BY变量的取值。● BY组合指的是具有相同BY变量值的观测的集合。当有多个BY变量时,BY组合就是使得所有BY变量的取值完全相同的观测的集合。

在使用BY语句时,所有输入数据集都必须是按BY变量排过序,或者有基于BY变量建立的索引。串接完毕后,新数据集中的观测也将按照BY变量排序。

示例,对work.old_employee_gen和work.new_employee_dept进行穿插串接。

由于原数据集中都没有按Emp_ID排序,在对其进行穿插串接时,首先必须对其按Emp_ID排序。可以使用SORT过程对数据集进行排序。

proc sort data=work.new_employee_dept; by Emp_ID;run;proc sort data=work.old_employee_gen; by Emp_ID;run;data work.employee_int; set work.old_employee_gen work.new_employee_dept; by Emp_ID;run;proc print data=work.employee_int; title 'All Employee Interleaving by ID';run;

按Emp_ID排序的穿插排序结果

使用LENGTH语句

当输入数据集中同名变量的长度不一样时,新数据集中该变量的长度等于SET语句中第一个数据集中对应变量的长度。在对于多个数据集进行串接时,最好先检查字符型变量的长度,避免在读取变量值时造成截断。如有需要,可以在使用SET语句之前使用LENGTH语句来定义变量的长度。基本形式如下:

LENGTH 变量1 <$> 长度 <变量2 <$> 长度 ···>;

定义字符型变量长度时,需要在长度前加上$符号。

示例,使用LENGTH语句将数据集work.employee中Emp_ID的长度为15。

data work.employee; length Emp_ID $15; set work.new_employee work.old_employee; by Emp_ID;run;

使用选项RENAME=

输入数据集中可能出现相同含义的变量不具有相同的变量名,在进行数据拼接时若不做另外处理,这两个变量将会被SAS认为是两个不同的字段,并分别存储在两个不同变量下。这时可以使用RENAME=选项将两个数据集中的变量名改为一致的名称,避免前面所描述问题出现。

使用RENAME=选项的基本形式如下:

数据集(RENAME= (原变量名1 = 新变量名1 <原变量名 = 新变量名 ···>));

4.1.2 使用APPEND过程实现纵向串接

使用APPEND过程进行数据集串接的基本形式如下:

PROC APPEND BASE=主数据集 <DATA=追加数据集> <FORCE>;

其中:● 主数据集表示需要增加观测的数据集。该主数据集可以是已经存在的数据集,也可以是不存在的数据集。当主数据集不存在时,在执行完APPEND过程后,将生成主数据集,并将追加数据集的观测复制到主数据集中。● 追加数据集中包含了需要被添加到主数据集中的观测。这个语句可以是默认的,此时,SAS会将当前数据集的观测添加到主数据集的后面。当前数据集指的是SAS系统最近一次生成的数据集。通常情况下,为了保证程序的可读性,不建议默认。● FORCE选项会强制将追加数据集中的观测添加到主数据集中。

使用APPEND过程进行串接时,SAS不会处理主数据集中的观测,而是直接将追加数据集的观测添加到主数据集最后一条观测的后面,且变量仅包含主数据集中的变量。

示例,运用APPEND过程将work.new_employee_dept和work.old_employee_gen两个数据集进行纵向串接,将串接后的数据集保存在work.old_employee_gen中。

data work.old_employee_gen_append; set work.old_employee_gen;run;proc append base=work.old_employee_gen_append data=work.new_employee_dept FORCE;run;proc print data=work.old_employee_gen_append;run;

使用append过程加上force选项的输出结果

串接后的结果中仅包含原有变量,观测数为两者观测之和。这时使用FORCE选项,如果去掉FORCE选项,串接将失败,这是因为追加数据集中含有主数据集中没有的变量。

使用APPEND过程时特别需要注意FORCE选项的使用:● 第一种情况,如果追加的数据集中存在不包含在主数据集中的变量,使用FORCE选项将会使得串接成功,但仅存在于追加数据集中的变量不会被添加到主数据集中。● 第二种情况,如果同名变量在主数据集和追加数据集中的类型不一样,那么需要使用FORCE选项,但是追加仅主数据集的观测对应的这个变量的值为缺失。● 第三种情况,如果同名变量在追加数据集中的长度大于主数据集中的长度,那么需要使用FORCE选项,在执行过程中,追加数据集中的变量值可能会被截断。

当主数据集中包含追加数据集中不含有的变量时,串接成功,同时系统会在日志中生成警告,并且追加数据集中不含有的变量之值都为缺失。另外,当两个数据集中同名变量的属性不一致时,将沿用主数据集中变量的属性。

4.1.3 SET语句与APPEND过程的比较

对两个数据集进行纵向串接时,如果两个数据集中的变量名称和属性都相同,使用SET语句和APPEND过程,可以得到完全一样的结果。但是使用APPEND过程的效率比使用SET语句高,尤其是当主数据集的观测量很大时,这是因为APPEND过程不对主数据集的观测进行操作,而是直接把追加数据集的观测加到主数据集的后面。

两者在常见情形下的比较

4.2 数据集的横向合并

数据集的横向合并,指的是将两个或多个数据集根据某种原则横向合并起来,形成新的数据集。SAS提供了MERGE语句来实现两个或多个数据集的横向合并。数据的横向合并主要分为以下两种情况。● 不使用BY语句合并,也称为一对一合并。● 使用BY语句,也称为匹配合并

4.2.1 不使用BY语句实现横向合并

不使用BY语句进行数据集横向合并的基本形式如下。

DATA 新数据集; MERGE 数据集1 数据集2 <数据集3 数据集4 ···>;RUN;

不使用BY语句进行数据集横向合并时,对输入数据集的排序没有要求。

示例,将包含员工基本信息的数据集和包含考核时间及地点的数据集合并,给每个员工分配考核时间与地点。

data work.staff; infile datalines dsd; length emp_name $20 title $15; input emp_name $ title $ ; datalines;Jacob Adams,AnalystEmily Anderson,AnlystMichael Arnold,Senior AnalystHannah Baker,ManagerJoshua Carter,Senior Analyst;run;data work.time; input time date9. room $11-24; format time date9.; datalines;01DEC2013 Meeting Room 102DEC2013 Meeting Room 203DEC2013 Meeting Room 103DEC2013 Meeting Room 204DEC2013 Meeting Room 304DEC2013 Meeting Room 1;run;data work.schedule; merge staff time;run;proc print data=work.schedule; title 'Schedule for Performance Management';run;

一对一合并的结果

PDV中对于SAS执行MERGE语句的步骤如下:

● DATA步编译阶段,SAS按照MERGE语句中数据集的排列顺序,依次读入各数据集中变量的描述部分,并将各变量置入PDV。SAS将PDV中的所有变量值都置为缺失值。● Data步执行阶段,SAS按照MERGE语句中数据集的排列顺序,从第一个数据集staff中读入第一次观测,并复制到PDV中。● SAS从第二个数据集time中读入第一条观测,并复制到PDV中。如果数据集中有同名的变量,后读入的值将覆盖先读入的值。● SAS执行DATA步中的其他语句,然后将PDV中的值写入新数据集schedule中。● SAS依次从各个数据集中读入第二条观测,并复制到PDV中。然后SAS执行DATA步中的其他语句,最后将PDV中的值写入新数据集中。● SAS依次处理各个数据集中的各条观测,直到处理完某一个数据集中的所有观测。在这里,SAS处理完第一个数据集work.staff中的第五条观测后,PDV中来自该数据集中的变量都被置为缺失。● SAS读入第二个数据集work.time的最后一条观测,并复制到PDV中。● SAS执行DATA步中的其他语句,然后将PDV中的值写入新数据库集中。此时,SAS已经处理完所有数据集中的所有观测。

SAS处理一对一合并的原则:● 新数据集的第一条观测包含各个输入数据集中第一条观测的信息,第二条观测包含各个输入数据集中第二条观测的信息,不足的观测用缺失值补足。● 新数据集含有的观测数为所有输入数据集的最大观测数。

SAS为防止开发人员写代码时因为失误未加BY语句把匹配合并写成了一对一合并,而提供系统选项MERGENOBY=:● MERGENOBY=NOWARN,这是系统的默认选项,在执行没有BY语句的MERGE语句时,系统进行一对一合并。● 当MERGENOBY=WARN时,系统给出警告,“WARNING:没有为MERGE语句指定BY语句。”● 当MERGENOBY=ERROR时,系统报错,“ERROR:没有为MERGE语句指定BY语句。”

4.2.2 使用BY语句实现横向合并

使用BY语句进行数据集横向合并的基本形式如下:

DATA 新数据集; MERGE 数据集1 数据集2 <数据集3 数据集4 ···>; BY 变量名1 <变量名2 变量名3 ···>;RUN;

和之前介绍SET语句时提及的一样,当使用BY语句时,输入数据集必须按BY变量排序。

SAS在处理匹配合并的DATA步程序时,主要分以下两个阶段:● 编译阶段,SAS在辨识出MERGE语句后,将按照其中数据集的排列顺序,依次读入所有数据集中变量的描述部分,包括DATA步中新创建变量的描述部分,包括DATA步中新创建变量的描述部分,并将所有变量置于PDV中。若不同的数据集中有同名的变量,则要求他们的数据类型必须相同,长度以第一次出现的变量为准。● 执行阶段,各输入数据集中的观测按BY变量进行匹配,在DATA步的每次循环中依次读入BY组合的每条观测。如果遇到同名的变量,后读入的变量值将覆盖先读入的变量值。由数据集中读入的变量值,会自动地保留到BY变量值在所有输入数据集中都改变为止。由DATA步新创建的变量,其变量值在每次循环中都不能再PDV中自动保留,也就是说,在处理下一条数据之前它将被置为缺失值。

BY变量值在所有数据集中唯一

BY变量值在所有数据集中都是唯一的,即在任一输入数据集中,没有两条或两条以上的观测具有相同的BY变量值。

示例,由于这本书里坑爹的作者经常用一些非SAS自带的Library,这里只好用之前用的自建的数据集做例子代替下。将saslib.contact2数据集与saslib.customer2数据集进行合并,姓名是两个数据集的共同变量,先按Name排序再将两个数据集合并。

saslib.contact2数据集
saslib.customer2数据集

proc sort data=saslib.contact2 out=work.contact; by Name;run;proc sort data=saslib.customer2 out=work.customer; by Name;run;data work.staff_info; merge work.contact work.customer; by Name;run;proc print data=work.staff_info; title 'Staff Infomation';run;

合并后的Staff Information数据集

当BY变量值在所有数据集中都唯一时,各个数据集的每个BY组合中都只有一条观测。PDV中SAS的处理步骤:● 在DATA步的编译阶段,SAS按照MERGE语句中数据集的排列顺序,依次读入各数据集变量的描述部分,并将各变量置入PDV。SAS将PDV中所有变量值置为缺失值。● SAS根据各数据集BY变量值的第一个值确定排在第一的BY组合。本例中是Adam Smith。因为saslib.contact2中第一个BY组合的BY变量为Adam Smith,saslib.customer2中第一个BY组合的BY变量为Emily Cooker,则SAS确定的第一的BY组合为Adam Smith的BY组合。● SAS根据MERGE语句中数据集的排列顺序,从第一个数据集saslib.contact2中读取第一个BY组合中的观测,并复制到PDV中。● SAS从第二个数据集saslib.customer2中读取相应BY组合中的观测,并复制到PDV中。如果某一个数据集中没有任何观测属于该BY组合,则仅包含于该数据集的变量在PDV中为缺失值。● SAS执行DATA步中的其他语句,本例中没有其他语句,然后将PDV中的值写入新的数据集中。这时所有数据集中都不再有观测属于第一个BY组合,SAS将PDV中所有变量值都重置为缺失。● SAS从各数据集中确定排在第二的BY组合。两个数据集中BY组合的BY变量值都为Emily Cooker。SAS重复以上的第三至五步,处理第二个BY组合。处理完后PDV中的变量值都置为缺失。● SAS根据各数据集BY变量值的第一个值确定排在第一的BY组合。本例中是George Collin。因为saslib.contact2中第一个BY组合的BY变量为George Collin,saslib.customer2中第一个BY组合的BY变量为Greg William,则SAS确定的第一的BY组合为George Collin的BY组合。● saslib.contact2中没有观测属于第三个BY组合,于是仅在saslib.contact2中存在的变量在PDV中将为缺失值,SAS接着从saslib.customer2中读取第三个BY组合观测,并复制到PDV中。● SAS执行DATA步中的其他语句,然后将PDV中的值写入新数据集,并将PDV中的变量值重置为缺失。● SAS按照以上的逻辑执行,直到处理完所有数据集的所有观测。

BY变量值在某一数据集中存在重复

BY变量值在某一输入数据集中存在重复值,即在其中一个输入数据集中,含有两条或两条以上的观测具有相同的BY变量值,也称为一对多合并。在匹配过程中会遵循如下原则:由输入数据集读入的变量值,会保留在PDV中,直到被下一个读入的观测值覆盖或该BY组合处理完毕被重置为缺失值为止。

saslib.contact2_raw中Name变量含有重复值

proc sort data=saslib.contact2_raw out=work.contact; by Name;run;proc sort data=saslib.customer2 out=work.customer; by Name;run;data work.staff_info; merge work.contact work.customer; by Name;run;proc print data=work.staff_info; title 'Staff Infomation';run;

生成的一对多合并数据集

BY变量值在多个数据集中存在重复

虽然在匹配合并时,一般情况下BY变量值至多在某一个数据集中有重复,但并不代表匹配合并只能处理这一种情况,它同样可以处理两个或两个以上输入数据集中的BY变量值重复的情况,也就是实现多对多合并。SAS的匹配原则和一对多合并时一样,并且新数据集中每一个BY变量值重复的次数和输入数据集中重复次数最多的一样。

示例,两个简单数据集的多对多合并。

data work.data1; input x y; datalines;1 21 32 43 5;run;data work.data2; input x z; datalines;1 31 41 42 12 34 4;run;data work.combined; merge work.data1 work.data2; by x;run;proc print data=work.combined noobs; title 'After Merging';run;

多对多合并后的数据集

在输入数据集中x=1的观测有3条记录,x=2的观测有2条记录,输入数据集中x=1和x=2的观测也分别是3条和2条。

将data1数据集中的x=2观测增加两条后:

data work.data1; input x y; datalines;1 21 32 42 02 23 5;run;data work.data2; input x z; datalines;1 31 41 42 12 34 4;run;data work.combined; merge work.data1 work.data2; by x;run;proc print data=work.combined noobs; title 'After Merging';run;

data1更改后多对多合并后的数据集

在输入数据集中x=2的观测变为3条记录后,输入数据集中x=2的观测也变为3条。

4.2.3 使用数据集选项IN=操作观测

数据集选项IN=的基本形式如下:

数据集(IN=变量)

数据集选项IN=可以运用在SET、MERGE、MODIFY、UPDATE语句中的任何数据集后面。变量时数值型临时变量,不同的数据集应定义不同的临时变量名称,临时变量可以在DATA步中使用,但是不会在数据集中输出。在某一数据集后面使用(IN=变量)时,如果PDV中对应的变量值是来自于这一数据集的观测,临时变量将被赋值为1,否则临时变量的值为0。特别是,当和BY语句一起使用时,可以通过判断PDV中BY变量的值是否来自于这一数据集来确定临时变量的值。

示例,对于前面使用的示例进行匹配合并时使用数据集选项IN=。由于选项IN=指定的只是临时变量,为了在输出数据集中能看到变量的值,因此在程序中又将这些临时变量的值赋给了新变量INA和INB。

proc sort data=saslib.contact2 out=work.contact; by Name;run;proc sort data=saslib.customer2 out=work.customer; by Name;run;data work.staff_info; merge work.contact (IN=a) work.customer (IN=b); by Name; ina=a; inb=b;run;proc print data=work.staff_info; title 'Staff Infomation';run;

加入选项IN=后的输出数据集

如果想得到同时存在两个原始数据集中,并写入新数据集的观测,可以通过IF语句实现。

proc sort data=saslib.contact2 out=work.contact; by Name;run;proc sort data=saslib.customer2 out=work.customer; by Name;run;data work.staff_info; merge work.contact (IN=a) work.customer (IN=b); by Name; if a and b then output; /* 可以简写为if a and b; */run;proc print data=work.staff_info; title 'Staff Infomation';run;

同时存在于两个原始数据集的观测

4.3 数据集的更新

数据集的更新,指的是用一个数据集中的数据来替换另一个数据集中的数据。SAS提供了UPDATE语句来实现数据集的更新。简单示例语句如下:

DATA WORK.DATA1; UPDATE WORK.DATA1 WORK.DATA2; BY X;RUN;

在运用UPDATE语句进行数据集更新时,通常称DATA1为主数据集,DATA2为更新数据集。主数据集和更新数据集通过BY变量X联系起来,更新数据集中的变量值替换主数据集中的变量值。对于更新数据集中存在缺失值的情况,在UPDATE语句中可以运用选项UPDATEMODE=来控制是否用缺失值替换数据集中的变量值,系统默认UPDATEMODE=MISSINGCHECK,也就是不替换;如果设UPDATEMODE=NOMISSINGCHECK,则不管更新数据集中的变量值是否是缺失值,都将替换。

使用UPDATE语句进行数据集更新的基本形式如下:

DATA 新数据集; UPDATE 主数据集 更新数据集 <UPDATEMODE = MISSINGCHECK | NOMISSINGCHECK>; BY 变量1 <变量2 变量3 ···>;RUN;

更新后数据集的名称可以是新的数据集名称,不需要和主数据集同名。新数据集中包含主数据集和更新数据集中的所有变量。

主数据集和更新数据集都必须按照BY变量排序。通常要求BY变量值在数据集中必须是唯一的,且不需要进行更新,例如BY变量可以是员工号或交易号。运用UPDATE语句时,如果SAS发现主数据集中BY变量值有重复,SAS会在日志中输出警告,此时虽然可更新成功,但是所有的更新只会作用在数据集中BY组合的第一条观测上。

示例,将客户信息存储在work.customer中,更新信息存储在work.updateinfo中。

data work.customer; input customer_ID $1-4 Name $6-19 Address $21-37 City $39-51 State $53-54; datalines;C001 Jacob Adams 111 Clancey Court Chapel NCC002 Emily Anderson 1009 Cherry St. York PAC003 Michael Arnold 4 Shepherd St. Vancouver BCC004 Hannah Baker Box 108 Milagro NM;run;proc print data=work.customer noobs; title 'Customer - Master Data';run;data work.updateinfo; infile datalines missover; input customer_ID $1-4 Name $6-19 Address $21-37 City $39-51 State $53-54; datalines;C001 14 Bridge St. San Francisco CAC002 Emily Cooker 42 Rue Marston C002 52 Rue Marston Paris C005 Jimmy Cruze Box 100 Cary NC;run;proc print data=work.updateinfo noobs; title 'Update Information - Yearly Transaction Data';run;

原信息表数据集与更新信息表数据集

对于客户来说customer_ID唯一,并且work.customer和work.updateinfo已经按照customer_ID排过序,因此此时在更新前无需重新排序。接下来用UPDATE语句对数据集work.customer进行更新。

data work.customer_update; update work.customer work.updateinfo; by customer_ID;run;proc print data=work.customer_update noobs; title 'Customer - Master Data Update';run;

更新后信息输出数据集

由于主数据集中BY变量是唯一的,更新数据集中的BY变量值存在重复值,可有如下发现:

● 当更新数据集中BY变量值存在重复值时,BY组合中的后一条观测会覆盖前一条观测。例如C002行。● 当更新数据集中的某一条观测在主数据集没有对应的观测时,SAS会直接将这条观测作为更新的基础,然后结合更新数据集中剩余的观测继续处理该观测。例如C005行。

UPDATE语句和MERGE语句都可以对两个数据集进行匹配合并,但是存在比较大的区别,如下:

● UPDATE语句只能操作两个数据集;MERGE语句可以对两个或两个以上数据集进行操作。● 使用UPDATE语句时必须使用BY语句;MERGE语句在不使用BY语句的时候也可以按观测号进行一对一合并。● 在处理缺失值时,UPDATE语句可以控制是否用缺失值对主数据集进行替换;MERGE语句中后一数据集中的缺失值一定会覆盖前一数据集中的值。● 当BY变量值在后一数据集或者更新数据集中不唯一时,UPDATE语句和MERGE语句的处理方式不一样。

4.4 数据集的更改

SAS提供了MODIFY语句进一步扩充了DATA步功能,它可以在原数据集上直接进行替代、删除或添加观测的操作。

4.4.1 单个数据集的更改

基本形式如下:

DATA 原数据集; MODIFY 原数据集;RUN;

运用MODIFY语句,可以更改原数据集中任何变量的值,但是由于该语句不能够更改原数据集的描述部分,因此不能在原数据集中添加或者删除变量。

示例,对于包含产品、库存和价格的数据集work.inventory进行修改。

data work.inventory; input Product_ID $ Instock Price; datalines;P001R 12 125.00P003T 34 40.00P301M 23 500.00PC02M 12 100.00;proc print data=work.inventory noobs; title 'Warehouse Inventory';run;

原数据集work.inventory

将数据集中每个Price提高15%,用MODIFY语句来实现修改。

data work.inventory; modify work.inventory; price = price*1.15;run;proc print data=work.inventory noobs; title 'Price reflects 15% increase';run;

更改后的数据集work.inventory

对于数据集的操作,其实也可以通过SET语句来实现。对于单个数据集的操作,MODIFY和SET语句相比较,由于MODIFY语句时在没有备份的情况下直接操作原数据集的,因此更加节省磁盘空间。

4.4.2 两个数据集的更改

使用MODIFY语句可以实现通过一个数据集对另一个数据集中的数据进行更改,基本形式如下:

DATA 主数据集; MODIFY 主数据集 修改数据集; BY 变量1 <变量2 变量3 ···>;RUN;

其中,主数据集表示需要更改的数据集,修改数据集中包含了用于更改的数据,此时必须使用BY语句。在使用MODIFY时,主数据集和修改数据集中的BY变量不需要事先排序或索引,但是按BY变量排序或索引可以提高运行效率,特别是在观测量很大的情况下。

在使用MODIFY更改数据集时,需要注意:

● 当主数据集中的BY变量值有重复值时,只有每个BY变量值的第一条观测会被更改;当修改数据集中的BY变量值有重复值时,系统会依次读入修改数据集中的每一条观测,并应用到主数据集,且后读入的观测会覆盖前依次读入的观测;当主数据集和修改数据集中的BY变量值都有重复值时,系统会依次操作修改数据集中BY组合中的每一条观测,并且应用到主数据集中对应BY组合的第一条观测中。● 如果在修改数据集中存在缺失值,系统默认不用缺失值更改主数据集中的数据。如果需要用缺失值更改主数据集,可以参考UPDATE语句中使用选项UPDATEMODE=实现操作。

实际上,在主数据集或修改数据集中BY变量值有重复值时,MODIFY语句的处理逻辑和UPDATE语句一样。

示例,work.inventory2中保存了另一部分的库存信息,现在需要根据work.inventory和work.inventory2中数据计算总库存。

data work.inventory2; input Product_ID $ Outstock; datalines;P001R 12P001R 30P001R 25P003T 34P301M 23;proc print data=work.inventory noobs; title 'Warehouse Inventory Inhouse';run;proc print data=work.inventory2 noobs; title 'Warehouse Inventory Overseas';run;

两个含有库存变量的数据集

可以看到在work.inventory2中同一产品P001R有三条观测。两个数据集中产品计算总库存。

data work.inventory; modify work.inventory work.inventory2; by product_id; instock=instock+outstock;run;proc print data=work.inventory noobs; title 'Total Inventory';run;

汇总后的总库存数据集

“instock=instock+outstock;”语句用于计算instock并输出到数据集work.inventory中,产品P001R的instock的值等于原work.inventory数据集中P001R的instock值加上work.inventory2中P001R的3条观测的instock值。

修改数据集中的所有观测在主数据集中都可以找到对应的观测,当修改数据集中含有一些新观测时,可以使用OUTPUT语句将新观测添加到主数据集中。

4.5 数据集处理的一点补充

4.5.1 使用数据集选项END=

很多情况下,在进行数据操作时需要知道SAS在什么时候处理输入数据集的最后一条观测,这时可以使用SAS提供的数据集选项END=来帮助辨识。使用数据集选项END=的基本形式如下:

SET 数据1 <数据集2 数据集3 ···> END=变量;

在使用SET、MERGE、MODIFY、UPDATE语句时,都可以使用该选项。这里语句中的变量是数值型的临时变量,当DATA步在操作数据集的最后一条观测时,该变量的取值为1,否则为0。临时变量在DATA步中使用,但是不会在数据集中输出。需要特别注意的是,在使用SET、MERGE、MODIFY、UPDATE语句进行多个数据集处理时,只有当DATA步处理所有输入数据集的最后一条观测时,该变量的取值才会由0变成1。

示例,work.sales中记录了各员工全年销售额,据此计算全年公司总销售额。

data work.sales; input Emp_ID $ Dept $ Sales Gender $; datalines;ET001 TSG 100 MED001 DSG 200 FED001 DSG 135 MEQ001 QSG 234 FET001 TSG 125 FET002 TSG 98 MEQ002 QSG 100 MEQ003 QSG 98 MED002 DSG 124 MET003 TSG 123 F;run;proc print data=work.sales; title 'Sales';run;

输出work.sales数据集

现运用END=选项计算公司全年的总销售额。

data work.total_sales; set work.sales END=last; total_sales+sales; if last then output; keep total_sales;run;proc print data=work.total_sales noobs; title 'Total Sales in Million';run;

生成的总销量数据集

代码要点的解读:

● END=last定义了临时变量last,当SAS在处理最后一条观测时,last的取值将变为1。● “total_sales+sales;”用来计算公司全年销售额,作用和“retain total_sales 0; total_sales=total_sales+sales;”一样。● IF语句和OUTPUT语句判断了最后一条观测,并将该观测输出到数据集中。|● KEEP语句仅将需要输出的total_sales变量保留在数据集中。

4.5.2 使用自动变量FIRST.与LAST.

在DATA步中若使用了BY语句,SAS要求读入的数据集必须按BY变量排序(MODIFY语句除外),同时,SAS在程序执行过程中会自动生成两个数值型临时变量:FIRST.变量和LAST.变量,他们分别用来识别BY组合的第一条观测和最后一条观测。

当只有一个BY变量时:● 当DATA步正在处理该BY变量值的第一条观测时,FIRST.变量为1,否则为0。● 当DATA步正在处理该BY变量值的最后一条观测时,LAST.变量为1,否则为0。

当有多个BY变量时,系统会自动生成多对FIRST.变量和LAST.变量。

data work.test; input x y $ @@; datalines;1 A 1 A 1 B 2 B 2 C;run;data work.try; set work.test; by x y; firstx=first.x; lastx=last.x; firsty=first.y; lasty=last.y;run;proc print data=work.try noobs;run;

生成的work.try数据集

当SAS处理第一条x=1的观测时,first.x=1;当SAS处理最后一条x=1的观测时,last.x=1;当SAS处理第一条x=1并且y='A'的观测时,first.y=1;当SAS处理最后一条x=1并且y='A'的观测时,last.y=1。

proc sort data=work.sales; by Dept Gender;run;data work.sales_dept; set work.sales; by Dept Gender; retain sales_by_dept; if first.Gender then sales_by_dept=0; sales_by_dept=sales_by_dept+sales; if last.Gender; keep Dept Gender sales_by_dept;run;proc print data=work.sales_dept noobs; title 'Sales by Department by Gender';run;

生成的各部门男女员工的销售额数据集

这里使用了RETAIN语句,使得在DATA步的循环中sales_by_dept的值可以保留在PDV中。BY语句创建的这两个临时变量不仅可在SET语句读入单个数据集时使用,在进行多个数据集的拼接时也常使用。

4.5.3 使用SET语句中的选项POINT=和NOBS=

在DATA步使用SET语句读入数据集时,可使用选项POINT=指明要读入的观测序号。使用选项POINT=的基本语法如下:

SET 数据集 POINT=指针变量;

其中指针变量用来指明要读入特定序号的观测,必须在SET语句执行前对它赋值。在有SET语句的DATA步程序中,系统将反复执行DATA步的语句,直到遇到数据中的文件结束标志。但是在使用选项POINT=时,系统会直接读入指针指向的记录,这就很可能导致系统不会遇到文件结束标志,容易陷入死循环。因而在按指针读入数据的程序中,经常会用到STOP语句。

如果要取得数据集中观测的个数,可以使用SET语句中的选项NOBS=,语法如下:

NOBS=变量;

其中的变量也是临时变量,在DATA步的编译阶段赋值。

4.5.4 使用多个SET语句

在SAS中实现同一个操作,往往可以有多种方法。使用SET进行横向合并的一个好处是,在合并之前不需要将输入数据集进行排序。

data work.data1; input x y @@; datalines;1 2 2 3 3 4;data work.data2; input x z @@; datalines;1 3 3 5 4 2 5 4;run;data work.combined; set work.data1; set work.data2;run;proc print data=work.combined; title 'Using Two Set in Data Step';run;

work.data1数据集
work.data2数据集
两个SET语句合并后的数据集

在使用多个SET语句时,PDV中的变量时读入数据集的并集。首先,DATA步从第一个SET语句中读入第一条观测,并复制到PDV中;然后从第二个SET语句中读入第一条观测,并复制到PDV中。当输入数据集中有同名变量时,后读入的数据集的变量值覆盖前一个数据集中同名变量的值。新数据集中的观测数是所有输入数据集中的观测数的最小值,因为当DATA步遇到第一个文件结束标志时,DATA步就结束执行。

示例,从sashelp.class数据集中针对每个学生列出比起年龄大的学生的姓名及年龄并制成报表。

data work.class_expand; set sashelp.class(keep=name age); do i=1 to nobs; set sashelp.class(keep=name age rename=(name=nameolder age=ageolder)) nobs=nobs point=i; if age lt ageolder then output; end;run;proc sort data=work.class_expand; by name age ageolder nameolder;run;proc print data=work.class_expand; title 'Students'; by name age; id name age;run;

在PRINT过程中使用了BY语句和ID语句,BY语句可使报表按BY变量组输出列表,ID语句中的变量用来代替观测序号,置于输出的最左侧。输出数据集很长就不列了。。

4.5.5 使用HASH对象处理多个数据集

相较于SET语句和MERGE语句,使用HASH对象有两个优点。第一,它对数据集的排序没有要求;第二,由于HASH对象是放入内存中的数据集,因此在内存允许的情况下,运用HASH对象进行数据集之间的关联时其效率更高。

一般在观测数很多的数据集与观测数较少的数据集进行匹配时,运用HASH对象将较小的数据集加载到内存中,可以非常快速的完成匹配。

定义HASH对象

使用HASH对象时,首先要定义HASH对象,基本形式如下:

DECLARE object HASH对象名 (主题1: 内容1 <, 主题2: 内容2, ···>>);HASH 对象名.DEFINEKEY (主题1: 内容1 <, 主题2: 内容2, ···>>);HASH 对象名.DEFINEDATA (主题1: 内容1 <, 主题2: 内容2, ···>>);HASH 对象名.DEFINEDONE();

第一行定义了HASH对象名称,object可以是HASH或HITER,HASH对象名由编程者定义。

第二行和第三行分别通过HASH对象名.method的语法定义了HASH对象的KEY变量和DATA变量,这些变量都是DATA步中的变量。

第四行表明HASH对象定义结束。

示例,将work.product中的变量Product_Name和Description的数据加到数据集work.sales中。

data work.sales; input Emp_ID $ Dept $ Sales Product_ID $; datalines;ET001 TSG 100 P001ED001 DSG 120 P001ET001 TSG 50 P002EC001 CSG 230 P004EQ001 QSG 150 P003ET001 TSG 210 P004ET002 TSG 40 P002ET003 TSG 150 P003;run;data work.product; infile datalines dsd; length Product_ID $4 Product_Name Description $35; input Product_ID $ Product_Name $ Description $; datalines;P001,Manufacturing Industry Solutions,Manufacturing Industry Solutions V2P002,Logistics Solutions,Logistics Solutions V1P003,Financial Service Solutions,Financial Service Solutions V3P004,Insurance Solutions,Insurance Solutions V2;run;

生成的数据集work.sales
生成的数据集work.product

下面通过HASH对象实现匹配。

data work.sales_description(drop=ErrorDesc) work.exception; length Product_ID $8 Product_Name Description $35; if _N_=1 then do; /* Define hash objective */ declare hash product_desc(dataset:'work.product'); product_desc.definekey('Product_ID'); product_desc.definedata('Product_Name','Description'); product_desc.definedone(); call missing(Product_ID,Product_Name,Description); end; set work.sales; /* Retrieve matching data */ rc=product_desc.find(); if rc = 0 then output sales_description; else do; ErrorDesc='No Product Description'; output exception; end; drop rc;run;

SAS处理的步骤如下:

● 在编译阶段,SAS读入数据集work.sales中变量及新创建的变量的描述部分,并将其置于PDV中。● 在执行阶段,当_N_=1时,定义HASH对象并加载数据到HASH对象中。Call Missing()语句可以消除日志中部分变量没有初始化的信息。HASH对象product_desc如下。

HASH对象的描述信息

● 读入work.sales数据集的第一条观测,并置入PDV。● 系统调用product_desc.find()在HASH对象中检索数据。product_desc.find()可以指定sales数据集中的KEY变量,当未指定任何KEY变量时,系统默认sales数据集的KEY变量和HASH对象的KEY变量同名。当系统检索到HASH对象中存在的某个KEY值和PDV中KEY变量的值相等时,则返回代码rc的取值为0,同名将HASH对象中对应的DATA变量的值读入PDV中。● 系统判断rc=0,将PDV中的数据写入数据集work.sales_description中。● 系统读入sales数据集的第二条观测,并重复第三步到第五步的操作,直到系统读入sales数据集中的所有观测。

输出数据集work.sales_description

数据集work.exception中创建了一个新变量保存未匹配成功的原因。这种方法便于调试,找到数据中存在的问题。本例中work.exception无观测。

使用.REPLACE操作

使用.REPLACE可以替换已经加载入内存中的HASH对象的数据。基本形式如下:

HASH 对象名.REPLACE(主题1: 内容1 <, 主题2: 内容2, ···>>);

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多