基于包含元数据的文件构建SAS数据集
Construct SAS dataset based on file containing metadata
我有两个文本文件,一个包含没有 headers 的原始数据,另一个包含相关的列名和长度。我想使用这两个文件构建一个 SAS 数据集,其中包含一个文件中的数据以及另一个文件中的列名和长度。
包含数据的文件是 fixed-width 文本文件。也就是说,每列数据都与文本文件的特定列对齐,并用 space 填充以确保对齐。
datafile.txt:
John 45 Has two kids
Marge 37 Likes books
Sally 29 Is an astronaut
Bill 60 Drinks coffee
包含元数据的文件 tab-delimited 有两列:一列是数据文件中列的名称,一列是该列的字符长度。名称按照它们在数据文件中出现的顺序列出。
metadata.txt:
Name 7
Age 5
Comments 15
我的目标是拥有一个如下所示的 SAS 数据集:
Name | Age | Comments
-------+------+-----------------
John | 45 | Has two kids
Marge | 37 | Likes books
Sally | 29 | Is an astronaut
Bill | 60 | Drinks coffee
我希望每个列都是具有元数据文件中指定长度的字符。
必须有比我天真的方法更好的方法,即使用导入的元数据构造一个 length
语句和一个 input
语句,如下所示:
/* Import metadata */
data meta;
length colname $ 50 collen 8;
infile 'C:\metadata.txt' dsd dlm='09'x;
input colname $ collen;
run;
/* Construct LENGTH and INPUT statements */
data _null_;
length lenstmt inptstmt $ 1000;
retain lenstmt inptstmt '' colstart 1;
set meta end=eof;
call catx(' ', lenstmt, colname, '$', collen);
call catx(' ', inptstmt, cats('@', colstart), colname, '$ &');
colstart + collen;
if eof then do;
call symputx('lenstmt', lenstmt);
call symputx('inptstmt', inptstmt);
end;
run;
/* Import data file */
data datafile;
length &lenstmt;
infile 'C:\datafile.txt' dsd dlm='09'x;
input &inptstmt;
run;
这让我得到了我需要的东西,但必须有一种更简洁的方法。如果没有足够的 space 分配给存储 length
和 input
语句的变量,或者如果语句长度超过最大宏变量长度,那么使用这种方法可能 运行 会遇到麻烦.
有什么想法吗?
call execute show 可以提供帮助。
data _null_;
retain start 0;
infile 'c:\metadata.txt' missover end=eof;
if _n_=1 then do;
start=1;
call execute('data final_output; infile "c:\datafile.txt" truncover; input ');
end;
input colname :.
collen :8.
;
call execute( '@'|| put(start,8. -l) || ' ' || colname || ' $'|| put(collen,8. -r) ||'. ' );
start=sum(start,collen);
if eof then do;
call execute(';run;');
end;
run;
proc contents data=final_output;run;
您正在做的是一种相当标准的方法。是的,您可以更仔细地检查一下;例如,为了谨慎起见,我会为这两个语句分配 767
。
不过,您可以通过一些方法来改善这一点,这可能会消除您的一些担忧。
首先,一个常见的解决方案是在行级别构建它(就像您所做的那样),然后使用 proc sql
创建宏变量。这比数据步长方法有更大的最大长度限制(如果不使用多个变量,数据步长方法最大值为 767
,SQL 是 64kib 时的两倍)。
proc sql;
select catx(' ',colname,'$',collen)
into :lenstmt separated by ' '
from meta; *and similar for inputstmt;
quit;
其次,您可以通过写入文件而不是宏变量来超过 64k 的限制。采取你的数据步骤,而不是积累然后使用 call symput
,将每一行写到一个 temp
文件(或两个)。然后 %include
这些文件而不是在输入数据步中使用宏变量 - 是的,您可以 %include
在数据步的中间。
还有其他方法,但这两种是最常见的,应该适用于大多数用例。其他一些方法包括 call execute
、run_macro
或使用文件打开命令直接使用文件。总的来说,它们要么比最常见的两个更复杂,要么用处不大,尽管它们当然也是可以接受的解决方案,并且在实践中并不少见。
我有两个文本文件,一个包含没有 headers 的原始数据,另一个包含相关的列名和长度。我想使用这两个文件构建一个 SAS 数据集,其中包含一个文件中的数据以及另一个文件中的列名和长度。
包含数据的文件是 fixed-width 文本文件。也就是说,每列数据都与文本文件的特定列对齐,并用 space 填充以确保对齐。
datafile.txt:
John 45 Has two kids
Marge 37 Likes books
Sally 29 Is an astronaut
Bill 60 Drinks coffee
包含元数据的文件 tab-delimited 有两列:一列是数据文件中列的名称,一列是该列的字符长度。名称按照它们在数据文件中出现的顺序列出。
metadata.txt:
Name 7
Age 5
Comments 15
我的目标是拥有一个如下所示的 SAS 数据集:
Name | Age | Comments
-------+------+-----------------
John | 45 | Has two kids
Marge | 37 | Likes books
Sally | 29 | Is an astronaut
Bill | 60 | Drinks coffee
我希望每个列都是具有元数据文件中指定长度的字符。
必须有比我天真的方法更好的方法,即使用导入的元数据构造一个 length
语句和一个 input
语句,如下所示:
/* Import metadata */
data meta;
length colname $ 50 collen 8;
infile 'C:\metadata.txt' dsd dlm='09'x;
input colname $ collen;
run;
/* Construct LENGTH and INPUT statements */
data _null_;
length lenstmt inptstmt $ 1000;
retain lenstmt inptstmt '' colstart 1;
set meta end=eof;
call catx(' ', lenstmt, colname, '$', collen);
call catx(' ', inptstmt, cats('@', colstart), colname, '$ &');
colstart + collen;
if eof then do;
call symputx('lenstmt', lenstmt);
call symputx('inptstmt', inptstmt);
end;
run;
/* Import data file */
data datafile;
length &lenstmt;
infile 'C:\datafile.txt' dsd dlm='09'x;
input &inptstmt;
run;
这让我得到了我需要的东西,但必须有一种更简洁的方法。如果没有足够的 space 分配给存储 length
和 input
语句的变量,或者如果语句长度超过最大宏变量长度,那么使用这种方法可能 运行 会遇到麻烦.
有什么想法吗?
call execute show 可以提供帮助。
data _null_;
retain start 0;
infile 'c:\metadata.txt' missover end=eof;
if _n_=1 then do;
start=1;
call execute('data final_output; infile "c:\datafile.txt" truncover; input ');
end;
input colname :.
collen :8.
;
call execute( '@'|| put(start,8. -l) || ' ' || colname || ' $'|| put(collen,8. -r) ||'. ' );
start=sum(start,collen);
if eof then do;
call execute(';run;');
end;
run;
proc contents data=final_output;run;
您正在做的是一种相当标准的方法。是的,您可以更仔细地检查一下;例如,为了谨慎起见,我会为这两个语句分配 767
。
不过,您可以通过一些方法来改善这一点,这可能会消除您的一些担忧。
首先,一个常见的解决方案是在行级别构建它(就像您所做的那样),然后使用 proc sql
创建宏变量。这比数据步长方法有更大的最大长度限制(如果不使用多个变量,数据步长方法最大值为 767
,SQL 是 64kib 时的两倍)。
proc sql;
select catx(' ',colname,'$',collen)
into :lenstmt separated by ' '
from meta; *and similar for inputstmt;
quit;
其次,您可以通过写入文件而不是宏变量来超过 64k 的限制。采取你的数据步骤,而不是积累然后使用 call symput
,将每一行写到一个 temp
文件(或两个)。然后 %include
这些文件而不是在输入数据步中使用宏变量 - 是的,您可以 %include
在数据步的中间。
还有其他方法,但这两种是最常见的,应该适用于大多数用例。其他一些方法包括 call execute
、run_macro
或使用文件打开命令直接使用文件。总的来说,它们要么比最常见的两个更复杂,要么用处不大,尽管它们当然也是可以接受的解决方案,并且在实践中并不少见。