更新:如果在2020年登录出现了选择地区的问题的话,通常选择US就可以正常登录。

前言

SAS 是一个数据分析的软件,或者说语言,广泛应用于医疗行业和其他需要数据分析的行业。

到了2019年,SAS的版本推出了十余个平行更新的版本,令人目不暇接。

很多时候,我会发现对于初学者来说,如何选择一个合适的版本进行入门学习,如何配置环境,成为了比学习SAS语法更大的障碍和难题。

因此我写了这一篇文章,来引导大家使用 SAS OnDemand for Academics 这个版本。

为什么选择这个版本?

SAS OnDemand for Academics(后面简称为ODA或SAS ODA)是一个面向学术而不是商业目的的版本。

给你选择SAS ODA 的七大理由!

优势

  1. 完全在云端!

    在本地,你不需要做任何环境的配置和准备,你只需要互联网接入即可,然后像在网上登录邮箱一样登录SAS,便可以愉快地使用了。

  2. 支持任何操作系统,随时随地!

    不管是Windows, Mac OS, Linux,只要一个安装了现代浏览器的操作系统都可以使用。甚至在手机上,也可轻松写代码。在机房上课或是在自己电脑上运行,都只需要登录一个帐号便可同步所有资料。

  3. 完全正版!

    你不用担心这是一个不稳定或者有后门不安全的破解版,又或者有麻烦的破解步骤。同时你不用花购买完整SAS服务的钱。

  4. 运行极速且不伤电脑!

    任何程序都是跑在SAS的服务器上的,所以本地不需要任何算力或资源。

  5. 完全免费!

    对于学生而言,是完全免费的。

  6. 保持最新!

    最新的SAS会更稳定,更少bug,同时完全兼容以前的代码。而且是服务器更新,本地不需要任何操作。

  7. 支持中文!

主要是查文档等用到中文会看着舒服一点。

劣势

  1. 需要网络接入。

2019年了朋友。

  1. 服务器可能需要维护。

    一般一个月一次,一次一小时,基本不会影响使用。

  2. 存储容量限制在10个G以内

    对初学者而言一般不需要跑这么大的数据

立即开始!

基本思路就是注册一个SAS帐号——成为教育帐号——登录。

就这么简单。

随着时间的推移,细节可能会有所迭代更新,但是总的注册三部曲应该不会改变。

注册SAS帐号

  1. 点击这里 注册一个SAS账户。你会看到这个界面。

SAS帐号注册

  1. 为了保证能审核通过,请务必如实填写。与SAS的关系是学生。

  2. 之后会在邮箱里看到这样一封邮件。

第一次邮件验证

  1. 点击“激活我的SAS个人账户”。你会看到如下页面。

激活账户

  1. 按照指示设置密码即可。SAS对于密码的复杂度要求还挺高的,你得输入一长串。记得一定要把密码记下来。

成为ODA账户

  1. 点击这里 将你的账户注册为ODA账户。你会看到这个界面。

SAS ODA 注册

  1. 上面的信息是对下一次系统维护的通知,不用管。

  2. 你所要做的就是在下面填入刚刚注册的邮箱和密码,然后点击submit。

  3. 之后会让你同意SAS ODA的使用范围,点击勾然后confirm就行了。之后会收到一封email。这次的email要花点时间,不像上次一样秒通过。采用edu邮箱的话,通过几率和速度都会大大增加,一般在五分钟到十分钟左右,个人觉得非常OK。

  4. 你的email会像这样。

SAS ODA E-mail

  1. 看到这封e-mail,代表你已经可以使用SAS ODA 账户了。

开始使用!

  1. 点击这里 进入SAS Studio。这个网址以后会经常用到,建议添加到收藏夹或桌面快捷方式等。

登录界面

  1. 输入刚刚的用户名和密码。
  2. 尽情使用吧!

SAS-UI

