[SAS] 批次修改變數名稱


最近遇到這個需求,於是發現SAS有所謂SAS Dictionary這個非常好用的資源,就用這篇文章來記錄一下吧。(看看我的人生已經變得如此乏味。)




在SAS裡一張資料表又稱為一個data set,而這些data set都存放於library中,library在你的硬碟裡有其路徑,通常指向一個特定的檔案夾,而在SAS裡則有相對應的libref(library reference),用來指認每個data set的位置。

什麼是SAS Dictionary?簡單地說,就是一個系統自建的library,存放敘述關於你所有libref底下所有data set的資料的資料。也就是一個元資料(Metadata)的資料庫,裡面有不少很好用的資料表。然而這些元資料是唯獨的,而且無法直接使用DATA STEP或PROC叫用,但是SAS自動為他們建立了一群View,可以被DATA STEP或PROC叫用。View是RDBMS術語,本質上指的是一組對既存資料表包裝好的查詢(Query)——這不重要——總而言之,當你需要看看這個Dictionary裡頭有些什麼玩意兒的時候,可以前往系統自建的SASHELP這個library裡面玩玩。

前面說Dictionary無法被叫用,不過事實上PROC SQL例外,可以直接對其進行查詢。一個有趣的地方在於,SAS強制規定libref名稱不能超過八個字元,而Dictionary這個libref擺明了違反這條規則,可以想像它不是一個正規的library,而是基於RDBMS精神而特立獨行的library。

無論如何,我的習慣是永遠對SASHELP進行查詢,即使是使用PROC SQL也不例外。其中一個我覺得非常好用的資料表就是VCOLUMN,它記錄了你所有資料表中的欄位資訊。是的,當你需要批次修改欄位(也就是變數啦)名稱的時候,一個很直覺的想法就是運用VCOLUMN的元資料。

舉個例子,假設今天你進行了一個資料表矩陣轉置的動作(PROC TRANSPOSE,或者在STATA中我們使用的RESHAPE command),因而產生了大量欄位,其中一些欄位以c_開頭,你想把它全部改成a_開頭。(就別問為什麼會有這樣的需求了,關於資料處理,需求總是從天而降!)

下面這個巨集可以進行這個批次更名的工作:


%macro batchRename;
proc sql noprint;
select left(put(count(1), 3.))
into :num
from sashelp.vcolumn
where memname = 'ADDIN' and name like 'c_%';

select 'a' || substr(name, 2), name
into :new1 - :new&num, :old1 - :old&num
from sashelp.vcolumn
where memname = 'ADDIN' and name like 'c_%';
quit;

data AddIn;
set AddIn;
%do i = 1 %to #
&&new&i = &&old&i;
drop &&old&i;
%end;
run;
%mend batchRename;
%batchRename


首先,對VCOLUMN進行查詢,把需要更名的欄位(在這張元資料表裡它們被當作紀錄存在NAME這個欄位裡)數量抓進一個巨集變數裡,這個變數之後在do loop裡面會呼叫。這個動作我目前慣用的伎倆是PROC SQL的INTO:,它的彈性頗大,從程式碼中可以看出來它至少兩種變體:將欄位第一筆紀錄儲存成一個巨集變數,將欄位每個紀錄個別儲存成好幾個巨集變數。(另外還有儲存一整個字串清單並且自行定義分隔字元(delimiter)也很好用。)不過這裡針對第一個用法有個小麻煩需要特別處理一下,那就是COUNT()抓進來的東西通常包含多個左空格,這跟欄位定義長度有關,所以必須先把它截掉,不然之後以字串拼接方式叫用這個變數的時候會把空格一起拚出來,然後就死定惹。

第二個SELECT同樣查詢VCOLUMN,但這次我要把需要更名的欄位名稱全部抓出來,並且抓了兩次,第一次只抓c_後面的部分,第二次抓全名,形成兩份欄位名稱清單。

最後,在DATA STEP底下將舊變數丟給新變數,並刪去舊的。結束。我沒有使用RENAME敘述句,而是創造新變數,這會增加運算負擔,但是彈性比較大,因為有的時候你不只是想改名稱,可能還想變更變數格式,SAS在這方面很龜毛,所以通常直接創造新欄位並且對它FORMAT會比在原來的欄位上動手腳要來得便利。

這邊的程式碼只處理一個特例的更名模式,不過原則上絕大多數的更名模式,如果不能用SQL的萬用字元解決,都還是可以採用SAS的Perl Regular Expression來處理,完全是依照你的需求而定。網路上也有高手直接寫了一個足以應萬變的批次更名專用的巨集程式,稍微搜尋一下就能找到。不過我不想搞得那麼複雜,需求到哪裡,程式碼就到哪裡,這是我的原則。



好啦,又到了拷貝時間。(?)
在STATA底下同樣以巨集來做批次更名,需要用到的程式碼字數一定比較少——少很多。SAS的語法與結構都太搞工了。或許是我還不夠深入這套軟體的精隨所在吧。(我躺~)

不過我承認導入PROC SQL是SAS十分吸引人的一個地方,當然前提是你的工作中有需要用到SQL-like的資料庫,這樣有時候在一些處理邏輯的轉換上確實很有懶人效用。

最後,關於SAS Dictionary的更多深入說明,可以參考SAS的官方文件



[2012-12-13補充]
對於取得特定資料表的變數名稱,除了上述的方法之外,另外還有兩種我覺得也有相當之彈性的方式,他們是:


  1. proc contents
  2. vname()


第一個是SAS PROC程序,會產生一張關於你的資料表的元資料表,其中也包括欄位名稱。第二個方法比較特別,vname()這個函數作用在array reference上,可以取得該reference的變數名稱。如果取得欄位變數之後要進行的工作是以一個DATA STEP為主,那麼配合vname()的好處就是不必另外再執行proc sql或者proc contents,兩者都需要相對較多額外的coding。

不過proc sql配合SAS Dictionary的做法基本上還是最有彈性的,只是有時候取得變數名稱這件事並不是任務裡非常重要的角色,能夠節省coding採用像是vname()的作法應該是比較簡潔有力。




1 comment(s):

Anonymous | 16 September, 2017 15:53

您好
是否能請問程式中的select left(put(count(1), 3.))
其中1和3是什麼意思?謝謝

Post a Comment

回應文章前請注意下列三勿原則:

1)勿拍照;(→會有靈異的照片從你的相機裡跑出來...
2)勿餵食;(→會有飢渴的猛獸從我的網誌裡跑出來...
3)勿告白。(→會有奇怪的東西從站長體內裡跑出來...

謝謝大家的配合。
( > ー <)b