SAS lag 的相反等於 lead ? LEAD in SAS

前言

當我們要把某一欄的資料往下移動一列(或者 n 列)的時候,SAS 有個很方便的 function:LAG() 或 LAGn()。那麼,有沒有向上移動 lead() 的function呢? 很遺憾的目前 SAS 並沒有這樣的 function (這和 SAS 處理資料的方式有關),不過這並不障礙我們把資料向上移動的決心,以下簡單介紹三種方式,都能輕鬆的讓資料往上搬動。讓我們一起來看看如何操作。

PROC EXPAND

單純的往上搬移

PROC EXPAND 是由 SAS/ETS 模組(Econometrics and Time Series Analysis) 所提供的程序,主要用於時間序列分析,他的其中一個功能正好可以用來處理資料的搬移,包括向上移以及向下移,這裡我們以向上移動的介紹為主,其語法的應用如下:

假設我們有一段資料,serial 為每個人的序號,想要把住院日期 date 往上移一列。

data test; input serial date;
datalines;
1 100
2 200
3 300
4 400
5 500
;

這時候,PROC EXPAND 的 CONVERT statement 可以幫助我們完成向上搬移的工作。

proc expand data=test out=lead_test method=none; 
id serial; 
convert date=lead_date / transformout=(lead 1); 
run;

proc expand →處理時間序列的程序,這裡我們牛刀小試借來處理資料上下移動
out= →將data set 另存,不指定的話 SAS 會以 DATAn 的名稱自動命名另存
method=none →指定資料轉換的方式。這裡我們只做單純的資料搬移,因此指定為 none 即可
id →資料移動的依據,用以辨別每個觀測值。需要為「數字」格式 (包含SAS的日期格式),且為排序
convert → 等號左邊的是原本的變項名稱,右邊為移動之後自訂的變項名稱。轉換的變項必須為「數字」格式

transformaout=(lead 1) → 指定往上搬移一列。可自訂欲移動的列數,例如:(lead 5) 為往上移5列

執行之後我們可以看到資料順利的往上搬運了一列:


每個人的資料往上搬移 (BY GROUP)

更多時候我們會遇到的情況是:一人多筆的資料,想要讓每個人的資料住上搬移。這時候我們只需要利用大家熟悉的 BY statement 即可,這樣以 test2 的資料做說明:

data test2; input id serial date;
datalines;
1 1 100
1 2 111
1 3 123
2 4 200
2 5 222
2 6 234
3 7 300
3 8 333
3 9 345
3 10 386
;
Proc expand data=test2 out=lead_test2 method=none;
id serial;
by id;
convert date=lead_date / transformout=(lead 1);
run;

由結果可以看到,每個人的資料皆往上移動了一列,也因此每個人的最後一筆資料是 missing的。

這裡值得說明的是,PROC EXPAND 是為了時間序列分析所開發的,因此資料本身是有序的(也需要是有序的)。由上述的例子我們可以發現, ID statement 的角色類似於「序號」的概念,用以辨別每個觀測值並做為資料移動的依據。因此,在執行 PROC EXPAND 之前,需要先排序想要移動的資料(欄位),以我們的例子來說就是每個人(id)以及日期的順序(serial),這樣一來 SAS 就知道是每個人的日期要移動了。

從語法來看會更明確完整:

proc sort data=test2;
   by id serial;
run;

Proc expand data=test2 out=lead_test2 method=none;
id serial;
by id;
convert date=lead_date / transformout=(lead 1);
run;

DATA STEP: merge

如果只是要把資料往上移動一列,直觀來說是不是可以直接把要移動的變項其第一筆資料去掉,再串回原本的資料即可了呢? 答案是:可以的。SAS data step 的 merge 就可以完美勝任:

data lead_test;
   merge test
               test (firstobs=2 rename=(date=lead_date) keep=date);
run;

firstobs=2 →資料由第二列開始讀取

這個方法是最簡單而直觀的一種。既然目的是新增一個欄位、讓所有的資料皆往上移動一列,那麼只要另外產生新的資料是從第二列開始(即 fisrtobs=2),並且串回原本的資料,再修改變項名稱(date=lead_date)做為區別就大功告成了。


每個人的資料往上搬移 (BY GROUP)

如果要把每個人的資料往上搬的時候,就要多一個條件來處理:

data lead_test2;
   merge test2
               test2 (firstobs=2 rename=(date=lead_date id=id1) keep=date id);
if id ne id1 then lead_date=.; /*指定同一個人 →不同人(即最後一筆) 為 missing*/
run;

對於一人多筆的情況,往上搬移一列也代表了搬移後每個人的最後一筆資料是 missing(因為沒有下一筆了),為了完成這個關鍵步驟,我們需要把 id 也考慮進來,在 merge的時候,兩個資料(原始資料 & 上移資料) 是不同 id 時,上移的資料會呈現 missing,剛好也就是原始資料每個人的最一筆。


PROC SQL

如果是用 SQL 的方法也可以輕鬆的執行這項任務:

proc sql;
create table lead_test as
select a.*, b.date as lead_date
from test a left join test b
on a.serial = b.serial-1;
quit;

SQL 所採取的概念和上述 merger 大同小異,先重新命名想要搬動的變項 date為 lead_date,再用利left join 的方式回串原本的資料集,並且以 serial 及serial -1 (取下一列)為串檔的條件


每個人的資料往上搬移 (BY GROUP)

要處理每個人資料往上搬移的情況也只需要多一個步驟:

proc sql;
create table lead_test as
select a.*, b.date as lead_date
from test a left join test b
on a.serial = b.serial-1 and a.id = b.id /*指定同一個人*/
quit;

可以看到只要多指定兩個資料集為同一個人(a.id=b.id),就可以把每個人的資料串起來。


結語

SAS裡有許多方法可以把資料往上移動,例如以lag()產生新的欄位後再倒序回串原本的資料(即所謂的 lag 的相反)。以上推薦的是最容易使用的三種方法,不需要太多資料的翻轉,大家可以根據自己的習慣來使用。各方法之間各有優缺點,但只要掌握了移動資料的概念,都可以輕鬆操作不卡關。有機會處理 lead 時大家不妨試試不同方法。


參考資料

Calculating Leads (and Lags) in SAS ® : One Problem, Many Solutions.
SAS/ETS User’s Guide:The EXPAND Procedure

 

如有任何問題和意見歡迎提出