SAS数据清洗之字符和数字处理
SAS数据清洗:
由于SAS数据集之间的关系一般不会用到,只是在proc sql中有所涉及,至今尚未运用过用于数据分析,所以在这里只讲单个数据集的处理。
在proc sql中我们可以看到:在定义数据集时涉及到字段名,字段属性,字段标签这三个最常用。我们在数据清洗时涉及到的数据集字段的处理,主要也就是围绕着字段名、字段属性和字段标签来进行处理。(对数据集结构的处理也就是对字段的增删改)
修改数据集名称和标签(label)
增加字段很简单,只需要在data步新建一个变量,对变量进行赋值即可。
删除变量可以使用drop和keep二者二选一。
下面谈一下最复杂的修改字段:
修改字段名最简单复杂的方法可以利用增加字段和删除字段二者结合。然后就是用sas中的rename关键字进行处理,格式为rename=(原字段名=新字段名)。
修改字段的标签:(定义label attrib label 变量名=label名)
修改使用modify 数据集名;label 变量名=label名。其中modify可以用于修改rename format和label。
修改sas的数据类型。
在sas中只有两种数据类型:数值型和字符型。日期在sas中存储形式是数值型,只是在显示时使用日期的format显示。
所以主要就是涉及到数值型和字符型之间的转换
数值转换为字符:
data;
x=2557898;
y=put(x, $8.);
put y;
run;
字符转换为数值:
data;
x=2557898;
y=put(x, 8.); /* y=x-0;*/
put y;
run;
数值型和日期型之间的转换主要就是利用日期的format,进行格式化的输入和输出。
SAS的日期时间都是数字型的,以便于计算。有各种格式可以从数值转变为字符型。1960-01-01是日期的开始,SAS的日期是0. 之后每天加一。1960-01-02的值为1. 1959-12-31的值为-1. 以此类推。时间是一秒为单位计算。从0(00:00:00)到86400(24:00:00)。每加一秒数值加一。
常用的日期时间转换的function如下:
data _null_;
a=mdy(12,31,1959);
b='02jan1960'd;
c='24:00:00't;
d='00:00:00't;
e='31dec90:5:00:00'dt;
format e datetime18.;
put a= b= c= d= e=;
run;
a=-1 b=1 c=86400 d=0 e=31DEC90:05:00:00
data _null_;
x=1;
format x ddmmyy10.;
put x= ;
run;
output:
x=02/01/1960
字符串的处理:
对于所有的数据的处理无外乎增删改查,虽然查在最后,但查是我们应该做的第一件事。
查找:既对字符串的切片,查找出任何位置任意长度的子字符串。
1.获得字符串的长度:length(strings)
2.获得字符串中某子字符的索引:index(s,s1) .s1在s中出现的位置,可以用于分割符的定位。
3.从固定的位置开始获得固定长度的字符串:substr(s,p,n). 从字符串s中的第p个字符开始抽取n个字符长的子串.
4.scan函数:scan(string,i,"char") 表示从字串string中以char为分隔符提取第i个字串。其中char可以是多个字符组合。
如:data work.test;
Title = 'A Tale of two Cities,Charles j.Dickens';
Word = scan(title,6,' ,');
put Word;
run;
output:Word=Charles
以下的例子很疑惑:
data a;
arg='abcdrf';
word=scan(arg,1);
put word=;
run;
output:word=abcdrf
很多资料显示这个的结果应该是word=a,但实验结果却是word=abcdrf,不明白是哪里不同?
data a;
arg='ABC.DEF(X=Y)';
word=scan(arg,2);
put word=;
run;
output:word=DEF
data a;
arg='ABC.DEF(X=Y)';
word=scan(arg,3);
put word=;
run;
output:word=X=Y
data a;
arg='ABC.DEF(X=Y)';
word=scan(arg,2,'.');
put word=;
run;
output:word=DEF(X=Y)
总结sas中scan函数在没有定义分隔符的情况下会有默认分隔符(已知:空格 . , ()等,可能所有的符合都是),在定了分割符后仅以此分隔符为分割。Scan中的第二个变量即数字为字符串被分割成多个子字符串后,所要查找的子字符串的索引(索引从1开始)。
5.Find函数:Pos=find(Text,'US','i',5),是要从第5个字符起找出US在源字符串Australia, US, Denmark中的起始位置,并且忽略大小写。
find函数中四个参数,Text不用说,代表源字符串,如果直接引用的话需要用引号括起来;US,代表将要查找的字符,加引号;i,在这里表示忽略大小写;5,表示从第5个字符开始查找。
Notes:不管起始位置是多少,返回的位置数值始终是在源字符串中的位置。
6.Substr函数:(修改函数)
data _null_;
x=12345;
y=substr(x,1,2);
y1=substr(x,9,2);
z=substr(left(x),1,2);
put x= y= y1= z=;
run;
Output:x=12345 y= y1=23 z=12
y为空的原因是x为数值型。y1为字符型,9在这里可能有特殊的含义。Left(x)后会转化为字符型。
字符替换(即所谓的右等):
data wb;
a='KIDNAP';
substr(a,1,3)='CAT';
put a;
run;
输出结果:CATNAP
另外一种情况:
data wb;
a='KIDNAP';
substr(a,1,4)='CAT';
put a;
run;
结果为:CAT AP
7. TRANWRD函数:(修改函数)
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
;
8.compress函数:(删除函数)
Compress(String, characters-to-remove, optional-modifiers)
optional-modifiers:
- a 大写 或小写字母
- d 数值(dignits)
- i 忽略大小写
- k 保留列表字符串,而不是去掉
- s 空间(blank,tabs,if,cr)
- p 标点符号
例子:仅保留数字。
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.
;
ouput:Wt_Lbs =155 198(英镑)
字符串的连接有cat和’||’两个:
data cat_catx;
length v_cats $6. v_catx $16.;
v_str1='CHI';
v_str2=' RAN';
v_str3='56789';
v_cats=cats(v_str1,v_str2);/*cats函数首先去掉每个要连接字符串的首尾空格*/
v_catx=catx('/',of v_str1-v_str3);/*catx函数也去掉每个要连接字符串的首尾空格,并且会在每个字符串之间插入分隔符号*/
run;
cats和count组合:
data Survey;
input (Q1-Q5)($1.);
Num = countc(cats(of Q1-Q5),'y','i');
datalines;
yynnY
nnnnn
;
总结:通过length,substr(left=),index find,scan函数组合我们可以实现对字符串的切片。
通过substr和tranwrd可以实现对字符串的修改。通过compress可以实现对字符串内部的删除,也会更改的一部分