SAS——读取多个csv文件的第一个和最后一个观察
SAS -- Read first and last observation of multiple csv files
我想读取 Linux 机器上一个文件夹中存储的大量 .csv 文件(几千兆字节)的第一条和最后一条记录。假设他们被称为have1.csv, have2.csv, ...
等等。
所以我尝试了下面的代码,它只给了我第一行。但不是最后一行。
%let datapath = ~/somefolder/;
data want;
length finame 0.;
/*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 @ 的概念或相互作用,请告诉我!我不知道我的错误在哪里...
这似乎对我有用,请尝试一下:
data want;
length finame 0.;
/*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 0.;
/*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;
如果您想在 INFILE 语句中使用通配符,您可以使用 EOV= 选项创建一个变量,该变量将在新文件启动时进行标记。请注意,您需要手动重置 EOV 标志。
在读取值之前读取并按住该行,以便您可以测试是否已启动新文件。这样你就可以输出前一个文件的最后一行。您还需要保留输入变量,以便上一个文件最后一行的值可用。
您还需要使用 END= 选项才能输出最后一个文件的最后一行。
示例:
data want ;
retain filename str;
length fname filename 0 ;
infile '/dir1/file*' filename=fname eov=eov end=eof truncover ;
input @;
if eov then output;
filename=fname ;
input str . ;
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
如果您想跳过每个文件的第一行(header 行),请在 input @;
语句之后添加此语句。
if _n_=1 or eov then input;
请注意,如果您的输入文件可能没有至少两条数据线(三行算 header 行),您将需要调整逻辑。
如果你有文件列表,那么代码就更清晰了。例如,如果您可以使用 PIPE 引擎,则可以使用 ls(或 Dir)命令来获取文件名。然后使用 FILEVAR= 选项动态读取每个单独的文件。
data want ;
infile 'ls ~/test/dir1/file*' pipe truncover ;
input fname 0.;
filename=fname;
infile csv filevar=fname dsd truncover firstobs=2 end=eof ;
do _n_=1 by 1 while (not eof);
input str :. ;
if _N_=1 or eof then output;
end;
run;
或者如果您的文件很大,您可以利用 PIPE 使用 head
和 tail
命令来查找每个文件的开头和结尾,而无需让 SAS 读取整个文件.您可能需要测试它是否真的提高了性能。
data want ;
infile 'ls ~/test/dir1/file*' pipe truncover ;
input filename 0.;
length cmd1 cmd2 0 ;
cmd1='head -2 '||filename ;
infile top pipe filevar=cmd1 dsd truncover firstobs=2 end=eof1 ;
if (not eof1) then do;
input str :. ;
output;
end;
cmd2='tail -1 '||filename ;
infile bottom pipe filevar=cmd2 dsd truncover firstobs=1 end=eof2;
if (not eof2) then do;
input str :. ;
output;
end;
run;
我想读取 Linux 机器上一个文件夹中存储的大量 .csv 文件(几千兆字节)的第一条和最后一条记录。假设他们被称为have1.csv, have2.csv, ...
等等。
所以我尝试了下面的代码,它只给了我第一行。但不是最后一行。
%let datapath = ~/somefolder/;
data want;
length finame 0.;
/*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 @ 的概念或相互作用,请告诉我!我不知道我的错误在哪里...
这似乎对我有用,请尝试一下:
data want;
length finame 0.;
/*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 0.;
/*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;
如果您想在 INFILE 语句中使用通配符,您可以使用 EOV= 选项创建一个变量,该变量将在新文件启动时进行标记。请注意,您需要手动重置 EOV 标志。
在读取值之前读取并按住该行,以便您可以测试是否已启动新文件。这样你就可以输出前一个文件的最后一行。您还需要保留输入变量,以便上一个文件最后一行的值可用。
您还需要使用 END= 选项才能输出最后一个文件的最后一行。
示例:
data want ;
retain filename str;
length fname filename 0 ;
infile '/dir1/file*' filename=fname eov=eov end=eof truncover ;
input @;
if eov then output;
filename=fname ;
input str . ;
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
如果您想跳过每个文件的第一行(header 行),请在 input @;
语句之后添加此语句。
if _n_=1 or eov then input;
请注意,如果您的输入文件可能没有至少两条数据线(三行算 header 行),您将需要调整逻辑。
如果你有文件列表,那么代码就更清晰了。例如,如果您可以使用 PIPE 引擎,则可以使用 ls(或 Dir)命令来获取文件名。然后使用 FILEVAR= 选项动态读取每个单独的文件。
data want ;
infile 'ls ~/test/dir1/file*' pipe truncover ;
input fname 0.;
filename=fname;
infile csv filevar=fname dsd truncover firstobs=2 end=eof ;
do _n_=1 by 1 while (not eof);
input str :. ;
if _N_=1 or eof then output;
end;
run;
或者如果您的文件很大,您可以利用 PIPE 使用 head
和 tail
命令来查找每个文件的开头和结尾,而无需让 SAS 读取整个文件.您可能需要测试它是否真的提高了性能。
data want ;
infile 'ls ~/test/dir1/file*' pipe truncover ;
input filename 0.;
length cmd1 cmd2 0 ;
cmd1='head -2 '||filename ;
infile top pipe filevar=cmd1 dsd truncover firstobs=2 end=eof1 ;
if (not eof1) then do;
input str :. ;
output;
end;
cmd2='tail -1 '||filename ;
infile bottom pipe filevar=cmd2 dsd truncover firstobs=1 end=eof2;
if (not eof2) then do;
input str :. ;
output;
end;
run;