2018年7月19日 星期四

Non printable 字元

問題:有特殊 ASCII 字碼存在資料裡面,而且用 print 也看不見,會造成資料 compare 不一致,或者 ods 輸出會出現莫名的換行或符號
解決:
可以先了解有哪些 non-printable 的字碼,可參考
ascii-table

ASCII extended sets characters 有兩種編碼

  • 十進位編碼 0 to 255 (decimal values)
  • 六進位編碼 00 to FF (hexadecimal values)

但一般的 ASCII table (不含extended sets) 是 0 to 127
因此前 33 個 (0 to 32) and character 127 are the non-printable characters

例如: HT ( Horizontal Tabulation )
就是鍵盤上的 tab
用十進位編碼來看是 9 號,可按鍵盤 tab 或者 ALT 數字鍵盤9
用六進位編碼是 09 ,在 SAS 裡用 '09'x

反過來說,要看變項值的 Hexadecimal

data a; a='!'; put a= a hex. ; run; 

第一、要檢查出來

  • 用 sas function 的 NOTPRINT(string <,start>) 如果是 NOTPRINT 回傳 1

依SAS原廠的範例,來看 132, 127254 被 NOTPRINT 定義為 non-printable

data test; 
  do dec=0 to 255;  
    byte=byte(dec);  
    hex=put(dec,hex2.);  
    notprint=notprint(byte);  
    output;  
  end;  

proc print data=test; run;

ref: SAS(R) 9.2 Language Reference: Dictionary, Fourth Edition

  • 用 prxmatch 等搜尋去找。 regular expression 輸入 hexadecimal values 是用 \x00

    data test2; set test;
    byte_re=put(byte, $hex2.);
    kk=prxmatch("/00|01|02|03|04|05|06|07|08|09|0A/", byte_re);
    kk2=find(byte,'00'x);
    kk3=prxmatch("/[^\x00\t\x20-\x7E]/", byte); /自定義查找/
    run;

第二、進行轉換

  • translate 取代1個字,就會留下1個位置,即原本9個不能換成8個。
    更可以1次把多個字作取代 f2_translate=translate(f1,' ','000102030405060708090A'x);

  • tranwrd 類似 translate 但只能1次換1個。

  • transtrn 1次換1個,但可以原本9個換成8個。

    data example;
    length f1 f2_translate f3_tranwrd f4_transtrn $20;
    f1 = cat('Part1','0A'x,'Part2');
    f2_translate = translate(f1,' ','0A'x);
    f3_tranwrd = tranwrd(f1,'0A'x,' ');
    f4_transtrn = transtrn(f1,'0A'x,trimn(''));
    run;

ref: https://programmer-pro.com/special-character-sas-hex/

2018年6月12日 星期二

AUTOEXEC相關

說明:要調整 sas work library 的位置。

改變 work library 的實體位置

  • 直接在電腦左下「程式集」按右鍵修改 SAS 的捷徑,在最尾端加上 -WORK "d:\SAS Temporary Files"
  • 或是去改個人設定檔,先查設定檔在哪
    proc options option=config;run;
    查看 log 應可見 CONFIG=C:\Program Files\SASHome2\SASFoundation\9.4\nls\zt\sasv9.cfg 之類的
    用文字編輯器打開加入以下
    -WORK "d:\SAS Temporary Files"

上面 proc option 的地方,若想做成 macro variable 可用
%put %SYSFUNC( GETOPTION( CONFIG )) ;
想完整列出所有option 用
proc options host;run;

ref: SAS System Options by Category
http://support.sas.com/documentation/cdl/en/hostwin/69955/HTML/default/viewer.htm#n0qn87565ybxoun12srorc6xhpz5.htm


說明:建立一個program,把開啟SAS想要 pre-loading 的相關程式,都納入,方便管理

預載 program

  • 依前項 work 的改法加入

-AUTOEXEC "D:\99_Tool\OneDrive\SAS_self\mysas\AUTOEXEC.SAS"
可以查詢 proc options option=Autoexec; run;

  • 或是所有想載入的所有程式放進 AUTOEXEC.SAS 裡帶入
    %inc "program path" / source2 ;
    上面 source2 會把原本的程式 put 出來

