2017-06-29 23 views
0

我想讀取存儲在Linux機器上一個文件夾中的大量.csv文件(幾個千兆字節)的第一個和最後一個記錄。假設他們被稱爲have1.csv, have2.csv, ...等。SAS - 讀取多個csv文件的第一個和最後一個觀察結果

所以我試了下面的代碼,它只給了我第一行。但不是最後一行。

%let datapath = ~/somefolder/;  
data want; 

length finame $300.; 
/*Reference all CSV files in input data folder*/ 
infile "&datapath.have*.csv" delimiter="," 
     MISSOVER DSD lrecl=32767 firstobs=2 
     eov=eov eof=eof filename=finame end=done; 

/*Define input format of variables*/ 
informat Var1 COMMA. Var2 COMMA. Var3 COMMA.; 
/*Loop over files*/ 
do while(not done); 

    /*Set trailing @ to hold the input open for the next input statement 
     this is because we have several files */ 
    input @; 

    /*If first line in file is encountered eov is set to 1, 
     however, we have firstobs=2, hence all lines would be skipped. 
     So we need to reset EOV to 0.*/ 
    if eov then 
    do; 
     /*Additional empty input statement 
     handles missing value at first loop*/ 
     input; 
     eov = 2; 
    end; 
    /*First observation*/ 
    if eov=2 then do; 
     input Var1--Var3; 
     fname=finame; 
     output; 
     eov = 0; 
    end; 

     /*Last observation*/ 
     if 0 then do; 
      eof:  input Var1--Var3; 
        fname=finame; 
        output; 
     end; 
     input; 

end; 
stop; 

run; 

我非常感謝您的幫助!如果我誤解了infile,end,eov,eof和input @的概念或相互作用,請告訴我!我不知道我的錯誤是...

+1

您是否還試圖跳過標題行?那是關於FIRSTOBS = option的評論? – Tom

+0

是的,很抱歉沒有提前回復。 –

回答

1

如果你想在你可以使用EOV =選項來INFILE語句中使用通配符創建一個變量來標記新文件何時開始。請注意,您需要手動重置EOV標誌。

在讀取值之前閱讀並按住該行,以便可以測試新文件是否已啓動。這樣你可以輸出前一個文件的最後一行。您還需要保留輸入變量,以便前一個文件最後一行的值可用。

您還需要使用END =選項才能輸出最後一個文件的最後一行。

例子:

data want ; 
    retain filename str; 
    length fname filename $200 ; 
    infile '/dir1/file*' filename=fname eov=eov end=eof truncover ; 
    input @; 
    if eov then output; 
    filename=fname ; 
    input str $30. ; 
    if _n_=1 or eov or eof then output; 
    eov=0; 
run; 

輸出示例:

Obs filename  str 
1  /dir1/file1 Line1 
2  /dir1/file1 Line3 
3  /dir1/file2 Line1 
4  /dir1/file2 line4 
5  /dir1/file3 Line1 
6  /dir1/file3 Line3 

如果你想跳過每個文件的第一行(標題行)的input @;聲明之後添加此語句。

if _n_=1 or eov then input; 

注意,您將需要調整的邏輯,如果有可能,你的輸入文件並不都至少有兩個數據線(三線計數標題行)。

+1

這是一個很好的答案,這是因爲我的CSV文件不包含帶有變量名稱的標題行。不幸的是,我的...抱歉沒有說清楚。 但是:有沒有辦法跳過每個文件中的第一個觀察,以便PDV不會從第一行中的標題信息接收輸入?在這種情況下,我認爲你的解決方案與保留聲明實際上將工作... –

+0

end =選項似乎只捕獲最後一個文件的最後一行。無論如何,你證明沒有while循環需要,謝謝。 – vasja

+0

跳過標題行並不難。使用EOV標誌來了解何時需要跳過。 – Tom

1

這似乎爲我工作,請嘗試:

data want; 

length finame $300.; 
/*Reference all CSV files in input data folder*/ 
infile "E:\temp\test\have*.txt" delimiter="," 
     MISSOVER DSD lrecl=32767 
     eov=eov filename=finame end=done; 

     /* Note: firstobs option seems to work on first file only */ 

