2012年5月26日

「數字前補零」—以Data step和Macro為例(SAS Macro教學)


前幾天朋友又問了一個問題,朋友手上資料有一串數字,例如5、6、20等,希望能在數字前面補零,補成為005、006、020。因為他的資料很多,而且數字長度不一定,從1~9999都有可能,因此沒辦法用CAT系列函數將零組合到數字前面。
由這個問題,開始這篇第一個程式,建立範例資料(這是我的習慣,沒有資料我寫不出程式>"< )

data zero1_1;
   input num1;
   datalines;
   1
   25
   562
   9480
   X
  ;
proc print data=zero1_1;
run;

接下來不囉唆,直接使用Format就可以解決這個問題了,而這裡要使用的Format為Zw.d
data zero1_2;
   set zero1_1;
   format num1 z5.;
   run;
proc print data=zero1_2;
run;

結果為下圖
擷取
只要簡簡單單使用Z5.這個格式便可以處理了。Z後面接這個5是指數字的總長度,因此以第4個觀察值來說,原本的數字就有4位了,因此SAS會只補一個0了。



其實朋友的問題比上述的狀況困難了一點,資料中欲轉換的變項為文字格式,他也希望在補零後依然保持文字格式…好吧這真的比較討厭了。依據朋友的說法在建另一個資料吧(同樣沒有資料我不會寫程式>"< )

data zero2_1;
   input num1 $;
   datalines;
   1
   25
   562
   9480
   X
   5X6
  ;
proc print data=zero2_1;
run;

下一段程式就可以完成朋友的需求了,不過大家有無注意到,資料中有幾個討厭的東西,也就是「X」和「5X6」這兩個純文字的訊息,朋友希望能完整保留到新的變項中,因此程式寫法調整為


data zero2_2;
   set zero2_1;
   num2=put(input(num1, best5.), z5.);
   if missing(input(num1, best5.)) then num2=num1;
run;
proc print data=zero2_2;
run;

以上這隻程式主要就是利用INPUT(文字轉數字),和PUT(數字轉文字)這兩個函數組合實用,及達到朋友的需求了(可以要求請客了 YA~)。而if missing..這一行語法是用來判斷num1中有沒有不能轉成數字的,如果不能轉,就直接取代num2中的值
其實INPUT和PUT的功能很強大,大家可以去GOOGLE看看。就SAS HELP這兩個函數的說明及語法為:
INPUT Function
Returns the value that is produced when SAS converts an expression using the specified informat.
INPUT(source, <? | ??>informat.)
PUT Function
Returns a value using a specified format.
PUT(source, format.)

結果如下圖:
擷取1
接下來我就想到,要如何把這個概念應用到Macro中了。在實際經驗中,許多政府機關提供的資料,常以補零數字作為資料的檔案名稱,例如CD201201、CD201205、CD201212等,如果要寫Macro的迴圈早期我都會用%if %then的方法寫,現在回想起來還真的是很天真可愛無邪...
一樣我們來建立一組類似的資料(10個資料)

data M001 M002 M003 M004 M005 M006 M007 M008 M009 M010 M011 M012 ;
   set zero2_1;
run;

假如我只想單純將資料都列印出來,但先來一個『錯誤』的寫法,讓大家欣賞一下~

%macro print_loop1 (start, end);
%do i=&start %to &end;
  proc print data=m&i;
  run;
%end;
%mend print_loop1;

%print_loop1 (001,012);

在LOG中可以看到一下錯誤訊息
擷取2
而正確的寫法應該為%print_loop2的寫法

%macro print_loop2 (start, end);
  %do i=&start %to &end;
   %let II=%sysfunc( PUTN(&i,z3.) );
   proc print data=m&II;
   run;
  %end;
%mend print_loop2;

%print_loop2 (001,012);

這裡的重點語法為
%let II=%sysfunc( PUTN(&i,z3.) );
%sysfunc為Macro很重要的函數,因為SAS內建的Macro Function不多,但透過%sysfunc這個巨集函數便可使用SAS內建函數(大部分),但大家一定很好奇,為何函數改成PUTN了,因為……因為…… 就「PUT」那麼剛好不能被%sysfunc使用!!
再寫一段複雜一點的Macro,此Macro的目的在把每一個資料檔加入補過零編號,然後堆疊在一起,程式的細節我就不贅述了。

%macro append_loop1 (start, end);
  %do i=&start %to &end;
   %let II=%sysfunc( PUTN(&i,z3.) );
   /*在資料中增加編號*/
   data m&II._new;
    format index $3.;
    set m&II;
    index="&II";
   run;

   /*將所有資料堆疊起來*/
   proc append
    base=total
    data=m&II._new;
   run;
%end;
%mend append_loop1;

%append_loop1 (001,012);

沒有留言:

張貼留言