好久不見! 很久沒上文章了,激勵自己朝第一百篇文章前進。不知道為甚麼最近的點閱暴增,原本月平均是一萬,現在每個月都兩萬多了。看來離百萬點閱不遠了。:.゚ヽ(*´∀`)ノ゚.:。
在上一篇中,提到基礎的macro language,本篇會提到
- SAS 內建的 Macro function
- 使用SQL 建立 Macro function
- 使用 DATA set 建立 macro function
SAS 內建的 Macro function
SAS 內建的macro function 並不會像SAS Base 一樣多,這邊簡單介紹幾個方法:
%UPCASE函數: 將字串轉換成大寫- 用途: 將 Macro 變數或字串值中的所有小寫字母轉換為大寫字母
%let my_string = Hello World!;
%let upper_string = %upcase(&my_string);
%put original: &my_string;
%put uppercase: &upper_string;

2. %SUBSTR 函數:擷取子字串
- 用途: 從 Macro 變數的值中擷取指定長度的子字串,從指定位置開始。語法:
%SUBSTR(string, start_position <, length>)
%let full_date = 20251215;
/* 從第 5 個字元開始,取 4 個字元 (月份和日期) */
%let month_day = %substr(&full_date, 5, 4);
/* 從第 1 個字元開始,取 4 個字元 (年份) */
%let year = %substr(&full_date, 1, 4);
%put Date: &full_date;
%put Month and Day: &month_day;
%put Year: &year;

3. %SCAN 函數:依分隔符號擷取字詞
- 用途: 根據指定的分隔符號(預設為空格)將 Macro 變數的值視為一系列字詞,並擷取第 n 個字詞。語法:
%SCAN(string, n <, delimiters>)
%let product_list = Apple Banana Cherry;
/* 擷取第2個字詞,使用空白作為分隔符號 */
%let third_item = %scan(&product_list, 2);
%put List: &product_list;
%put Third Item: &third_item;

4. %SYSFUNC 函數:在 Macro 程式中使用 DATA Step 函數
%SYSFUNC是很好用的 macro 函數,有許多DATA Step 函數沒有在macro 函數中,我們可以使用%SYSFUNC- 用途: 允許在 Macro 程式環境中執行幾乎所有的 SAS DATA Step 函數。這對於執行複雜的字串操作或日期/時間計算非常重要
- 以下是取得今天的日期,以及格式化日期
/* 取得今天的 SAS 日期值 (DATA Step 函數 TODAY()) */
%let today_date_num = %sysfunc(today());
/* 將 SAS 日期值格式化為 YYYY-MM-DD (DATA Step 函數 PUTN()) */
%let formatted_date = %sysfunc(putn(&today_date_num, yymmdd10.));
%put Todays SAS Date Number: &today_date_num;
%put Formatted Date: &formatted_date;i

- 壓字串: 刪掉字串裡面的空白
%let x = AB C D;
%put %sysfunc(compress(&x));

- Macro function 裡面沒有Propcase 可以使用,但是可以藉著
%SYSFUNC
%put %sysfunc(propcase(HAPPY));

- 另外macro 不用使用引號,下面兩個的結果是一樣的
%put %scan(&syslast,2,.)
%put %sysfunc(scan(&syslast,2,"."))


5. %SYSEVALF 函數:執行浮點數運算
- 用途: 在 Macro 程式環境中執行浮點數(帶小數點的數字)的算術運算。由於標準 Macro Facility 只能處理整數,因此
%SYSEVALF對於任何涉及小數的計算是必需的。
%let pi = 3.14159;
%let radius = 5.5;
/* 計算圓面積: Pi * r^2 */
%let area = %sysevalf(&pi * (&radius * &radius));
%put Pi: π
%put Radius: &radius;
%put Area of Circle: &area;

6. %STR 函數:遮罩 (Mask) Special Symbols
- 用途: 遮罩 Macro 程式中的特殊字元,如
&、%、;、=、<、>、(、)等,使其不被 Macro 處理器解讀為程式碼的一部分,而是視為普通的文字字元。常用於定義包含特殊符號的 Macro 變數值。
/* 正確範例: 使用 %STR 遮罩 '&' 符號 */
%let correct_var = %str(data_A&B);
%put Correct Variable Name: &correct_var;

7. %NRSTR 函數:不解析和遮罩特殊符號
- 用途:
%NRSTR是 Non-Resolving (不解析) 的%STR版本。它除了像%STR一樣遮罩特殊符號外,還能防止 Macro 變數在定義時被解析。常用於當您希望一個 Macro 變數的值本身包含另一個未定義的 Macro 引用時。
/* 假設我們想要建立一個包含將來會定義的變數的字串 */
%let future_var_ref = %nrstr(&project_name._list);
%put Future Variable Reference: &future_var_ref;
/* 稍後,我們定義 project_name */
%let project_name = Report;
/* 此時,&future_var_ref 的值已經被設定為 '&project_name._list',
即使 &project_name 已經定義,&future_var_ref 也不會被解析為 'Report_list'。
它會保持原始的不解析狀態。*/
%put Final Check: &future_var_ref;

使用SQL 建立 Macro variable
除了%let 之外,還有其他建立 Macro variable 的方法。在 SAS 中使用 PROC SQL 建立 Macro 變數(透過 INTO 子句)是自動化報表的關鍵技術。它比傳統的 %LET 更強大,因為它可以直接從數據集中提取資訊。這能讓我們直接從資料表中擷取數值(如總金額、最大日期或清單)並存入變數中供後續使用。
1. 建立單一 Macro 變數
這是最基礎的用法,通常用於儲存「統計值」或「常數」。
- 語法重點:
SELECT 欄位 INTO :變數名稱(注意冒號在變數名稱之前)。 - 範例: 計算資料集中的總觀測數。
proc sql noprint;
select count(*) into :total_obs
from sashelp.cars;
quit;
%put contains a total of &total_obs vehicles;

2. 建立多個獨立的 Macro 變數
如果想將多個欄位分別存入不同的變數,只需依序排列即可。
- 範例: 取得馬力最強的車型名稱與其馬力值。
proc sql noprint;
select Make, Horsepower into :top_make, :top_hp
from sashelp.cars
having Horsepower = max(Horsepower);
quit;
%put The most powerful vehicle is &top_make, with a horsepower of &top_hp!;

3. 建立「清單式」Macro 變數
這是 PROC SQL 最強大的地方!它可以將一整欄的值合併成一個字串,中間用指定的分隔符號(如逗號)隔開。這在處理 WHERE ... IN (&list) 時非常有效。
- 語法重點:
SEPARATED BY '分隔符號'。 - 範例: 取得所有車輛品牌清單,並用逗號隔開。
proc sql noprint;
select distinct Make into :make_list separated by ', '
from sashelp.cars;
quit;
%put List: &make_list;

4. 處理字串前後空格:TRIMMED
在建立變數時,SQL 預設可能會保留欄位的原始長度(包含空白)。剛剛在第二點就多了許多的空白,使用 TRIMMED 可以自動去除空格,讓產出的變數更乾淨。
- 語法:
INTO :var_name trimmed - 範例:
proc sql noprint;
select distinct Origin into :country trimmed
from sashelp.cars where Make='Audi';
quit;
%put The place of production is: [&country]; /* 使用中括號檢查是否有空白 */

從 DATA Step 寫Macro variable :CALL SYMPUTX
1. 為什麼不能在 data step 直接使用 %LET?
如果你在Data Step 裡面用%let 的話,會發現你的 macro variable 是錯的,為什麼呢?
- 靜態 vs. 動態:
%LET是在 Macro 處理階段(編譯時)執行的,它通常只能處理您「手寫」進去的內容。 - 無法直接讀取資料:
%LET無法直接看到資料集(Data Set)內部的數值。如果您想根據資料集中某個特定觀測值(Observation)來決定變數內容,%LET是做不到的。 - 大量生成: 如果您有 100 個類別,想產生 100 個對應的 Macro 變數,手寫 100 個
%LET非常不切實際。
下面有一個錯誤的示範,下面的程式不管製造廠商(Make)是哪一個,出現的 foot 都是 foot = &make Does Not have a hybrid Car; 大家可以自己跑跑看,或是更換 make。最主要的原因就是在跑data step 和 macro 的時候,sas 會先處理 Macro,所以當我們還沒有 HybridFlag 的時候,就會執行 %let foot 這一段,因為沒有 HybridFlag這個數值, 後面的foot 先執行的話,會直接進到 else,所以不管放什麼make,foot 都會是 foot = &make Does Not have a hybrid Car;
%let make=Honda;
data &make;
set sashelp.cars end=lastrow;
where upcase(Make)="%upcase(&make)";
retain HybridFlag;
if Type="Hybrid" then HybridFlag=1;
if lastrow then do;
if HybridFlag=1 then do;
%let foot = &make offers Hybrid Cars;
end;
else do;
%let foot = &make Does Not have a hybrid Car;
end;
end;
run;
為了解決沒辦法在Data Step 中使用 %let ,我們使用 CALL SYSPUTX
2. 在Data Step 中使用CALL SYMPUTX
當我們在 DATA Step 處理資料時,如果發現某個數值很重要,想要「外帶」出去給後續程式使用,這時就要用 CALL SYMPUTX。
- 語法:
CALL SYMPUTX('Macro變數名稱', 資料項, '儲存位置'); - 優點:
- 自動去除數值前後的空格(比舊版的 CALL SYMPUT 更聰明)。
- 自動將數值轉為字串,不會在 Log 出現轉換警告。
3. 實戰範例:從資料中提取關鍵指標
sashelp 中的dataset : class 有班上同學的資料,我們取第一個同學的名字出來
data _null_;
set sashelp.class;
if _n_ = 1 then do;
call symputx('first_student', Name);
end;
run;
%put The first student is: &first_student;

4. 進階技巧:動態批次建立變數
這是 CALL SYMPUTX 最強大的地方:結合資料欄位來命名。
- 情境: 我們想根據不同的產品類別(Type),分別建立對應的平均售價變數。
proc means data=sashelp.cars nway noprint;
class type;
var msrp;
output out=car_summary mean=avg_price;
run;
data _null_;
set car_summary;
call symputx(cats(type, '_price'), avg_price);
run;
%put The average selling price of a Sedan is: &Sedan_price;
%put The average selling price of a SUV is: &SUV_price;

今天就先講到這裡,預計下一篇是 macro 系列的最後一篇