简单使用技巧

  1. 界面布局左侧是工具栏,右侧是多Tab标签页。
  2. 左边一般展开第一个,即查看文件。在文件(主目录)里上传数据,就可以使用了。上传按钮就是上面的那个向上的箭头。
  3. 右侧多标签,可以一些标签看数据,一些标签看结果,一些标签写代码。
  4. 在右上角问号左边的那个按钮点开,有一个参数选择,进去以后可以调整一些设置。我建议初学者在“代码和日志”里打开“启用提示”。在常规里有个字符策略,这个牵涉到了乱码问题,要详细讲讲。

我们都知道中文比英文更加难表示。计算机内部都是以二进制表示的,英文直接在ASCII码里规定好了,比如A在计算机内部是用1000001表示的。而中文有很多套规范,同一个汉字,在不同编码下是不一样的。比如“我”这个字在GB2132规范下是“10010101”而在UTF-8下是“001001101”(具体值是我随便编的,不是真实值)。所以如果一个中文文件,如果用了GB2132编码而你用了UTF-8打开,就会乱码。

为了规范这一种情况,大家都在逐步地往UTF-8靠。这是一个支持多种语言的编码。在网页上,已经基本实现了。你在浏览器中打开任意一个页面,按F12查看源码,基本都会发现有一条是<meta charset = "utf-8"> 这是在告诉浏览器,我的网页是用UTF-8编码的,请用同样的编码打开我。

回到我们的SAS。我们的SAS在今天的默认编码自然而然是UTF-8。但是,我们操作的案例的数据编码可能并不是使用UTF-8的,直接打开可能会乱码。我们有两个方式,一个是将原数据用UTF-8编码重新保存一下,一个是将SAS的默认打开编码改为文件的编码。我建议使用前者,毕竟UTF-8是更优雅更新的方式。

字符编码的问题广泛存在于非英文语言的世界。不仅仅是SAS,其他你遇到的乱码问题,十有八九就是字符编码引起的。所以这个知识点请务必牢记。

  1. 在你的程序里想要使用你刚刚上传的文件,需要告诉程序我们的文件在哪。比如说我们在电脑里C盘根目录下放了一个data.csv,那么我使用infile “C:/data.csv” 来导入数据就很自然。但是现在你把文件上传到了网上,你要使用的路径应该是在SAS服务器上的地址,显然不能使用C盘这样的标识。假如,你在“文件(主目录)”里上传了一个文件data.csv,那么它的路径实际上是/home/SAS分配给你的用户名/data.csv 这样的路径表示方法可能对你来说有点奇怪,但是在网络世界里,服务器世界里,大家都是使用Linux的,而Linux的路径就是这样以斜杠开头的。SAS分配给你的用户名是在网页右下角“用户:nxxxxx”那里的,一般是n开头的一串数字。你也可以上传文件后,对文件右击选择属性,就可以看到它的位置了。
  2. SAS 新版是区分xls和xlsx的,旧版不区分,注意一下。考试不会考这些,不用管。 平时操作注意一下就行了,用错了会报错。
  3. SAS 新版舍弃了一些老的绘图包,用了一些新的来替代。考试不会考这些,不用管,问题不大。
  4. 临时文件夹(即生成的data保存的位置)在左侧下方“逻辑库”->“我的逻辑库”->“WORK”文件夹内。
  5. 可以看一下《SAS Little Book》这本书作语法学习参考。

代码参考

为了方便大家踏入SAS的大门和语法逻辑,放上了我将“Investor sentiment and bond risk premia”一篇论文的SAS 复现代码。这篇论文对数学公式阐述地很清楚,所以也很适合在SAS里复现,推荐一下。我在里面用到了大量macro,大量使用了语法糖,也写了详尽的注释,应该说是一篇不错的学习资料,供大家参考。

所有的数据可以点击这里下载

注意第一行的路径改成你自己的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
/*You need to change the directory of the lib to replicate the project*/
%let Project = sentiment/;

