继续《SAS编程与数据挖掘商业案例》学习笔记,本文侧重数据处理实践,包括:@H_404_3@HASH@H_404_3@对象、自定义@H_404_3@format、以及功能强大的正则表达式@H_404_3@
一:@H_404_3@HASH@H_404_3@对象@H_404_3@@H_404_3@
Hash@H_404_3@对象又称散列表,是根据关键码值而直接进行访问的数据结构,是根据关键码值而直接进行访问的数据结构,@H_404_3@@H_404_3@
sas@H_404_3@提供了两个类来处理哈希表,用于存储数据的@H_404_3@hash@H_404_3@和用于遍历的@H_404_3@hiter,hash@H_404_3@类提供了查找、添加、修改、删除等方法,@H_404_3@hiter@H_404_3@提供了用于定位和遍历的@H_404_3@first@H_404_3@、@H_404_3@next@H_404_3@等方法。@H_404_3@@H_404_3@
优点:键值的查找是在内存中进行的,有利于提高性能;@H_404_3@
hash@H_404_3@表可以在数据步运行时,动态的添加更新或删除观测;@H_404_3@@H_404_3@
hash@H_404_3@表中可以很快的定位数据,减少查找次数;@H_404_3@@H_404_3@
常用方法:@H_404_3@
definekey@H_404_3@:定义键@H_404_3@@H_404_3@
Definedata:@H_404_3@定义值@H_404_3@@H_404_3@
definedone@H_404_3@:定义完成,可以载入数据@H_404_3@@H_404_3@
add@H_404_3@:添加键值,如在@H_404_3@hash@H_404_3@表中已存在,则忽略;@H_404_3@@H_404_3@
replace@H_404_3@:如果健在@H_404_3@hash@H_404_3@表中存在,则替换,如果不存在则添加键值@H_404_3@@H_404_3@
remove@H_404_3@:清除键值对@H_404_3@@H_404_3@
find@H_404_3@:查找健值,如果存在则将值写入对应变量@H_404_3@@H_404_3@
check@H_404_3@:查找键值,如果存在则返回@H_404_3@rc=@H_404_3@0@H_404_3@,不修改当前变量的值;@H_404_3@@H_404_3@
output@H_404_3@:将@H_404_3@hash@H_404_3@表输出到数据集@H_404_3@@H_404_3@
clear@H_404_3@:清空@H_404_3@hash@H_404_3@表,但并不删除对象@H_404_3@@H_404_3@
equal@H_404_3@:判断两个@H_404_3@hash@H_404_3@类是否相等@H_404_3@@H_404_3@
@H_404_3@
find@H_404_3@方法的示例:@H_404_3@@H_404_3@
libname chapt12 'f:\data_model\book_data\chapt12';@H_404_3@
data results;@H_404_3@
if _n_=0 then set chapt12.participants;@H_404_3@@H_404_3@@H_404_3@
if _n_ = 1 then do;@H_404_3@
declare hash h(dataset:'chapt12.participants');@H_404_3@@H_404_3@@H_404_3@
h.definekey('name');@H_404_3@
h.definedata('gender','treatment');@H_404_3@
h.definedone();@H_404_3@
end;@H_404_3@
set chapt12.weight;@H_404_3@
if h.find() = 0 then@H_404_3@
output;@H_404_3@
run;@H_404_3@
@H_404_3@
hiter@H_404_3@对象的引例:@H_404_3@@H_404_3@
data patients;@H_404_3@
length patient_id $ 16 discharge 8;@H_404_3@
input patient_id discharge:date9.;@H_404_3@
datalines;@H_404_3@
smith-4123 15mar2004@H_404_3@
hagen-2834 23apr2004@H_404_3@
smith-2437 15jan2004@H_404_3@
flinn-2940 12feb2004@H_404_3@
;@H_404_3@
data _null_;@H_404_3@
if _n_=0 then set patients;@H_404_3@
declare hash ht(dataset:"patients",ordered:"ascending");@H_404_3@
ht.definekey("patient_id");@H_404_3@
ht.definedata("patient_id","discharge");@H_404_3@
ht.definedone();@H_404_3@
declare hiter iter("ht");@H_404_3@
rc = iter.first();@H_404_3@
do while (rc=0);@H_404_3@
put patient_id discharge:date9.;@H_404_3@
rc = iter.next();@H_404_3@
run;@H_404_3@
用@H_404_3@declare hiter iter("ht");@H_404_3@给@H_404_3@hash@H_404_3@表@H_404_3@ht@H_404_3@定义了一个遍历器@H_404_3@iter@H_404_3@,之后调用@H_404_3@first@H_404_3@方法将遍历器定位到@H_404_3@hash@H_404_3@表的第一条观测,然后使用@H_404_3@next@H_404_3@方法遍历@H_404_3@hash@H_404_3@表中的所有记录并输出。@H_404_3@@H_404_3@
商业实战@H_404_3@-@H_404_3@两个数据集的合并:@H_404_3@@H_404_3@
data both1(drop=rc);@H_404_3@
declare hash plan ();@H_404_3@
rc = plan.definekey ('plan_id');@H_404_3@
rc = plan.definedata ('plan_desc');@H_404_3@
rc = plan.definedone ();@H_404_3@
do until (eof1) ;@H_404_3@
set chapt12.plans end = eof1;@H_404_3@
rc = plan.add ();@H_404_3@
do until (eof2) ;@H_404_3@
set chapt12.members end = eof2;@H_404_3@
call missing(plan_desc);@H_404_3@
rc = plan.find ();@H_404_3@
output;@H_404_3@
stop;@H_404_3@
上述程序可以简化为:@H_404_3@
data both2;@H_404_3@
length plan_id $3 plan_desc $20;@H_404_3@
if _n_ = 1 then do;@H_404_3@
declare hash h(dataset:'chapt12.plans');@H_404_3@
h.definekey('plan_id');@H_404_3@
h.definedata('plan_desc');@H_404_3@
h.definedone();@H_404_3@
call missing(plan_desc);@H_404_3@
end;@H_404_3@
set chapt12.members;@H_404_3@
rc=h.find();@H_404_3@
二:@H_404_3@format@H_404_3@@H_404_3@
自定义@H_404_3@format@H_404_3@:@H_404_3@@H_404_3@
Proc Format;@H_404_3@
Value $ Sex_Fmt@H_404_3@
'F'='@H_404_3@女@H_404_3@'@H_404_3@@H_404_3@
'M'='@H_404_3@男@H_404_3@'@H_404_3@@H_404_3@
Other = '@H_404_3@未知@H_404_3@';@H_404_3@@H_404_3@
Value Age_Dur@H_404_3@
Low-10="10@H_404_3@岁以下@H_404_3@"@H_404_3@@H_404_3@@H_404_3@
11-13="11-13@H_404_3@岁@H_404_3@"@H_404_3@@H_404_3@
14-<15="14-15"@H_404_3@
15-High="15@H_404_3@岁以上@H_404_3@";@H_404_3@@H_404_3@
Run;@H_404_3@
应用:@H_404_3@
Datatest;@H_404_3@
Setsashelp.class(keep=sex age);@H_404_3@
x=put(sex,$sex_fmt);y=put(age,age_dur.);@H_404_3@
三:正则表达式:@H_404_3@
/.../@H_404_3@一个正则表达式的起止;@H_404_3@@H_404_3@
|@H_404_3@@H_404_3@数项之间的选择,“或”运算;@H_404_3@@H_404_3@
()@H_404_3@匹配组,标记一个子表达式的开始和结束位置;@H_404_3@@H_404_3@
.@H_404_3@除换行符以外的任意字符;@H_404_3@@H_404_3@
\w@H_404_3@任一单词字符,数字大小写字母以及下划线@H_404_3@@H_404_3@
\W@H_404_3@任一非单词字符@H_404_3@@H_404_3@
\s@H_404_3@任一空白字符,包括空格、制表符、换行符、回车符、中文全角空格等;@H_404_3@@H_404_3@
\S@H_404_3@任一非空白字符,@H_404_3@@H_404_3@
\d0-@H_404_3@9@H_404_3@任一数字@H_404_3@@H_404_3@
\D@H_404_3@任一非数字字符@H_404_3@@H_404_3@
[...]@H_404_3@
[^...]@H_404_3@
[a-z]@H_404_3@从@H_404_3@a@H_404_3@到@H_404_3@z@H_404_3@@H_404_3@
[^a-z]@H_404_3@不在从@H_404_3@a@H_404_3@到@H_404_3@z@H_404_3@范围内的任意字符@H_404_3@@H_404_3@
^@H_404_3@匹配输入字符串的开始位置@H_404_3@@H_404_3@
$@H_404_3@匹配输入字符串的结尾位置@H_404_3@@H_404_3@
\b@H_404_3@描述单词的前或后边界@H_404_3@@H_404_3@
\B@H_404_3@表示非单词边界@H_404_3@@H_404_3@
*@H_404_3@匹配@H_404_3@0@H_404_3@次或多次@H_404_3@@H_404_3@
+@H_404_3@匹配一次或多次@H_404_3@@H_404_3@
?@H_404_3@匹配零次或@H_404_3@一次@H_404_3@@H_404_3@
{n}@H_404_3@匹配@H_404_3@n@H_404_3@次@H_404_3@@H_404_3@
{n,}@H_404_3@匹配@H_404_3@n@H_404_3@次以上@H_404_3@@H_404_3@
404_3@匹配@H_404_3@n@H_404_3@到@H_404_3@m@H_404_3@次@H_404_3@@H_404_3@
@H_404_3@
常用函数:@H_404_3@
Prxparse@H_404_3@定义一个正则表达式@H_404_3@@H_404_3@
Prxmatch@H_404_3@返回匹配模式的首次匹配位置@H_404_3@@H_404_3@
Call prxsubstr@H_404_3@返回匹配模式在目标字符串的开始位置和长度@H_404_3@@H_404_3@
Prxposn@H_404_3@返回正则表达式子表达式对应的匹配模式值@H_404_3@@H_404_3@
Callprxposn@H_404_3@返回正则表达式子表达式对应的匹配模式和长度@H_404_3@@H_404_3@
Cal lprxnext@H_404_3@返回匹配模式在目标字符串中的多个匹配位置和长度@H_404_3@@H_404_3@
Prxchange@H_404_3@替代匹配模式的值@H_404_3@@H_404_3@
Call prxchange@H_404_3@替代匹配模式的值@H_404_3@@H_404_3@
eg1@H_404_3@:@H_404_3@@H_404_3@
if _n_ = 1 then pattern_num = rxparse("/cat/");@H_404_3@
@H_404_3@
retain pattern_num;@H_404_3@
input string $30.;@H_404_3@
position = rxmatch(pattern_num,string);@H_404_3@
file print;@H_404_3@
put pattern_num= string= position=;@H_404_3@
there is a cat in this line.@H_404_3@
does not match cat@H_404_3@
cat in the beginning@H_404_3@
at the end,a cat@H_404_3@
cat@H_404_3@
eg2@H_404_3@:数据验证@H_404_3@@H_404_3@
data match_phone;@H_404_3@
set chapt12.phone_numbers;@H_404_3@
if _n_ = 1 then pattern = prxparse("/\(\d\d\d\) ?\d\d\d-\d{4}/");@H_404_3@
retain pattern;@H_404_3@
if prxmatch(pattern,phone) gt 0 then output;@H_404_3@
找出不匹配的手机号码@H_404_3@
data unmatch_phone;@H_404_3@
where not prxmatch("/\(\d\d\d\) ?\d\d\d-\d{4}/",phone);@H_404_3@
Eg3:@H_404_3@提取匹配某种模式的字符串@H_404_3@@H_404_3@
data extract;@H_404_3@
pattern = prxparse("/\(\d\d\d\) ?\d\d\d-\d{4}/");@H_404_3@
if missing(pattern) then do;@H_404_3@
put "error in compiling regular expression";@H_404_3@
stop;@H_404_3@
end;@H_404_3@
length number $ 15;@H_404_3@
input string $char80.;@H_404_3@
call prxsubstr(pattern,string,start,length);@H_404_3@
if start gt 0 then do;@H_404_3@
number = substr (string,length);@H_404_3@
number = compress(number," ");@H_404_3@
output;@H_404_3@
keep number;@H_404_3@
this line does not have any phone numbers on it@H_404_3@
this line does: (123)345-4567 la di la di la@H_404_3@
also valid (123) 999-9999@H_404_3@
two numbers here (333)444-5555 and (800)123-4567@H_404_3@
eg4@H_404_3@:提取名字@H_404_3@@H_404_3@
data ReversedNames;@H_404_3@
input name & $32.;@H_404_3@
datalines;@H_404_3@
Jones,Fred@H_404_3@
Kavich,Kate@H_404_3@
Turley,Ron@H_404_3@
Dulix,Yolanda@H_404_3@
data FirstLastNames;@H_404_3@
length first last $ 16;@H_404_3@
keep first last;@H_404_3@
retain re;@H_404_3@
if _N_ = 1 then@H_404_3@
re = prxparse('/(\w+),(\w+)/');@H_404_3@
set ReversedNames;@H_404_3@
if prxmatch(re,name) then@H_404_3@
do;@H_404_3@
last = prxposn(re,1,name);@H_404_3@
first = prxposn(re,2,255)">注:@H_404_3@1,2@H_404_3@分别代表正则表达式中的两个组@H_404_3@@H_404_3@
eg5@H_404_3@:提取符合规定的名字@H_404_3@@H_404_3@
data old;@H_404_3@
input name $60.;@H_404_3@
Judith S Reaveley@H_404_3@
Ralph F. Morgan@H_404_3@
Jess Ennis@H_404_3@
Carol Echols@H_404_3@
Kelly Hansen Huff@H_404_3@
Judith@H_404_3@
Nick@H_404_3@
Jones@H_404_3@
data new;@H_404_3@
length first middle last $ 40;@H_404_3@
re1 = prxparse('/(\S+)\s+([^\s]+\s+)?(\S+)/o');@H_404_3@
re2 = prxparse('/(\S+)(\s+)([^\s]+\s+)(?)(\S+)/o');@H_404_3@
set old;@H_404_3@
id1=prxmatch(re1,255)">id2=prxmatch(re2,255)">if id1 then@H_404_3@
first = prxposn(re1,255)">middle = prxposn(re1,255)">last = prxposn(re1,3,255)">if id2 then test=prxposn(re1,4,255)">put test=;@H_404_3@
Eg6:@H_404_3@返回匹配模式的多个位置@H_404_3@@H_404_3@
expressionid = prxparse('/[crb]at/');@H_404_3@
text = 'the woods have a bat,cat,and a rat!';@H_404_3@
start = 1;@H_404_3@
stop = length(text);@H_404_3@
call prxnext(expressionid,stop,text,position,255)">do while (position > 0);@H_404_3@
found = substr(text,255)">put found= position= length=;@H_404_3@
call prxnext(expressionid,255)">注:首次执行@H_404_3@call prxnext@H_404_3@返回一个@H_404_3@position@H_404_3@,然后进入循环,在抽取满足条件的子串中,再次执行@H_404_3@all prxnext@H_404_3@,此时会返回下一个匹配的@H_404_3@position@H_404_3@;@H_404_3@@H_404_3@
Eg7:@H_404_3@替换文本@H_404_3@@H_404_3@
data cat_and_mouse;@H_404_3@
input text $char40.;@H_404_3@
length new_text $ 80;@H_404_3@
if _n_ = 1 then match = prxparse("s/[Cc]at/mouse/");@H_404_3@
retain match;@H_404_3@
call prxchange(match,-1,new_text,len,trunc,num);@H_404_3@@H_404_3@@H_404_3@
if trunc then put "note: new_text was truncated";@H_404_3@
the Cat in the hat@H_404_3@
there are two cat cats in this line@H_404_3@
here is no replacement@H_404_3@
run;@H_404_3@