ref: Store and Recall Macros with SAS Macro Libraries


說明:單純預載需要的 macro

預載需要的 macro 有以下方法

  1. 直接用 %inc
  2. 讀取 compiled sas macro
  3. Autocall library

1. 直接用 %inc
若是同 folder 多個以 sas as the file extension 的 program ,可以用

filename files "program folder";  
%include files(name_of_program 1);
%include files(name_of_program 2);

2. 讀取 compiled sas macro

第1.設定 System options to store 以及 Macro options to store ,做出 compiled sas macro , dlcreatedir 可用來建立 folder

options dlcreatedir mstored sasmstore=one;  
libname one "d:\test";  
%Macro DAY1() / store source des="a test for store macro";  
  %put 今日是 &sysdate;  
%Mend DAY1;  
%Macro DAY2() / store source des="a test for store macro";  
  %put 明日是 %sysfunc(intnx(DAY, "&sysdate"d ,1),DATE7.);  
%Mend DAY2;  

第2.下次要用時,也要設定 mstored sasmstore

libname one "d:\test";  
options mstored sasmstore=one;  
%DAY1();  
%DAY2();  

若有多個 lib 可以用

options mstored sasmstore=ALL_LIB;  
libname one "d:\test";  
libname two "d:\test\two";  
libname ALL_LIB (one two);  

第3.可用以下做個確認

proc catalog catalog=one.sasmacr; 
  contents; 
quit;

第4.這類 compiled sas macro ,可以用 %copy 在 log 取得原始碼

options mstored sasmstore=one;  
%copy DAY1 /source;  

3. Autocall library

第1.建立好自己的 macro ,並確認沒有 store source

%Macro DAY1() /  des="a test for store macro";
%put 今日是 &sysdate;
%Mend DAY1;

第2.設定 System option to recall

Options mautosource sasautos=one;  
filename one 'd:\test';   
*;   
%DAY1();    

這兒 macro 的 compile 是發生在第4行時(叫出來用),並存在WORK裡。前項 folder 之下所有 sas file 的 macro 都可直接呼叫。
注意,如果第一次呼叫失敗,修改好程式後,要把 sas 關了再開重跑。
所以若原本有 store source ,那就有額外 option 要處理,我猜測是 mstored sasmstore 以及 sasmstore 的 library 之類的。

第3. 多個位置的應用。用逗號或 space 分開

Options mautosource sasautos= (one,two) ;  
filename one 'd:\test';   
filename two 'd:\test\two';   
*;   
%DAY1();  
*;   
%DAY2();  

整體來說,前3個方法可以用在同1個 program 裡,它們的 search sequence 為

  1. current session 做出來的 (WORK.SASMACR) ,如 %inc 或當下寫的
  2. compiled sas macro
  3. Autocall library
  4. SASHELP

ref: Store and Recall Macros with SAS Macro Libraries


說明:單純預載需要的 format

預載 format

  • 用 %inc 來 reload
  • 不reload的方式

不reload的方式

第1.前次 format 再建立時要指定輸出的 LIBRARY

options dlcreatedir;  
libname one "d:\test";  
PROC FORMAT LIBRARY=one PAGE;
value $sex ‘M’=’Male’
           ‘F’=’Female’;
run;

那個 PAGE 是同時把 format print 出來看

第2.後續要用時

libname one "d:\test";  
proc format library=one; run;  

第3.如果想 check

proc catalog catalog=one.formats;  
  contents;  
quit;
proc format library = one.formats;
  select $sex;
run;

ref: Base SAS® 9.4 Procedures Guide,

2018年6月11日 星期一

建資料或讀資料

說明:資料本身有 contain semicolons

Use the DATALINES4 statement 範例如下

libname two "d:\test\two";  
filename file1 "d:\test\two\DAY1.sas";  
data a ;  
  length code $100.;  
  input code &  $ ;  
  file file1 ;  
  put code $ ;  
datalines4;  
%Macro DAY1() / store source des="a test for store macro";  
  %put 今日是 &sysdate;  
%Mend DAY1;   
;;;;   
run;  

