SAS - 读取多个csv文件的第一次和最后一次观察

时间:2017-06-29 12:09:21

标签: csv input file-io sas eof

我想阅读存储在Linux机器上一个文件夹中的大量.csv文件(几千兆字节)的第一个和最后一个记录。假设它们被称为have1.csv, have2.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 @的概念或相互作用,请告诉我!我不知道我的错误在哪里......

3 个答案:

答案 0 :(得分:2)

如果要在INFILE语句中使用通配符,可以使用EOV =选项创建一个变量,该变量将在新文件启动时进行标记。请注意,您需要手动重置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 :(得分: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;

实际上,正如下面的Tom描述的那样,不需要while循环(危险的东西:-))。 我现在修改了代码: (需要添加RETAIN,因为我们正在循环数据步骤本身)

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;

答案 2 :(得分: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;