主要学习资料:《SAS应用统计分析》
大二的时候专门开过这门课,比较困惑的是统计常用的R却没有单独开课,所以SAS应该是大学期间学的最好的统计软件,不过好久没有学过,由于实习需要,现在只能从头来过。
由于这本书第一章先讲描述分析,直接跳过了基本语法内容,所以先从第12章开始,看INPUT语句的用法。
列表输入法(list input)
特点:INPUT后跟的变量顺序与DATALINES中的数据顺序对应,数据用空格隔开
DATA test;
INPUT x1 x2 $;
DATALINES;
1 M
2 F
;
当分隔符不为空格,比如为逗号时,在INFILE语句中用DLM
选项指定分隔符
DATA test;
INFILE DATALINES DLM = ",";
INPUT x1 x2 $;
DATALINES;
1,M
2,F
;
当分隔符为逗号,且需要忽略引号,直接读取引号中内容时,用DSD
选项:
DATA test;
INFILE DATALINES DSD;
INPUT x1 x2 $;
DATALINES;
1,"M"
2,,
在list input中指定输入格式
这种要求通常发生在数据为时间序列数据时。
- 方法一:在DATA步中加
INFORMAT
语句指定变量的格式
DATA test;
INFORMAT dob MMDDYY10.;
INPUT name $ dob;
DATALINES;
john 10/21/1965
;
也可以用于指定字符型变量的长度,因为在SAS中字符变量一般默认8个字符长度:
DATA test;
INFORMAT name $ 20., dob MMDDYY10.;
INPUT name$ dob;
DATALINES;
ihavealongname 10/21/1965
;
- 方法二:在INPUT语句中,在变量后跟冒号直接指定变量对应的格式
DATA test;
INFILE DATALINES DLM = ',';
INPUT name $ dob : MMDDYY10.;
DATALINES;
john,10/21/1965
;
格式修正符
列表输入法中,若变量后跟格式修正符&
,则不再以空格作为分隔符,此法可以将两个用空格分隔的变量读成一个变量。
DATA test;
INPUT id name & : $30. score;
DATALINES;
1 jame smith 20;
栏位输入法(column input)
栏位输入法要求数据按栏位对齐,按照固定格式读取数据
DATA test;
INPUT id 1-3
gender $ 4;
DATALINES;
001M
2 F
这个方法现在一般很少看到使用,因为用于读取日期和非标准值数据很麻烦,但也有它的优点:不需要考虑数据是左对齐还是右对齐,只要是在栏位内的值都能读取。
格式化输入(formatted input)
格式化输入需要用栏位指针@
指定起始位置,并在变量后声明变量的格式。
一般数值数据的读取格式为W.d
,其中W表示数值变量的长度,d表示小数位数,例如123按照3.2
读取,结果为1.23;而1.23按照4.
读取,结果却仍然是1.23,这是为什么呢?
- 第一,小数点占一个栏位
- 第二,数值本身的小数点优先于
W.d
中隐含的小数点
DATA test;
INPUT @1 id 3.
@4 date MMDDYY10.;
DATALINES;
注意通过制定栏位和指针,可以反复读取数据。
每个被试读取多行数据
此时需要使用行指针#
,来指明读取数据位于哪一行。注意只有当一行数据读取完后行指针才会移动到下一行,否则会报错
DATA test;
INPUT #1 ID 1-3 @4 dob MMDDYY10.
#2 weight 1-3 @4 height 3.;
DATALINES;
00110/21/1997
100180
;
但是假如一个被试有多行数据,只需读取前两行,则需要在INPUT
语句末尾加上#N
,表示该被试实际上有N行数据
DATA test
INPUT #1id dob
#2 address #6;
输入格式列表
这个输入方法就比较有意思了。首先SAS中名字相似的变量可以一起输入,其次格式相同的变量可以用括号括起来一起输入。
举个🌰:
DATA test;
INPUT @1 id $3.
@4 (ques1-ques4 ques4_1 ques5-quest9) (1.)
@20 (dob leave_date)(MMDDYY10.);
还有一个更厉害的操作:假如相似的变量不是连着放在一起的,而是中间有其他变量隔开,此时可以在栏位中加上+/-
号,表示向前或向后移动指针。
例如假如对一组病人做了12次测试,每次测两个指标,每个指标占3个栏位,测试结果按照:
ID SBP1 DPB1 SBP2 DBP2 ... SBP12 DBP12
排列,我们可以这么读这批变量
DATA test
INPUT @1 id $3.
@4 (SBP1-SBP12)(3. +3)
@7 (DBP1-DBP12)(3. +3);
@和@@
这两个符号的使用就更巧妙了,因为需要搞清楚SAS读数据的原理:
- 首先DATA步创建临时数据集,然后INPUT语句创建变量,此时每个变量下都是空值,然后运行到DATALINES语句表示要从下面的内容中读入数据
- 此时每读一行为一个DATA步循环,读取后将变量存储到一个临时的空间中(忘了叫什么名字了),直到读到数据集末尾的分号
;
,才将所有的数据引入数据集中。
也就是说,SAS完成一个INPUT循环后,就会自动将指针移到下一行,下一次开始INPUT循环后会从下一行开始读取。假如DATA步里有多个INPUT语句,则第二个INPUT语句只能读到第二行的内容。这时就需要@
和@@
了。
@
表示保持此行,即INPUT语句完成该次循环后,指针不会跳转到下一行,如果遇到下一个没有以@结尾的INPUT语句,则会跳转到下一行,或者如果遇到DATALINES,则会跳转到下一行。
@@
表示强制执行此行,即INPUT语句完成该次循环后,即使遇到了DATALINES也不会跳转到下一行。
举个🌰:
DATA test;
INPUT X Y @@;
DATALINES;
1 2 7 9 3 4 10 12
15 18
;
读取的结果是:
(1,2) (7,9),...,(15,18)
但如果是:
DATA test
INPUT X Y @;
DATALINES;
1 2 7 9 3 4 10 12
15 18
;
则结果仍然是
(1,2) (15,18)
不规则数据的读取
到这里要加大力度了,首先对于不平衡数据:
组别 | 观测值 |
---|---|
C | 1 2 3 4 5 |
T | 2 3 4 |
这种数据应该如何读取?
当然可以一步一步输入:
DATA t;
INPUT group $ x @@;
DATALINES;
c 1 c 2 c 3 c 4 c 5
t 2 t 3 t 4
但是数据多了就很麻烦
给出一个究极解决方法:
DATA test;
RETAIN GROUP;
INPUT DUMMY $ @@;
IF DUMMY = 'C' OR DUMMY = 'T' THEN GROUP = DUMMY;
ELSE DO;
X = INPUT(DUMMY, 5.0)
OUTPUT;
END;
DROP DUMMY;
DATALINES;
C 1 2 3 4 5
T 2 3 4;
解析要点:
-
INPUT
语句,用@@
强制保持此行,逐个读入数值 -
RETAIN
语句:使得每次DATA步循环开始时,变量GROUP会保持上一次读取的值,不会被自动设置为缺失。 -
INPUT()
函数,当DUMMY的值为数字内容的字符时,将DUMMY的值赋给X,同时用INPUT转化成数值格式 -
OUTPUT
语句,用该OUTPUT代替DATA步末尾的OUTPUT,使得读完X的值后才输出该次观测,而不会读完做完IF语句就直接输出,此时只会有组名而没有对应数据
当然,还有更复杂的多变量不平衡数据的读入:
读这个数据就需要使用多重循环了:
DATA test;
DO GENDER = 'M','F';
DO GROUP = 'A','B','C';
INPUT DUMMY $ @;
DO WHILE (DUMMY NE '#');
SCORE = INPUT(DUMMY, 6.0);
OUTPUT;
INPUT DUMMY $ @;
END;
END;
END;
DROP DUMMY;
DATALINES中用#分割不同组的数据
或者还有另一种方法
DATA test;
RETAIN GROUP GENDER;
LENGTH GROUP GENDER $ 1;
INPUT DUMMY $ @@;
IF VERIFY(DUMMY, 'ABCMF ') = 0 THEN DO;
GROUP = SUBSTR(DUMMY,1,1);
GENDER = SUBSTR(DUMMY,2,1);
DELETE;
END;
ELSE SCORE = INPUT(DUMMY,6.);
DROP DUMMY;
DATALINES;
AM 20 30 40 20 50
BM 70 80 90
...
;
要点解读:
-
RETAIN
语句的功能和前面一样 -
LENGTH
语句指定变量长度为1个字符 - 首先读入DUMMY,此时默认为8个字符,因此读入两个字母后会在尾部自动添加6个空格,因此利用
VERIFY()
函数比对时要在参数中加空格。如果确实在字符串中,则GROUP值为字符串的第一个数值,GENDER值为字符串第二个数值。然后删除此行观测(因为X没有读到值) - 进入下一个DATA步的循环,此时DUMMY内容为数值,赋给SCORE,运行到末尾,自动输出该次观测。