input code & 這裡面的 & 很重要,沒放的話,遇到 space 就不讀了,變成
%Macro
%put
%Mend

ref: Store and Recall Macros with SAS Macro Libraries

2018年3月27日 星期二

Array的應用

問題:在 data step 中去處理一大串的變項或數值
說明:
用 Array 來處理,基本架構是
array array-name <$> array-elements <(initial-values)>;
array-elements 可以用

  • list 變項名稱,但是大原則是這些 list 要全為 numeric 或 character ,不能 numeric 或 character 混著用
  • special variable, 如 NUMERIC CHARACTER ALL 。 會直接挑 data set 全部的 numeric 或 character 或全部。
  • 上面都會建立實體的 variable ,但 Temporary 則不會,只會放在 Program data vector。

data a; array A {10} ; array nums {3:10} temporary (90, 91,92 ,93 ,94, 95, 96, 40); do i =1 to dim(A); if i<3 then A=i10; else A=i10+nums; end; run;

就不必寫 array 來找

data a; a=40; b=whichn(a, 90, 91,92 ,93 ,94, 95, 96, 40); put a / b ; run;

IMPLICIT SUBSCRIPTING array-name 改成了array-name (n) 的 n 不放 * 不放 數值,並且放了個空變項,這是舊版SAS的用法 DATA AGE_; z=100;put z; array item(z) age1989 age1993 age1996 age1999 age2003 age2007 age2011 age2015(1989 1993 1996 1999 2003 2007 2011 2015); do over item; put item z ; item=item+1000; put item z ; end; RUN;

INVALID INDEX RANGE DATA AGE_; array item() age1989 age1993 age1996 age1999 age2003 age2007 age2011 age2015 (1989 1993 1996 1999 2003 2007 2011 2015); array X(8); ii=1; do until ( ii gt 8); / do until ( ii ge 8); 不行到8就停了 X8就做不出來*/

put "from 1st put   " ii;
x(ii)=item(ii)+1000;    
ii=ii+1;
put "from 2nd put   " ii;

end; RUN;

FUNCTION NAME AS AN ARRAY NAME ARRAY REFERENCED IN MULTIPLE DATA STEPS, BUT DEFINED IN ONLY ONE

ref: Arrays Made Easy An Introduction to Arrays and Array Processing


/Chapter 15 Processing Variables with Arrays/ /* ARRAY array-name ; An ARRAY statement is not an executable statement; it merely defines an array. 全數值變項 或 全文字變項 array test {4} n1 n2 n3 n4; array test [10:13] n1 n2 n3 n4; array test (*) n1- n4; do i=1 to 4 do i=1 to dim(test)
elements 也可用 NUMERIC CHARACTER ALL Temporary 。 資料檔中沒有 element 列出 的變項,會自動建立該變項 不寫element,系統會自行產生新變項。

*/

data test_array; Array L[4] spring summer fall winter; Array K[5] ; run; /Arrays of Character Variables 各變項預設為$8. / array Chars {5} $ 24; /Assigning Initial Values to Arrays/ data test_array; Array L[5] spring summer fall winter all (1 2 3 4 5); Array K[4] Temporary (11 12 13 14 ) ; Array Z [4]; Do i=1 to dim(z); Z[i]= 1000L[i+1] +100L[i] + K[i] ; End; run; /Multidimensional Arrays/ DATA test_array2 ; array new{3,4} x1-x12; /3 rows, 4 columns/ new(2,3)=0; run; /* x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 */

/Referencing Elements of a Two-Dimensional Array/ data finance.quarters(drop=i j); set finance.monthly; array m{4,3} month1-month12; array Qtr{4}; do i=1 to 4; /*當 i=1,M1j 就全進到 PDV了 */ qtr=0; do j=1 to 3; /*j=1,M11加總入qtr1 / qtr+m{i,j}; end; end; run; / month1 month2 month3 month4 month4 month4 month7 month7 month7 month10 month10 month10 / /資料轉秩/ DATA trans; input id spring summer fall winter ; datalines; 100 0 5 10 15 101 20 25 30 35 102 40 45 50 55 103 60 65 70 75 ; data trans2 ; set trans; array contrib{4} spring summer fall winter ; do i =1 to 4; Value=contrib[i]; output; end; run; proc print ; var id i value ;run; / id i Value 100 1 0 100 2 5 100 3 10 100 4 15 101 1 20 101 2 25 101 3 30 101 4 35 */ /quiz/ data coat; input category high1-high3 / low1-low3; array compare{2,3} high1-high3 low1-low3; do i=1 to 2; do j=1 to 3; compare{i,j}=round( compare{i,j} * 1.12 ); end; end; datalines; 5555 9 8 7 6 4 3 2 1 8888 21 12 34 64 13 14 15 16 ; run;

