在實務工作將資料中所有的欄位(變項)先以文字格式(character format)匯入,在做後續的分析是很實務的作法。為何會有這種作法?主要是SAS以PROC IMPORT匯入CSV檔案時,會預先猜測欄位的格式,在"自動"給予文字或數字格式,如果不幸某個欄位是以混合格式(mixed format)鍵入,SAS很有可能會採用數字格式匯入該欄位,而導致欄位中非數字的資料變成遺漏值(missing value)。
這篇小文章中我打算介紹三種方法來處理這樣的方法,如果你的時間不夠,請直接跳到方法三。
方法1: PROC IMPORT中使用GUESSINGROWS
使用GUESSINGROWS可以讓SAS在猜欄位格式時,多讀一點欄位後再猜測,預設為20列,最多可以設到2147483647列。
如果以【程式1】的PROC IMPORT程式匯入練習用檔案時,可看到欄位V2的第23筆資料為遺漏值。
【程式1】
PROC IMPORT OUT= WORK.data1
DATAFILE= "d:\TEMP\bigdata.csv"
DBMS=CSV REPLACE;
GETNAMES=YES;
DATAROW=2;
RUN;
在【程式1】中加入GURSSINGROWS並設定適當列數後,可順利將欄位V2以文字格式匯入【程式2】。為何要說適當列數,因為當設定的列數過多時,例如設為最大值,遇到大型的CSV檔案會花較長時間執行,寫程式的時候必須要斟酌考慮的。
【程式2】
PROC IMPORT OUT= WORK.data2
DATAFILE= "d:\TEMP\bigdata.csv"
DBMS=CSV REPLACE;
GETNAMES=YES;
DATAROW=2;
GUESSINGROWS=1000 ;
RUN;
方法2: 利用PROC IMPORT的LOG資料
這是一個偷吃步吧,當執行完PROC IMPORT後,至LOG視窗中,可以注意到有一段SAS幫大家寫好的匯檔程式,如下圖:
然後只要把從 data WORK.DATA2 一直到 run; (也就是程式行號50到68),中間的語法複製到程式編輯視窗,在把一些與巨集相關的語法刪除後(第51和67號),最重要將所有informat、format、input做適當的修改後,就可以順利把所有欄位都讀成文字格式了。
所謂適當修改就是把informat和format後面的格式改成 $數字. (有點),然後把input的欄位名稱後面補上 $ 。修改後程式於【程式3】
【程式3】
data WORK.DATA2 ;
infile 'd:\TEMP\bigdata.csv' delimiter = ',' MISSOVER DSD lrecl=32767 firstobs=2 ;
informat v1 $20. ;
informat v2 $20. ;
informat v3 $20. ;
informat v4 $20. ;
format v1 $20. ;
format v2 $12. ;
format v3 $20. ;
format v4 $20. ;
input
v1 $
v2 $
v3 $
v4 $
;
run;
此部分說明,也可參考 [SAS] 完整匯入Excel格式檔案 的介紹。
方法3: SAS MACRO匯入CSV資料
必須先說明,這個一段主程式不是我所寫的,主程式的來源為 http://stackoverflow.com/questions/14614369/import-all-columns-from-csv-as-character 這篇的解答。
這是一隻程式的功能就是將方法二所需要的步驟,利用MACRO自動撰寫匯入程式,因此非常的好用,有這個巨集後也不用在人工一個一個將欄位修改成文字格式,而原程式沒有考量欄位資料可能會多於8個字元,因此我稍作修改,增加可設定欄位長度。
【程式4】
%macro readcsv(dsn,fn,vl);
/* MACRO TO READ ALL COLUMNS OF A CSV AS CHARACTER */
/* PARAMETERS: */
/* DSN - THE NAME OF THE SAS DATA SET TO CREATE */
/* FN - THE EXTERNAL FILE TO READ (QUOTED) */
/* VL - THE VARIABLES LENGTH */
/* EXAMPLE: */
/* %READCSVE (WANT, 'C:\TEMP\TEMPFILE.CSV', 10); */ %let test_length=500;
data _null_;
infile &fn LRECL=32767;
input;
i = 1;
length headers inputstr lengthstr_test lengthstr $32767;
headers = compress(_infile_,"""'");
newvar = scan(headers,1,',');
do until (newvar = ' ');
inputstr = trim(inputstr) || ' ' || trim(newvar) || " $";
lengthstr= trim(lengthstr) || ' ' || trim(newvar) || " $&vl..";
i + 1;
newvar = scan(headers,i,',');
end;
call symput('inputstr',inputstr);
call symput('lengthstr',lengthstr);
stop;
run;
%put inputstr &inputstr;
%put lengthstr &lengthstr;
data &dsn;
infile &fn firstobs=2 dsd dlm=',' missover dsd lrecl=32767 firstobs=2;
length &lengthstr.;
input &inputstr.;
run;
%mend readcsv;
用法非常簡單,%readcsv( 輸出資料名稱, '資料路徑和名稱組合' , 欄位長度 ),如果要讀入範例的檔案,巨集的寫法為下;
%readcsv (data3, 'd:\TEMP\bigdata.csv', 50);
需要留意的事情是,因為欄位長度設定是所有欄位均適用,如果設定太長,資料筆數又多,匯入的資料檔會變成相當巨大,執行上也會很慢。
當然還是有人工解決辦法,先把長度設為適當數字,然後在執行巨集前先執行下面的系統指令
OPTION MPRINT;
在執行巨集後,到LOG視窗中,應該會看到一段匯入的程式,我想聰明的你應該知道要如何利用這段程式了。
如果您覺得這篇文章對您有幫助,文章旁的廣告您也剛好有興趣,請幫忙點擊廣告,讓我多一點寫下去的動力。感謝您的幫忙!
練習用資料和本文章使用程式語法: http://goo.gl/zo60Q
沒有留言:
張貼留言