/*Define input format of variables*/ 
informat Var1 COMMA. Var2 COMMA. Var3 COMMA.; 

input; /* skip header in first file */ 

input Var1--Var3; /* read first real record in first file */ 
fname=finame; 
output; 

/* Loop over files*/ 
do while(not done); 

    input @;/* try input do determine eov condition */ 

    if eov then do;/* new file detected - we're on header record, but variables contain values from previous record - see "read values" */ 
     output; /* variables contain values from previous record - output those values */ 
     input; /* skip header */ 
     eov = 0; 
     input Var1--Var3; /* read first real observation */ 
     fname=finame; 
     output; /* first line of new file */ 
    end; 

    input Var1--Var3; /* read values - it might be last record */ 
end; 
output; /* output last record of last file */ 
run; 

其實,如下湯姆介紹,沒有必要爲while循環(危險的事情: - ))。 我現在已經修改了代碼: (需要添加保留,因爲我們在數據步驟本身循環)

data want; 

length finame $300.; 
/*Reference all CSV files in input data folder*/ 
infile "E:\temp\test\have*.txt" delimiter="," 
     MISSOVER DSD lrecl=32767 
     eov=eov filename=finame end=done; 

informat Var1 COMMA. Var2 COMMA. Var3 COMMA.; 
retain Var1 Var2 Var3 fname; 
if _N_ = 1 then do; /* first file */ 
    input; /* skip header in first file */ 
    input Var1--Var3; /* read first real record in first file */ 
    fname=finame; 
    output; 
end; 

input @; /* try input do determine eov condition */ 

if eov then do; /* new file detected - we've moved past header record, but variables contain values from previous record - see "read values" */ 
    output; /* variables contain values from previous record - output those values */ 
    input; /* skip header */ 
    eov = 0; 
    input Var1--Var3; /* read first real observation */ 
    fname=finame; 
    output; /* first line of new file */ 
end; 
else input Var1--Var3; 
if done then output; 
run; 
+0

這個很好用。輸入每一行但只輸出最後一行的技巧並沒有出現在我身上......很好地完成了!謝謝! :D –

+0

對不起。我無法給出兩個答案。湯姆是對的。他的代碼更清晰一點。但你的作品也很好。我被撕裂......感謝你們的巨大幫助! :D –

1

如果您有文件列表,則代碼更清晰。例如,如果您可以使用PIPE引擎,則可以使用ls(或Dir)命令來獲取文件名。然後使用FILEVAR =選項來動態讀取每個單獨的文件。

data want ; 
    infile 'ls ~/test/dir1/file*' pipe truncover ; 
    input fname $200.; 
    filename=fname; 
    infile csv filevar=fname dsd truncover firstobs=2 end=eof ; 
    do _n_=1 by 1 while (not eof); 
    input str :$30. ; 
    if _N_=1 or eof then output; 
    end; 
run; 

或者,如果你的文件很大,你可以利用使用PIPE使用headtail命令來查找每個文件的開頭和結尾,而不需要有SAS讀取整個文件。您可能需要測試以確定它是否實際提高了性能。

data want ; 
    infile 'ls ~/test/dir1/file*' pipe truncover ; 
    input filename $200.; 
    length cmd1 cmd2 $200 ; 
    cmd1='head -2 '||filename ; 
    infile top pipe filevar=cmd1 dsd truncover firstobs=2 end=eof1 ; 
    if (not eof1) then do; 
    input str :$30. ; 
    output; 
    end; 
    cmd2='tail -1 '||filename ; 
    infile bottom pipe filevar=cmd2 dsd truncover firstobs=1 end=eof2; 
    if (not eof2) then do; 
    input str :$30. ; 
    output; 
    end; 
run; 
+0

頭部和尾部解決方案不會更快。方式較慢。必須用find命令調整它''cd〜/ thepath; find。type -f -name「」* .csv「」-print「'生成列表... –

相關問題