/* 篩選多個變項,是否有某範圍的值 */ data k ; input a1 a2 a3 ; cards; 10 20 99 1 2 20 99 99 99 5 99 99 ;

data k2 ; set k ; Array k (*) a1-a3; DO i = 1 to 3 ; if 0<k(i) <10 then x1=1; if 0<k(i) <10 then x2=sum(X2 , 1 ); if 0<k(i) <10 then x3+1; end; run;


問題: 由大到小 作 iteration 正確:要加 by 數值
do i3 =dim(AGREE) to 2 by -1; end;

一時忘了,用以下就不行,無法反向
do i3 =dim(AGREE) to 2 ; end;

2018年3月14日 星期三

excel方便用

問題:有兩個變項清單,要如何確認A清單所列變項,有在B清單上
說明:
VLOOKUP(B清單,A清單,1,0)
如果有,公式會回傳對得到的變項名稱;如果沒有,回傳 #N/A
搭配 if() 和 isna() 會更好用,讓連不到的回傳空
=IF(ISNA(VLOOKUP(B清單,A清單,1,0)),"",VLOOKUP(B清單,A清單,1,0))


2018年2月23日 星期五

SQL應用

問題:要一次把整個 dataset variables 都 rename
說明:
ref: Renaming All Variables in a SAS Data Set Using the Information from PROC SQL's Dictionary Tables

options macrogen mprint mlogic ;
%macro rename(lib,dsn);
  options pageno=1 nodate ;
  proc contents data=&lib..&dsn;
    title "Before Renaming All Variables";
  run;
  proc sql noprint;
    select nvar into :num_vars
    from dictionary.tables
    where libname="&LIB" and memname="&DSN";
    select distinct(name) into :var1-:var%TRIM(%LEFT(&num_vars))
    from dictionary.columns where libname="&LIB" and memname="&DSN";
  quit;

  proc datasets library=&LIB;
    modify  &DSN;
    rename
      %do i=1 %to &num_vars;
      &&var&i=NEWNAME_&&var&i.  %end;
     ;
  quit;
  options pageno=1 nodate;
  proc contents data=&lib..&dsn;
    title "After Renaming All Variables";
  run;
%mend rename;
%rename(WORK,ONE);

以上的用法有2個重點

  • 應用 sas 系統 library SASHELP 的 dictionary tables
  • sql 的 into: 可產生 macro variable
    適用只有1列資料
    例如 select NAME into: source_err from dataset
    若有n列 NAME 希望得到 &source_err 為 n1,n2,n3 就要下 separated by
    例如 select NAME into: source_err separated by ',' from dataset
    若有n列 NAME 希望得到 n 個 macro variable 就如下
    select distinct(name) into :var1-:varN

另外,能用 dictionary tables 的話也可以直接套 call execute() 去改 data set


2018年1月10日 星期三

macro應用

問題:把一個數字套用format容易,但若要用macro facility來處理呢?
說明:
在data step要套用format,直覺想到以下

data a;
  a=1.5;
  b=put(1.5,z5.2);
  put b; 
  put a z5.2 ;
run;

若要把上面put出來的01.50弄成macro variable,需要再套一個call symput指令。
後來摸過%SYSFUNC(),它可以套用dataset的function,就摸索出以下方式

  • %put %SYSFUNC( abs(1.5), z5.2) ;放一個能得到1.5的function,用簡單的abs就好
  • %put %SYSFUNC( putn(1.5,z5.2)) ;

