继续之前的读书笔记,本次讲解sas主要的变量操作,包括基本赋值语句、累加语句、keep语句、retain语句、array语句、rename语句、length语句。
1.基本赋值语句
z=x y;
z=sum(x,y); sum函数的好处是可以克服缺失值的影响;
2.如果表达式中既有数值型变量又有字符型变量的话,则会将字符型变量转化为数值型变量
3.系统在编译阶段,对于赋值语句变量,如果是字符型变量则长度为1,如果是数值型变量则长度为8,接下来,系统将执行赋值语句,这时变量长度将由第一次读入的表达式的运算结果决定。一旦第一次读入确定长度之后,下一次pdv无论读入多长的数据,都不会改变变量的长度。除非提前用length语句限定变量的长度。
4.累加语句
x 1,x (-1)都是累加语句,其中x必须是数值型变量,在编译阶段,pdv自动设置累加变量值为0,它的值从当前执行后一直保留在pdv中,直到下一次执行为止。
在项目实践中,一般用retain语句替代累加语句,因为retain语句可以初始化累加变量为一个非零值,而累积语句初始值只能为0.
5.keep语句(keep语句不是可执行语句)
Data a3;
set sashelp.class(keep=name weight);
x=1;
keep name x;
Run;
Data a4(keep=name x);
set sashelp.class(keep=name weight);
x=1;
Run;
以上两个程序输出结果一样,
但是对于读入数据集时,keep=的形式性能要远高于keep的形式,因为keep=的形式是仅限后面的变量进入pdv,对于拥有几百个变量的数据集而言,keep=效率要高;
不过对于数据输出方面,keep=与keep性能没有区别;上面两个语句性能完全一样。
6.retain语句(不是一个可执行语句)
pdv运行规律:data语句与run语句构成了一个循环语句,一般情况下每读一遍data步所有语句时,pdv都会清空所有变量值,并设置为缺失值。然后根据执行语句,再次对变量进行赋值。当data步使用retain语句时,pdv则不会清空retain语句对应变量,而是一直保留直到下次该变量再次执行。
retain语句在data步中有着广泛的应用,对于数据集的操作可以到单元格,而一般函数只能操作到列,实际应用中retain会使程序更加灵活化。可以控制一个变量的值不变,除非有外部的条件的变化而变化。
retain语句主要实现以下需求:
汇总数据,累加变量,纵向比较变量,创建flag标识变量,处理缺失值,迭代累加字符变量值。
Eg:关于汇总数据的一个例子(按照每个id,汇总cns的值,汇总id的记录数,如果txn_cde变量取101和201两个值,则累加计算一次,计算txn_dte的最小值,)
libname chapt4 "f:\data_model\book_data\chapt4";
data chapt4.retain1;
input id txn_cde$ cns txn_dte$;
cards;
10 101 10 20070101
10 101 20 20080402
10 201 30 20050203
20 101 40 20040105
20 201 50 20040105
20 301 60 20070806
20 201 70 20050607
30 301 80 20070501
30 401 90 20070306
;
run;
proc sort data= chapt4.retain1;by id txn_dte;run;
data test1;
set chapt4.retain1;
by id txn_dte; -- 按照id
txn_dte排序后逐条读取数据
retain min_dte sum_cns cnt cnt_condition;
if first.id then do; --由于赋值语句比较多,需要用do语句套住所有的赋值语句,不是循环。
min_dte=txn_dte;
sum_cns=0;
cnt=0;
cnt_condition=0;
end;
min_dte=min(min_dte,txn_dte);
sum_cns cns;
cnt 1;
cnt_condition (txn_cde in("101" "201"));
if last.id; ---只有每次id对应数据读完后才会执行此举。
run;
整个程序分为5个模块,
Set/by模块:set语句是读数据集,必不可少,一定注意by语句不能丢,by后面的变量就是之前排序中的变量。
retain模块:对需要retain的变量进行声明
first模块:对retain变量按照每一个first.id做初始化
主体模块:该模块介于first与last之间,主要是完成对需求的运算
last模块:该模块主要完成最后的输出。
7.array语句
array在sas中其实是一个变量指针,他真用内存空间约4B,其作用是指向需要操作的变量对象,array可以把无序的无规则的变量名统一,
常用的array语句:
Array rain{5} x1-x5; 数组中有5个元素,分别为x1-x5
Array ar(3); 输出变量默认为ar1-ar3
Array ar(1:3) 等价于上一个
Array x{1:5,1:3} score1-score15; 规定一个5*3二维数组,score1-score15一次逐行放入数组
Array ab(*) x y z; 等价于array
ab(3) x y z;
Array test(3) _temporary_ (90 80 70)
Array days{7} d1-d7 (1,2,3,4,5,6,7);
数组函数:
维度函数dim(),array mult {5,10,2} mult1-mult100,
dim(mult)=dim(mult,1)=5
Dim2(mult)=dim(mult,2)=10
Dim3(mult)=dim(mult,3)=100
Eg:
options pageno=1 nodate ps=64 ls=80;
data temp;
input x @@;
cards;
10 20 30
;
run;
data test (drop=i);
set temp;
array a(5) A1-A5;
do i=1 to 5;
a(i)=i;
end;
run;
proc print noobs data=test;
run;
总结:array其实是一个横向操作语句,也就是说,当数据指针停留在某一条观测时,可以通过array语句,把所有的array声明的变量都操作一遍。
商业实战:
缺失值的填充;
data missing;
input x y$ z$ m;
cards;
. . . 1
2 . 3 .
;
run;
data result;
set missing;
array char _character_;
array numr _numeric_;
do over char;
if char eq "" then char="null";
end;
do over numr;
if numr eq . then numr=0;
end;
run;
对于所有的字符型变量。缺失时都赋值为null。对于所有的数字型变量,缺失事都赋值为0
常见累加器,比如统计价格人数
data score;
input id$ x y z;
cards;
a 75 84 65
b 54 74 71
c 51 56 52
d 50 50 60
;
run;
data qualify;
set score;
k=0;
array chengji(3) x y z;
array base(3)_temporary_(60,60,60);
do i=1 to 3;
if chengji(i) ge base(i) then k 1;
end;
if k=3 then output qualify;
run;
统计所有成绩都及格的学生记录。
横向排序变量
data a;
input x1-x7;
cards;
23 44 81 13 42 34 26
14 18 10 20 33 11 50
;
run;
data final;
set a;
array arr(1:7) x:;
array copy(1:7) cx1-cx7;
do m=1 to dim(arr);
copy(m)=arr(m);
end;
do i=1 to dim(copy);
do j=i 1 to dim(copy);
if copy(j)>copy(i) then do;
temp=copy(j);copy(j)=copy(i);copy(i)=temp;
end;
end;
end;
run;
利用冒泡算法实现横向排序
纵向横向综合应用:删除缺失值比例超过一定阈值的变量列表:
options symbolgen;
data missing;
input n1 n2 n3 n4 n5 n6 n7 n8 c1$ c2$ c3$ c4$;
datalines;
1 . 1 . 1 . 1 4 a . c .
1 1 . . 2 . . 5 e . g h
1 . 1 . 3 . . 6 . . k l
1 . . . . . . . a b c d
;
data _null_;
if 0 then
set missing nobs=obs;
array num_vars[*] _NUMERIC_;
array char_vars[*] _CHARACTER_;
call symputx(‘num_qty‘, dim(num_vars));
call symputx(‘char_qty‘, dim(char_vars));
call symputx(‘m_obs‘,obs);
stop;
run;
%put &num_qty &char_qty &m_obs;
data _null_;
set missing end=finished;
array num_vars[*] _NUMERIC_;
array char_vars[*] _CHARACTER_;
array num_miss [&num_qty] (&num_qty * 0);
array char_miss [&char_qty] (&char_qty * 0);
length list $ 50;
do i=1 to dim(num_vars);
if num_vars(i) eq . then num_miss(i) 1;
end;
do i=1 to dim(char_vars);
if char_vars(i) eq ‘‘ then char_miss(i) 1;
end;
if finished then do;
do i= 1 to dim(num_vars);
if num_miss(i)/&m_obs. ge 0.7 then list=trim(list)||‘ ‘||trim(vname(num_vars(i)));
end;
do i= 1 to dim(char_vars);
if char_miss(i)/&m_obs. ge 0.7 then list=trim(list)||‘ ‘||trim(vname(char_vars(i)));
end;
call symputx(‘mlist‘,list);
end;
run;
%put &mlist;
data notmiss;
set missing(drop=&mlist);
run;
这是一个典型的array语句和retain语句综合应用的例子
第一个_null_程序,区分并计算所有数值型和字符型变量的个数,并把结果赋值给两个不同的宏变量;同时获取数据集missing的观测数,也赋值给另外一个宏变量m_obs;整个程序都是在编译阶段完成的,没有任何的执行语句,这是为了提高效率,
第二步,对数值型和字符型分布计算每一个变量的缺失值个数,并累加,最后根据预设的阈值,,找到满足的所有变量,并赋值给宏变量mlist
第三步:data步,drop满足条件的变量。
第二步说明“:
编译过程声明了四个数组,
num_vars指向所有的数值型变量(n1-n8),使用_numeric_故不再输出数据集中显示
char_vars指向所有的数值型变量(c1-c8),使用_character_故不再输出数据集中显示
数组num_miss,默认变量名是num_miss1-num_miss8,并设置初始值为0;
数组char_miss,默认变量名是char_miss1-char_miss8,并设置初始值为0;
接下来程序读取数据集missing第一条观测,执行两个do语句,
第一个do语句,获得num_miss1=0,num_miss2=1,`````num_miss8=1
第二个do语句,获得char_miss1=``````
接下来,判断if finished语句,由于还没有读到文件末尾,所以程序不会执行该语句,直接run语句,输出第一条观测,程序调回data开头,继续执行下一个观测,
依次下去,知道最后一条记录。然后执行if finshed语句,完成最后两个do语句:
第三个do语句,执行8次,如果缺失值超过70%,则执行
list=trim(list)||‘ ‘||trim(vname(num_vars(i))),从而获取全部的缺失值大于70%的数值型变量;
第四个do语句,同样的道理
最后总结,array语句在处理横向操作方面功能非常庞大,只要有array出现的地方,就要有do语句,
8.rename语句
Rename old_name=new_name;
注意:rename语句是一个声明语句,不是赋值语句
rename语句如果和keep语句同时出现的时候,系统先编译keep语句,后编译rename语句。drop类似于keep语句。
eg:
data a;
input x y;
cards;
1 2
;
data b;
set a;
rename y=yy;
keep x y;
run;
9.length语句
Length m1 3 m2 $8;
Length m1 m2 7;
都是合法的length语句
可以改变原有数据集变量的长度。对于字符型变量,语句要在set语句之前,对于数值型变量,可以在任何地方,
10.label语句
Label x1=‘ab‘ x2=‘cd‘;