[SAS] Macro Language 2: 內建macro,使用 SQL/DATA step 建立Macro variable

by wenwu
0 comment

好久不見! 很久沒上文章了,激勵自己朝第一百篇文章前進。不知道為甚麼最近的點閱暴增,原本月平均是一萬,現在每個月都兩萬多了。看來離百萬點閱不遠了。:.゚ヽ(*´∀`)ノ゚.:。

在上一篇中,提到基礎的macro language,本篇會提到

  1. SAS 內建的 Macro function
  2. 使用SQL 建立 Macro function
  3. 使用 DATA set 建立 macro function

SAS 內建的 Macro function

SAS 內建的macro function 並不會像SAS Base 一樣多,這邊簡單介紹幾個方法:

  1. %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: &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變數名稱', 資料項, '儲存位置');
        • 優點:
          1. 自動去除數值前後的空格(比舊版的 CALL SYMPUT 更聰明)。
          2. 自動將數值轉為字串,不會在 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 系列的最後一篇

        Related Articles

        發表迴響