但請注意下列這種無法用

  • %put %SYSFUNC(1.5, z5.2) ; 這個錯在sysfunc沒有指定function,只有給1.5。
  • %put %SYSFUNC( put(1.5,z5.2)) ; 目前不知為何,只知道sysfunc要配putn,putc,inputn,inputc才能用

也可以把要指定的format格式弄成macro variable,方便後續開發運用,如以下的例子

%MACRO now( fmt= DATETIME23.3 ) /DES= 'timestamp';  
  %SYSFUNC( DATETIME(), &fmt )  
%MEND  now ;  
%put %now;

ref: The Big Introduction from the Smallest Macro

這篇有一些東西還沒仔細看,A user-defined format that creates a date-time string that import wizards treat as a value.

PROC FORMAT ; 
   PICTURE  
       u_dt .= ' ' 
       OTHER ='%0Y/%0m/%0d %0H:%0M:%0S'( DATATYPE= DATETIME ); 
RUN ; 

問題:Quotes within Quotes ,有時候會錯亂? 說明:
下面例子呈現data step幾個原則

%let saying = Nevermore; 
data _null_; 
 Nevermore='test';
 
 a1='&saying';
 a1A_1='"&saying"';
 a1A_2='""&saying""';
 a1A_3='"""&saying"""';
 a1B_1='''&saying''';
 a1B_2='''''&saying''''';
 a1B_3='''''''&saying''''''';

 a2="&saying";
 a2A_1="'&saying'";
 a2A_2="''&saying''";
 a2A_3="'''&saying'''";
 a2B_1="""&saying""";
 a2B_2="""""&saying""""";
 a2B_3="""""""&saying""""""";

 put (a:) (=/);
run;

/*Log output 如下*/

a1=&saying
a1A_1="&saying"
a1A_2=""&saying""
a1A_3="""&saying"""
a1B_1='&saying'
a1B_2=''&saying''
a1B_3='''&saying'''
a2=Nevermore
a2A_1='Nevermore'
a2A_2=''Nevermore''
a2A_3='''Nevermore'''
a2B_1="Nevermore"
a2B_2=""Nevermore""
a2B_3="""Nevermore"""
  • 當最外層quotes是single quote,裡面的macro variable 就無法被 resolve,如a1系列。最外層是 double quote ,macro variable 就有可能被resovle,如 a2A_1。
    作者對此的註解是 during the assignment of the variable’s value, the inner single quotes are masked, not seen as parsing characters, so the macro variable resolves.
  • 當最外層quotes與內層quotes是同一種quote,在內層的quote就必須用連續2個quote為單位,此時,會把連續2個quote轉成1個quote,如對照一下 a1A_1 和 a1B_1 ,可發現雖然最後都是1個 single or double quote,但是 a1B_1 的內層是2個 quote ; 比較 a1A_3 a1B_3 就更明顯,a1B_3用了6個。
    要注意,連續2個quote轉成1個quote,此件事發生的時機為: when the parser sees two single (or double) quotes immediately following each other, the parser resolves them into one quote mark after the closing quote has been determined.

下面的例子,就只有 note1 的結果會和 note0 相同。

data _null_; 
 note0 = "The raven sayth: 'Nevermore'"; 
 note1 = "The raven sayth: '&saying'"; 
 note2 = 'The raven sayth: ''&saying'''; 
 note2_1 = 'The raven sayth: ''''&saying'''''; 
 note3 = "The raven sayth: ""&saying"""; 
 put (note:) (=/); 
run;