/*A sas format of Fama_Bliss*/
data Fama_Bliss;
infile "&Project/Fama_Bliss_price.txt" FIRSTOBS=2;
input date YYMMDD8. price1 price2 price3 price4 price5;
if _n_ > 1;
format date YYMMDD10.;
run;

/**********************/
/*For counting y_t^(n)*/
/**********************/
%macro county;
%do i = 1 %to 5;
data y_&i (keep = date y_&i);
set Fama_Bliss;
y_&i = - (1/&i)*log(price&i);
run;

%if &i = 1 %then %do;
data y_all;
set y_&i;
run;
%end;

%else %do;
data y_all;
merge y_all y_&i;
by date;
run;
%end;

%end;

%mend county;
%county;


/*****************************/
/*For counting f_t^{n to n+1}*/
/*****************************/
%macro countf;
%do i = 1 %to 4;
%let iplus = %eval(&i+1);
data f_&i (keep = date f_&i);
set Fama_Bliss;
f_&i = price&i - price&iplus;
run;

%if &i = 1 %then %do;
data f_all;
set f_&i;
run;
%end;

%else %do;
data f_all;
merge f_all f_&i;
by date;
run;
%end;

%end;

%mend countf;
%countf;


/**************************/
/*For counting r_{t+1}^{n}*/
/**************************/
%macro countr;
%do i = 1 %to 4;
%let iplus = %eval(&i+1);
data r_&i (keep = date r_&iplus);
set Fama_Bliss;
price_lag&iplus = lag(price&iplus);
r_&iplus = price&i - price_lag&iplus;
run;

%if &i = 1 %then %do;
data r_all;
set r_&i;
run;
%end;

%else %do;
data r_all;
merge r_all r_&i;
by date;
run;
%end;

%end;

%mend countr;
%countr;


/***************************/
/*For counting rx_{t+1}^{n}*/
/***************************/
/*Need merge first*/
data Fama_Bliss_all;
merge Fama_Bliss r_all y_all f_all;
by date;
run;

%macro countrx;
%do i = 1 %to 4;
%let iplus = %eval(&i+1);
data rx_&i (keep = date rx_&iplus);
set Fama_Bliss_all;
y_lag_1 = lag (y_1);
rx_&iplus = r_&iplus - y_lag_1;
run;

%if &i = 1 %then %do;
data rx_all;
set rx_&i;
run;
%end;

%else %do;
data rx_all;
merge rx_all rx_&i;
by date;
run;
%end;

%end;

%mend countrx;
%countrx;


/*******************/
/*For counting rxba*/
/*******************/
data rxba (keep = rxba date);
set rx_all;
rxba = 1/4 * (rx_2 + rx_3 + rx_4 + rx_5);
run;


/*******************/
/*For counting CP_t*/
/*******************/
/*Merge data first*/
data Fama_Bliss_all;
merge Fama_Bliss_all rxba;
by date;
y_lag_1 = lag (y_1);
f_lag_1 = lag (f_1);
f_lag_2 = lag (f_2);
f_lag_3 = lag (f_3);
f_lag_4 = lag (f_4);
run;

data for_reg;
set Fama_Bliss_all(Firstobs=2);
run;
/*REG and get the fit value*/
proc model data = for_reg outparms=CP_t;
parm b0-b5;
rxba = b0 + b1 * y_lag_1 + b2 * f_lag_1 + b3 * f_lag_2 + b4 * f_lag_3 + b5 * f_lag_4;
fit rxba;
run;
/*Get the Fitted Value*/
data CP_t;
set CP_t;
merge_temp = 1;
run;

data for_reg;
set for_reg;
merge_temp = 1;
run;

data fit;
merge CP_t for_reg;
by merge_temp;
run;

data CP(keep=CP date);
set fit;
CP = b0 + b1 * y_lag_1 + b2 * f_lag_1 + b3 * f_lag_2 + b4 * f_lag_3 + b5 * f_lag_4;
if CP = . then delete;
run;


