2016年5月30日

【SAS 小技巧】替換文字中的中文字串(K函數+Macro)

曾在用K函數處理中文字串文章中提及專門用來處理中文的K函數,但沒有實際的例子,最近發生了一個案例,把處理心得分享給大家參考。

原問題為,想把地址中的某一條路刪除。

例如:
台北市中山路50號 → 台北市50號
北市板橋區中山路二段 → 北市板橋區二段

這樣的取代如果用EXCEL或WORD不難處理,但在SAS中似乎不是那麼人性。

以下程式我不會特別說明語法的指令,有興趣者可請自行研究或連至我推薦的網頁查詢。

先建立練習用檔案。

data a;
format text $50.;
input text $;
cards;
台北市中山路50號
台北市中正路50號
台北市中山山路50號
台北市中正北路50號
北市板橋區中山路二段
桃園市中壢區建國路1號
;

先用眼睛判斷的話,第1筆和第5筆地址中的「中山路」要刪除,而第3筆的「中山山路」則應保留。

我用了一個稍微複雜的判斷式處理,程式在下面。

data b;
set a;
format text1  $50.;
    if kindex(text,"中山路")>=1 then do;
       text1=kcompress(KUPDATE(text,kindex(text,"中山路"),3,""));
       chk=1;
       end;
else text1=text;
run;

proc print data=b;
run;

逐一解釋比較重要的部分。
if kindex(text,"中山路")>=1 then do』,利用「kindex」找出字串包含有中山路的第一個位置(注意是字串非單獨的字),以第1筆地址來看,kindex回應4,如果是第2筆地址則回應為0。所以當kindex回應等於或大於1時表示該地址中有「中山路」字串的存在,我們在進一步的處理。

text1=kcompress(KUPDATE(text,kindex(text,"中山路"),3,""))』,這一串函數中最重要的就是「kupdate」了,使用這個函數將字串中的特定「範圍」的字或字串取代成其他的,而要取代的起點則透過『kindex(text,"中山路")』設定,要取代的長度則設定為「3」。

else text1=text』,將沒有包含「中山路」的地址直接使用原本的地址。不過要直接取代成原來變項的話,則可以跳過這一段語法。

結果如下圖:

結果1


當然我們不能只滿足這樣的語法,希望能把Macro的精神放到程式裡面,讓大家可以更偷懶!!

我們先來看看程式可改進的地方,最主要的問題就是需要自訂長度,如果能把自訂長度「3」改為自動抓取需要取代字串長度,且不用寫那麼多if then else可能會更棒。

所以把程式稍做修改成下面的macro語法了。

%macro textchange (var, ctext);
    %let clength=%sysfunc(KLENGTH(&ctext));
    %put &clength;
    if kindex(&var,"&ctext")>=1 then do;
        &var=kcompress(KUPDATE(&var,kindex(&var,"&ctext"),&clength,""));
    end;
%mend textchange;

以上的macro程式主要新增自動抓取文字長度的語法,『%let clength=%sysfunc(KLENGTH(&ctext))』,「%sysfunc」讓macro語法可以使用SAS內建函數的巨集函數。「klength」為k函數用來找出字串的長度。

實際使用的程式和結果

data c;
set a;
%textchange(text, 中山路);
%textchange(text, 中正北路);
%textchange(text, 建國路);
run;

proc print data=c;
run;

結果2

以上的部分主要解決將特定字串刪除的的問題。


在來我將這個問題轉換一下,處理地址的時候,常遇到六都的「鄉鎮市」轉換為區,寫了一段稍微複雜的程式給大家參考,不過這組程式沒有很精簡,但我也懶得修改了,也請容許我不說明這個程式的細節了。

Macro程式(使用兩組程式,個人不喜歡把程式包來包去)

%macro subprog ( subleng);
                &var=KSTRCAT( ksubstr(&var,1,kindex(&var,"&old_text")+2),
                            KTRANSLATE(ksubstr(&var,kindex(&var,"&old_text")+3,&subleng),"區區區","鄉鎮市"),
                            ksubstr(&var,kindex(&var,"&old_text")+3+&subleng)) ;
                &var=kcompress(KUPDATE(&var,kindex(&var,"&old_text"),&clength,"&new_text"));
%mend subprog;

%macro textchange (var, old_text, new_text);
    %let clength=%sysfunc(KLENGTH(&old_text));
    %put &clength;
    if kindex(&var,"&old_text")>=1 then do;
        if ksubstr(&var,kindex(&var,"&old_text")+3,3) not in ("那瑪夏") then do;
             %subprog (3);
            end;
        else do;
             %subprog (4);
            end;
        end;
%mend textchange;

程式使用說明:

『%textchange (要取代的變項, 被取代的字串, 新字串);』
範例:
%textchange(text, 台北縣, 新北市);

建立練習資料檔

data a1;
format text $100.;
input text $;
cards;
24104台北縣三重市重新路4段12號6樓
33049桃園縣桃園市三元街156號
33001桃園縣桃園市市府路1號
22055臺北縣板橋市文化路一段48號
849高雄縣那瑪夏鄉達卡努瓦里大光巷230號
台中市北區學士路91號
台中縣大甲鎮鎮政路40號
;

實際應用

data a2;
set a1;
%textchange(text, 台北縣, 新北市);
%textchange(text, 臺北縣, 新北市);
%textchange(text, 桃園縣, 桃園市);
%textchange(text, 台中縣, 臺中市);
%textchange(text, 高雄縣, 高雄市);
run;

proc print data=a2;
run;

結果

結果3

沒有留言:

張貼留言