其他文章裡摘錄的重點

  • 要產生一個title 為 Tom's Truck 可用 title1 "Tom's Truck"; 或者是 title1 'Tom''s Truck';
  • 以前 X command 在 windows 系統下有點麻煩,X command 要用quote, 而 windows 的 path 一定要用 double quote,造成冏境 x 'dir "c:\&temp\*.sas"' ;
    &temp無法被 resolve。
    但現在X command 本身不必用 quote 了,可省下最外層的single quote。
  • DM STATEMENT 一定要有 quote,但 sas 9 開始它 can contain macro variables,並且第1個 quote 是用於把指令轉傳給 Display manager 去處理,因此 dm 'log; file "c:\&temp\logdump1.log"'; Display manager 只收到 log; file "c:\&temp\logdump1.log" , 當然可以 resovle &temp。 另個例子,dm "log; file 'c:\&temp\logdump1.log'"; 稍有不同,因為 file 收到的是 'c:\&temp\logdump1.log' ,第1次沒有轉,但因 file 會把 quote 當成 parsing characters within the command 所以 &temp 又可以轉了。
  • CALL DEFINE STATEMENT在用時,call define(_col_,'style', 'style={flyover="&temp Mean WT"}'); 是可以被 resovle 的。
  • FILENAME STATEMENT
    想要下的指令 filename tmpdat pipe 'dir "c:\&temp\*.sas" /o:n /b'; 但它無法被 resolve。本例的必要條件有2個,1. pipe 之後的 dir... 一定要放在 quotation mark 裡面,2. dir 之後的 path 一定要放在 double quotes 裡面。解法如下...

Using the DATA Step Quote Function
用 sysfunc + Quote Function 來替代條件1,最簡單
filename tmpdat pipe %sysfunc(quote(dir "c:\&temp\*.sas" /o:n /b));

Using Repeated Quote Marks
把外層換成 double quotes ,內層換成 doubled double quotes ,如此 the interior string is marked in a second pass of the parser, after the macro variable has been resolved.
filename tmpdat pipe "dir ""c:\&temp\*.sas"" /o:n /b";

Using Macro Quoting Functions
針對外層的 single quote ,用 macro quoting function 來處理,這有很多種,並且每種用途不太相同,常用的有

  • %STR(%')
  • %BQUOTE()

例如可把原式改為
filename tmpdat pipe %bquote(')dir "c:\&temp\*.sas" %bquote(/o:n /b'); 但這會有錯,因為 single quote 被蓋著,沒有 remove macro quoting ,後面的 dir 就被當成 unquoted option ,不符合條件1
可用 %UNQUOTE() 把整個 dir command 包起來處理
filename tmpdat pipe %Unquote(%bquote(')dir "c:\&temp\*.sas" %bquote(/o:n /b'));
整包送進去 macro facility 處理,最後要離開時會得到'dir "c:\My Loc\*.sas" /o:n /b',這串才能正確接著 filename statement 。具有 %LEFT %TRIM 也可以 remove macro quoting 但應用的時機不同。

ref: Quotes within Quotes When Single (‘) and Double (“) Quotes are not Enough


說明:put出日期有關的資訊

%put &sysdate.;
%put %sysfunc(today(),date9. );

%put &systime. ;
%put %sysfunc(time(),time5. );

%put %sysfunc(datetime(),datetime20. );


說明: CALL EXECUTE made easy for SAS data-driven programming

文中指出,基本的概念是:
As the DATA step iterates, the code is appended to the queue as many times as there are iterations of the DATA step. After the DATA step completes, the code in the queue gets executed in the order of its creation (First In First Out).

有 macro reference 的情況

  • macro reference in double quotes
    they will be resolved by the SAS macro pre-processor during the DATA step compilation. Nothing unusual.

  • macro reference in single quotes
    will be resolved by CALL EXECUTE itself. 同上也是在 pushed out of the DATA step 之前就會完成。
    因為 CALL EXECUTE 具有 macro resolution privilege

  • CALL SYMPUT or SYMPUTX statement (in a DATA step) or an INTO clause (in PROC SQL)
    WARNING: Apparent symbolic reference VARLIST not resolved.
    它們的 macro variable 還沒真的建立之前,就被要求要 resovle
    解決的方式可以直接把整個 statement 用 %nrstr 包起來 ,如:
    call execute('%nrstr(%onetable('!!strip(tname)!!'));');
    強迫推到 queue 裡,之後再 resolve macro
    NOTE: CALL EXECUTE generated line.
    1 + %onetable(ADDRESS);

  • 把 MACRO 的名稱也用 dataset variable 代入
    arg=cats('%nrstr(%', MNAME_variable, '(parameter=', Para, '))' );
    call execute(arg);

ref: https://blogs.sas.com/content/sgf/2017/08/02/call-execute-for-sas-data-driven-programming/