/*************/
/*Counting LN*/
/*************/
proc import datafile = "&Project/Updated_LN_Macro_Factors_2018AUG.xlsx"
OUT = LN
DBMS = xlsx replace;
run;
/*Change the date to be month*/
data rxba;
set rxba;
year = year (date);
month = month (date);
run;

data LN;
set LN;
year = year (data);
month = month (data);
run;
/*Merge and calculate LN*/
data for_reg;
merge LN(in = a) rxba(in = b);
by year month;
if a=1 and b=1;
run;
data for_reg;
set for_reg;
F1_lag = lag (F1);
F13_lag = lag (F13);
F3_lag = lag (F3);
F4_lag = lag (F4);
F8_lag = lag (F8);
run;
proc model data = for_reg outparms=LN_t;
parm b0-b5;
rxba = b0 + b1 * F1_lag + b2 * F13_lag + b3 * F3_lag + b4 * F4_lag + b5 * F8_lag;
fit rxba;
run;
/*Get the Fitted Value*/
data LN_t;
set LN_t;
merge_temp = 1;
run;

data for_reg;
set for_reg;
merge_temp = 1;
run;

data fit;
merge LN_t for_reg;
by merge_temp;
run;

data LN(keep=LN year month);
set fit;
LN = b0 + b1 * F1_lag + b2 * F13_lag + b3 * F3_lag + b4 * F4_lag + b5 * F8_lag;
if LN = . then delete;
run;

data CP (keep = CP year month);
set CP;
year = year (date);
month = month (date);
run;

/*************/
/*Counting BW*/
/*************/

proc import datafile = "&Project/Copy of Investor_Sentiment_Data_20160331_POST.xlsx"
OUT = BW
DBMS = xlsx replace;
Sheet = DATA;
run;

data BW;
set BW;
month = mod(yearmo,100);
year = (yearmo - month ) / 100;
run;

data BW;
merge BW(in=a) rxba(in=b);
by year month;
if a = 1 and b = 1;
run;

data for_reg;
set BW;
deltaSent = dif(SENT);
SENT = lag(SENT);
SENT2 = lag(SENT2);
deltaSent = lag(deltaSent);
run;

proc model data = for_reg outparms=BW_t;
parm b0-b3;
rxba = b0 + b1 * SENT + b2 * SENT2 + b3 * deltaSENT;
fit rxba;
run;
/*Get the Fitted Value*/
data BW_t;
set BW_t;
merge_temp = 1;
run;

data for_reg;
set for_reg;
merge_temp = 1;
run;

data fit;
merge BW_t for_reg;
by merge_temp;
run;

data BW(keep=BW year month);
set fit;
BW = b0 + b1 * SENT + b2 * SENT2 + b3 * deltaSENT;
if BW = . then delete;
run;


/******************/
/*Final Regression*/
/******************/
data rx_all;
set rx_all;
year = year (date);
month = month (date);
run;

data final(drop = date);
merge CP(in = a) BW(in = b) LN(in = c) rx_all(in = d);
by year month;
if a = 1 and b = 1 and c = 1 and d = 1;
run;

%macro predict;
%do i = 2 %to 5;
proc reg data = final outest = rx_predict_&i;
model rx_&i = CP BW LN;
run;

%if i=2 %then %do;
data rx_predict_all;
set rx_predict_&i;
run;
%end;
%else %do;
data rx_predict_all;
set rx_predict_all rx_predict_&i;
run;
%end;
%end;
%mend predict;
%predict;

proc export data=rx_predict_all
outfile="&Project/rx_predict_all.csv";
run;

部分运行结果如下:

结果

完整结果太长了,可以点击这里查看。最终得到的回归数据结果会产生在rx_predict_all文件里。

具体内容可以参阅我的《投资者情绪与债券溢价》论文 SAS复现一文。

如果还有别的问题的话,欢迎在下面吐槽或留言~